WooCommerce Cart Abandonment Recovery - Version 1.2.10

Version Description

Download this release

Release Info

Developer cartflowswp
Plugin Icon 128x128 WooCommerce Cart Abandonment Recovery
Version 1.2.10
Comparing to
See all releases

Code changes from version 1.2.9 to 1.2.10

Files changed (37) hide show
  1. admin/assets/css/admin-cart-abandonment-rtl.css +430 -430
  2. admin/assets/css/admin-cart-abandonment.css +430 -430
  3. admin/assets/js/admin-email-templates.js +303 -303
  4. admin/assets/js/admin-mce.js +108 -108
  5. admin/bsf-analytics/Gruntfile.js +0 -210
  6. admin/bsf-analytics/assets/css/unminified/style-rtl.css +20 -20
  7. admin/bsf-analytics/assets/css/unminified/style.css +20 -20
  8. admin/bsf-analytics/bin/block-commits-with-merge-conflict.sh +0 -19
  9. admin/bsf-analytics/class-bsf-analytics-loader.php +118 -118
  10. admin/bsf-analytics/class-bsf-analytics.php +508 -508
  11. admin/bsf-analytics/composer.json +0 -22
  12. admin/bsf-analytics/composer.lock +0 -711
  13. admin/bsf-analytics/package-lock.json +0 -3978
  14. admin/bsf-analytics/package.json +0 -30
  15. admin/bsf-analytics/phpcs.xml.dist +0 -19
  16. admin/bsf-analytics/version.json +3 -3
  17. assets/images/cartflows-icon.svg +18 -18
  18. assets/images/cartflows-logo.svg +50 -50
  19. changelog.txt +113 -108
  20. classes/class-cartflows-ca-loader.php +382 -377
  21. classes/class-cartflows-ca-settings.php +819 -775
  22. classes/class-cartflows-ca-update.php +4 -0
  23. languages/woo-cart-abandonment-recovery.pot +896 -884
  24. lib/notices/class-astra-notices.php +360 -360
  25. modules/cart-abandonment/assets/js/cart-abandonment-tracking.js +166 -166
  26. modules/cart-abandonment/class-cartflows-ca-cart-abandonment-db.php +4 -0
  27. modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php +4 -0
  28. modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php +2276 -2266
  29. modules/cart-abandonment/class-cartflows-ca-email-templates-table.php +4 -0
  30. modules/cart-abandonment/class-cartflows-ca-email-templates.php +5 -0
  31. modules/cart-abandonment/class-cartflows-ca-module-loader.php +4 -0
  32. modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php +252 -247
  33. modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php +4 -0
  34. modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-tabs.php +4 -0
  35. readme.txt +245 -240
  36. uninstall.php +42 -0
  37. woo-cart-abandonment-recovery.php +24 -24
admin/assets/css/admin-cart-abandonment-rtl.css CHANGED
@@ -1,430 +1,430 @@
1
- .wcf-ca-ibox {
2
- clear: both;
3
- margin-bottom: 25px;
4
- margin-top: 0;
5
- padding: 0;
6
- }
7
-
8
- .wcf-ca-ibox-title {
9
- background-color: white;
10
- border-image: none;
11
- border-width: 3px 0px 0;
12
- color: inherit;
13
- margin-bottom: 0;
14
- padding: 14px 15px 7px;
15
- min-height: 48px;
16
- }
17
-
18
- .wcf-ca-ibox-content {
19
- background-color: white;
20
- color: inherit;
21
- padding: 15px 20px 20px 20px;
22
- border-color: #d7dadc;
23
- border-image: none;
24
- border-style: solid solid none;
25
- border-width: 1px 0px;
26
- }
27
-
28
- .wcf-ca-raw {
29
- margin-right: -15px;
30
- margin-left: -15px;
31
- }
32
-
33
- .wcf-ca-grid-container {
34
- display: grid;
35
- grid-template-columns: 1fr 1fr 1fr;
36
- grid-gap: 20px;
37
- }
38
-
39
- .grid-container > div {
40
- background-color: rgba(255, 255, 255, 0.8);
41
- text-align: center;
42
- padding: 20px 0;
43
- font-size: 30px;
44
- }
45
-
46
-
47
- .wcf-ca-center-msg {
48
- margin: auto;
49
- width: 50%;
50
- padding: 10px;
51
- margin-top: 20px;
52
- text-align: center;
53
- }
54
-
55
-
56
- .wcf-ca-switch {
57
- cursor: pointer;
58
- text-indent: -999em;
59
- display: block;
60
- width: 38px;
61
- height: 22px;
62
- border-radius: 30px;
63
- border: none;
64
- position: relative;
65
- box-sizing: border-box;
66
- -webkit-transition: all .3s ease;
67
- transition: all .3s ease;
68
- box-shadow: inset 0 0 0 0 transparent;
69
- }
70
- .wcf-ca-switch:focus {
71
- outline: none;
72
- }
73
- .wcf-ca-switch:before {
74
- border-radius: 50%;
75
- background: #ffffff;
76
- content: '';
77
- position: absolute;
78
- display: block;
79
- width: 18px;
80
- height: 18px;
81
- top: 2px;
82
- right: 2px;
83
- -webkit-transition: all .15s ease;
84
- transition: all .15s ease;
85
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
86
- }
87
- .wcf-ca-switch[wcf-ca-template-switch="on"] {
88
- box-shadow: inset 0 0 0 11px #008000;
89
- }
90
- .wcf-ca-switch[wcf-ca-template-switch="on"]:before {
91
- -webkit-transform: translateX(-16px);
92
- transform: translateX(-16px);
93
- }
94
- .wcf-ca-switch[wcf-ca-template-switch="off"] {
95
- background: #ccc;
96
- }
97
- .wcf-ca-switch.wcap-loading {
98
- cursor: default;
99
- opacity: 0.5;
100
- }
101
-
102
- .wcf-ca-trigger-input{
103
- height: 28px;
104
- width: 40%;
105
- margin-left: 10px;
106
- }
107
-
108
- .wcf-ca-report-btn {
109
- padding: 15px 0px 15px 0px;
110
- display: flex;
111
- width: 100%;
112
- position: relative;
113
- }
114
-
115
- .wcf-ca-email-inputs {
116
- width: 25%;
117
- }
118
-
119
- .wcf-ca-coupon-inputs {
120
- width: 10%;
121
- vertical-align: middle;
122
- }
123
-
124
- .wcf-ca-filter-input{
125
- height: 28px;
126
- width: 7em;
127
- margin: 0;
128
- text-align: center;
129
- }
130
-
131
- .wcf-ca-left-report-field-group {
132
- flex: 1;
133
- margin-top: 0px;
134
- }
135
- .wcf-ca-right-report-field-group {
136
- flex: 1;
137
- text-align: left;
138
-
139
- }
140
- .wcf-search-orders{
141
- display: inline-block;
142
- }
143
- .wcf_export_orders{
144
- display: inline-block;
145
- vertical-align: bottom;
146
- padding-right: 5px;
147
- }
148
-
149
- .wcf-ca-report-table-row {
150
- color: #636363;
151
- border: 1px solid #e5e5e5;
152
- }
153
-
154
- /* Newly Added for the modification of the User Order detail window UI */
155
-
156
- /* Column Classes */
157
-
158
- .wcf-ca-column-one{
159
- width: 100%;
160
- }
161
-
162
- .wcf-ca-column-two,
163
- .wcf-ca-user-address{
164
- width: 50%;
165
- }
166
-
167
- .wcf-pull-left{
168
- float: right;
169
- }
170
- .wcf-ca-email-data,
171
- .wcf-ca-user-detail{
172
- border-radius: 3px;
173
- padding: 20px 25px;
174
- min-height: 330px;
175
- max-height: 420px;
176
- width: 100%;
177
- overflow: auto;
178
- }
179
-
180
- .wcf-ca-user-detail{
181
- max-height: 100%;
182
- }
183
-
184
- .wcf-ca-user-order{
185
- border-radius: 3px;
186
- padding: 25px 25px 30px 25px;
187
- overflow: hidden;
188
- height: auto;
189
- width: 100%;
190
- }
191
-
192
- /* Column Classes */
193
-
194
- /* Section classes */
195
-
196
- .wcf-ca-margin-right{
197
- margin-left: 13px;
198
- }
199
- .wcf-ca-margin-left{
200
- margin-right: 13px;
201
- }
202
-
203
- .wcf-ca-column{
204
- background-color: #fff;
205
- border-radius: 3px;
206
- display: flex;
207
- }
208
-
209
- /* Section Classes */
210
-
211
-
212
- .wcf-ca-right-report-field-group .back-button{
213
- height: auto;
214
- padding: 3px 5px 3px 10px;
215
- }
216
-
217
- .wcf-ca-left-report-field-group .back-button .dashicons{
218
- vertical-align: middle;
219
- }
220
-
221
- .wcf-ca-panel{
222
- cursor: default;
223
- /*background-color: #fff;*/
224
- border-radius: 5px;
225
- display: flex;
226
- padding: 0px;
227
- margin: 10px 0 25px;
228
- }
229
-
230
- .wcf-table{
231
- border: none;
232
- text-align: right;
233
- width: 100%;
234
- }
235
- .wcf-table tr,
236
- .wcf-table tr th,
237
- .wcf-table tr td{
238
- border: none;
239
- }
240
-
241
- .wcf-table tr th a,
242
- .wcf-table tr td a{
243
- text-decoration: none;
244
- cursor: pointer;
245
- }
246
-
247
- .wcf-table tr th{
248
- vertical-align: bottom;
249
- border-bottom: 2px solid #ddd;
250
- }
251
-
252
- .wcf-table tr td{
253
- border-bottom: 1px solid #ddd;
254
- line-height: 1.42857143;
255
- vertical-align: top;
256
- position: relative;
257
- }
258
-
259
- .wcf-ca-panel h2{
260
- margin: 0;
261
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
262
- font-size: 21px;
263
- font-weight: 400;
264
- line-height: 1.2;
265
- text-shadow: -1px 1px 1px #fff;
266
- padding: 10px 0 15px;
267
- margin: 0;
268
- }
269
-
270
- .wcf-ca-user-order table{
271
- width: 100%;
272
- }
273
-
274
- .wcf-ca-user-order table thead th{
275
- text-align: center;
276
- padding: 1em 3em;
277
- font-weight: 400;
278
- color: #999;
279
- /*background: #f8f8f8;*/
280
- -webkit-touch-callout: none;
281
- -webkit-user-select: none;
282
- -moz-user-select: none;
283
- -ms-user-select: none;
284
- user-select: none;
285
- }
286
-
287
- .wcf-ca-user-order table thead th:first-of-type{
288
- padding-right: 3.6em;
289
- text-align: right;
290
- }
291
-
292
- .wcf-ca-user-order table tbody tr td {
293
- text-align: center;
294
- padding: 1em 3em;
295
- vertical-align: middle;
296
- }
297
-
298
- .wcf-ca-user-order table tbody tr td:first-of-type{
299
- text-align: right;
300
- /*width: 38px;*/
301
- padding-right: 3em;
302
- }
303
-
304
-
305
- .wcf-ca-user-order table tbody tr#wcf-ca-discount td:first-of-type,
306
- .wcf-ca-user-order table tbody tr#wcf-ca-other td:first-of-type,
307
- .wcf-ca-user-order table tbody tr#wcf-ca-shipping td:first-of-type,
308
- .wcf-ca-user-order table tbody tr#wcf-ca-cart-total td:first-of-type {
309
- text-align: left;
310
- font-weight: 600;
311
- width: 82%;
312
- }
313
- .wcf-ca-user-order table tbody tr#wcf-ca-discount td {
314
- border-top: 1px solid #ccc;
315
- }
316
-
317
- .wcf-ca-tooltip-text.display_tool_tip{
318
- display: block;
319
- }
320
-
321
- .wcf-ca-tooltip-text::before {
322
- border-right: 5px solid transparent;
323
- border-left: 5px solid transparent;
324
- border-top: 5px solid transparent;
325
- border-bottom: 5px solid #444;
326
- bottom: auto;
327
- content: " ";
328
- font-size: 0;
329
- right: 25px;
330
- line-height: 0;
331
- margin-right: -5px;
332
- position: absolute;
333
- top: -10px;
334
- width: 0;
335
- }
336
-
337
- .wcf-ca-tooltip-text {
338
- background: #444;
339
- border-radius: 3px;
340
- color: #fff;
341
- height: auto;
342
- right: 0;
343
- margin-top: 10px;
344
- max-width: 150px;
345
- position: absolute;
346
- padding: 6px 10px;
347
- width: 100%;
348
- display: none;
349
- z-index: 10000;
350
- }
351
- /* Newly Added for the modification of the User Order detail window UI */
352
-
353
-
354
-
355
- .wcf-ca-tags {
356
- list-style: none;
357
- margin: 0;
358
- overflow: hidden;
359
- padding: 0;
360
- }
361
-
362
- .wcf-ca-tags li {
363
- float: right;
364
- }
365
-
366
- .wcf-ca-tag {
367
- background-color: #f16334;
368
- border-radius: 0 3px 3px 0;
369
- color: #fff;
370
- display: inline-block;
371
- height: 26px;
372
- line-height: 26px;
373
- padding: 0 23px 0 20px;
374
- position: relative;
375
- margin: 0 0 0px 5px;
376
- text-decoration: none;
377
- -webkit-transition: color 0.2s;
378
- }
379
-
380
- .wcf-ca-tag::before {
381
- background: #fff;
382
- border-radius: 10px;
383
- box-shadow: inset 0 1px rgba(0, 0, 0, 0.25);
384
- content: '';
385
- height: 6px;
386
- right: 10px;
387
- position: absolute;
388
- width: 6px;
389
- top: 10px;
390
- }
391
-
392
- .wcf-ca-tag::after {
393
- background: #fff;
394
- border-bottom: 13px solid transparent;
395
- border-right: 10px solid #f16334;;
396
- border-top: 13px solid transparent;
397
- content: '';
398
- position: absolute;
399
- left: 0;
400
- top: 0;
401
- }
402
-
403
- .wcf-ca-tag:hover {
404
- background-color: #f16334;
405
- color: white;
406
- }
407
-
408
- .wcf-ca-tag:hover::after {
409
- border-right-color: #f16334;
410
- }
411
-
412
- .wcf-sub-heading {
413
- font-weight: 400;
414
- }
415
-
416
- .wcf-ca-spinner{
417
- float:unset;
418
- }
419
-
420
- .wcf-ca-response-msg{
421
- vertical-align: sub;
422
- line-height: 28px;
423
- margin-right: 10px;
424
- font-weight: bold;
425
- }
426
-
427
- .wcf-ca-export-icon{
428
- line-height: 1.8;
429
- font-size:17px;
430
- }
1
+ .wcf-ca-ibox {
2
+ clear: both;
3
+ margin-bottom: 25px;
4
+ margin-top: 0;
5
+ padding: 0;
6
+ }
7
+
8
+ .wcf-ca-ibox-title {
9
+ background-color: white;
10
+ border-image: none;
11
+ border-width: 3px 0px 0;
12
+ color: inherit;
13
+ margin-bottom: 0;
14
+ padding: 14px 15px 7px;
15
+ min-height: 48px;
16
+ }
17
+
18
+ .wcf-ca-ibox-content {
19
+ background-color: white;
20
+ color: inherit;
21
+ padding: 15px 20px 20px 20px;
22
+ border-color: #d7dadc;
23
+ border-image: none;
24
+ border-style: solid solid none;
25
+ border-width: 1px 0px;
26
+ }
27
+
28
+ .wcf-ca-raw {
29
+ margin-right: -15px;
30
+ margin-left: -15px;
31
+ }
32
+
33
+ .wcf-ca-grid-container {
34
+ display: grid;
35
+ grid-template-columns: 1fr 1fr 1fr;
36
+ grid-gap: 20px;
37
+ }
38
+
39
+ .grid-container > div {
40
+ background-color: rgba(255, 255, 255, 0.8);
41
+ text-align: center;
42
+ padding: 20px 0;
43
+ font-size: 30px;
44
+ }
45
+
46
+
47
+ .wcf-ca-center-msg {
48
+ margin: auto;
49
+ width: 50%;
50
+ padding: 10px;
51
+ margin-top: 20px;
52
+ text-align: center;
53
+ }
54
+
55
+
56
+ .wcf-ca-switch {
57
+ cursor: pointer;
58
+ text-indent: -999em;
59
+ display: block;
60
+ width: 38px;
61
+ height: 22px;
62
+ border-radius: 30px;
63
+ border: none;
64
+ position: relative;
65
+ box-sizing: border-box;
66
+ -webkit-transition: all .3s ease;
67
+ transition: all .3s ease;
68
+ box-shadow: inset 0 0 0 0 transparent;
69
+ }
70
+ .wcf-ca-switch:focus {
71
+ outline: none;
72
+ }
73
+ .wcf-ca-switch:before {
74
+ border-radius: 50%;
75
+ background: #ffffff;
76
+ content: '';
77
+ position: absolute;
78
+ display: block;
79
+ width: 18px;
80
+ height: 18px;
81
+ top: 2px;
82
+ right: 2px;
83
+ -webkit-transition: all .15s ease;
84
+ transition: all .15s ease;
85
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
86
+ }
87
+ .wcf-ca-switch[wcf-ca-template-switch="on"] {
88
+ box-shadow: inset 0 0 0 11px #008000;
89
+ }
90
+ .wcf-ca-switch[wcf-ca-template-switch="on"]:before {
91
+ -webkit-transform: translateX(-16px);
92
+ transform: translateX(-16px);
93
+ }
94
+ .wcf-ca-switch[wcf-ca-template-switch="off"] {
95
+ background: #ccc;
96
+ }
97
+ .wcf-ca-switch.wcap-loading {
98
+ cursor: default;
99
+ opacity: 0.5;
100
+ }
101
+
102
+ .wcf-ca-trigger-input{
103
+ height: 28px;
104
+ width: 40%;
105
+ margin-left: 10px;
106
+ }
107
+
108
+ .wcf-ca-report-btn {
109
+ padding: 15px 0px 15px 0px;
110
+ display: flex;
111
+ width: 100%;
112
+ position: relative;
113
+ }
114
+
115
+ .wcf-ca-email-inputs {
116
+ width: 25%;
117
+ }
118
+
119
+ .wcf-ca-coupon-inputs {
120
+ width: 10%;
121
+ vertical-align: middle;
122
+ }
123
+
124
+ .wcf-ca-filter-input{
125
+ height: 28px;
126
+ width: 7em;
127
+ margin: 0;
128
+ text-align: center;
129
+ }
130
+
131
+ .wcf-ca-left-report-field-group {
132
+ flex: 1;
133
+ margin-top: 0px;
134
+ }
135
+ .wcf-ca-right-report-field-group {
136
+ flex: 1;
137
+ text-align: left;
138
+
139
+ }
140
+ .wcf-search-orders{
141
+ display: inline-block;
142
+ }
143
+ .wcf_export_orders{
144
+ display: inline-block;
145
+ vertical-align: bottom;
146
+ padding-right: 5px;
147
+ }
148
+
149
+ .wcf-ca-report-table-row {
150
+ color: #636363;
151
+ border: 1px solid #e5e5e5;
152
+ }
153
+
154
+ /* Newly Added for the modification of the User Order detail window UI */
155
+
156
+ /* Column Classes */
157
+
158
+ .wcf-ca-column-one{
159
+ width: 100%;
160
+ }
161
+
162
+ .wcf-ca-column-two,
163
+ .wcf-ca-user-address{
164
+ width: 50%;
165
+ }
166
+
167
+ .wcf-pull-left{
168
+ float: right;
169
+ }
170
+ .wcf-ca-email-data,
171
+ .wcf-ca-user-detail{
172
+ border-radius: 3px;
173
+ padding: 20px 25px;
174
+ min-height: 330px;
175
+ max-height: 420px;
176
+ width: 100%;
177
+ overflow: auto;
178
+ }
179
+
180
+ .wcf-ca-user-detail{
181
+ max-height: 100%;
182
+ }
183
+
184
+ .wcf-ca-user-order{
185
+ border-radius: 3px;
186
+ padding: 25px 25px 30px 25px;
187
+ overflow: hidden;
188
+ height: auto;
189
+ width: 100%;
190
+ }
191
+
192
+ /* Column Classes */
193
+
194
+ /* Section classes */
195
+
196
+ .wcf-ca-margin-right{
197
+ margin-left: 13px;
198
+ }
199
+ .wcf-ca-margin-left{
200
+ margin-right: 13px;
201
+ }
202
+
203
+ .wcf-ca-column{
204
+ background-color: #fff;
205
+ border-radius: 3px;
206
+ display: flex;
207
+ }
208
+
209
+ /* Section Classes */
210
+
211
+
212
+ .wcf-ca-right-report-field-group .back-button{
213
+ height: auto;
214
+ padding: 3px 5px 3px 10px;
215
+ }
216
+
217
+ .wcf-ca-left-report-field-group .back-button .dashicons{
218
+ vertical-align: middle;
219
+ }
220
+
221
+ .wcf-ca-panel{
222
+ cursor: default;
223
+ /*background-color: #fff;*/
224
+ border-radius: 5px;
225
+ display: flex;
226
+ padding: 0px;
227
+ margin: 10px 0 25px;
228
+ }
229
+
230
+ .wcf-table{
231
+ border: none;
232
+ text-align: right;
233
+ width: 100%;
234
+ }
235
+ .wcf-table tr,
236
+ .wcf-table tr th,
237
+ .wcf-table tr td{
238
+ border: none;
239
+ }
240
+
241
+ .wcf-table tr th a,
242
+ .wcf-table tr td a{
243
+ text-decoration: none;
244
+ cursor: pointer;
245
+ }
246
+
247
+ .wcf-table tr th{
248
+ vertical-align: bottom;
249
+ border-bottom: 2px solid #ddd;
250
+ }
251
+
252
+ .wcf-table tr td{
253
+ border-bottom: 1px solid #ddd;
254
+ line-height: 1.42857143;
255
+ vertical-align: top;
256
+ position: relative;
257
+ }
258
+
259
+ .wcf-ca-panel h2{
260
+ margin: 0;
261
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
262
+ font-size: 21px;
263
+ font-weight: 400;
264
+ line-height: 1.2;
265
+ text-shadow: -1px 1px 1px #fff;
266
+ padding: 10px 0 15px;
267
+ margin: 0;
268
+ }
269
+
270
+ .wcf-ca-user-order table{
271
+ width: 100%;
272
+ }
273
+
274
+ .wcf-ca-user-order table thead th{
275
+ text-align: center;
276
+ padding: 1em 3em;
277
+ font-weight: 400;
278
+ color: #999;
279
+ /*background: #f8f8f8;*/
280
+ -webkit-touch-callout: none;
281
+ -webkit-user-select: none;
282
+ -moz-user-select: none;
283
+ -ms-user-select: none;
284
+ user-select: none;
285
+ }
286
+
287
+ .wcf-ca-user-order table thead th:first-of-type{
288
+ padding-right: 3.6em;
289
+ text-align: right;
290
+ }
291
+
292
+ .wcf-ca-user-order table tbody tr td {
293
+ text-align: center;
294
+ padding: 1em 3em;
295
+ vertical-align: middle;
296
+ }
297
+
298
+ .wcf-ca-user-order table tbody tr td:first-of-type{
299
+ text-align: right;
300
+ /*width: 38px;*/
301
+ padding-right: 3em;
302
+ }
303
+
304
+
305
+ .wcf-ca-user-order table tbody tr#wcf-ca-discount td:first-of-type,
306
+ .wcf-ca-user-order table tbody tr#wcf-ca-other td:first-of-type,
307
+ .wcf-ca-user-order table tbody tr#wcf-ca-shipping td:first-of-type,
308
+ .wcf-ca-user-order table tbody tr#wcf-ca-cart-total td:first-of-type {
309
+ text-align: left;
310
+ font-weight: 600;
311
+ width: 82%;
312
+ }
313
+ .wcf-ca-user-order table tbody tr#wcf-ca-discount td {
314
+ border-top: 1px solid #ccc;
315
+ }
316
+
317
+ .wcf-ca-tooltip-text.display_tool_tip{
318
+ display: block;
319
+ }
320
+
321
+ .wcf-ca-tooltip-text::before {
322
+ border-right: 5px solid transparent;
323
+ border-left: 5px solid transparent;
324
+ border-top: 5px solid transparent;
325
+ border-bottom: 5px solid #444;
326
+ bottom: auto;
327
+ content: " ";
328
+ font-size: 0;
329
+ right: 25px;
330
+ line-height: 0;
331
+ margin-right: -5px;
332
+ position: absolute;
333
+ top: -10px;
334
+ width: 0;
335
+ }
336
+
337
+ .wcf-ca-tooltip-text {
338
+ background: #444;
339
+ border-radius: 3px;
340
+ color: #fff;
341
+ height: auto;
342
+ right: 0;
343
+ margin-top: 10px;
344
+ max-width: 150px;
345
+ position: absolute;
346
+ padding: 6px 10px;
347
+ width: 100%;
348
+ display: none;
349
+ z-index: 10000;
350
+ }
351
+ /* Newly Added for the modification of the User Order detail window UI */
352
+
353
+
354
+
355
+ .wcf-ca-tags {
356
+ list-style: none;
357
+ margin: 0;
358
+ overflow: hidden;
359
+ padding: 0;
360
+ }
361
+
362
+ .wcf-ca-tags li {
363
+ float: right;
364
+ }
365
+
366
+ .wcf-ca-tag {
367
+ background-color: #f16334;
368
+ border-radius: 0 3px 3px 0;
369
+ color: #fff;
370
+ display: inline-block;
371
+ height: 26px;
372
+ line-height: 26px;
373
+ padding: 0 23px 0 20px;
374
+ position: relative;
375
+ margin: 0 0 0px 5px;
376
+ text-decoration: none;
377
+ -webkit-transition: color 0.2s;
378
+ }
379
+
380
+ .wcf-ca-tag::before {
381
+ background: #fff;
382
+ border-radius: 10px;
383
+ box-shadow: inset 0 1px rgba(0, 0, 0, 0.25);
384
+ content: '';
385
+ height: 6px;
386
+ right: 10px;
387
+ position: absolute;
388
+ width: 6px;
389
+ top: 10px;
390
+ }
391
+
392
+ .wcf-ca-tag::after {
393
+ background: #fff;
394
+ border-bottom: 13px solid transparent;
395
+ border-right: 10px solid #f16334;;
396
+ border-top: 13px solid transparent;
397
+ content: '';
398
+ position: absolute;
399
+ left: 0;
400
+ top: 0;
401
+ }
402
+
403
+ .wcf-ca-tag:hover {
404
+ background-color: #f16334;
405
+ color: white;
406
+ }
407
+
408
+ .wcf-ca-tag:hover::after {
409
+ border-right-color: #f16334;
410
+ }
411
+
412
+ .wcf-sub-heading {
413
+ font-weight: 400;
414
+ }
415
+
416
+ .wcf-ca-spinner{
417
+ float:unset;
418
+ }
419
+
420
+ .wcf-ca-response-msg{
421
+ vertical-align: sub;
422
+ line-height: 28px;
423
+ margin-right: 10px;
424
+ font-weight: bold;
425
+ }
426
+
427
+ .wcf-ca-export-icon{
428
+ line-height: 1.8;
429
+ font-size:17px;
430
+ }
admin/assets/css/admin-cart-abandonment.css CHANGED
@@ -1,430 +1,430 @@
1
- .wcf-ca-ibox {
2
- clear: both;
3
- margin-bottom: 25px;
4
- margin-top: 0;
5
- padding: 0;
6
- }
7
-
8
- .wcf-ca-ibox-title {
9
- background-color: white;
10
- border-image: none;
11
- border-width: 3px 0px 0;
12
- color: inherit;
13
- margin-bottom: 0;
14
- padding: 14px 15px 7px;
15
- min-height: 48px;
16
- }
17
-
18
- .wcf-ca-ibox-content {
19
- background-color: white;
20
- color: inherit;
21
- padding: 15px 20px 20px 20px;
22
- border-color: #d7dadc;
23
- border-image: none;
24
- border-style: solid solid none;
25
- border-width: 1px 0px;
26
- }
27
-
28
- .wcf-ca-raw {
29
- margin-left: -15px;
30
- margin-right: -15px;
31
- }
32
-
33
- .wcf-ca-grid-container {
34
- display: grid;
35
- grid-template-columns: 1fr 1fr 1fr;
36
- grid-gap: 20px;
37
- }
38
-
39
- .grid-container > div {
40
- background-color: rgba(255, 255, 255, 0.8);
41
- text-align: center;
42
- padding: 20px 0;
43
- font-size: 30px;
44
- }
45
-
46
-
47
- .wcf-ca-center-msg {
48
- margin: auto;
49
- width: 50%;
50
- padding: 10px;
51
- margin-top: 20px;
52
- text-align: center;
53
- }
54
-
55
-
56
- .wcf-ca-switch {
57
- cursor: pointer;
58
- text-indent: -999em;
59
- display: block;
60
- width: 38px;
61
- height: 22px;
62
- border-radius: 30px;
63
- border: none;
64
- position: relative;
65
- box-sizing: border-box;
66
- -webkit-transition: all .3s ease;
67
- transition: all .3s ease;
68
- box-shadow: inset 0 0 0 0 transparent;
69
- }
70
- .wcf-ca-switch:focus {
71
- outline: none;
72
- }
73
- .wcf-ca-switch:before {
74
- border-radius: 50%;
75
- background: #ffffff;
76
- content: '';
77
- position: absolute;
78
- display: block;
79
- width: 18px;
80
- height: 18px;
81
- top: 2px;
82
- left: 2px;
83
- -webkit-transition: all .15s ease;
84
- transition: all .15s ease;
85
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
86
- }
87
- .wcf-ca-switch[wcf-ca-template-switch="on"] {
88
- box-shadow: inset 0 0 0 11px #008000;
89
- }
90
- .wcf-ca-switch[wcf-ca-template-switch="on"]:before {
91
- -webkit-transform: translateX(16px);
92
- transform: translateX(16px);
93
- }
94
- .wcf-ca-switch[wcf-ca-template-switch="off"] {
95
- background: #ccc;
96
- }
97
- .wcf-ca-switch.wcap-loading {
98
- cursor: default;
99
- opacity: 0.5;
100
- }
101
-
102
- .wcf-ca-trigger-input{
103
- height: 28px;
104
- width: 40%;
105
- margin-right: 10px;
106
- }
107
-
108
- .wcf-ca-report-btn {
109
- padding: 15px 0px 15px 0px;
110
- display: flex;
111
- width: 100%;
112
- position: relative;
113
- }
114
-
115
- .wcf-ca-email-inputs {
116
- width: 25%;
117
- }
118
-
119
- .wcf-ca-coupon-inputs {
120
- width: 10%;
121
- vertical-align: middle;
122
- }
123
-
124
- .wcf-ca-filter-input{
125
- height: 28px;
126
- width: 7em;
127
- margin: 0;
128
- text-align: center;
129
- }
130
-
131
- .wcf-ca-left-report-field-group {
132
- flex: 1;
133
- margin-top: 0px;
134
- }
135
- .wcf-ca-right-report-field-group {
136
- flex: 1;
137
- text-align: right;
138
-
139
- }
140
- .wcf-search-orders{
141
- display: inline-block;
142
- }
143
- .wcf_export_orders{
144
- display: inline-block;
145
- vertical-align: bottom;
146
- padding-left: 5px;
147
- }
148
-
149
- .wcf-ca-report-table-row {
150
- color: #636363;
151
- border: 1px solid #e5e5e5;
152
- }
153
-
154
- /* Newly Added for the modification of the User Order detail window UI */
155
-
156
- /* Column Classes */
157
-
158
- .wcf-ca-column-one{
159
- width: 100%;
160
- }
161
-
162
- .wcf-ca-column-two,
163
- .wcf-ca-user-address{
164
- width: 50%;
165
- }
166
-
167
- .wcf-pull-left{
168
- float: left;
169
- }
170
- .wcf-ca-email-data,
171
- .wcf-ca-user-detail{
172
- border-radius: 3px;
173
- padding: 20px 25px;
174
- min-height: 330px;
175
- max-height: 420px;
176
- width: 100%;
177
- overflow: auto;
178
- }
179
-
180
- .wcf-ca-user-detail{
181
- max-height: 100%;
182
- }
183
-
184
- .wcf-ca-user-order{
185
- border-radius: 3px;
186
- padding: 25px 25px 30px 25px;
187
- overflow: hidden;
188
- height: auto;
189
- width: 100%;
190
- }
191
-
192
- /* Column Classes */
193
-
194
- /* Section classes */
195
-
196
- .wcf-ca-margin-right{
197
- margin-right: 13px;
198
- }
199
- .wcf-ca-margin-left{
200
- margin-left: 13px;
201
- }
202
-
203
- .wcf-ca-column{
204
- background-color: #fff;
205
- border-radius: 3px;
206
- display: flex;
207
- }
208
-
209
- /* Section Classes */
210
-
211
-
212
- .wcf-ca-right-report-field-group .back-button{
213
- height: auto;
214
- padding: 3px 10px 3px 5px;
215
- }
216
-
217
- .wcf-ca-left-report-field-group .back-button .dashicons{
218
- vertical-align: middle;
219
- }
220
-
221
- .wcf-ca-panel{
222
- cursor: default;
223
- /*background-color: #fff;*/
224
- border-radius: 5px;
225
- display: flex;
226
- padding: 0px;
227
- margin: 10px 0 25px;
228
- }
229
-
230
- .wcf-table{
231
- border: none;
232
- text-align: left;
233
- width: 100%;
234
- }
235
- .wcf-table tr,
236
- .wcf-table tr th,
237
- .wcf-table tr td{
238
- border: none;
239
- }
240
-
241
- .wcf-table tr th a,
242
- .wcf-table tr td a{
243
- text-decoration: none;
244
- cursor: pointer;
245
- }
246
-
247
- .wcf-table tr th{
248
- vertical-align: bottom;
249
- border-bottom: 2px solid #ddd;
250
- }
251
-
252
- .wcf-table tr td{
253
- border-bottom: 1px solid #ddd;
254
- line-height: 1.42857143;
255
- vertical-align: top;
256
- position: relative;
257
- }
258
-
259
- .wcf-ca-panel h2{
260
- margin: 0;
261
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
262
- font-size: 21px;
263
- font-weight: 400;
264
- line-height: 1.2;
265
- text-shadow: 1px 1px 1px #fff;
266
- padding: 10px 0 15px;
267
- margin: 0;
268
- }
269
-
270
- .wcf-ca-user-order table{
271
- width: 100%;
272
- }
273
-
274
- .wcf-ca-user-order table thead th{
275
- text-align: center;
276
- padding: 1em 3em;
277
- font-weight: 400;
278
- color: #999;
279
- /*background: #f8f8f8;*/
280
- -webkit-touch-callout: none;
281
- -webkit-user-select: none;
282
- -moz-user-select: none;
283
- -ms-user-select: none;
284
- user-select: none;
285
- }
286
-
287
- .wcf-ca-user-order table thead th:first-of-type{
288
- padding-left: 3.6em;
289
- text-align: left;
290
- }
291
-
292
- .wcf-ca-user-order table tbody tr td {
293
- text-align: center;
294
- padding: 1em 3em;
295
- vertical-align: middle;
296
- }
297
-
298
- .wcf-ca-user-order table tbody tr td:first-of-type{
299
- text-align: left;
300
- /*width: 38px;*/
301
- padding-left: 3em;
302
- }
303
-
304
-
305
- .wcf-ca-user-order table tbody tr#wcf-ca-discount td:first-of-type,
306
- .wcf-ca-user-order table tbody tr#wcf-ca-other td:first-of-type,
307
- .wcf-ca-user-order table tbody tr#wcf-ca-shipping td:first-of-type,
308
- .wcf-ca-user-order table tbody tr#wcf-ca-cart-total td:first-of-type {
309
- text-align: right;
310
- font-weight: 600;
311
- width: 82%;
312
- }
313
- .wcf-ca-user-order table tbody tr#wcf-ca-discount td {
314
- border-top: 1px solid #ccc;
315
- }
316
-
317
- .wcf-ca-tooltip-text.display_tool_tip{
318
- display: block;
319
- }
320
-
321
- .wcf-ca-tooltip-text::before {
322
- border-left: 5px solid transparent;
323
- border-right: 5px solid transparent;
324
- border-top: 5px solid transparent;
325
- border-bottom: 5px solid #444;
326
- bottom: auto;
327
- content: " ";
328
- font-size: 0;
329
- left: 25px;
330
- line-height: 0;
331
- margin-left: -5px;
332
- position: absolute;
333
- top: -10px;
334
- width: 0;
335
- }
336
-
337
- .wcf-ca-tooltip-text {
338
- background: #444;
339
- border-radius: 3px;
340
- color: #fff;
341
- height: auto;
342
- left: 0;
343
- margin-top: 10px;
344
- max-width: 150px;
345
- position: absolute;
346
- padding: 6px 10px;
347
- width: 100%;
348
- display: none;
349
- z-index: 10000;
350
- }
351
- /* Newly Added for the modification of the User Order detail window UI */
352
-
353
-
354
-
355
- .wcf-ca-tags {
356
- list-style: none;
357
- margin: 0;
358
- overflow: hidden;
359
- padding: 0;
360
- }
361
-
362
- .wcf-ca-tags li {
363
- float: left;
364
- }
365
-
366
- .wcf-ca-tag {
367
- background-color: #f16334;
368
- border-radius: 3px 0 0 3px;
369
- color: #fff;
370
- display: inline-block;
371
- height: 26px;
372
- line-height: 26px;
373
- padding: 0 20px 0 23px;
374
- position: relative;
375
- margin: 0 5px 0px 0;
376
- text-decoration: none;
377
- -webkit-transition: color 0.2s;
378
- }
379
-
380
- .wcf-ca-tag::before {
381
- background: #fff;
382
- border-radius: 10px;
383
- box-shadow: inset 0 1px rgba(0, 0, 0, 0.25);
384
- content: '';
385
- height: 6px;
386
- left: 10px;
387
- position: absolute;
388
- width: 6px;
389
- top: 10px;
390
- }
391
-
392
- .wcf-ca-tag::after {
393
- background: #fff;
394
- border-bottom: 13px solid transparent;
395
- border-left: 10px solid #f16334;;
396
- border-top: 13px solid transparent;
397
- content: '';
398
- position: absolute;
399
- right: 0;
400
- top: 0;
401
- }
402
-
403
- .wcf-ca-tag:hover {
404
- background-color: #f16334;
405
- color: white;
406
- }
407
-
408
- .wcf-ca-tag:hover::after {
409
- border-left-color: #f16334;
410
- }
411
-
412
- .wcf-sub-heading {
413
- font-weight: 400;
414
- }
415
-
416
- .wcf-ca-spinner{
417
- float:unset;
418
- }
419
-
420
- .wcf-ca-response-msg{
421
- vertical-align: sub;
422
- line-height: 28px;
423
- margin-left: 10px;
424
- font-weight: bold;
425
- }
426
-
427
- .wcf-ca-export-icon{
428
- line-height: 1.8;
429
- font-size:17px;
430
- }
1
+ .wcf-ca-ibox {
2
+ clear: both;
3
+ margin-bottom: 25px;
4
+ margin-top: 0;
5
+ padding: 0;
6
+ }
7
+
8
+ .wcf-ca-ibox-title {
9
+ background-color: white;
10
+ border-image: none;
11
+ border-width: 3px 0px 0;
12
+ color: inherit;
13
+ margin-bottom: 0;
14
+ padding: 14px 15px 7px;
15
+ min-height: 48px;
16
+ }
17
+
18
+ .wcf-ca-ibox-content {
19
+ background-color: white;
20
+ color: inherit;
21
+ padding: 15px 20px 20px 20px;
22
+ border-color: #d7dadc;
23
+ border-image: none;
24
+ border-style: solid solid none;
25
+ border-width: 1px 0px;
26
+ }
27
+
28
+ .wcf-ca-raw {
29
+ margin-left: -15px;
30
+ margin-right: -15px;
31
+ }
32
+
33
+ .wcf-ca-grid-container {
34
+ display: grid;
35
+ grid-template-columns: 1fr 1fr 1fr;
36
+ grid-gap: 20px;
37
+ }
38
+
39
+ .grid-container > div {
40
+ background-color: rgba(255, 255, 255, 0.8);
41
+ text-align: center;
42
+ padding: 20px 0;
43
+ font-size: 30px;
44
+ }
45
+
46
+
47
+ .wcf-ca-center-msg {
48
+ margin: auto;
49
+ width: 50%;
50
+ padding: 10px;
51
+ margin-top: 20px;
52
+ text-align: center;
53
+ }
54
+
55
+
56
+ .wcf-ca-switch {
57
+ cursor: pointer;
58
+ text-indent: -999em;
59
+ display: block;
60
+ width: 38px;
61
+ height: 22px;
62
+ border-radius: 30px;
63
+ border: none;
64
+ position: relative;
65
+ box-sizing: border-box;
66
+ -webkit-transition: all .3s ease;
67
+ transition: all .3s ease;
68
+ box-shadow: inset 0 0 0 0 transparent;
69
+ }
70
+ .wcf-ca-switch:focus {
71
+ outline: none;
72
+ }
73
+ .wcf-ca-switch:before {
74
+ border-radius: 50%;
75
+ background: #ffffff;
76
+ content: '';
77
+ position: absolute;
78
+ display: block;
79
+ width: 18px;
80
+ height: 18px;
81
+ top: 2px;
82
+ left: 2px;
83
+ -webkit-transition: all .15s ease;
84
+ transition: all .15s ease;
85
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
86
+ }
87
+ .wcf-ca-switch[wcf-ca-template-switch="on"] {
88
+ box-shadow: inset 0 0 0 11px #008000;
89
+ }
90
+ .wcf-ca-switch[wcf-ca-template-switch="on"]:before {
91
+ -webkit-transform: translateX(16px);
92
+ transform: translateX(16px);
93
+ }
94
+ .wcf-ca-switch[wcf-ca-template-switch="off"] {
95
+ background: #ccc;
96
+ }
97
+ .wcf-ca-switch.wcap-loading {
98
+ cursor: default;
99
+ opacity: 0.5;
100
+ }
101
+
102
+ .wcf-ca-trigger-input{
103
+ height: 28px;
104
+ width: 40%;
105
+ margin-right: 10px;
106
+ }
107
+
108
+ .wcf-ca-report-btn {
109
+ padding: 15px 0px 15px 0px;
110
+ display: flex;
111
+ width: 100%;
112
+ position: relative;
113
+ }
114
+
115
+ .wcf-ca-email-inputs {
116
+ width: 25%;
117
+ }
118
+
119
+ .wcf-ca-coupon-inputs {
120
+ width: 10%;
121
+ vertical-align: middle;
122
+ }
123
+
124
+ .wcf-ca-filter-input{
125
+ height: 28px;
126
+ width: 7em;
127
+ margin: 0;
128
+ text-align: center;
129
+ }
130
+
131
+ .wcf-ca-left-report-field-group {
132
+ flex: 1;
133
+ margin-top: 0px;
134
+ }
135
+ .wcf-ca-right-report-field-group {
136
+ flex: 1;
137
+ text-align: right;
138
+
139
+ }
140
+ .wcf-search-orders{
141
+ display: inline-block;
142
+ }
143
+ .wcf_export_orders{
144
+ display: inline-block;
145
+ vertical-align: bottom;
146
+ padding-left: 5px;
147
+ }
148
+
149
+ .wcf-ca-report-table-row {
150
+ color: #636363;
151
+ border: 1px solid #e5e5e5;
152
+ }
153
+
154
+ /* Newly Added for the modification of the User Order detail window UI */
155
+
156
+ /* Column Classes */
157
+
158
+ .wcf-ca-column-one{
159
+ width: 100%;
160
+ }
161
+
162
+ .wcf-ca-column-two,
163
+ .wcf-ca-user-address{
164
+ width: 50%;
165
+ }
166
+
167
+ .wcf-pull-left{
168
+ float: left;
169
+ }
170
+ .wcf-ca-email-data,
171
+ .wcf-ca-user-detail{
172
+ border-radius: 3px;
173
+ padding: 20px 25px;
174
+ min-height: 330px;
175
+ max-height: 420px;
176
+ width: 100%;
177
+ overflow: auto;
178
+ }
179
+
180
+ .wcf-ca-user-detail{
181
+ max-height: 100%;
182
+ }
183
+
184
+ .wcf-ca-user-order{
185
+ border-radius: 3px;
186
+ padding: 25px 25px 30px 25px;
187
+ overflow: hidden;
188
+ height: auto;
189
+ width: 100%;
190
+ }
191
+
192
+ /* Column Classes */
193
+
194
+ /* Section classes */
195
+
196
+ .wcf-ca-margin-right{
197
+ margin-right: 13px;
198
+ }
199
+ .wcf-ca-margin-left{
200
+ margin-left: 13px;
201
+ }
202
+
203
+ .wcf-ca-column{
204
+ background-color: #fff;
205
+ border-radius: 3px;
206
+ display: flex;
207
+ }
208
+
209
+ /* Section Classes */
210
+
211
+
212
+ .wcf-ca-right-report-field-group .back-button{
213
+ height: auto;
214
+ padding: 3px 10px 3px 5px;
215
+ }
216
+
217
+ .wcf-ca-left-report-field-group .back-button .dashicons{
218
+ vertical-align: middle;
219
+ }
220
+
221
+ .wcf-ca-panel{
222
+ cursor: default;
223
+ /*background-color: #fff;*/
224
+ border-radius: 5px;
225
+ display: flex;
226
+ padding: 0px;
227
+ margin: 10px 0 25px;
228
+ }
229
+
230
+ .wcf-table{
231
+ border: none;
232
+ text-align: left;
233
+ width: 100%;
234
+ }
235
+ .wcf-table tr,
236
+ .wcf-table tr th,
237
+ .wcf-table tr td{
238
+ border: none;
239
+ }
240
+
241
+ .wcf-table tr th a,
242
+ .wcf-table tr td a{
243
+ text-decoration: none;
244
+ cursor: pointer;
245
+ }
246
+
247
+ .wcf-table tr th{
248
+ vertical-align: bottom;
249
+ border-bottom: 2px solid #ddd;
250
+ }
251
+
252
+ .wcf-table tr td{
253
+ border-bottom: 1px solid #ddd;
254
+ line-height: 1.42857143;
255
+ vertical-align: top;
256
+ position: relative;
257
+ }
258
+
259
+ .wcf-ca-panel h2{
260
+ margin: 0;
261
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
262
+ font-size: 21px;
263
+ font-weight: 400;
264
+ line-height: 1.2;
265
+ text-shadow: 1px 1px 1px #fff;
266
+ padding: 10px 0 15px;
267
+ margin: 0;
268
+ }
269
+
270
+ .wcf-ca-user-order table{
271
+ width: 100%;
272
+ }
273
+
274
+ .wcf-ca-user-order table thead th{
275
+ text-align: center;
276
+ padding: 1em 3em;
277
+ font-weight: 400;
278
+ color: #999;
279
+ /*background: #f8f8f8;*/
280
+ -webkit-touch-callout: none;
281
+ -webkit-user-select: none;
282
+ -moz-user-select: none;
283
+ -ms-user-select: none;
284
+ user-select: none;
285
+ }
286
+
287
+ .wcf-ca-user-order table thead th:first-of-type{
288
+ padding-left: 3.6em;
289
+ text-align: left;
290
+ }
291
+
292
+ .wcf-ca-user-order table tbody tr td {
293
+ text-align: center;
294
+ padding: 1em 3em;
295
+ vertical-align: middle;
296
+ }
297
+
298
+ .wcf-ca-user-order table tbody tr td:first-of-type{
299
+ text-align: left;
300
+ /*width: 38px;*/
301
+ padding-left: 3em;
302
+ }
303
+
304
+
305
+ .wcf-ca-user-order table tbody tr#wcf-ca-discount td:first-of-type,
306
+ .wcf-ca-user-order table tbody tr#wcf-ca-other td:first-of-type,
307
+ .wcf-ca-user-order table tbody tr#wcf-ca-shipping td:first-of-type,
308
+ .wcf-ca-user-order table tbody tr#wcf-ca-cart-total td:first-of-type {
309
+ text-align: right;
310
+ font-weight: 600;
311
+ width: 82%;
312
+ }
313
+ .wcf-ca-user-order table tbody tr#wcf-ca-discount td {
314
+ border-top: 1px solid #ccc;
315
+ }
316
+
317
+ .wcf-ca-tooltip-text.display_tool_tip{
318
+ display: block;
319
+ }
320
+
321
+ .wcf-ca-tooltip-text::before {
322
+ border-left: 5px solid transparent;
323
+ border-right: 5px solid transparent;
324
+ border-top: 5px solid transparent;
325
+ border-bottom: 5px solid #444;
326
+ bottom: auto;
327
+ content: " ";
328
+ font-size: 0;
329
+ left: 25px;
330
+ line-height: 0;
331
+ margin-left: -5px;
332
+ position: absolute;
333
+ top: -10px;
334
+ width: 0;
335
+ }
336
+
337
+ .wcf-ca-tooltip-text {
338
+ background: #444;
339
+ border-radius: 3px;
340
+ color: #fff;
341
+ height: auto;
342
+ left: 0;
343
+ margin-top: 10px;
344
+ max-width: 150px;
345
+ position: absolute;
346
+ padding: 6px 10px;
347
+ width: 100%;
348
+ display: none;
349
+ z-index: 10000;
350
+ }
351
+ /* Newly Added for the modification of the User Order detail window UI */
352
+
353
+
354
+
355
+ .wcf-ca-tags {
356
+ list-style: none;
357
+ margin: 0;
358
+ overflow: hidden;
359
+ padding: 0;
360
+ }
361
+
362
+ .wcf-ca-tags li {
363
+ float: left;
364
+ }
365
+
366
+ .wcf-ca-tag {
367
+ background-color: #f16334;
368
+ border-radius: 3px 0 0 3px;
369
+ color: #fff;
370
+ display: inline-block;
371
+ height: 26px;
372
+ line-height: 26px;
373
+ padding: 0 20px 0 23px;
374
+ position: relative;
375
+ margin: 0 5px 0px 0;
376
+ text-decoration: none;
377
+ -webkit-transition: color 0.2s;
378
+ }
379
+
380
+ .wcf-ca-tag::before {
381
+ background: #fff;
382
+ border-radius: 10px;
383
+ box-shadow: inset 0 1px rgba(0, 0, 0, 0.25);
384
+ content: '';
385
+ height: 6px;
386
+ left: 10px;
387
+ position: absolute;
388
+ width: 6px;
389
+ top: 10px;
390
+ }
391
+
392
+ .wcf-ca-tag::after {
393
+ background: #fff;
394
+ border-bottom: 13px solid transparent;
395
+ border-left: 10px solid #f16334;;
396
+ border-top: 13px solid transparent;
397
+ content: '';
398
+ position: absolute;
399
+ right: 0;
400
+ top: 0;
401
+ }
402
+
403
+ .wcf-ca-tag:hover {
404
+ background-color: #f16334;
405
+ color: white;
406
+ }
407
+
408
+ .wcf-ca-tag:hover::after {
409
+ border-left-color: #f16334;
410
+ }
411
+
412
+ .wcf-sub-heading {
413
+ font-weight: 400;
414
+ }
415
+
416
+ .wcf-ca-spinner{
417
+ float:unset;
418
+ }
419
+
420
+ .wcf-ca-response-msg{
421
+ vertical-align: sub;
422
+ line-height: 28px;
423
+ margin-left: 10px;
424
+ font-weight: bold;
425
+ }
426
+
427
+ .wcf-ca-export-icon{
428
+ line-height: 1.8;
429
+ font-size:17px;
430
+ }
admin/assets/js/admin-email-templates.js CHANGED
@@ -1,304 +1,304 @@
1
- (function ($) {
2
-
3
- CartAbandonmentSettings = {
4
-
5
- init: function () {
6
-
7
- $("#wcf_ca_custom_filter_from").datepicker({
8
- dateFormat: 'yy-mm-dd',
9
- maxDate: '0',
10
- onClose: function( selectedDate ) {
11
- jQuery( "#wcf_ca_custom_filter_to" ).datepicker( "option", "minDate", selectedDate );
12
- }
13
- }).attr('readonly','readonly').css('background', 'white');
14
-
15
- $("#wcf_ca_custom_filter_to").datepicker({
16
- dateFormat: 'yy-mm-dd',
17
- maxDate: '0',
18
- onClose: function( selectedDate ) {
19
- jQuery( "#wcf_ca_custom_filter_from" ).datepicker( "option", "maxDate", selectedDate );
20
- }
21
- }).attr('readonly','readonly').css('background', 'white');
22
-
23
- $("#wcf_ca_custom_filter").click(function () {
24
- var from = $("#wcf_ca_custom_filter_from").val().trim();
25
- var to = $("#wcf_ca_custom_filter_to").val().trim();
26
- var url = window.location.search;
27
- url = url + "&from_date=" + from + "&to_date=" + to + "&filter=custom";
28
- window.location.href = url;
29
-
30
- });
31
-
32
- $("#wcf_search_id_submit").click( function () {
33
- var search = $("#wcf_search_id_search_input").val().trim();
34
- window.location.href = window.location.search + "&search_term=" + search;
35
- } );
36
-
37
- // Hide initially.
38
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry, #wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status, #wcf_ca_gdpr_message").closest('tr').hide();
39
-
40
-
41
- if( $("#wcf_ca_gdpr_status:checked").length ) {
42
- $("#wcf_ca_gdpr_message").closest('tr').show();
43
- }
44
-
45
- if ($("#wcf_ca_zapier_tracking_status:checked").length) {
46
- $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').show();
47
- }
48
-
49
- if ( $("#wcf_ca_coupon_code_status:checked").length && $("#wcf_ca_zapier_tracking_status:checked").length ) {
50
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').show();
51
- }
52
-
53
- $("#wcf_ca_coupon_code_status").click(
54
- function () {
55
- if (!$("#wcf_ca_coupon_code_status:checked").length) {
56
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeOut();
57
- } else {
58
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeIn();
59
- }
60
- }
61
- );
62
-
63
- $("#wcf_ca_gdpr_status").click(
64
- function () {
65
- if (!$("#wcf_ca_gdpr_status:checked").length) {
66
- $("#wcf_ca_gdpr_message").closest('tr').fadeOut();
67
- } else {
68
- $("#wcf_ca_gdpr_message").closest('tr').fadeIn();
69
- }
70
- }
71
- );
72
-
73
- $("#wcf_ca_zapier_tracking_status").click(
74
- function () {
75
- if (!$("#wcf_ca_zapier_tracking_status:checked").length) {
76
- $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').fadeOut();
77
- } else {
78
- $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').fadeIn();
79
- }
80
-
81
- if ($("#wcf_ca_coupon_code_status:checked").length && $("#wcf_ca_zapier_tracking_status:checked").length) {
82
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeIn();
83
- } else {
84
- $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeOut();
85
- }
86
- }
87
- );
88
-
89
- }
90
- }
91
-
92
-
93
- EmailTemplatesAdmin = {
94
-
95
-
96
- init: function () {
97
-
98
- $(document).on('click', '#wcf_preview_email', EmailTemplatesAdmin.send_test_email);
99
- $(document).on('click', '.wcf-ca-switch.wcf-toggle-template-status', EmailTemplatesAdmin.toggle_activate_template);
100
- $(document).on('click', '#wcf_ca_delete_coupons', EmailTemplatesAdmin.delete_coupons);
101
- $(document).on('click', '#wcf_ca_export_orders', EmailTemplatesAdmin.export_orders);
102
- $(document).on('click', '.wcar-switch-grid', EmailTemplatesAdmin.toggle_activate_template_on_grid);
103
- var coupon_child_fields = "#wcf_email_discount_type, #wcf_email_discount_amount, #wcf_email_coupon_expiry_date, #wcf_free_shipping_coupon, #wcf_auto_coupon_apply, #wcf_individual_use_only";
104
- $(coupon_child_fields).closest('tr').toggle($("#wcf_override_global_coupon").is(":checked"));
105
- $(document).on('click', '#wcf_override_global_coupon', function () {
106
- $(coupon_child_fields).closest('tr').fadeToggle($("#wcf_override_global_coupon").is(":checked"));
107
- });
108
- },
109
-
110
- send_test_email: function () {
111
-
112
- var email_body = '';
113
- if (jQuery("#wp-wcf_email_body-wrap").hasClass("tmce-active")) {
114
- email_body = tinyMCE.get('wcf_email_body').getContent();
115
- } else {
116
- email_body = jQuery('#wcf_email_body').val();
117
- }
118
-
119
- var email_subject = $('#wcf_email_subject').val();
120
- var email_send_to = $('#wcf_send_test_email').val();
121
- var wp_nonce = $("#_wpnonce").val();
122
-
123
- $(this).next('div.error').remove();
124
-
125
- if (!$.trim(email_body)) {
126
- $(this).after('<div class="error-message wcf-ca-error-msg"> Email body is required! </div>');
127
- } else if (!$.trim(email_subject)) {
128
- $(this).after('<div class="error-message wcf-ca-error-msg"> Email subject is required! </div>');
129
- } else if (!$.trim(email_send_to)) {
130
- $(this).after('<div class="error-message wcf-ca-error-msg"> You must add your email id! </div>');
131
- }
132
- else {
133
-
134
- var data = {
135
- email_subject: email_subject,
136
- email_body: email_body,
137
- email_send_to: email_send_to,
138
- action: 'wcf_ca_preview_email_send',
139
- security: wp_nonce,
140
- };
141
- $("#wcf_preview_email").css('cursor', 'wait').attr("disabled", true);
142
-
143
- // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
144
- $.post(
145
- ajaxurl, data, function (response) {
146
- $("#mail_response_msg").empty().fadeIn();;
147
-
148
- if (response.success) {
149
- var htmlString = "<strong> Email has been sent successfully! </strong>";
150
- $("#mail_response_msg").css('color','green').html(htmlString).delay(3000).fadeOut();
151
-
152
- } else {
153
- var htmlString = "<strong> Email sending failed! Please check your SMTP settings! </a></strong>"
154
- $("#mail_response_msg").css('color','red').html(htmlString).delay(3000).fadeOut();;
155
- }
156
- $("#wcf_preview_email").css('cursor', '').attr("disabled", false);
157
-
158
- }
159
- );
160
- }
161
-
162
- $(".wcf-ca-error-msg").delay(2000).fadeOut();
163
- },
164
-
165
- delete_coupons: function () {
166
- if (confirm(wcf_ca_localized_vars._confirm_msg)) {
167
- var data = {
168
- action: 'wcf_ca_delete_garbage_coupons',
169
- security: wcf_ca_localized_vars._delete_coupon_nonce
170
- };
171
- $('.wcf-ca-spinner').show();
172
-
173
- $('.wcf-ca-spinner').addClass("is-active");
174
- $("#wcf_ca_delete_coupons").css('cursor', 'wait').attr("disabled", true);
175
- $.post(
176
- ajaxurl, data, function (response) {
177
- $(".wcf-ca-response-msg").empty().fadeIn();;
178
- if (response.success) {
179
- $('.wcf-ca-spinner').hide();
180
- $(".wcf-ca-response-msg").css('color','green').html(response.data).delay(5000).fadeOut();
181
- }
182
-
183
- $("#wcf_ca_delete_coupons").css('cursor', '').attr("disabled", false);
184
- }
185
- );
186
- }
187
- },
188
- export_orders: function () {
189
- if( confirm( wcf_ca_localized_vars._confirm_msg_export ) ) {
190
- window.location.href = window.location.search + "&export_data=true";
191
- }
192
- },
193
- toggle_activate_template_on_grid: function () {
194
- var $switch, state, new_state;
195
- $switch = $(this);
196
- state = $switch.attr('wcf-ca-template-switch');
197
- var css = (state === 'on') ? 'green' : 'red';
198
-
199
- $.post(
200
- ajaxurl, {
201
- action: 'activate_email_templates',
202
- id: $(this).attr('id'),
203
- state: state,
204
- security: wcf_ca_details.email_toggle_button_nonce
205
- }, function (response) {
206
-
207
- $("#wcf_activate_email_template").val(new_state == 'on' ? 1 : 0);
208
-
209
- $(".wcar_tmpl_response_msg").remove();
210
-
211
- $("<span class='wcar_tmpl_response_msg'> " + response.data + " </span>").insertAfter($switch).delay(2000).fadeOut().css('color', css);
212
-
213
- }
214
- );
215
- },
216
-
217
-
218
- toggle_activate_template: function () {
219
- var $switch, state, new_state;
220
- $switch = $(this);
221
- state = $switch.attr('wcf-ca-template-switch');
222
- new_state = state === 'on' ? 'off' : 'on';
223
- $("#wcf_activate_email_template").val(new_state == 'on' ? 1 : 0);
224
- $switch.attr('wcf-ca-template-switch', new_state);
225
- }
226
- }
227
-
228
- ZapierSettings = {
229
- init: function () {
230
-
231
- $(document).delegate("#wcf_ca_trigger_web_hook_abandoned_btn", "click",
232
- { 'order_status': 'abandoned' },
233
- ZapierSettings.zapier_trigger_sample);
234
- },
235
- zapier_trigger_sample: function( event ) {
236
-
237
- var zapier_webhook_url = $("#wcf_ca_zapier_cart_"+ event.data.order_status +"_webhook").val().trim();
238
-
239
- if ( ! zapier_webhook_url.length ) {
240
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Webhook URL is required.").fadeIn().css('color', '#dc3232').delay(2000).fadeOut();
241
- return;
242
- }
243
-
244
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Triggering...").fadeIn();
245
-
246
- var now = new Date();
247
- var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
248
- datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();
249
- if ($.trim(zapier_webhook_url) !== "") {
250
- var sample_data = {
251
- "first_name": wcf_ca_details.name,
252
- "last_name": wcf_ca_details.surname,
253
- "email": wcf_ca_details.email,
254
- "order_status": event.data.order_status,
255
- "checkout_url": window.location.origin + "/checkout/?wcf_ac_token=something",
256
- "coupon_code": "abcgefgh",
257
- "product_names": "Product1, Product2 & Product3",
258
- "cart_total": wcf_ca_details.woo_currency_symbol + "20",
259
- "product_table": '<table align= left; cellpadding="10" cellspacing="0" style="float: none; border: 1px solid #e5e5e5;"> <tr align="center"> <th style="color: #636363; border: 1px solid #e5e5e5;">Item</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Name</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Quantity</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Price</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Line Subtotal</th> </tr> <tr style=color: #636363; border: 1px solid #e5e5e5; align="center"> <td style="color: #636363; border: 1px solid #e5e5e5;"><img class="demo_img" style="height: 42px; width: 42px;" src="http://localhost/wp-local/wp-content/uploads/2019/11/wGdyS_Zgz1A.jpg"></td> <td style="color: #636363; border: 1px solid #e5e5e5;">Product1</td> <td style="color: #636363; border: 1px solid #e5e5e5;"> 1 </td> <td style="color: #636363; border: 1px solid #e5e5e5;">&pound;85.00</td> <td style="color: #636363; border: 1px solid #e5e5e5;" >&pound;85.00</td> </tr> </table>'
260
- };
261
- $.ajax({
262
- url: zapier_webhook_url,
263
- type: 'POST',
264
- data: sample_data,
265
- success: function(data) {
266
- if (data.status == "success") {
267
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Success!").css('color', '#46b450');
268
- } else {
269
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Failed!").css('color', '#dc3232');
270
- }
271
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").fadeIn().delay(2000).fadeOut();
272
- },
273
- error: function() {
274
- $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Failed!").css('color', '#dc3232');
275
- }
276
- });
277
- } else {
278
- $("wcf_ca"+ event.data.order_status +"_btn_message").text("Please verify webhook URL.").fadeIn().delay(2000).fadeOut();;
279
- }
280
- },
281
- }
282
-
283
- ToolTipHover = {
284
- init: function () {
285
-
286
- $(".wcf-ca-report-table-row .wcf-ca-icon-row").hover(function(){
287
- $(this).find('.wcf-ca-tooltip-text').toggleClass("display_tool_tip");
288
- });
289
- },
290
- }
291
-
292
-
293
- $(document).ready(
294
- function () {
295
- EmailTemplatesAdmin.init();
296
- CartAbandonmentSettings.init();
297
- ZapierSettings.init();
298
- ToolTipHover.init();
299
- }
300
-
301
- );
302
-
303
-
304
  })(jQuery);
1
+ (function ($) {
2
+
3
+ CartAbandonmentSettings = {
4
+
5
+ init: function () {
6
+
7
+ $("#wcf_ca_custom_filter_from").datepicker({
8
+ dateFormat: 'yy-mm-dd',
9
+ maxDate: '0',
10
+ onClose: function( selectedDate ) {
11
+ jQuery( "#wcf_ca_custom_filter_to" ).datepicker( "option", "minDate", selectedDate );
12
+ }
13
+ }).attr('readonly','readonly').css('background', 'white');
14
+
15
+ $("#wcf_ca_custom_filter_to").datepicker({
16
+ dateFormat: 'yy-mm-dd',
17
+ maxDate: '0',
18
+ onClose: function( selectedDate ) {
19
+ jQuery( "#wcf_ca_custom_filter_from" ).datepicker( "option", "maxDate", selectedDate );
20
+ }
21
+ }).attr('readonly','readonly').css('background', 'white');
22
+
23
+ $("#wcf_ca_custom_filter").click(function () {
24
+ var from = $("#wcf_ca_custom_filter_from").val().trim();
25
+ var to = $("#wcf_ca_custom_filter_to").val().trim();
26
+ var url = window.location.search;
27
+ url = url + "&from_date=" + from + "&to_date=" + to + "&filter=custom";
28
+ window.location.href = url;
29
+
30
+ });
31
+
32
+ $("#wcf_search_id_submit").click( function () {
33
+ var search = $("#wcf_search_id_search_input").val().trim();
34
+ window.location.href = window.location.search + "&search_term=" + search;
35
+ } );
36
+
37
+ // Hide initially.
38
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry, #wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status, #wcf_ca_gdpr_message").closest('tr').hide();
39
+
40
+
41
+ if( $("#wcf_ca_gdpr_status:checked").length ) {
42
+ $("#wcf_ca_gdpr_message").closest('tr').show();
43
+ }
44
+
45
+ if ($("#wcf_ca_zapier_tracking_status:checked").length) {
46
+ $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').show();
47
+ }
48
+
49
+ if ( $("#wcf_ca_coupon_code_status:checked").length && $("#wcf_ca_zapier_tracking_status:checked").length ) {
50
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').show();
51
+ }
52
+
53
+ $("#wcf_ca_coupon_code_status").click(
54
+ function () {
55
+ if (!$("#wcf_ca_coupon_code_status:checked").length) {
56
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeOut();
57
+ } else {
58
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeIn();
59
+ }
60
+ }
61
+ );
62
+
63
+ $("#wcf_ca_gdpr_status").click(
64
+ function () {
65
+ if (!$("#wcf_ca_gdpr_status:checked").length) {
66
+ $("#wcf_ca_gdpr_message").closest('tr').fadeOut();
67
+ } else {
68
+ $("#wcf_ca_gdpr_message").closest('tr').fadeIn();
69
+ }
70
+ }
71
+ );
72
+
73
+ $("#wcf_ca_zapier_tracking_status").click(
74
+ function () {
75
+ if (!$("#wcf_ca_zapier_tracking_status:checked").length) {
76
+ $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').fadeOut();
77
+ } else {
78
+ $("#wcf_ca_zapier_cart_abandoned_webhook, #wcf_ca_coupon_code_status").closest('tr').fadeIn();
79
+ }
80
+
81
+ if ($("#wcf_ca_coupon_code_status:checked").length && $("#wcf_ca_zapier_tracking_status:checked").length) {
82
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeIn();
83
+ } else {
84
+ $("#wcf_ca_discount_type, #wcf_ca_coupon_amount, #wcf_ca_coupon_expiry").closest('tr').fadeOut();
85
+ }
86
+ }
87
+ );
88
+
89
+ }
90
+ }
91
+
92
+
93
+ EmailTemplatesAdmin = {
94
+
95
+
96
+ init: function () {
97
+
98
+ $(document).on('click', '#wcf_preview_email', EmailTemplatesAdmin.send_test_email);
99
+ $(document).on('click', '.wcf-ca-switch.wcf-toggle-template-status', EmailTemplatesAdmin.toggle_activate_template);
100
+ $(document).on('click', '#wcf_ca_delete_coupons', EmailTemplatesAdmin.delete_coupons);
101
+ $(document).on('click', '#wcf_ca_export_orders', EmailTemplatesAdmin.export_orders);
102
+ $(document).on('click', '.wcar-switch-grid', EmailTemplatesAdmin.toggle_activate_template_on_grid);
103
+ var coupon_child_fields = "#wcf_email_discount_type, #wcf_email_discount_amount, #wcf_email_coupon_expiry_date, #wcf_free_shipping_coupon, #wcf_auto_coupon_apply, #wcf_individual_use_only";
104
+ $(coupon_child_fields).closest('tr').toggle($("#wcf_override_global_coupon").is(":checked"));
105
+ $(document).on('click', '#wcf_override_global_coupon', function () {
106
+ $(coupon_child_fields).closest('tr').fadeToggle($("#wcf_override_global_coupon").is(":checked"));
107
+ });
108
+ },
109
+
110
+ send_test_email: function () {
111
+
112
+ var email_body = '';
113
+ if (jQuery("#wp-wcf_email_body-wrap").hasClass("tmce-active")) {
114
+ email_body = tinyMCE.get('wcf_email_body').getContent();
115
+ } else {
116
+ email_body = jQuery('#wcf_email_body').val();
117
+ }
118
+
119
+ var email_subject = $('#wcf_email_subject').val();
120
+ var email_send_to = $('#wcf_send_test_email').val();
121
+ var wp_nonce = $("#_wpnonce").val();
122
+
123
+ $(this).next('div.error').remove();
124
+
125
+ if (!$.trim(email_body)) {
126
+ $(this).after('<div class="error-message wcf-ca-error-msg"> Email body is required! </div>');
127
+ } else if (!$.trim(email_subject)) {
128
+ $(this).after('<div class="error-message wcf-ca-error-msg"> Email subject is required! </div>');
129
+ } else if (!$.trim(email_send_to)) {
130
+ $(this).after('<div class="error-message wcf-ca-error-msg"> You must add your email id! </div>');
131
+ }
132
+ else {
133
+
134
+ var data = {
135
+ email_subject: email_subject,
136
+ email_body: email_body,
137
+ email_send_to: email_send_to,
138
+ action: 'wcf_ca_preview_email_send',
139
+ security: wp_nonce,
140
+ };
141
+ $("#wcf_preview_email").css('cursor', 'wait').attr("disabled", true);
142
+
143
+ // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
144
+ $.post(
145
+ ajaxurl, data, function (response) {
146
+ $("#mail_response_msg").empty().fadeIn();;
147
+
148
+ if (response.success) {
149
+ var htmlString = "<strong> Email has been sent successfully! </strong>";
150
+ $("#mail_response_msg").css('color','green').html(htmlString).delay(3000).fadeOut();
151
+
152
+ } else {
153
+ var htmlString = "<strong> Email sending failed! Please check your SMTP settings! </a></strong>"
154
+ $("#mail_response_msg").css('color','red').html(htmlString).delay(3000).fadeOut();;
155
+ }
156
+ $("#wcf_preview_email").css('cursor', '').attr("disabled", false);
157
+
158
+ }
159
+ );
160
+ }
161
+
162
+ $(".wcf-ca-error-msg").delay(2000).fadeOut();
163
+ },
164
+
165
+ delete_coupons: function () {
166
+ if (confirm(wcf_ca_localized_vars._confirm_msg)) {
167
+ var data = {
168
+ action: 'wcf_ca_delete_garbage_coupons',
169
+ security: wcf_ca_localized_vars._delete_coupon_nonce
170
+ };
171
+ $('.wcf-ca-spinner').show();
172
+
173
+ $('.wcf-ca-spinner').addClass("is-active");
174
+ $("#wcf_ca_delete_coupons").css('cursor', 'wait').attr("disabled", true);
175
+ $.post(
176
+ ajaxurl, data, function (response) {
177
+ $(".wcf-ca-response-msg").empty().fadeIn();;
178
+ if (response.success) {
179
+ $('.wcf-ca-spinner').hide();
180
+ $(".wcf-ca-response-msg").css('color','green').html(response.data).delay(5000).fadeOut();
181
+ }
182
+
183
+ $("#wcf_ca_delete_coupons").css('cursor', '').attr("disabled", false);
184
+ }
185
+ );
186
+ }
187
+ },
188
+ export_orders: function () {
189
+ if( confirm( wcf_ca_localized_vars._confirm_msg_export ) ) {
190
+ window.location.href = window.location.search + "&export_data=true";
191
+ }
192
+ },
193
+ toggle_activate_template_on_grid: function () {
194
+ var $switch, state, new_state;
195
+ $switch = $(this);
196
+ state = $switch.attr('wcf-ca-template-switch');
197
+ var css = (state === 'on') ? 'green' : 'red';
198
+
199
+ $.post(
200
+ ajaxurl, {
201
+ action: 'activate_email_templates',
202
+ id: $(this).attr('id'),
203
+ state: state,
204
+ security: wcf_ca_details.email_toggle_button_nonce
205
+ }, function (response) {
206
+
207
+ $("#wcf_activate_email_template").val(new_state == 'on' ? 1 : 0);
208
+
209
+ $(".wcar_tmpl_response_msg").remove();
210
+
211
+ $("<span class='wcar_tmpl_response_msg'> " + response.data + " </span>").insertAfter($switch).delay(2000).fadeOut().css('color', css);
212
+
213
+ }
214
+ );
215
+ },
216
+
217
+
218
+ toggle_activate_template: function () {
219
+ var $switch, state, new_state;
220
+ $switch = $(this);
221
+ state = $switch.attr('wcf-ca-template-switch');
222
+ new_state = state === 'on' ? 'off' : 'on';
223
+ $("#wcf_activate_email_template").val(new_state == 'on' ? 1 : 0);
224
+ $switch.attr('wcf-ca-template-switch', new_state);
225
+ }
226
+ }
227
+
228
+ ZapierSettings = {
229
+ init: function () {
230
+
231
+ $(document).delegate("#wcf_ca_trigger_web_hook_abandoned_btn", "click",
232
+ { 'order_status': 'abandoned' },
233
+ ZapierSettings.zapier_trigger_sample);
234
+ },
235
+ zapier_trigger_sample: function( event ) {
236
+
237
+ var zapier_webhook_url = $("#wcf_ca_zapier_cart_"+ event.data.order_status +"_webhook").val().trim();
238
+
239
+ if ( ! zapier_webhook_url.length ) {
240
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Webhook URL is required.").fadeIn().css('color', '#dc3232').delay(2000).fadeOut();
241
+ return;
242
+ }
243
+
244
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Triggering...").fadeIn();
245
+
246
+ var now = new Date();
247
+ var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
248
+ datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();
249
+ if ($.trim(zapier_webhook_url) !== "") {
250
+ var sample_data = {
251
+ "first_name": wcf_ca_details.name,
252
+ "last_name": wcf_ca_details.surname,
253
+ "email": wcf_ca_details.email,
254
+ "order_status": event.data.order_status,
255
+ "checkout_url": window.location.origin + "/checkout/?wcf_ac_token=something",
256
+ "coupon_code": "abcgefgh",
257
+ "product_names": "Product1, Product2 & Product3",
258
+ "cart_total": wcf_ca_details.woo_currency_symbol + "20",
259
+ "product_table": '<table align= left; cellpadding="10" cellspacing="0" style="float: none; border: 1px solid #e5e5e5;"> <tr align="center"> <th style="color: #636363; border: 1px solid #e5e5e5;">Item</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Name</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Quantity</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Price</th> <th style="color: #636363; border: 1px solid #e5e5e5;">Line Subtotal</th> </tr> <tr style=color: #636363; border: 1px solid #e5e5e5; align="center"> <td style="color: #636363; border: 1px solid #e5e5e5;"><img class="demo_img" style="height: 42px; width: 42px;" src="http://localhost/wp-local/wp-content/uploads/2019/11/wGdyS_Zgz1A.jpg"></td> <td style="color: #636363; border: 1px solid #e5e5e5;">Product1</td> <td style="color: #636363; border: 1px solid #e5e5e5;"> 1 </td> <td style="color: #636363; border: 1px solid #e5e5e5;">&pound;85.00</td> <td style="color: #636363; border: 1px solid #e5e5e5;" >&pound;85.00</td> </tr> </table>'
260
+ };
261
+ $.ajax({
262
+ url: zapier_webhook_url,
263
+ type: 'POST',
264
+ data: sample_data,
265
+ success: function(data) {
266
+ if (data.status == "success") {
267
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Success!").css('color', '#46b450');
268
+ } else {
269
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Failed!").css('color', '#dc3232');
270
+ }
271
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").fadeIn().delay(2000).fadeOut();
272
+ },
273
+ error: function() {
274
+ $("#wcf_ca_"+ event.data.order_status +"_btn_message").text("Trigger Failed!").css('color', '#dc3232');
275
+ }
276
+ });
277
+ } else {
278
+ $("wcf_ca"+ event.data.order_status +"_btn_message").text("Please verify webhook URL.").fadeIn().delay(2000).fadeOut();;
279
+ }
280
+ },
281
+ }
282
+
283
+ ToolTipHover = {
284
+ init: function () {
285
+
286
+ $(".wcf-ca-report-table-row .wcf-ca-icon-row").hover(function(){
287
+ $(this).find('.wcf-ca-tooltip-text').toggleClass("display_tool_tip");
288
+ });
289
+ },
290
+ }
291
+
292
+
293
+ $(document).ready(
294
+ function () {
295
+ EmailTemplatesAdmin.init();
296
+ CartAbandonmentSettings.init();
297
+ ZapierSettings.init();
298
+ ToolTipHover.init();
299
+ }
300
+
301
+ );
302
+
303
+
304
  })(jQuery);
admin/assets/js/admin-mce.js CHANGED
@@ -1,108 +1,108 @@
1
- (function ($) {
2
- $(document).ready(
3
- function () {
4
- tinymce.PluginManager.add(
5
- 'cartflows_ac', function (editor, url) {
6
-
7
- editor.addButton(
8
- 'cartflows_ac', {
9
- type: 'menubutton',
10
- text: 'WCAR Fields',
11
- icon: false,
12
- menu: [
13
- {
14
- text: wcf_ca_details.admin_firstname,
15
- value: '{{admin.firstname}}',
16
- onclick: function () {
17
- editor.insertContent(this.value());
18
- }
19
- },
20
- {
21
- text: wcf_ca_details.admin_company,
22
- value: '{{admin.company}}',
23
- onclick: function () {
24
- editor.insertContent(this.value());
25
- }
26
- },
27
- {
28
- text: wcf_ca_details.abandoned_product_details_table,
29
- value: '{{cart.product.table}}',
30
- onclick: function () {
31
- editor.insertContent(this.value());
32
- }
33
- },
34
- {
35
- text: wcf_ca_details.abandoned_product_names,
36
- value: '{{cart.product.names}}',
37
- onclick: function () {
38
- editor.insertContent(this.value());
39
- }
40
- },
41
- {
42
- text: wcf_ca_details.cart_checkout_url,
43
- value: '{{cart.checkout_url}}',
44
- onclick: function () {
45
- editor.insertContent(this.value());
46
- }
47
- },
48
- {
49
- text: wcf_ca_details.coupon_code,
50
- value: '{{cart.coupon_code}}',
51
- onclick: function () {
52
- editor.insertContent(this.value());
53
- }
54
- },
55
- {
56
- text: wcf_ca_details.customer_firstname,
57
- value: '{{customer.firstname}}',
58
- onclick: function () {
59
- editor.insertContent(this.value());
60
- }
61
- },
62
- {
63
- text: wcf_ca_details.customer_lastname,
64
- value: '{{customer.lastname}}',
65
- onclick: function () {
66
- editor.insertContent(this.value());
67
- }
68
- },
69
- {
70
- text: wcf_ca_details.customer_full_name,
71
- value: '{{customer.fullname}}',
72
- onclick: function () {
73
- editor.insertContent(this.value());
74
- }
75
- },
76
- {
77
- text: wcf_ca_details.cart_abandonment_date,
78
- value: '{{cart.abandoned_date}}',
79
- onclick: function () {
80
- editor.insertContent(this.value());
81
- }
82
- },
83
- {
84
- text: wcf_ca_details.site_url,
85
- value: '{{site.url}}',
86
- onclick: function () {
87
- editor.insertContent(this.value());
88
- }
89
- },
90
- {
91
- text: wcf_ca_details.unsubscribe_link,
92
- value: '{{cart.unsubscribe}}',
93
- onclick: function () {
94
- editor.insertContent(this.value());
95
- }
96
- },
97
- ].sort(function(a,b){
98
- return a.text.localeCompare(b.text);
99
- })
100
- }
101
- );
102
- }
103
- );
104
- }
105
- );
106
-
107
- })(jQuery);
108
-
1
+ (function ($) {
2
+ $(document).ready(
3
+ function () {
4
+ tinymce.PluginManager.add(
5
+ 'cartflows_ac', function (editor, url) {
6
+
7
+ editor.addButton(
8
+ 'cartflows_ac', {
9
+ type: 'menubutton',
10
+ text: 'WCAR Fields',
11
+ icon: false,
12
+ menu: [
13
+ {
14
+ text: wcf_ca_details.admin_firstname,
15
+ value: '{{admin.firstname}}',
16
+ onclick: function () {
17
+ editor.insertContent(this.value());
18
+ }
19
+ },
20
+ {
21
+ text: wcf_ca_details.admin_company,
22
+ value: '{{admin.company}}',
23
+ onclick: function () {
24
+ editor.insertContent(this.value());
25
+ }
26
+ },
27
+ {
28
+ text: wcf_ca_details.abandoned_product_details_table,
29
+ value: '{{cart.product.table}}',
30
+ onclick: function () {
31
+ editor.insertContent(this.value());
32
+ }
33
+ },
34
+ {
35
+ text: wcf_ca_details.abandoned_product_names,
36
+ value: '{{cart.product.names}}',
37
+ onclick: function () {
38
+ editor.insertContent(this.value());
39
+ }
40
+ },
41
+ {
42
+ text: wcf_ca_details.cart_checkout_url,
43
+ value: '{{cart.checkout_url}}',
44
+ onclick: function () {
45
+ editor.insertContent(this.value());
46
+ }
47
+ },
48
+ {
49
+ text: wcf_ca_details.coupon_code,
50
+ value: '{{cart.coupon_code}}',
51
+ onclick: function () {
52
+ editor.insertContent(this.value());
53
+ }
54
+ },
55
+ {
56
+ text: wcf_ca_details.customer_firstname,
57
+ value: '{{customer.firstname}}',
58
+ onclick: function () {
59
+ editor.insertContent(this.value());
60
+ }
61
+ },
62
+ {
63
+ text: wcf_ca_details.customer_lastname,
64
+ value: '{{customer.lastname}}',
65
+ onclick: function () {
66
+ editor.insertContent(this.value());
67
+ }
68
+ },
69
+ {
70
+ text: wcf_ca_details.customer_full_name,
71
+ value: '{{customer.fullname}}',
72
+ onclick: function () {
73
+ editor.insertContent(this.value());
74
+ }
75
+ },
76
+ {
77
+ text: wcf_ca_details.cart_abandonment_date,
78
+ value: '{{cart.abandoned_date}}',
79
+ onclick: function () {
80
+ editor.insertContent(this.value());
81
+ }
82
+ },
83
+ {
84
+ text: wcf_ca_details.site_url,
85
+ value: '{{site.url}}',
86
+ onclick: function () {
87
+ editor.insertContent(this.value());
88
+ }
89
+ },
90
+ {
91
+ text: wcf_ca_details.unsubscribe_link,
92
+ value: '{{cart.unsubscribe}}',
93
+ onclick: function () {
94
+ editor.insertContent(this.value());
95
+ }
96
+ },
97
+ ].sort(function(a,b){
98
+ return a.text.localeCompare(b.text);
99
+ })
100
+ }
101
+ );
102
+ }
103
+ );
104
+ }
105
+ );
106
+
107
+ })(jQuery);
108
+
admin/bsf-analytics/Gruntfile.js DELETED
@@ -1,210 +0,0 @@
1
- module.exports = function (grunt) {
2
- 'use strict';
3
- // Project configuration
4
- var autoprefixer = require('autoprefixer');
5
- var flexibility = require('postcss-flexibility');
6
-
7
- var pkgInfo = grunt.file.readJSON('package.json');
8
-
9
- grunt.initConfig({
10
- pkg: grunt.file.readJSON('package.json'),
11
-
12
- rtlcss: {
13
- options: {
14
- // rtlcss options
15
- config: {
16
- preserveComments: true,
17
- greedy: true
18
- },
19
- // generate source maps
20
- map: false
21
- },
22
- dist: {
23
- files: [
24
- {
25
- expand: true,
26
- cwd: 'assets/css/unminified',
27
- src: [
28
- '*.css',
29
- '!*-rtl.css',
30
- ],
31
- dest: 'assets/css/unminified',
32
- ext: '-rtl.css'
33
- },
34
- ]
35
- }
36
- },
37
-
38
- postcss: {
39
- options: {
40
- map: false,
41
- processors: [
42
- flexibility,
43
- autoprefixer({
44
- browsers: [
45
- 'Android >= 2.1',
46
- 'Chrome >= 21',
47
- 'Edge >= 12',
48
- 'Explorer >= 7',
49
- 'Firefox >= 17',
50
- 'Opera >= 12.1',
51
- 'Safari >= 6.0'
52
- ],
53
- cascade: false
54
- })
55
- ]
56
- },
57
- style: {
58
- expand: true,
59
- src: [
60
- 'assets/css/unminified/**.css',
61
- '!assets/css/unminified/**-rtl.css'
62
- ]
63
- }
64
- },
65
-
66
- cssmin: {
67
- options: {
68
- keepSpecialComments: 0
69
- },
70
- css: {
71
- files: [
72
- {
73
- src: 'assets/css/unminified/style.css',
74
- dest: 'assets/css/minified/style.min.css',
75
- },
76
- {
77
- src: 'assets/css/unminified/style-rtl.css',
78
- dest: 'assets/css/minified/style-rtl.min.css',
79
- },
80
- ]
81
- }
82
- },
83
-
84
- copy: {
85
- main: {
86
- options: {
87
- mode: true
88
- },
89
- src: [
90
- '**',
91
- '!node_modules/**',
92
- '!build/**',
93
- '!css/sourcemap/**',
94
- '!.git/**',
95
- '!bin/**',
96
- '!.gitlab-ci.yml',
97
- '!bin/**',
98
- '!tests/**',
99
- '!phpunit.xml.dist',
100
- '!*.sh',
101
- '!*.map',
102
- '!Gruntfile.js',
103
- '!package.json',
104
- '!.gitignore',
105
- '!phpunit.xml',
106
- '!README.md',
107
- '!sass/**',
108
- '!codesniffer.ruleset.xml',
109
- '!vendor/**',
110
- '!composer.json',
111
- '!composer.lock',
112
- '!package-lock.json',
113
- '!phpcs.xml.dist',
114
- ],
115
- dest: 'bsf-analytics/'
116
- }
117
- },
118
-
119
- compress: {
120
- main: {
121
- options: {
122
- archive: 'bsf-analytics-' + pkgInfo.version + '.zip',
123
- mode: 'zip'
124
- },
125
- files: [
126
- {
127
- src: [
128
- './bsf-analytics/**'
129
- ]
130
-
131
- }
132
- ]
133
- }
134
- },
135
-
136
- clean: {
137
- main: ["bsf-analytics"],
138
- zip: ["*.zip"]
139
-
140
- },
141
-
142
- replace: {
143
-
144
- analytics_const: {
145
- src: ['class-bsf-analytics.php'],
146
- overwrite: true,
147
- replacements: [
148
- {
149
- from: /BSF_ANALYTICS_VERSION', '.*?'/g,
150
- to: 'BSF_ANALYTICS_VERSION\', \'<%= pkg.version %>\''
151
- }
152
- ]
153
- },
154
-
155
- analytics_function_comment: {
156
- src: [
157
- '*.php',
158
- '**/*.php',
159
- '!node_modules/**',
160
- '!php-tests/**',
161
- '!bin/**',
162
- ],
163
- overwrite: true,
164
- replacements: [
165
- {
166
- from: 'x.x.x',
167
- to: '<%=pkg.version %>'
168
- }
169
- ]
170
- },
171
- },
172
- }
173
- );
174
-
175
- // Load grunt tasks
176
- grunt.loadNpmTasks('grunt-rtlcss');
177
- grunt.loadNpmTasks( 'grunt-postcss' );
178
-
179
- grunt.loadNpmTasks('grunt-contrib-cssmin');
180
- grunt.loadNpmTasks('grunt-contrib-copy');
181
- grunt.loadNpmTasks('grunt-contrib-compress');
182
- grunt.loadNpmTasks('grunt-contrib-clean');
183
-
184
- /* Version Bump Task */
185
- grunt.loadNpmTasks( 'grunt-bumpup' );
186
- grunt.loadNpmTasks( 'grunt-text-replace' );
187
-
188
- // rtlcss, you will still need to install ruby and sass on your system manually to run this
189
- grunt.registerTask('rtl', ['rtlcss']);
190
-
191
- // Style
192
- grunt.registerTask('style', ['rtl']);
193
-
194
- // min all
195
- grunt.registerTask('minify', ['style', 'cssmin:css']);
196
-
197
- // Grunt release - Create installable package of the local files
198
- grunt.registerTask('release', ['clean:zip', 'copy:main', 'compress:main', 'clean:main']);
199
-
200
- // Version Bump `grunt bump-version --ver=<version-number>`
201
- grunt.registerTask( 'bump-version', function() {
202
-
203
- var newVersion = grunt.option('ver');
204
-
205
- if ( newVersion ) {
206
- grunt.task.run( 'bumpup:' + newVersion );
207
- grunt.task.run( 'replace' );
208
- }
209
- } );
210
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/assets/css/unminified/style-rtl.css CHANGED
@@ -1,21 +1,21 @@
1
- [ID*="-optin-notice"] {
2
- padding: 1px 12px;
3
- border-right-color: #007cba;
4
- }
5
-
6
- [ID*="-optin-notice"] .notice-container {
7
- padding-top: 10px;
8
- padding-bottom: 12px;
9
- }
10
-
11
- [ID*="-optin-notice"] .notice-content {
12
- margin: 0;
13
- }
14
-
15
- [ID*="-optin-notice"] .notice-heading {
16
- padding: 0 0 12px 20px;
17
- }
18
-
19
- [ID*="-optin-notice"] .button-primary {
20
- margin-left: 5px;
21
  }
1
+ [ID*="-optin-notice"] {
2
+ padding: 1px 12px;
3
+ border-right-color: #007cba;
4
+ }
5
+
6
+ [ID*="-optin-notice"] .notice-container {
7
+ padding-top: 10px;
8
+ padding-bottom: 12px;
9
+ }
10
+
11
+ [ID*="-optin-notice"] .notice-content {
12
+ margin: 0;
13
+ }
14
+
15
+ [ID*="-optin-notice"] .notice-heading {
16
+ padding: 0 0 12px 20px;
17
+ }
18
+
19
+ [ID*="-optin-notice"] .button-primary {
20
+ margin-left: 5px;
21
  }
admin/bsf-analytics/assets/css/unminified/style.css CHANGED
@@ -1,21 +1,21 @@
1
- [ID*="-optin-notice"] {
2
- padding: 1px 12px;
3
- border-left-color: #007cba;
4
- }
5
-
6
- [ID*="-optin-notice"] .notice-container {
7
- padding-top: 10px;
8
- padding-bottom: 12px;
9
- }
10
-
11
- [ID*="-optin-notice"] .notice-content {
12
- margin: 0;
13
- }
14
-
15
- [ID*="-optin-notice"] .notice-heading {
16
- padding: 0 20px 12px 0;
17
- }
18
-
19
- [ID*="-optin-notice"] .button-primary {
20
- margin-right: 5px;
21
  }
1
+ [ID*="-optin-notice"] {
2
+ padding: 1px 12px;
3
+ border-left-color: #007cba;
4
+ }
5
+
6
+ [ID*="-optin-notice"] .notice-container {
7
+ padding-top: 10px;
8
+ padding-bottom: 12px;
9
+ }
10
+
11
+ [ID*="-optin-notice"] .notice-content {
12
+ margin: 0;
13
+ }
14
+
15
+ [ID*="-optin-notice"] .notice-heading {
16
+ padding: 0 20px 12px 0;
17
+ }
18
+
19
+ [ID*="-optin-notice"] .button-primary {
20
+ margin-right: 5px;
21
  }
admin/bsf-analytics/bin/block-commits-with-merge-conflict.sh DELETED
@@ -1,19 +0,0 @@
1
- #!/bin/sh
2
-
3
- ## pre-commit script to prevent merge markers from being committed.
4
-
5
- changed=$(git diff --cached --name-only)
6
-
7
- if [[ -z "$changed" ]]
8
- then
9
- exit 0
10
- fi
11
-
12
- echo $changed | xargs egrep '[><]{7}' -H -I --line-number
13
-
14
- ## If the egrep command has any hits - echo a warning and exit with non-zero status.
15
- if [ $? == 0 ]
16
- then
17
- echo "\n\nWARNING: You have merge markers in the above files, lines. Fix them before committing.\n\n"
18
- exit 1
19
- fi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/class-bsf-analytics-loader.php CHANGED
@@ -1,118 +1,118 @@
1
- <?php
2
- /**
3
- * BSF analytics loader file.
4
- *
5
- * @version 1.0.0
6
- *
7
- * @package bsf-analytics
8
- */
9
-
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- exit();
12
- }
13
-
14
- /**
15
- * Class BSF_Analytics_Loader.
16
- */
17
- class BSF_Analytics_Loader {
18
-
19
- /**
20
- * Analytics Entities.
21
- *
22
- * @access private
23
- * @var array Entities array.
24
- */
25
- private $entities = array();
26
-
27
- /**
28
- * Analytics Version.
29
- *
30
- * @access private
31
- * @var float analytics version.
32
- */
33
- private $analytics_version = '';
34
-
35
- /**
36
- * Analytics path.
37
- *
38
- * @access private
39
- * @var string path array.
40
- */
41
- private $analytics_path = '';
42
-
43
- /**
44
- * Instance
45
- *
46
- * @access private
47
- * @var object Class object.
48
- */
49
- private static $instance = null;
50
-
51
- /**
52
- * Get instace of class.
53
- *
54
- * @return object
55
- */
56
- public static function get_instance() {
57
- if ( null === self::$instance ) {
58
- self::$instance = new self();
59
- }
60
-
61
- return self::$instance;
62
- }
63
-
64
- /**
65
- * Constructor
66
- */
67
- public function __construct() {
68
- add_action( 'init', array( $this, 'load_analytics' ) );
69
- }
70
-
71
- /**
72
- * Set entity for analytics.
73
- *
74
- * @param string $data Entity attributes data.
75
- * @return void
76
- */
77
- public function set_entity( $data ) {
78
- array_push( $this->entities, $data );
79
- }
80
-
81
- /**
82
- * Load Analytics library.
83
- *
84
- * @return void
85
- */
86
- public function load_analytics() {
87
- $unique_entities = array();
88
-
89
- if ( ! empty( $this->entities ) ) {
90
- foreach ( $this->entities as $entity ) {
91
- foreach ( $entity as $key => $data ) {
92
-
93
- if ( isset( $data['path'] ) ) {
94
- if ( file_exists( $data['path'] . '/version.json' ) ) {
95
- $file_contents = file_get_contents( $data['path'] . '/version.json' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
96
- $analytics_version = json_decode( $file_contents, 1 );
97
- $analytics_version = $analytics_version['bsf-analytics-ver'];
98
-
99
- if ( version_compare( $analytics_version, $this->analytics_version, '>' ) ) {
100
- $this->analytics_version = $analytics_version;
101
- $this->analytics_path = $data['path'];
102
- }
103
- }
104
- }
105
-
106
- if ( ! isset( $unique_entities[ $key ] ) ) {
107
- $unique_entities[ $key ] = $data;
108
- }
109
- }
110
- }
111
-
112
- if ( file_exists( $this->analytics_path ) && ! class_exists( 'BSF_Analytics' ) ) {
113
- require_once $this->analytics_path . '/class-bsf-analytics.php';
114
- new BSF_Analytics( $unique_entities, $this->analytics_path, $this->analytics_version );
115
- }
116
- }
117
- }
118
- }
1
+ <?php
2
+ /**
3
+ * BSF analytics loader file.
4
+ *
5
+ * @version 1.0.0
6
+ *
7
+ * @package bsf-analytics
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit();
12
+ }
13
+
14
+ /**
15
+ * Class BSF_Analytics_Loader.
16
+ */
17
+ class BSF_Analytics_Loader {
18
+
19
+ /**
20
+ * Analytics Entities.
21
+ *
22
+ * @access private
23
+ * @var array Entities array.
24
+ */
25
+ private $entities = array();
26
+
27
+ /**
28
+ * Analytics Version.
29
+ *
30
+ * @access private
31
+ * @var float analytics version.
32
+ */
33
+ private $analytics_version = '';
34
+
35
+ /**
36
+ * Analytics path.
37
+ *
38
+ * @access private
39
+ * @var string path array.
40
+ */
41
+ private $analytics_path = '';
42
+
43
+ /**
44
+ * Instance
45
+ *
46
+ * @access private
47
+ * @var object Class object.
48
+ */
49
+ private static $instance = null;
50
+
51
+ /**
52
+ * Get instace of class.
53
+ *
54
+ * @return object
55
+ */
56
+ public static function get_instance() {
57
+ if ( null === self::$instance ) {
58
+ self::$instance = new self();
59
+ }
60
+
61
+ return self::$instance;
62
+ }
63
+
64
+ /**
65
+ * Constructor
66
+ */
67
+ public function __construct() {
68
+ add_action( 'init', array( $this, 'load_analytics' ) );
69
+ }
70
+
71
+ /**
72
+ * Set entity for analytics.
73
+ *
74
+ * @param string $data Entity attributes data.
75
+ * @return void
76
+ */
77
+ public function set_entity( $data ) {
78
+ array_push( $this->entities, $data );
79
+ }
80
+
81
+ /**
82
+ * Load Analytics library.
83
+ *
84
+ * @return void
85
+ */
86
+ public function load_analytics() {
87
+ $unique_entities = array();
88
+
89
+ if ( ! empty( $this->entities ) ) {
90
+ foreach ( $this->entities as $entity ) {
91
+ foreach ( $entity as $key => $data ) {
92
+
93
+ if ( isset( $data['path'] ) ) {
94
+ if ( file_exists( $data['path'] . '/version.json' ) ) {
95
+ $file_contents = file_get_contents( $data['path'] . '/version.json' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
96
+ $analytics_version = json_decode( $file_contents, 1 );
97
+ $analytics_version = $analytics_version['bsf-analytics-ver'];
98
+
99
+ if ( version_compare( $analytics_version, $this->analytics_version, '>' ) ) {
100
+ $this->analytics_version = $analytics_version;
101
+ $this->analytics_path = $data['path'];
102
+ }
103
+ }
104
+ }
105
+
106
+ if ( ! isset( $unique_entities[ $key ] ) ) {
107
+ $unique_entities[ $key ] = $data;
108
+ }
109
+ }
110
+ }
111
+
112
+ if ( file_exists( $this->analytics_path ) && ! class_exists( 'BSF_Analytics' ) ) {
113
+ require_once $this->analytics_path . '/class-bsf-analytics.php';
114
+ new BSF_Analytics( $unique_entities, $this->analytics_path, $this->analytics_version );
115
+ }
116
+ }
117
+ }
118
+ }
admin/bsf-analytics/class-bsf-analytics.php CHANGED
@@ -1,508 +1,508 @@
1
- <?php
2
- /**
3
- * BSF analytics class file.
4
- *
5
- * @version 1.0.0
6
- *
7
- * @package bsf-analytics
8
- */
9
-
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- exit; // Exit if accessed directly.
12
- }
13
-
14
- if ( ! class_exists( 'BSF_Analytics' ) ) {
15
-
16
- /**
17
- * BSF analytics
18
- */
19
- class BSF_Analytics {
20
-
21
- /**
22
- * Member Variable
23
- *
24
- * @var array Entities data.
25
- */
26
- private $entities;
27
-
28
- /**
29
- * Member Variable
30
- *
31
- * @var string Usage tracking document URL
32
- */
33
- public $usage_doc_link = 'https://store.brainstormforce.com/usage-tracking/?utm_source=wp_dashboard&utm_medium=general_settings&utm_campaign=usage_tracking';
34
-
35
- /**
36
- * Setup actions, load files.
37
- *
38
- * @param array $args entity data for analytics.
39
- * @param string $analytics_path directory path to analytics library.
40
- * @param float $analytics_version analytics library version.
41
- * @since 1.0.0
42
- */
43
- public function __construct( $args, $analytics_path, $analytics_version ) {
44
-
45
- // Bail when no analytics entities are registered.
46
- if ( empty( $args ) ) {
47
- return;
48
- }
49
-
50
- $this->entities = $args;
51
-
52
- define( 'BSF_ANALYTICS_VERSION', $analytics_version );
53
- define( 'BSF_ANALYTICS_URI', $this->get_analytics_url( $analytics_path ) );
54
-
55
- add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
56
- add_action( 'admin_notices', array( $this, 'option_notice' ) );
57
- add_action( 'init', array( $this, 'maybe_track_analytics' ), 99 );
58
-
59
- $this->set_actions();
60
-
61
- add_action( 'admin_init', array( $this, 'register_usage_tracking_setting' ) );
62
-
63
- $this->includes();
64
- }
65
-
66
- /**
67
- * Setup actions for admin notice style and analytics cron event.
68
- *
69
- * @since 1.0.4
70
- */
71
- public function set_actions() {
72
-
73
- foreach ( $this->entities as $key => $data ) {
74
- add_action( 'astra_notice_before_markup_' . $key . '-optin-notice', array( $this, 'enqueue_assets' ) );
75
- add_action( 'update_option_' . $key . '_analytics_optin', array( $this, 'update_analytics_option_callback' ), 10, 3 );
76
- add_action( 'add_option_' . $key . '_analytics_optin', array( $this, 'add_analytics_option_callback' ), 10, 2 );
77
- }
78
- }
79
-
80
- /**
81
- * BSF Analytics URL
82
- *
83
- * @param string $analytics_path directory path to analytics library.
84
- * @return String URL of bsf-analytics directory.
85
- * @since 1.0.0
86
- */
87
- public function get_analytics_url( $analytics_path ) {
88
-
89
- $content_dir_path = wp_normalize_path( WP_CONTENT_DIR );
90
-
91
- $analytics_path = wp_normalize_path( $analytics_path );
92
-
93
- return str_replace( $content_dir_path, content_url(), $analytics_path );
94
- }
95
-
96
- /**
97
- * Get API URL for sending analytics.
98
- *
99
- * @return string API URL.
100
- * @since 1.0.0
101
- */
102
- private function get_api_url() {
103
- return defined( 'BSF_API_URL' ) ? BSF_API_URL : 'https://support.brainstormforce.com/';
104
- }
105
-
106
- /**
107
- * Enqueue Scripts.
108
- *
109
- * @since 1.0.0
110
- * @return void
111
- */
112
- public function enqueue_assets() {
113
-
114
- /**
115
- * Load unminified if SCRIPT_DEBUG is true.
116
- *
117
- * Directory and Extensions.
118
- */
119
- $dir_name = ( SCRIPT_DEBUG ) ? 'unminified' : 'minified';
120
- $file_rtl = ( is_rtl() ) ? '-rtl' : '';
121
- $css_ext = ( SCRIPT_DEBUG ) ? '.css' : '.min.css';
122
-
123
- $css_uri = BSF_ANALYTICS_URI . '/assets/css/' . $dir_name . '/style' . $file_rtl . $css_ext;
124
-
125
- wp_enqueue_style( 'bsf-analytics-admin-style', $css_uri, false, BSF_ANALYTICS_VERSION, 'all' );
126
- }
127
-
128
- /**
129
- * Send analytics API call.
130
- *
131
- * @since 1.0.0
132
- */
133
- public function send() {
134
- wp_remote_post(
135
- $this->get_api_url() . 'wp-json/bsf-core/v1/analytics/',
136
- array(
137
- 'body' => BSF_Analytics_Stats::instance()->get_stats(),
138
- 'timeout' => 5,
139
- 'blocking' => false,
140
- )
141
- );
142
- }
143
-
144
- /**
145
- * Check if usage tracking is enabled.
146
- *
147
- * @return bool
148
- * @since 1.0.0
149
- */
150
- public function is_tracking_enabled() {
151
-
152
- foreach ( $this->entities as $key => $data ) {
153
-
154
- $is_enabled = get_site_option( $key . '_analytics_optin' ) === 'yes' ? true : false;
155
- $is_enabled = $this->is_white_label_enabled( $key ) ? false : $is_enabled;
156
-
157
- if ( apply_filters( $key . '_tracking_enabled', $is_enabled ) ) {
158
- return true;
159
- }
160
- }
161
-
162
- return false;
163
- }
164
-
165
- /**
166
- * Check if WHITE label is enabled for BSF products.
167
- *
168
- * @param string $source source of analytics.
169
- * @return bool
170
- * @since 1.0.0
171
- */
172
- public function is_white_label_enabled( $source ) {
173
-
174
- $options = apply_filters( $source . '_white_label_options', array() );
175
- $is_enabled = false;
176
-
177
- if ( is_array( $options ) ) {
178
- foreach ( $options as $option ) {
179
- if ( true === $option ) {
180
- $is_enabled = true;
181
- break;
182
- }
183
- }
184
- }
185
-
186
- return $is_enabled;
187
- }
188
-
189
- /**
190
- * Display admin notice for usage tracking.
191
- *
192
- * @since 1.0.0
193
- */
194
- public function option_notice() {
195
-
196
- if ( ! current_user_can( 'manage_options' ) ) {
197
- return;
198
- }
199
-
200
- foreach ( $this->entities as $key => $data ) {
201
-
202
- $time_to_display = isset( $data['time_to_display'] ) ? $data['time_to_display'] : '+24 hours';
203
- $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
204
-
205
- // Don't display the notice if tracking is disabled or White Label is enabled for any of our plugins.
206
- if ( false !== get_site_option( $key . '_analytics_optin', false ) || $this->is_white_label_enabled( $key ) ) {
207
- continue;
208
- }
209
-
210
- // Show tracker consent notice after 24 hours from installed time.
211
- if ( strtotime( $time_to_display, $this->get_analytics_install_time( $key ) ) > time() ) {
212
- continue;
213
- }
214
-
215
- /* translators: %s product name */
216
- $notice_string = __( 'Want to help make <strong>%1s</strong> even more awesome? Allow us to collect non-sensitive diagnostic data and usage information. ', 'woo-cart-abandonment-recovery' );
217
-
218
- if ( is_multisite() ) {
219
- $notice_string .= __( 'This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
220
- }
221
-
222
- $language_dir = is_rtl() ? 'rtl' : 'ltr';
223
-
224
- Astra_Notices::add_notice(
225
- array(
226
- 'id' => $key . '-optin-notice',
227
- 'type' => '',
228
- 'message' => sprintf(
229
- '<div class="notice-content">
230
- <div class="notice-heading">
231
- %1$s
232
- </div>
233
- <div class="astra-notices-container">
234
- <a href="%2$s" class="astra-notices button-primary">
235
- %3$s
236
- </a>
237
- <a href="%4$s" data-repeat-notice-after="%5$s" class="astra-notices button-secondary">
238
- %6$s
239
- </a>
240
- </div>
241
- </div>',
242
- /* translators: %s usage doc link */
243
- sprintf( $notice_string . '<span dir="%2s"><a href="%3s" target="_blank" rel="noreferrer noopener">%4s</a><span>', esc_html( $data['product_name'] ), $language_dir, esc_url( $usage_doc_link ), __( ' Know More.', 'woo-cart-abandonment-recovery' ) ),
244
- add_query_arg(
245
- array(
246
- $key . '_analytics_optin' => 'yes',
247
- $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
248
- 'bsf_analytics_source' => $key,
249
- )
250
- ),
251
- __( 'Yes! Allow it', 'woo-cart-abandonment-recovery' ),
252
- add_query_arg(
253
- array(
254
- $key . '_analytics_optin' => 'no',
255
- $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
256
- 'bsf_analytics_source' => $key,
257
- )
258
- ),
259
- MONTH_IN_SECONDS,
260
- __( 'No Thanks', 'woo-cart-abandonment-recovery' )
261
- ),
262
- 'show_if' => true,
263
- 'repeat-notice-after' => false,
264
- 'priority' => 18,
265
- 'display-with-other-notices' => true,
266
- )
267
- );
268
- }
269
- }
270
-
271
- /**
272
- * Process usage tracking opt out.
273
- *
274
- * @since 1.0.0
275
- */
276
- public function handle_optin_optout() {
277
-
278
- if ( ! current_user_can( 'manage_options' ) ) {
279
- return;
280
- }
281
-
282
- $source = isset( $_GET['bsf_analytics_source'] ) ? sanitize_text_field( wp_unslash( $_GET['bsf_analytics_source'] ) ) : '';
283
-
284
- if ( ! isset( $_GET[ $source . '_analytics_nonce' ] ) ) {
285
- return;
286
- }
287
-
288
- if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_nonce' ] ) ), $source . '_analytics_optin' ) ) {
289
- return;
290
- }
291
-
292
- $optin_status = isset( $_GET[ $source . '_analytics_optin' ] ) ? sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_optin' ] ) ) : '';
293
-
294
- if ( 'yes' === $optin_status ) {
295
- $this->optin( $source );
296
- } elseif ( 'no' === $optin_status ) {
297
- $this->optout( $source );
298
- }
299
-
300
- wp_safe_redirect(
301
- remove_query_arg(
302
- array(
303
- $source . '_analytics_optin',
304
- $source . '_analytics_nonce',
305
- 'bsf_analytics_source',
306
- )
307
- )
308
- );
309
- }
310
-
311
- /**
312
- * Opt in to usage tracking.
313
- *
314
- * @param string $source source of analytics.
315
- * @since 1.0.0
316
- */
317
- private function optin( $source ) {
318
- update_site_option( $source . '_analytics_optin', 'yes' );
319
- }
320
-
321
- /**
322
- * Opt out to usage tracking.
323
- *
324
- * @param string $source source of analytics.
325
- * @since 1.0.0
326
- */
327
- private function optout( $source ) {
328
- update_site_option( $source . '_analytics_optin', 'no' );
329
- }
330
-
331
- /**
332
- * Load analytics stat class.
333
- *
334
- * @since 1.0.0
335
- */
336
- private function includes() {
337
- require_once __DIR__ . '/class-bsf-analytics-stats.php';
338
- }
339
-
340
- /**
341
- * Register usage tracking option in General settings page.
342
- *
343
- * @since 1.0.0
344
- */
345
- public function register_usage_tracking_setting() {
346
-
347
- foreach ( $this->entities as $key => $data ) {
348
-
349
- if ( ! apply_filters( $key . '_tracking_enabled', true ) || $this->is_white_label_enabled( $key ) ) {
350
- return;
351
- }
352
-
353
- $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
354
- $author = isset( $data['author'] ) ? $data['author'] : 'Brainstorm Force';
355
-
356
- register_setting(
357
- 'general', // Options group.
358
- $key . '_analytics_optin', // Option name/database.
359
- array( 'sanitize_callback' => array( $this, 'sanitize_option' ) ) // sanitize callback function.
360
- );
361
-
362
- add_settings_field(
363
- $key . '-analytics-optin', // Field ID.
364
- __( 'Usage Tracking', 'woo-cart-abandonment-recovery' ), // Field title.
365
- array( $this, 'render_settings_field_html' ), // Field callback function.
366
- 'general',
367
- 'default', // Settings page slug.
368
- array(
369
- 'type' => 'checkbox',
370
- 'title' => $author,
371
- 'name' => $key . '_analytics_optin',
372
- 'label_for' => $key . '-analytics-optin',
373
- 'id' => $key . '-analytics-optin',
374
- 'usage_doc_link' => $usage_doc_link,
375
- )
376
- );
377
- }
378
- }
379
-
380
- /**
381
- * Sanitize Callback Function
382
- *
383
- * @param bool $input Option value.
384
- * @since 1.0.0
385
- */
386
- public function sanitize_option( $input ) {
387
-
388
- if ( ! $input || 'no' === $input ) {
389
- return 'no';
390
- }
391
-
392
- return 'yes';
393
- }
394
-
395
- /**
396
- * Print settings field HTML.
397
- *
398
- * @param array $args arguments to field.
399
- * @since 1.0.0
400
- */
401
- public function render_settings_field_html( $args ) {
402
- ?>
403
- <fieldset>
404
- <label for="<?php echo esc_attr( $args['label_for'] ); ?>">
405
- <input id="<?php echo esc_attr( $args['id'] ); ?>" type="checkbox" value="1" name="<?php echo esc_attr( $args['name'] ); ?>" <?php checked( get_site_option( $args['name'], 'no' ), 'yes' ); ?>>
406
- <?php
407
- /* translators: %s Product title */
408
- echo esc_html( sprintf( __( 'Allow %s products to track non-sensitive usage tracking data.', 'woo-cart-abandonment-recovery' ), $args['title'] ) );// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
409
-
410
- if ( is_multisite() ) {
411
- esc_html_e( ' This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
412
- }
413
- ?>
414
- </label>
415
- <?php
416
- echo wp_kses_post( sprintf( '<a href="%1s" target="_blank" rel="noreferrer noopener">%2s</a>', esc_url( $args['usage_doc_link'] ), __( 'Learn More.', 'woo-cart-abandonment-recovery' ) ) );
417
- ?>
418
- </fieldset>
419
- <?php
420
- }
421
-
422
- /**
423
- * Set analytics installed time in option.
424
- *
425
- * @param string $source source of analytics.
426
- * @return string $time analytics installed time.
427
- * @since 1.0.0
428
- */
429
- private function get_analytics_install_time( $source ) {
430
-
431
- $time = get_site_option( $source . '_analytics_installed_time' );
432
-
433
- if ( ! $time ) {
434
- $time = time();
435
- update_site_option( $source . '_analytics_installed_time', time() );
436
- }
437
-
438
- return $time;
439
- }
440
-
441
- /**
442
- * Schedule/unschedule cron event on updation of option.
443
- *
444
- * @param string $old_value old value of option.
445
- * @param string $value value of option.
446
- * @param string $option Option name.
447
- * @since 1.0.0
448
- */
449
- public function update_analytics_option_callback( $old_value, $value, $option ) {
450
- if ( is_multisite() ) {
451
- $this->add_option_to_network( $option, $value );
452
- }
453
- }
454
-
455
- /**
456
- * Analytics option add callback.
457
- *
458
- * @param string $option Option name.
459
- * @param string $value value of option.
460
- * @since 1.0.0
461
- */
462
- public function add_analytics_option_callback( $option, $value ) {
463
- if ( is_multisite() ) {
464
- $this->add_option_to_network( $option, $value );
465
- }
466
- }
467
-
468
- /**
469
- * Send analaytics track event if tracking is enabled.
470
- *
471
- * @since 1.0.0
472
- */
473
- public function maybe_track_analytics() {
474
-
475
- if ( ! $this->is_tracking_enabled() ) {
476
- return;
477
- }
478
-
479
- $analytics_track = get_site_transient( 'bsf_analytics_track' );
480
-
481
- // If the last data sent is 2 days old i.e. transient is expired.
482
- if ( ! $analytics_track ) {
483
- $this->send();
484
- set_site_transient( 'bsf_analytics_track', true, 2 * DAY_IN_SECONDS );
485
- }
486
- }
487
-
488
- /**
489
- * Save analytics option to network.
490
- *
491
- * @param string $option name of option.
492
- * @param string $value value of option.
493
- * @since 1.0.0
494
- */
495
- public function add_option_to_network( $option, $value ) {
496
-
497
- // If action coming from general settings page.
498
- if ( isset( $_POST['option_page'] ) && 'general' === $_POST['option_page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
499
-
500
- if ( get_site_option( $option ) ) {
501
- update_site_option( $option, $value );
502
- } else {
503
- add_site_option( $option, $value );
504
- }
505
- }
506
- }
507
- }
508
- }
1
+ <?php
2
+ /**
3
+ * BSF analytics class file.
4
+ *
5
+ * @version 1.0.0
6
+ *
7
+ * @package bsf-analytics
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit; // Exit if accessed directly.
12
+ }
13
+
14
+ if ( ! class_exists( 'BSF_Analytics' ) ) {
15
+
16
+ /**
17
+ * BSF analytics
18
+ */
19
+ class BSF_Analytics {
20
+
21
+ /**
22
+ * Member Variable
23
+ *
24
+ * @var array Entities data.
25
+ */
26
+ private $entities;
27
+
28
+ /**
29
+ * Member Variable
30
+ *
31
+ * @var string Usage tracking document URL
32
+ */
33
+ public $usage_doc_link = 'https://store.brainstormforce.com/usage-tracking/?utm_source=wp_dashboard&utm_medium=general_settings&utm_campaign=usage_tracking';
34
+
35
+ /**
36
+ * Setup actions, load files.
37
+ *
38
+ * @param array $args entity data for analytics.
39
+ * @param string $analytics_path directory path to analytics library.
40
+ * @param float $analytics_version analytics library version.
41
+ * @since 1.0.0
42
+ */
43
+ public function __construct( $args, $analytics_path, $analytics_version ) {
44
+
45
+ // Bail when no analytics entities are registered.
46
+ if ( empty( $args ) ) {
47
+ return;
48
+ }
49
+
50
+ $this->entities = $args;
51
+
52
+ define( 'BSF_ANALYTICS_VERSION', $analytics_version );
53
+ define( 'BSF_ANALYTICS_URI', $this->get_analytics_url( $analytics_path ) );
54
+
55
+ add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
56
+ add_action( 'admin_notices', array( $this, 'option_notice' ) );
57
+ add_action( 'init', array( $this, 'maybe_track_analytics' ), 99 );
58
+
59
+ $this->set_actions();
60
+
61
+ add_action( 'admin_init', array( $this, 'register_usage_tracking_setting' ) );
62
+
63
+ $this->includes();
64
+ }
65
+
66
+ /**
67
+ * Setup actions for admin notice style and analytics cron event.
68
+ *
69
+ * @since 1.0.4
70
+ */
71
+ public function set_actions() {
72
+
73
+ foreach ( $this->entities as $key => $data ) {
74
+ add_action( 'astra_notice_before_markup_' . $key . '-optin-notice', array( $this, 'enqueue_assets' ) );
75
+ add_action( 'update_option_' . $key . '_analytics_optin', array( $this, 'update_analytics_option_callback' ), 10, 3 );
76
+ add_action( 'add_option_' . $key . '_analytics_optin', array( $this, 'add_analytics_option_callback' ), 10, 2 );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * BSF Analytics URL
82
+ *
83
+ * @param string $analytics_path directory path to analytics library.
84
+ * @return String URL of bsf-analytics directory.
85
+ * @since 1.0.0
86
+ */
87
+ public function get_analytics_url( $analytics_path ) {
88
+
89
+ $content_dir_path = wp_normalize_path( WP_CONTENT_DIR );
90
+
91
+ $analytics_path = wp_normalize_path( $analytics_path );
92
+
93
+ return str_replace( $content_dir_path, content_url(), $analytics_path );
94
+ }
95
+
96
+ /**
97
+ * Get API URL for sending analytics.
98
+ *
99
+ * @return string API URL.
100
+ * @since 1.0.0
101
+ */
102
+ private function get_api_url() {
103
+ return defined( 'BSF_API_URL' ) ? BSF_API_URL : 'https://support.brainstormforce.com/';
104
+ }
105
+
106
+ /**
107
+ * Enqueue Scripts.
108
+ *
109
+ * @since 1.0.0
110
+ * @return void
111
+ */
112
+ public function enqueue_assets() {
113
+
114
+ /**
115
+ * Load unminified if SCRIPT_DEBUG is true.
116
+ *
117
+ * Directory and Extensions.
118
+ */
119
+ $dir_name = ( SCRIPT_DEBUG ) ? 'unminified' : 'minified';
120
+ $file_rtl = ( is_rtl() ) ? '-rtl' : '';
121
+ $css_ext = ( SCRIPT_DEBUG ) ? '.css' : '.min.css';
122
+
123
+ $css_uri = BSF_ANALYTICS_URI . '/assets/css/' . $dir_name . '/style' . $file_rtl . $css_ext;
124
+
125
+ wp_enqueue_style( 'bsf-analytics-admin-style', $css_uri, false, BSF_ANALYTICS_VERSION, 'all' );
126
+ }
127
+
128
+ /**
129
+ * Send analytics API call.
130
+ *
131
+ * @since 1.0.0
132
+ */
133
+ public function send() {
134
+ wp_remote_post(
135
+ $this->get_api_url() . 'wp-json/bsf-core/v1/analytics/',
136
+ array(
137
+ 'body' => BSF_Analytics_Stats::instance()->get_stats(),
138
+ 'timeout' => 5,
139
+ 'blocking' => false,
140
+ )
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Check if usage tracking is enabled.
146
+ *
147
+ * @return bool
148
+ * @since 1.0.0
149
+ */
150
+ public function is_tracking_enabled() {
151
+
152
+ foreach ( $this->entities as $key => $data ) {
153
+
154
+ $is_enabled = get_site_option( $key . '_analytics_optin' ) === 'yes' ? true : false;
155
+ $is_enabled = $this->is_white_label_enabled( $key ) ? false : $is_enabled;
156
+
157
+ if ( apply_filters( $key . '_tracking_enabled', $is_enabled ) ) {
158
+ return true;
159
+ }
160
+ }
161
+
162
+ return false;
163
+ }
164
+
165
+ /**
166
+ * Check if WHITE label is enabled for BSF products.
167
+ *
168
+ * @param string $source source of analytics.
169
+ * @return bool
170
+ * @since 1.0.0
171
+ */
172
+ public function is_white_label_enabled( $source ) {
173
+
174
+ $options = apply_filters( $source . '_white_label_options', array() );
175
+ $is_enabled = false;
176
+
177
+ if ( is_array( $options ) ) {
178
+ foreach ( $options as $option ) {
179
+ if ( true === $option ) {
180
+ $is_enabled = true;
181
+ break;
182
+ }
183
+ }
184
+ }
185
+
186
+ return $is_enabled;
187
+ }
188
+
189
+ /**
190
+ * Display admin notice for usage tracking.
191
+ *
192
+ * @since 1.0.0
193
+ */
194
+ public function option_notice() {
195
+
196
+ if ( ! current_user_can( 'manage_options' ) ) {
197
+ return;
198
+ }
199
+
200
+ foreach ( $this->entities as $key => $data ) {
201
+
202
+ $time_to_display = isset( $data['time_to_display'] ) ? $data['time_to_display'] : '+24 hours';
203
+ $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
204
+
205
+ // Don't display the notice if tracking is disabled or White Label is enabled for any of our plugins.
206
+ if ( false !== get_site_option( $key . '_analytics_optin', false ) || $this->is_white_label_enabled( $key ) ) {
207
+ continue;
208
+ }
209
+
210
+ // Show tracker consent notice after 24 hours from installed time.
211
+ if ( strtotime( $time_to_display, $this->get_analytics_install_time( $key ) ) > time() ) {
212
+ continue;
213
+ }
214
+
215
+ /* translators: %s product name */
216
+ $notice_string = __( 'Want to help make <strong>%1s</strong> even more awesome? Allow us to collect non-sensitive diagnostic data and usage information. ', 'woo-cart-abandonment-recovery' );
217
+
218
+ if ( is_multisite() ) {
219
+ $notice_string .= __( 'This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
220
+ }
221
+
222
+ $language_dir = is_rtl() ? 'rtl' : 'ltr';
223
+
224
+ Astra_Notices::add_notice(
225
+ array(
226
+ 'id' => $key . '-optin-notice',
227
+ 'type' => '',
228
+ 'message' => sprintf(
229
+ '<div class="notice-content">
230
+ <div class="notice-heading">
231
+ %1$s
232
+ </div>
233
+ <div class="astra-notices-container">
234
+ <a href="%2$s" class="astra-notices button-primary">
235
+ %3$s
236
+ </a>
237
+ <a href="%4$s" data-repeat-notice-after="%5$s" class="astra-notices button-secondary">
238
+ %6$s
239
+ </a>
240
+ </div>
241
+ </div>',
242
+ /* translators: %s usage doc link */
243
+ sprintf( $notice_string . '<span dir="%2s"><a href="%3s" target="_blank" rel="noreferrer noopener">%4s</a><span>', esc_html( $data['product_name'] ), $language_dir, esc_url( $usage_doc_link ), __( ' Know More.', 'woo-cart-abandonment-recovery' ) ),
244
+ add_query_arg(
245
+ array(
246
+ $key . '_analytics_optin' => 'yes',
247
+ $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
248
+ 'bsf_analytics_source' => $key,
249
+ )
250
+ ),
251
+ __( 'Yes! Allow it', 'woo-cart-abandonment-recovery' ),
252
+ add_query_arg(
253
+ array(
254
+ $key . '_analytics_optin' => 'no',
255
+ $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
256
+ 'bsf_analytics_source' => $key,
257
+ )
258
+ ),
259
+ MONTH_IN_SECONDS,
260
+ __( 'No Thanks', 'woo-cart-abandonment-recovery' )
261
+ ),
262
+ 'show_if' => true,
263
+ 'repeat-notice-after' => false,
264
+ 'priority' => 18,
265
+ 'display-with-other-notices' => true,
266
+ )
267
+ );
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Process usage tracking opt out.
273
+ *
274
+ * @since 1.0.0
275
+ */
276
+ public function handle_optin_optout() {
277
+
278
+ if ( ! current_user_can( 'manage_options' ) ) {
279
+ return;
280
+ }
281
+
282
+ $source = isset( $_GET['bsf_analytics_source'] ) ? sanitize_text_field( wp_unslash( $_GET['bsf_analytics_source'] ) ) : '';
283
+
284
+ if ( ! isset( $_GET[ $source . '_analytics_nonce' ] ) ) {
285
+ return;
286
+ }
287
+
288
+ if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_nonce' ] ) ), $source . '_analytics_optin' ) ) {
289
+ return;
290
+ }
291
+
292
+ $optin_status = isset( $_GET[ $source . '_analytics_optin' ] ) ? sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_optin' ] ) ) : '';
293
+
294
+ if ( 'yes' === $optin_status ) {
295
+ $this->optin( $source );
296
+ } elseif ( 'no' === $optin_status ) {
297
+ $this->optout( $source );
298
+ }
299
+
300
+ wp_safe_redirect(
301
+ remove_query_arg(
302
+ array(
303
+ $source . '_analytics_optin',
304
+ $source . '_analytics_nonce',
305
+ 'bsf_analytics_source',
306
+ )
307
+ )
308
+ );
309
+ }
310
+
311
+ /**
312
+ * Opt in to usage tracking.
313
+ *
314
+ * @param string $source source of analytics.
315
+ * @since 1.0.0
316
+ */
317
+ private function optin( $source ) {
318
+ update_site_option( $source . '_analytics_optin', 'yes' );
319
+ }
320
+
321
+ /**
322
+ * Opt out to usage tracking.
323
+ *
324
+ * @param string $source source of analytics.
325
+ * @since 1.0.0
326
+ */
327
+ private function optout( $source ) {
328
+ update_site_option( $source . '_analytics_optin', 'no' );
329
+ }
330
+
331
+ /**
332
+ * Load analytics stat class.
333
+ *
334
+ * @since 1.0.0
335
+ */
336
+ private function includes() {
337
+ require_once __DIR__ . '/class-bsf-analytics-stats.php';
338
+ }
339
+
340
+ /**
341
+ * Register usage tracking option in General settings page.
342
+ *
343
+ * @since 1.0.0
344
+ */
345
+ public function register_usage_tracking_setting() {
346
+
347
+ foreach ( $this->entities as $key => $data ) {
348
+
349
+ if ( ! apply_filters( $key . '_tracking_enabled', true ) || $this->is_white_label_enabled( $key ) ) {
350
+ return;
351
+ }
352
+
353
+ $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
354
+ $author = isset( $data['author'] ) ? $data['author'] : 'Brainstorm Force';
355
+
356
+ register_setting(
357
+ 'general', // Options group.
358
+ $key . '_analytics_optin', // Option name/database.
359
+ array( 'sanitize_callback' => array( $this, 'sanitize_option' ) ) // sanitize callback function.
360
+ );
361
+
362
+ add_settings_field(
363
+ $key . '-analytics-optin', // Field ID.
364
+ __( 'Usage Tracking', 'woo-cart-abandonment-recovery' ), // Field title.
365
+ array( $this, 'render_settings_field_html' ), // Field callback function.
366
+ 'general',
367
+ 'default', // Settings page slug.
368
+ array(
369
+ 'type' => 'checkbox',
370
+ 'title' => $author,
371
+ 'name' => $key . '_analytics_optin',
372
+ 'label_for' => $key . '-analytics-optin',
373
+ 'id' => $key . '-analytics-optin',
374
+ 'usage_doc_link' => $usage_doc_link,
375
+ )
376
+ );
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Sanitize Callback Function
382
+ *
383
+ * @param bool $input Option value.
384
+ * @since 1.0.0
385
+ */
386
+ public function sanitize_option( $input ) {
387
+
388
+ if ( ! $input || 'no' === $input ) {
389
+ return 'no';
390
+ }
391
+
392
+ return 'yes';
393
+ }
394
+
395
+ /**
396
+ * Print settings field HTML.
397
+ *
398
+ * @param array $args arguments to field.
399
+ * @since 1.0.0
400
+ */
401
+ public function render_settings_field_html( $args ) {
402
+ ?>
403
+ <fieldset>
404
+ <label for="<?php echo esc_attr( $args['label_for'] ); ?>">
405
+ <input id="<?php echo esc_attr( $args['id'] ); ?>" type="checkbox" value="1" name="<?php echo esc_attr( $args['name'] ); ?>" <?php checked( get_site_option( $args['name'], 'no' ), 'yes' ); ?>>
406
+ <?php
407
+ /* translators: %s Product title */
408
+ echo esc_html( sprintf( __( 'Allow %s products to track non-sensitive usage tracking data.', 'woo-cart-abandonment-recovery' ), $args['title'] ) );// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
409
+
410
+ if ( is_multisite() ) {
411
+ esc_html_e( ' This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
412
+ }
413
+ ?>
414
+ </label>
415
+ <?php
416
+ echo wp_kses_post( sprintf( '<a href="%1s" target="_blank" rel="noreferrer noopener">%2s</a>', esc_url( $args['usage_doc_link'] ), __( 'Learn More.', 'woo-cart-abandonment-recovery' ) ) );
417
+ ?>
418
+ </fieldset>
419
+ <?php
420
+ }
421
+
422
+ /**
423
+ * Set analytics installed time in option.
424
+ *
425
+ * @param string $source source of analytics.
426
+ * @return string $time analytics installed time.
427
+ * @since 1.0.0
428
+ */
429
+ private function get_analytics_install_time( $source ) {
430
+
431
+ $time = get_site_option( $source . '_analytics_installed_time' );
432
+
433
+ if ( ! $time ) {
434
+ $time = time();
435
+ update_site_option( $source . '_analytics_installed_time', time() );
436
+ }
437
+
438
+ return $time;
439
+ }
440
+
441
+ /**
442
+ * Schedule/unschedule cron event on updation of option.
443
+ *
444
+ * @param string $old_value old value of option.
445
+ * @param string $value value of option.
446
+ * @param string $option Option name.
447
+ * @since 1.0.0
448
+ */
449
+ public function update_analytics_option_callback( $old_value, $value, $option ) {
450
+ if ( is_multisite() ) {
451
+ $this->add_option_to_network( $option, $value );
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Analytics option add callback.
457
+ *
458
+ * @param string $option Option name.
459
+ * @param string $value value of option.
460
+ * @since 1.0.0
461
+ */
462
+ public function add_analytics_option_callback( $option, $value ) {
463
+ if ( is_multisite() ) {
464
+ $this->add_option_to_network( $option, $value );
465
+ }
466
+ }
467
+
468
+ /**
469
+ * Send analaytics track event if tracking is enabled.
470
+ *
471
+ * @since 1.0.0
472
+ */
473
+ public function maybe_track_analytics() {
474
+
475
+ if ( ! $this->is_tracking_enabled() ) {
476
+ return;
477
+ }
478
+
479
+ $analytics_track = get_site_transient( 'bsf_analytics_track' );
480
+
481
+ // If the last data sent is 2 days old i.e. transient is expired.
482
+ if ( ! $analytics_track ) {
483
+ $this->send();
484
+ set_site_transient( 'bsf_analytics_track', true, 2 * DAY_IN_SECONDS );
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Save analytics option to network.
490
+ *
491
+ * @param string $option name of option.
492
+ * @param string $value value of option.
493
+ * @since 1.0.0
494
+ */
495
+ public function add_option_to_network( $option, $value ) {
496
+
497
+ // If action coming from general settings page.
498
+ if ( isset( $_POST['option_page'] ) && 'general' === $_POST['option_page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
499
+
500
+ if ( get_site_option( $option ) ) {
501
+ update_site_option( $option, $value );
502
+ } else {
503
+ add_site_option( $option, $value );
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }
admin/bsf-analytics/composer.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "require-dev": {
3
- "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
4
- "wp-coding-standards/wpcs": "2.2.1",
5
- "phpcompatibility/phpcompatibility-wp": "2.1.0",
6
- "brainmaestro/composer-git-hooks": "^2.6"
7
- },
8
- "scripts": {
9
- "format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source",
10
- "lint": "phpcs --standard=phpcs.xml.dist --report-summary --report-source",
11
- "post-install-cmd": "vendor/bin/cghooks add --ignore-lock",
12
- "post-update-cmd": "vendor/bin/cghooks update"
13
- },
14
- "extra": {
15
- "hooks": {
16
- "pre-commit": [
17
- "echo committing as $(git config user.name)",
18
- "sh bin/block-commits-with-merge-conflict.sh"
19
- ]
20
- }
21
- }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/composer.lock DELETED
@@ -1,711 +0,0 @@
1
- {
2
- "_readme": [
3
- "This file locks the dependencies of your project to a known state",
4
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
- "This file is @generated automatically"
6
- ],
7
- "content-hash": "3fea1d501a95bbd7e1f1492dae0a3847",
8
- "packages": [],
9
- "packages-dev": [
10
- {
11
- "name": "brainmaestro/composer-git-hooks",
12
- "version": "v2.8.3",
13
- "source": {
14
- "type": "git",
15
- "url": "https://github.com/BrainMaestro/composer-git-hooks.git",
16
- "reference": "97888dd34e900931117747cd34a42fdfcf271142"
17
- },
18
- "dist": {
19
- "type": "zip",
20
- "url": "https://api.github.com/repos/BrainMaestro/composer-git-hooks/zipball/97888dd34e900931117747cd34a42fdfcf271142",
21
- "reference": "97888dd34e900931117747cd34a42fdfcf271142",
22
- "shasum": ""
23
- },
24
- "require": {
25
- "php": "^5.6 || >=7.0",
26
- "symfony/console": "^3.2 || ^4.0 || ^5.0"
27
- },
28
- "require-dev": {
29
- "ext-json": "*",
30
- "friendsofphp/php-cs-fixer": "^2.9",
31
- "phpunit/phpunit": "^5.7 || ^7.0"
32
- },
33
- "bin": [
34
- "cghooks"
35
- ],
36
- "type": "library",
37
- "extra": {
38
- "hooks": {
39
- "pre-commit": "composer check-style",
40
- "pre-push": [
41
- "composer test",
42
- "appver=$(grep -o -E '\\d.\\d.\\d' cghooks)",
43
- "tag=$(git describe --tags --abbrev=0)",
44
- "if [ \"$tag\" != \"v$appver\" ]; then",
45
- "echo \"The most recent tag $tag does not match the application version $appver\\n\"",
46
- "tag=${tag#v}",
47
- "sed -i -E \"s/$appver/$tag/\" cghooks",
48
- "exit 1",
49
- "fi"
50
- ]
51
- }
52
- },
53
- "autoload": {
54
- "psr-4": {
55
- "BrainMaestro\\GitHooks\\": "src/"
56
- },
57
- "files": [
58
- "src/helpers.php"
59
- ]
60
- },
61
- "notification-url": "https://packagist.org/downloads/",
62
- "license": [
63
- "MIT"
64
- ],
65
- "authors": [
66
- {
67
- "name": "Ezinwa Okpoechi",
68
- "email": "brainmaestro@outlook.com"
69
- }
70
- ],
71
- "description": "Easily manage git hooks in your composer config",
72
- "keywords": [
73
- "HOOK",
74
- "composer",
75
- "git"
76
- ],
77
- "time": "2019-12-09T09:49:20+00:00"
78
- },
79
- {
80
- "name": "dealerdirect/phpcodesniffer-composer-installer",
81
- "version": "v0.4.4",
82
- "source": {
83
- "type": "git",
84
- "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
85
- "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08"
86
- },
87
- "dist": {
88
- "type": "zip",
89
- "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/2e41850d5f7797cbb1af7b030d245b3b24e63a08",
90
- "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08",
91
- "shasum": ""
92
- },
93
- "require": {
94
- "composer-plugin-api": "^1.0",
95
- "php": "^5.3|^7",
96
- "squizlabs/php_codesniffer": "*"
97
- },
98
- "require-dev": {
99
- "composer/composer": "*",
100
- "wimg/php-compatibility": "^8.0"
101
- },
102
- "suggest": {
103
- "dealerdirect/qa-tools": "All the PHP QA tools you'll need"
104
- },
105
- "type": "composer-plugin",
106
- "extra": {
107
- "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
108
- },
109
- "autoload": {
110
- "psr-4": {
111
- "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
112
- }
113
- },
114
- "notification-url": "https://packagist.org/downloads/",
115
- "license": [
116
- "MIT"
117
- ],
118
- "authors": [
119
- {
120
- "name": "Franck Nijhof",
121
- "email": "f.nijhof@dealerdirect.nl",
122
- "homepage": "http://workingatdealerdirect.eu",
123
- "role": "Developer"
124
- }
125
- ],
126
- "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
127
- "homepage": "http://workingatdealerdirect.eu",
128
- "keywords": [
129
- "PHPCodeSniffer",
130
- "PHP_CodeSniffer",
131
- "code quality",
132
- "codesniffer",
133
- "composer",
134
- "installer",
135
- "phpcs",
136
- "plugin",
137
- "qa",
138
- "quality",
139
- "standard",
140
- "standards",
141
- "style guide",
142
- "stylecheck",
143
- "tests"
144
- ],
145
- "time": "2017-12-06T16:27:17+00:00"
146
- },
147
- {
148
- "name": "phpcompatibility/php-compatibility",
149
- "version": "9.3.5",
150
- "source": {
151
- "type": "git",
152
- "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
153
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
154
- },
155
- "dist": {
156
- "type": "zip",
157
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
158
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
159
- "shasum": ""
160
- },
161
- "require": {
162
- "php": ">=5.3",
163
- "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
164
- },
165
- "conflict": {
166
- "squizlabs/php_codesniffer": "2.6.2"
167
- },
168
- "require-dev": {
169
- "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
170
- },
171
- "suggest": {
172
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
173
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
174
- },
175
- "type": "phpcodesniffer-standard",
176
- "notification-url": "https://packagist.org/downloads/",
177
- "license": [
178
- "LGPL-3.0-or-later"
179
- ],
180
- "authors": [
181
- {
182
- "name": "Wim Godden",
183
- "homepage": "https://github.com/wimg",
184
- "role": "lead"
185
- },
186
- {
187
- "name": "Juliette Reinders Folmer",
188
- "homepage": "https://github.com/jrfnl",
189
- "role": "lead"
190
- },
191
- {
192
- "name": "Contributors",
193
- "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
194
- }
195
- ],
196
- "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
197
- "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
198
- "keywords": [
199
- "compatibility",
200
- "phpcs",
201
- "standards"
202
- ],
203
- "time": "2019-12-27T09:44:58+00:00"
204
- },
205
- {
206
- "name": "phpcompatibility/phpcompatibility-paragonie",
207
- "version": "1.3.0",
208
- "source": {
209
- "type": "git",
210
- "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
211
- "reference": "b862bc32f7e860d0b164b199bd995e690b4b191c"
212
- },
213
- "dist": {
214
- "type": "zip",
215
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/b862bc32f7e860d0b164b199bd995e690b4b191c",
216
- "reference": "b862bc32f7e860d0b164b199bd995e690b4b191c",
217
- "shasum": ""
218
- },
219
- "require": {
220
- "phpcompatibility/php-compatibility": "^9.0"
221
- },
222
- "require-dev": {
223
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5",
224
- "paragonie/random_compat": "dev-master",
225
- "paragonie/sodium_compat": "dev-master"
226
- },
227
- "suggest": {
228
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
229
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
230
- },
231
- "type": "phpcodesniffer-standard",
232
- "notification-url": "https://packagist.org/downloads/",
233
- "license": [
234
- "LGPL-3.0-or-later"
235
- ],
236
- "authors": [
237
- {
238
- "name": "Wim Godden",
239
- "role": "lead"
240
- },
241
- {
242
- "name": "Juliette Reinders Folmer",
243
- "role": "lead"
244
- }
245
- ],
246
- "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
247
- "homepage": "http://phpcompatibility.com/",
248
- "keywords": [
249
- "compatibility",
250
- "paragonie",
251
- "phpcs",
252
- "polyfill",
253
- "standards"
254
- ],
255
- "time": "2019-11-04T15:17:54+00:00"
256
- },
257
- {
258
- "name": "phpcompatibility/phpcompatibility-wp",
259
- "version": "2.1.0",
260
- "source": {
261
- "type": "git",
262
- "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
263
- "reference": "41bef18ba688af638b7310666db28e1ea9158b2f"
264
- },
265
- "dist": {
266
- "type": "zip",
267
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/41bef18ba688af638b7310666db28e1ea9158b2f",
268
- "reference": "41bef18ba688af638b7310666db28e1ea9158b2f",
269
- "shasum": ""
270
- },
271
- "require": {
272
- "phpcompatibility/php-compatibility": "^9.0",
273
- "phpcompatibility/phpcompatibility-paragonie": "^1.0"
274
- },
275
- "require-dev": {
276
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5"
277
- },
278
- "suggest": {
279
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
280
- "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
281
- },
282
- "type": "phpcodesniffer-standard",
283
- "notification-url": "https://packagist.org/downloads/",
284
- "license": [
285
- "LGPL-3.0-or-later"
286
- ],
287
- "authors": [
288
- {
289
- "name": "Wim Godden",
290
- "role": "lead"
291
- },
292
- {
293
- "name": "Juliette Reinders Folmer",
294
- "role": "lead"
295
- }
296
- ],
297
- "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
298
- "homepage": "http://phpcompatibility.com/",
299
- "keywords": [
300
- "compatibility",
301
- "phpcs",
302
- "standards",
303
- "wordpress"
304
- ],
305
- "time": "2019-08-28T14:22:28+00:00"
306
- },
307
- {
308
- "name": "psr/container",
309
- "version": "1.0.0",
310
- "source": {
311
- "type": "git",
312
- "url": "https://github.com/php-fig/container.git",
313
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
314
- },
315
- "dist": {
316
- "type": "zip",
317
- "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
318
- "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
319
- "shasum": ""
320
- },
321
- "require": {
322
- "php": ">=5.3.0"
323
- },
324
- "type": "library",
325
- "extra": {
326
- "branch-alias": {
327
- "dev-master": "1.0.x-dev"
328
- }
329
- },
330
- "autoload": {
331
- "psr-4": {
332
- "Psr\\Container\\": "src/"
333
- }
334
- },
335
- "notification-url": "https://packagist.org/downloads/",
336
- "license": [
337
- "MIT"
338
- ],
339
- "authors": [
340
- {
341
- "name": "PHP-FIG",
342
- "homepage": "http://www.php-fig.org/"
343
- }
344
- ],
345
- "description": "Common Container Interface (PHP FIG PSR-11)",
346
- "homepage": "https://github.com/php-fig/container",
347
- "keywords": [
348
- "PSR-11",
349
- "container",
350
- "container-interface",
351
- "container-interop",
352
- "psr"
353
- ],
354
- "time": "2017-02-14T16:28:37+00:00"
355
- },
356
- {
357
- "name": "squizlabs/php_codesniffer",
358
- "version": "3.5.4",
359
- "source": {
360
- "type": "git",
361
- "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
362
- "reference": "dceec07328401de6211037abbb18bda423677e26"
363
- },
364
- "dist": {
365
- "type": "zip",
366
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dceec07328401de6211037abbb18bda423677e26",
367
- "reference": "dceec07328401de6211037abbb18bda423677e26",
368
- "shasum": ""
369
- },
370
- "require": {
371
- "ext-simplexml": "*",
372
- "ext-tokenizer": "*",
373
- "ext-xmlwriter": "*",
374
- "php": ">=5.4.0"
375
- },
376
- "require-dev": {
377
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
378
- },
379
- "bin": [
380
- "bin/phpcs",
381
- "bin/phpcbf"
382
- ],
383
- "type": "library",
384
- "extra": {
385
- "branch-alias": {
386
- "dev-master": "3.x-dev"
387
- }
388
- },
389
- "notification-url": "https://packagist.org/downloads/",
390
- "license": [
391
- "BSD-3-Clause"
392
- ],
393
- "authors": [
394
- {
395
- "name": "Greg Sherwood",
396
- "role": "lead"
397
- }
398
- ],
399
- "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
400
- "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
401
- "keywords": [
402
- "phpcs",
403
- "standards"
404
- ],
405
- "time": "2020-01-30T22:20:29+00:00"
406
- },
407
- {
408
- "name": "symfony/console",
409
- "version": "v5.0.5",
410
- "source": {
411
- "type": "git",
412
- "url": "https://github.com/symfony/console.git",
413
- "reference": "d29e2d36941de13600c399e393a60b8cfe59ac49"
414
- },
415
- "dist": {
416
- "type": "zip",
417
- "url": "https://api.github.com/repos/symfony/console/zipball/d29e2d36941de13600c399e393a60b8cfe59ac49",
418
- "reference": "d29e2d36941de13600c399e393a60b8cfe59ac49",
419
- "shasum": ""
420
- },
421
- "require": {
422
- "php": "^7.2.5",
423
- "symfony/polyfill-mbstring": "~1.0",
424
- "symfony/polyfill-php73": "^1.8",
425
- "symfony/service-contracts": "^1.1|^2"
426
- },
427
- "conflict": {
428
- "symfony/dependency-injection": "<4.4",
429
- "symfony/event-dispatcher": "<4.4",
430
- "symfony/lock": "<4.4",
431
- "symfony/process": "<4.4"
432
- },
433
- "provide": {
434
- "psr/log-implementation": "1.0"
435
- },
436
- "require-dev": {
437
- "psr/log": "~1.0",
438
- "symfony/config": "^4.4|^5.0",
439
- "symfony/dependency-injection": "^4.4|^5.0",
440
- "symfony/event-dispatcher": "^4.4|^5.0",
441
- "symfony/lock": "^4.4|^5.0",
442
- "symfony/process": "^4.4|^5.0",
443
- "symfony/var-dumper": "^4.4|^5.0"
444
- },
445
- "suggest": {
446
- "psr/log": "For using the console logger",
447
- "symfony/event-dispatcher": "",
448
- "symfony/lock": "",
449
- "symfony/process": ""
450
- },
451
- "type": "library",
452
- "extra": {
453
- "branch-alias": {
454
- "dev-master": "5.0-dev"
455
- }
456
- },
457
- "autoload": {
458
- "psr-4": {
459
- "Symfony\\Component\\Console\\": ""
460
- },
461
- "exclude-from-classmap": [
462
- "/Tests/"
463
- ]
464
- },
465
- "notification-url": "https://packagist.org/downloads/",
466
- "license": [
467
- "MIT"
468
- ],
469
- "authors": [
470
- {
471
- "name": "Fabien Potencier",
472
- "email": "fabien@symfony.com"
473
- },
474
- {
475
- "name": "Symfony Community",
476
- "homepage": "https://symfony.com/contributors"
477
- }
478
- ],
479
- "description": "Symfony Console Component",
480
- "homepage": "https://symfony.com",
481
- "time": "2020-02-24T15:05:31+00:00"
482
- },
483
- {
484
- "name": "symfony/polyfill-mbstring",
485
- "version": "v1.14.0",
486
- "source": {
487
- "type": "git",
488
- "url": "https://github.com/symfony/polyfill-mbstring.git",
489
- "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2"
490
- },
491
- "dist": {
492
- "type": "zip",
493
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2",
494
- "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2",
495
- "shasum": ""
496
- },
497
- "require": {
498
- "php": ">=5.3.3"
499
- },
500
- "suggest": {
501
- "ext-mbstring": "For best performance"
502
- },
503
- "type": "library",
504
- "extra": {
505
- "branch-alias": {
506
- "dev-master": "1.14-dev"
507
- }
508
- },
509
- "autoload": {
510
- "psr-4": {
511
- "Symfony\\Polyfill\\Mbstring\\": ""
512
- },
513
- "files": [
514
- "bootstrap.php"
515
- ]
516
- },
517
- "notification-url": "https://packagist.org/downloads/",
518
- "license": [
519
- "MIT"
520
- ],
521
- "authors": [
522
- {
523
- "name": "Nicolas Grekas",
524
- "email": "p@tchwork.com"
525
- },
526
- {
527
- "name": "Symfony Community",
528
- "homepage": "https://symfony.com/contributors"
529
- }
530
- ],
531
- "description": "Symfony polyfill for the Mbstring extension",
532
- "homepage": "https://symfony.com",
533
- "keywords": [
534
- "compatibility",
535
- "mbstring",
536
- "polyfill",
537
- "portable",
538
- "shim"
539
- ],
540
- "time": "2020-01-13T11:15:53+00:00"
541
- },
542
- {
543
- "name": "symfony/polyfill-php73",
544
- "version": "v1.14.0",
545
- "source": {
546
- "type": "git",
547
- "url": "https://github.com/symfony/polyfill-php73.git",
548
- "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675"
549
- },
550
- "dist": {
551
- "type": "zip",
552
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675",
553
- "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675",
554
- "shasum": ""
555
- },
556
- "require": {
557
- "php": ">=5.3.3"
558
- },
559
- "type": "library",
560
- "extra": {
561
- "branch-alias": {
562
- "dev-master": "1.14-dev"
563
- }
564
- },
565
- "autoload": {
566
- "psr-4": {
567
- "Symfony\\Polyfill\\Php73\\": ""
568
- },
569
- "files": [
570
- "bootstrap.php"
571
- ],
572
- "classmap": [
573
- "Resources/stubs"
574
- ]
575
- },
576
- "notification-url": "https://packagist.org/downloads/",
577
- "license": [
578
- "MIT"
579
- ],
580
- "authors": [
581
- {
582
- "name": "Nicolas Grekas",
583
- "email": "p@tchwork.com"
584
- },
585
- {
586
- "name": "Symfony Community",
587
- "homepage": "https://symfony.com/contributors"
588
- }
589
- ],
590
- "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
591
- "homepage": "https://symfony.com",
592
- "keywords": [
593
- "compatibility",
594
- "polyfill",
595
- "portable",
596
- "shim"
597
- ],
598
- "time": "2020-01-13T11:15:53+00:00"
599
- },
600
- {
601
- "name": "symfony/service-contracts",
602
- "version": "v2.0.1",
603
- "source": {
604
- "type": "git",
605
- "url": "https://github.com/symfony/service-contracts.git",
606
- "reference": "144c5e51266b281231e947b51223ba14acf1a749"
607
- },
608
- "dist": {
609
- "type": "zip",
610
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749",
611
- "reference": "144c5e51266b281231e947b51223ba14acf1a749",
612
- "shasum": ""
613
- },
614
- "require": {
615
- "php": "^7.2.5",
616
- "psr/container": "^1.0"
617
- },
618
- "suggest": {
619
- "symfony/service-implementation": ""
620
- },
621
- "type": "library",
622
- "extra": {
623
- "branch-alias": {
624
- "dev-master": "2.0-dev"
625
- }
626
- },
627
- "autoload": {
628
- "psr-4": {
629
- "Symfony\\Contracts\\Service\\": ""
630
- }
631
- },
632
- "notification-url": "https://packagist.org/downloads/",
633
- "license": [
634
- "MIT"
635
- ],
636
- "authors": [
637
- {
638
- "name": "Nicolas Grekas",
639
- "email": "p@tchwork.com"
640
- },
641
- {
642
- "name": "Symfony Community",
643
- "homepage": "https://symfony.com/contributors"
644
- }
645
- ],
646
- "description": "Generic abstractions related to writing services",
647
- "homepage": "https://symfony.com",
648
- "keywords": [
649
- "abstractions",
650
- "contracts",
651
- "decoupling",
652
- "interfaces",
653
- "interoperability",
654
- "standards"
655
- ],
656
- "time": "2019-11-18T17:27:11+00:00"
657
- },
658
- {
659
- "name": "wp-coding-standards/wpcs",
660
- "version": "2.2.1",
661
- "source": {
662
- "type": "git",
663
- "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
664
- "reference": "b5a453203114cc2284b1a614c4953456fbe4f546"
665
- },
666
- "dist": {
667
- "type": "zip",
668
- "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b5a453203114cc2284b1a614c4953456fbe4f546",
669
- "reference": "b5a453203114cc2284b1a614c4953456fbe4f546",
670
- "shasum": ""
671
- },
672
- "require": {
673
- "php": ">=5.4",
674
- "squizlabs/php_codesniffer": "^3.3.1"
675
- },
676
- "require-dev": {
677
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6",
678
- "phpcompatibility/php-compatibility": "^9.0",
679
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
680
- },
681
- "suggest": {
682
- "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
683
- },
684
- "type": "phpcodesniffer-standard",
685
- "notification-url": "https://packagist.org/downloads/",
686
- "license": [
687
- "MIT"
688
- ],
689
- "authors": [
690
- {
691
- "name": "Contributors",
692
- "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
693
- }
694
- ],
695
- "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
696
- "keywords": [
697
- "phpcs",
698
- "standards",
699
- "wordpress"
700
- ],
701
- "time": "2020-02-04T02:52:06+00:00"
702
- }
703
- ],
704
- "aliases": [],
705
- "minimum-stability": "stable",
706
- "stability-flags": [],
707
- "prefer-stable": false,
708
- "prefer-lowest": false,
709
- "platform": [],
710
- "platform-dev": []
711
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/package-lock.json DELETED
@@ -1,3978 +0,0 @@
1
- {
2
- "name": "bsf-analytics",
3
- "version": "1.0.0",
4
- "lockfileVersion": 1,
5
- "requires": true,
6
- "dependencies": {
7
- "abbrev": {
8
- "version": "1.1.1",
9
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
10
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
11
- },
12
- "ajv": {
13
- "version": "6.12.0",
14
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
15
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
16
- "dev": true,
17
- "requires": {
18
- "fast-deep-equal": "^3.1.1",
19
- "fast-json-stable-stringify": "^2.0.0",
20
- "json-schema-traverse": "^0.4.1",
21
- "uri-js": "^4.2.2"
22
- }
23
- },
24
- "amdefine": {
25
- "version": "1.0.1",
26
- "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
27
- "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
28
- "dev": true
29
- },
30
- "ansi-regex": {
31
- "version": "2.1.1",
32
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
33
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
34
- "dev": true
35
- },
36
- "ansi-styles": {
37
- "version": "3.2.1",
38
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
39
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
40
- "dev": true,
41
- "requires": {
42
- "color-convert": "^1.9.0"
43
- }
44
- },
45
- "aproba": {
46
- "version": "1.2.0",
47
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
48
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
49
- "dev": true
50
- },
51
- "archiver": {
52
- "version": "1.3.0",
53
- "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz",
54
- "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=",
55
- "dev": true,
56
- "requires": {
57
- "archiver-utils": "^1.3.0",
58
- "async": "^2.0.0",
59
- "buffer-crc32": "^0.2.1",
60
- "glob": "^7.0.0",
61
- "lodash": "^4.8.0",
62
- "readable-stream": "^2.0.0",
63
- "tar-stream": "^1.5.0",
64
- "walkdir": "^0.0.11",
65
- "zip-stream": "^1.1.0"
66
- },
67
- "dependencies": {
68
- "async": {
69
- "version": "2.6.3",
70
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
71
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
72
- "dev": true,
73
- "requires": {
74
- "lodash": "^4.17.14"
75
- }
76
- }
77
- }
78
- },
79
- "archiver-utils": {
80
- "version": "1.3.0",
81
- "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz",
82
- "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=",
83
- "dev": true,
84
- "requires": {
85
- "glob": "^7.0.0",
86
- "graceful-fs": "^4.1.0",
87
- "lazystream": "^1.0.0",
88
- "lodash": "^4.8.0",
89
- "normalize-path": "^2.0.0",
90
- "readable-stream": "^2.0.0"
91
- }
92
- },
93
- "are-we-there-yet": {
94
- "version": "1.1.5",
95
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
96
- "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
97
- "dev": true,
98
- "requires": {
99
- "delegates": "^1.0.0",
100
- "readable-stream": "^2.0.6"
101
- }
102
- },
103
- "argparse": {
104
- "version": "1.0.10",
105
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
106
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
107
- "dev": true,
108
- "requires": {
109
- "sprintf-js": "~1.0.2"
110
- },
111
- "dependencies": {
112
- "sprintf-js": {
113
- "version": "1.0.3",
114
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
115
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
116
- "dev": true
117
- }
118
- }
119
- },
120
- "arr-diff": {
121
- "version": "4.0.0",
122
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
123
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
124
- },
125
- "arr-flatten": {
126
- "version": "1.1.0",
127
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
128
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
129
- },
130
- "arr-union": {
131
- "version": "3.1.0",
132
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
133
- "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
134
- },
135
- "array-each": {
136
- "version": "1.0.1",
137
- "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
138
- "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8="
139
- },
140
- "array-find-index": {
141
- "version": "1.0.2",
142
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
143
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
144
- "dev": true
145
- },
146
- "array-slice": {
147
- "version": "1.1.0",
148
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
149
- "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w=="
150
- },
151
- "array-unique": {
152
- "version": "0.3.2",
153
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
154
- "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
155
- },
156
- "asn1": {
157
- "version": "0.2.4",
158
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
159
- "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
160
- "dev": true,
161
- "requires": {
162
- "safer-buffer": "~2.1.0"
163
- }
164
- },
165
- "assert-plus": {
166
- "version": "1.0.0",
167
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
168
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
169
- "dev": true
170
- },
171
- "assign-symbols": {
172
- "version": "1.0.0",
173
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
174
- "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
175
- },
176
- "async": {
177
- "version": "1.5.2",
178
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
179
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
180
- "dev": true
181
- },
182
- "async-foreach": {
183
- "version": "0.1.3",
184
- "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
185
- "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
186
- "dev": true
187
- },
188
- "asynckit": {
189
- "version": "0.4.0",
190
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
191
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
192
- "dev": true
193
- },
194
- "atob": {
195
- "version": "2.1.2",
196
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
197
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
198
- },
199
- "autoprefixer": {
200
- "version": "9.7.6",
201
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz",
202
- "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==",
203
- "dev": true,
204
- "requires": {
205
- "browserslist": "^4.11.1",
206
- "caniuse-lite": "^1.0.30001039",
207
- "chalk": "^2.4.2",
208
- "normalize-range": "^0.1.2",
209
- "num2fraction": "^1.2.2",
210
- "postcss": "^7.0.27",
211
- "postcss-value-parser": "^4.0.3"
212
- }
213
- },
214
- "aws-sign2": {
215
- "version": "0.7.0",
216
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
217
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
218
- "dev": true
219
- },
220
- "aws4": {
221
- "version": "1.9.1",
222
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
223
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
224
- "dev": true
225
- },
226
- "balanced-match": {
227
- "version": "1.0.0",
228
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
229
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
230
- "dev": true
231
- },
232
- "base": {
233
- "version": "0.11.2",
234
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
235
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
236
- "requires": {
237
- "cache-base": "^1.0.1",
238
- "class-utils": "^0.3.5",
239
- "component-emitter": "^1.2.1",
240
- "define-property": "^1.0.0",
241
- "isobject": "^3.0.1",
242
- "mixin-deep": "^1.2.0",
243
- "pascalcase": "^0.1.1"
244
- },
245
- "dependencies": {
246
- "define-property": {
247
- "version": "1.0.0",
248
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
249
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
250
- "requires": {
251
- "is-descriptor": "^1.0.0"
252
- }
253
- },
254
- "is-accessor-descriptor": {
255
- "version": "1.0.0",
256
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
257
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
258
- "requires": {
259
- "kind-of": "^6.0.0"
260
- }
261
- },
262
- "is-data-descriptor": {
263
- "version": "1.0.0",
264
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
265
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
266
- "requires": {
267
- "kind-of": "^6.0.0"
268
- }
269
- },
270
- "is-descriptor": {
271
- "version": "1.0.2",
272
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
273
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
274
- "requires": {
275
- "is-accessor-descriptor": "^1.0.0",
276
- "is-data-descriptor": "^1.0.0",
277
- "kind-of": "^6.0.2"
278
- }
279
- }
280
- }
281
- },
282
- "base64-js": {
283
- "version": "1.3.1",
284
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
285
- "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
286
- "dev": true
287
- },
288
- "bcrypt-pbkdf": {
289
- "version": "1.0.2",
290
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
291
- "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
292
- "dev": true,
293
- "requires": {
294
- "tweetnacl": "^0.14.3"
295
- }
296
- },
297
- "bl": {
298
- "version": "1.2.2",
299
- "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
300
- "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
301
- "dev": true,
302
- "requires": {
303
- "readable-stream": "^2.3.5",
304
- "safe-buffer": "^5.1.1"
305
- }
306
- },
307
- "block-stream": {
308
- "version": "0.0.9",
309
- "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
310
- "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
311
- "dev": true,
312
- "requires": {
313
- "inherits": "~2.0.0"
314
- }
315
- },
316
- "brace-expansion": {
317
- "version": "1.1.11",
318
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
319
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
320
- "dev": true,
321
- "requires": {
322
- "balanced-match": "^1.0.0",
323
- "concat-map": "0.0.1"
324
- }
325
- },
326
- "braces": {
327
- "version": "2.3.2",
328
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
329
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
330
- "requires": {
331
- "arr-flatten": "^1.1.0",
332
- "array-unique": "^0.3.2",
333
- "extend-shallow": "^2.0.1",
334
- "fill-range": "^4.0.0",
335
- "isobject": "^3.0.1",
336
- "repeat-element": "^1.1.2",
337
- "snapdragon": "^0.8.1",
338
- "snapdragon-node": "^2.0.1",
339
- "split-string": "^3.0.2",
340
- "to-regex": "^3.0.1"
341
- },
342
- "dependencies": {
343
- "extend-shallow": {
344
- "version": "2.0.1",
345
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
346
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
347
- "requires": {
348
- "is-extendable": "^0.1.0"
349
- }
350
- }
351
- }
352
- },
353
- "browserslist": {
354
- "version": "4.11.1",
355
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz",
356
- "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==",
357
- "dev": true,
358
- "requires": {
359
- "caniuse-lite": "^1.0.30001038",
360
- "electron-to-chromium": "^1.3.390",
361
- "node-releases": "^1.1.53",
362
- "pkg-up": "^2.0.0"
363
- }
364
- },
365
- "buffer": {
366
- "version": "5.6.0",
367
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
368
- "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
369
- "dev": true,
370
- "requires": {
371
- "base64-js": "^1.0.2",
372
- "ieee754": "^1.1.4"
373
- }
374
- },
375
- "buffer-alloc": {
376
- "version": "1.2.0",
377
- "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
378
- "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
379
- "dev": true,
380
- "requires": {
381
- "buffer-alloc-unsafe": "^1.1.0",
382
- "buffer-fill": "^1.0.0"
383
- }
384
- },
385
- "buffer-alloc-unsafe": {
386
- "version": "1.1.0",
387
- "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
388
- "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
389
- "dev": true
390
- },
391
- "buffer-crc32": {
392
- "version": "0.2.13",
393
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
394
- "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
395
- "dev": true
396
- },
397
- "buffer-fill": {
398
- "version": "1.0.0",
399
- "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
400
- "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
401
- "dev": true
402
- },
403
- "cache-base": {
404
- "version": "1.0.1",
405
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
406
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
407
- "requires": {
408
- "collection-visit": "^1.0.0",
409
- "component-emitter": "^1.2.1",
410
- "get-value": "^2.0.6",
411
- "has-value": "^1.0.0",
412
- "isobject": "^3.0.1",
413
- "set-value": "^2.0.0",
414
- "to-object-path": "^0.3.0",
415
- "union-value": "^1.0.0",
416
- "unset-value": "^1.0.0"
417
- }
418
- },
419
- "camelcase": {
420
- "version": "2.1.1",
421
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
422
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
423
- "dev": true
424
- },
425
- "camelcase-keys": {
426
- "version": "2.1.0",
427
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
428
- "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
429
- "dev": true,
430
- "requires": {
431
- "camelcase": "^2.0.0",
432
- "map-obj": "^1.0.0"
433
- }
434
- },
435
- "caniuse-lite": {
436
- "version": "1.0.30001042",
437
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz",
438
- "integrity": "sha512-igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw==",
439
- "dev": true
440
- },
441
- "caseless": {
442
- "version": "0.12.0",
443
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
444
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
445
- "dev": true
446
- },
447
- "chalk": {
448
- "version": "2.4.2",
449
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
450
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
451
- "dev": true,
452
- "requires": {
453
- "ansi-styles": "^3.2.1",
454
- "escape-string-regexp": "^1.0.5",
455
- "supports-color": "^5.3.0"
456
- }
457
- },
458
- "chownr": {
459
- "version": "1.1.4",
460
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
461
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
462
- "dev": true,
463
- "optional": true
464
- },
465
- "class-utils": {
466
- "version": "0.3.6",
467
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
468
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
469
- "requires": {
470
- "arr-union": "^3.1.0",
471
- "define-property": "^0.2.5",
472
- "isobject": "^3.0.0",
473
- "static-extend": "^0.1.1"
474
- },
475
- "dependencies": {
476
- "define-property": {
477
- "version": "0.2.5",
478
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
479
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
480
- "requires": {
481
- "is-descriptor": "^0.1.0"
482
- }
483
- }
484
- }
485
- },
486
- "clean-css": {
487
- "version": "4.2.3",
488
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
489
- "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
490
- "dev": true,
491
- "requires": {
492
- "source-map": "~0.6.0"
493
- },
494
- "dependencies": {
495
- "source-map": {
496
- "version": "0.6.1",
497
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
498
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
499
- "dev": true
500
- }
501
- }
502
- },
503
- "cliui": {
504
- "version": "3.2.0",
505
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
506
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
507
- "dev": true,
508
- "requires": {
509
- "string-width": "^1.0.1",
510
- "strip-ansi": "^3.0.1",
511
- "wrap-ansi": "^2.0.0"
512
- }
513
- },
514
- "code-point-at": {
515
- "version": "1.1.0",
516
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
517
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
518
- "dev": true
519
- },
520
- "coffeescript": {
521
- "version": "1.10.0",
522
- "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
523
- "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
524
- "dev": true
525
- },
526
- "collection-visit": {
527
- "version": "1.0.0",
528
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
529
- "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
530
- "requires": {
531
- "map-visit": "^1.0.0",
532
- "object-visit": "^1.0.0"
533
- }
534
- },
535
- "color-convert": {
536
- "version": "1.9.3",
537
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
538
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
539
- "dev": true,
540
- "requires": {
541
- "color-name": "1.1.3"
542
- }
543
- },
544
- "color-name": {
545
- "version": "1.1.3",
546
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
547
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
548
- "dev": true
549
- },
550
- "colors": {
551
- "version": "1.1.2",
552
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
553
- "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
554
- "dev": true
555
- },
556
- "combined-stream": {
557
- "version": "1.0.8",
558
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
559
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
560
- "dev": true,
561
- "requires": {
562
- "delayed-stream": "~1.0.0"
563
- }
564
- },
565
- "commander": {
566
- "version": "2.1.0",
567
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
568
- "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=",
569
- "dev": true
570
- },
571
- "component-emitter": {
572
- "version": "1.3.0",
573
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
574
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
575
- },
576
- "compress-commons": {
577
- "version": "1.2.2",
578
- "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz",
579
- "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=",
580
- "dev": true,
581
- "requires": {
582
- "buffer-crc32": "^0.2.1",
583
- "crc32-stream": "^2.0.0",
584
- "normalize-path": "^2.0.0",
585
- "readable-stream": "^2.0.0"
586
- }
587
- },
588
- "concat-map": {
589
- "version": "0.0.1",
590
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
591
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
592
- "dev": true
593
- },
594
- "console-control-strings": {
595
- "version": "1.1.0",
596
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
597
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
598
- "dev": true
599
- },
600
- "copy-descriptor": {
601
- "version": "0.1.1",
602
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
603
- "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
604
- },
605
- "core-util-is": {
606
- "version": "1.0.2",
607
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
608
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
609
- "dev": true
610
- },
611
- "crc": {
612
- "version": "3.8.0",
613
- "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
614
- "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
615
- "dev": true,
616
- "requires": {
617
- "buffer": "^5.1.0"
618
- }
619
- },
620
- "crc32-stream": {
621
- "version": "2.0.0",
622
- "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz",
623
- "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=",
624
- "dev": true,
625
- "requires": {
626
- "crc": "^3.4.4",
627
- "readable-stream": "^2.0.0"
628
- }
629
- },
630
- "cross-spawn": {
631
- "version": "3.0.1",
632
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
633
- "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
634
- "dev": true,
635
- "requires": {
636
- "lru-cache": "^4.0.1",
637
- "which": "^1.2.9"
638
- }
639
- },
640
- "currently-unhandled": {
641
- "version": "0.4.1",
642
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
643
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
644
- "dev": true,
645
- "requires": {
646
- "array-find-index": "^1.0.1"
647
- }
648
- },
649
- "dashdash": {
650
- "version": "1.14.1",
651
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
652
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
653
- "dev": true,
654
- "requires": {
655
- "assert-plus": "^1.0.0"
656
- }
657
- },
658
- "dateformat": {
659
- "version": "1.0.12",
660
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
661
- "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
662
- "dev": true,
663
- "requires": {
664
- "get-stdin": "^4.0.1",
665
- "meow": "^3.3.0"
666
- }
667
- },
668
- "debug": {
669
- "version": "2.6.9",
670
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
671
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
672
- "requires": {
673
- "ms": "2.0.0"
674
- }
675
- },
676
- "decamelize": {
677
- "version": "1.2.0",
678
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
679
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
680
- "dev": true
681
- },
682
- "decode-uri-component": {
683
- "version": "0.2.0",
684
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
685
- "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
686
- },
687
- "decompress-response": {
688
- "version": "4.2.1",
689
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
690
- "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
691
- "dev": true,
692
- "optional": true,
693
- "requires": {
694
- "mimic-response": "^2.0.0"
695
- }
696
- },
697
- "deep-extend": {
698
- "version": "0.6.0",
699
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
700
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
701
- "dev": true,
702
- "optional": true
703
- },
704
- "define-property": {
705
- "version": "2.0.2",
706
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
707
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
708
- "requires": {
709
- "is-descriptor": "^1.0.2",
710
- "isobject": "^3.0.1"
711
- },
712
- "dependencies": {
713
- "is-accessor-descriptor": {
714
- "version": "1.0.0",
715
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
716
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
717
- "requires": {
718
- "kind-of": "^6.0.0"
719
- }
720
- },
721
- "is-data-descriptor": {
722
- "version": "1.0.0",
723
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
724
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
725
- "requires": {
726
- "kind-of": "^6.0.0"
727
- }
728
- },
729
- "is-descriptor": {
730
- "version": "1.0.2",
731
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
732
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
733
- "requires": {
734
- "is-accessor-descriptor": "^1.0.0",
735
- "is-data-descriptor": "^1.0.0",
736
- "kind-of": "^6.0.2"
737
- }
738
- }
739
- }
740
- },
741
- "delayed-stream": {
742
- "version": "1.0.0",
743
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
744
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
745
- "dev": true
746
- },
747
- "delegates": {
748
- "version": "1.0.0",
749
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
750
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
751
- "dev": true
752
- },
753
- "detect-file": {
754
- "version": "1.0.0",
755
- "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
756
- "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc="
757
- },
758
- "detect-libc": {
759
- "version": "1.0.3",
760
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
761
- "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
762
- "dev": true,
763
- "optional": true
764
- },
765
- "diff": {
766
- "version": "3.5.0",
767
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
768
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
769
- "dev": true
770
- },
771
- "duplexer": {
772
- "version": "0.1.1",
773
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
774
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
775
- "dev": true
776
- },
777
- "ecc-jsbn": {
778
- "version": "0.1.2",
779
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
780
- "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
781
- "dev": true,
782
- "requires": {
783
- "jsbn": "~0.1.0",
784
- "safer-buffer": "^2.1.0"
785
- }
786
- },
787
- "electron-to-chromium": {
788
- "version": "1.3.409",
789
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.409.tgz",
790
- "integrity": "sha512-CB2HUXiMsaVYY5VvcpELhDShiTRhI2FfN7CuacEZ5mDmMFuSG/ZVm8HoSya0+S61RvUd3TjIjFSKywqHZpRPzQ==",
791
- "dev": true
792
- },
793
- "end-of-stream": {
794
- "version": "1.4.4",
795
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
796
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
797
- "dev": true,
798
- "requires": {
799
- "once": "^1.4.0"
800
- }
801
- },
802
- "error-ex": {
803
- "version": "1.3.2",
804
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
805
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
806
- "dev": true,
807
- "requires": {
808
- "is-arrayish": "^0.2.1"
809
- }
810
- },
811
- "escape-string-regexp": {
812
- "version": "1.0.5",
813
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
814
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
815
- "dev": true
816
- },
817
- "esprima": {
818
- "version": "4.0.1",
819
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
820
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
821
- "dev": true
822
- },
823
- "eventemitter2": {
824
- "version": "0.4.14",
825
- "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
826
- "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
827
- "dev": true
828
- },
829
- "exit": {
830
- "version": "0.1.2",
831
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
832
- "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
833
- "dev": true
834
- },
835
- "expand-brackets": {
836
- "version": "2.1.4",
837
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
838
- "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
839
- "requires": {
840
- "debug": "^2.3.3",
841
- "define-property": "^0.2.5",
842
- "extend-shallow": "^2.0.1",
843
- "posix-character-classes": "^0.1.0",
844
- "regex-not": "^1.0.0",
845
- "snapdragon": "^0.8.1",
846
- "to-regex": "^3.0.1"
847
- },
848
- "dependencies": {
849
- "define-property": {
850
- "version": "0.2.5",
851
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
852
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
853
- "requires": {
854
- "is-descriptor": "^0.1.0"
855
- }
856
- },
857
- "extend-shallow": {
858
- "version": "2.0.1",
859
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
860
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
861
- "requires": {
862
- "is-extendable": "^0.1.0"
863
- }
864
- }
865
- }
866
- },
867
- "expand-template": {
868
- "version": "2.0.3",
869
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
870
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
871
- "dev": true,
872
- "optional": true
873
- },
874
- "expand-tilde": {
875
- "version": "2.0.2",
876
- "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
877
- "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
878
- "requires": {
879
- "homedir-polyfill": "^1.0.1"
880
- }
881
- },
882
- "extend": {
883
- "version": "3.0.2",
884
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
885
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
886
- },
887
- "extend-shallow": {
888
- "version": "3.0.2",
889
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
890
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
891
- "requires": {
892
- "assign-symbols": "^1.0.0",
893
- "is-extendable": "^1.0.1"
894
- },
895
- "dependencies": {
896
- "is-extendable": {
897
- "version": "1.0.1",
898
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
899
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
900
- "requires": {
901
- "is-plain-object": "^2.0.4"
902
- }
903
- }
904
- }
905
- },
906
- "extglob": {
907
- "version": "2.0.4",
908
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
909
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
910
- "requires": {
911
- "array-unique": "^0.3.2",
912
- "define-property": "^1.0.0",
913
- "expand-brackets": "^2.1.4",
914
- "extend-shallow": "^2.0.1",
915
- "fragment-cache": "^0.2.1",
916
- "regex-not": "^1.0.0",
917
- "snapdragon": "^0.8.1",
918
- "to-regex": "^3.0.1"
919
- },
920
- "dependencies": {
921
- "define-property": {
922
- "version": "1.0.0",
923
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
924
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
925
- "requires": {
926
- "is-descriptor": "^1.0.0"
927
- }
928
- },
929
- "extend-shallow": {
930
- "version": "2.0.1",
931
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
932
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
933
- "requires": {
934
- "is-extendable": "^0.1.0"
935
- }
936
- },
937
- "is-accessor-descriptor": {
938
- "version": "1.0.0",
939
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
940
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
941
- "requires": {
942
- "kind-of": "^6.0.0"
943
- }
944
- },
945
- "is-data-descriptor": {
946
- "version": "1.0.0",
947
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
948
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
949
- "requires": {
950
- "kind-of": "^6.0.0"
951
- }
952
- },
953
- "is-descriptor": {
954
- "version": "1.0.2",
955
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
956
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
957
- "requires": {
958
- "is-accessor-descriptor": "^1.0.0",
959
- "is-data-descriptor": "^1.0.0",
960
- "kind-of": "^6.0.2"
961
- }
962
- }
963
- }
964
- },
965
- "extsprintf": {
966
- "version": "1.3.0",
967
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
968
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
969
- "dev": true
970
- },
971
- "fast-deep-equal": {
972
- "version": "3.1.1",
973
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
974
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
975
- "dev": true
976
- },
977
- "fast-json-stable-stringify": {
978
- "version": "2.1.0",
979
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
980
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
981
- "dev": true
982
- },
983
- "figures": {
984
- "version": "1.7.0",
985
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
986
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
987
- "dev": true,
988
- "requires": {
989
- "escape-string-regexp": "^1.0.5",
990
- "object-assign": "^4.1.0"
991
- }
992
- },
993
- "file-sync-cmp": {
994
- "version": "0.1.1",
995
- "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz",
996
- "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=",
997
- "dev": true
998
- },
999
- "fill-range": {
1000
- "version": "4.0.0",
1001
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
1002
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
1003
- "requires": {
1004
- "extend-shallow": "^2.0.1",
1005
- "is-number": "^3.0.0",
1006
- "repeat-string": "^1.6.1",
1007
- "to-regex-range": "^2.1.0"
1008
- },
1009
- "dependencies": {
1010
- "extend-shallow": {
1011
- "version": "2.0.1",
1012
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
1013
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
1014
- "requires": {
1015
- "is-extendable": "^0.1.0"
1016
- }
1017
- }
1018
- }
1019
- },
1020
- "find-up": {
1021
- "version": "2.1.0",
1022
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
1023
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
1024
- "dev": true,
1025
- "requires": {
1026
- "locate-path": "^2.0.0"
1027
- }
1028
- },
1029
- "findup": {
1030
- "version": "0.1.5",
1031
- "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz",
1032
- "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=",
1033
- "dev": true,
1034
- "requires": {
1035
- "colors": "~0.6.0-1",
1036
- "commander": "~2.1.0"
1037
- },
1038
- "dependencies": {
1039
- "colors": {
1040
- "version": "0.6.2",
1041
- "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
1042
- "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
1043
- "dev": true
1044
- }
1045
- }
1046
- },
1047
- "findup-sync": {
1048
- "version": "2.0.0",
1049
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
1050
- "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
1051
- "requires": {
1052
- "detect-file": "^1.0.0",
1053
- "is-glob": "^3.1.0",
1054
- "micromatch": "^3.0.4",
1055
- "resolve-dir": "^1.0.1"
1056
- }
1057
- },
1058
- "fined": {
1059
- "version": "1.2.0",
1060
- "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
1061
- "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
1062
- "requires": {
1063
- "expand-tilde": "^2.0.2",
1064
- "is-plain-object": "^2.0.3",
1065
- "object.defaults": "^1.1.0",
1066
- "object.pick": "^1.2.0",
1067
- "parse-filepath": "^1.0.1"
1068
- }
1069
- },
1070
- "flagged-respawn": {
1071
- "version": "1.0.1",
1072
- "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
1073
- "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q=="
1074
- },
1075
- "for-in": {
1076
- "version": "1.0.2",
1077
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
1078
- "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
1079
- },
1080
- "for-own": {
1081
- "version": "1.0.0",
1082
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
1083
- "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
1084
- "requires": {
1085
- "for-in": "^1.0.1"
1086
- }
1087
- },
1088
- "forever-agent": {
1089
- "version": "0.6.1",
1090
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
1091
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
1092
- "dev": true
1093
- },
1094
- "form-data": {
1095
- "version": "2.3.3",
1096
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
1097
- "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
1098
- "dev": true,
1099
- "requires": {
1100
- "asynckit": "^0.4.0",
1101
- "combined-stream": "^1.0.6",
1102
- "mime-types": "^2.1.12"
1103
- }
1104
- },
1105
- "fragment-cache": {
1106
- "version": "0.2.1",
1107
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
1108
- "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
1109
- "requires": {
1110
- "map-cache": "^0.2.2"
1111
- }
1112
- },
1113
- "fs-constants": {
1114
- "version": "1.0.0",
1115
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
1116
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
1117
- "dev": true
1118
- },
1119
- "fs.realpath": {
1120
- "version": "1.0.0",
1121
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
1122
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
1123
- "dev": true
1124
- },
1125
- "fstream": {
1126
- "version": "1.0.12",
1127
- "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
1128
- "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
1129
- "dev": true,
1130
- "requires": {
1131
- "graceful-fs": "^4.1.2",
1132
- "inherits": "~2.0.0",
1133
- "mkdirp": ">=0.5 0",
1134
- "rimraf": "2"
1135
- },
1136
- "dependencies": {
1137
- "mkdirp": {
1138
- "version": "0.5.5",
1139
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
1140
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
1141
- "dev": true,
1142
- "requires": {
1143
- "minimist": "^1.2.5"
1144
- }
1145
- }
1146
- }
1147
- },
1148
- "gauge": {
1149
- "version": "2.7.4",
1150
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
1151
- "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
1152
- "dev": true,
1153
- "requires": {
1154
- "aproba": "^1.0.3",
1155
- "console-control-strings": "^1.0.0",
1156
- "has-unicode": "^2.0.0",
1157
- "object-assign": "^4.1.0",
1158
- "signal-exit": "^3.0.0",
1159
- "string-width": "^1.0.1",
1160
- "strip-ansi": "^3.0.1",
1161
- "wide-align": "^1.1.0"
1162
- }
1163
- },
1164
- "gaze": {
1165
- "version": "1.1.3",
1166
- "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
1167
- "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
1168
- "dev": true,
1169
- "requires": {
1170
- "globule": "^1.0.0"
1171
- }
1172
- },
1173
- "get-caller-file": {
1174
- "version": "1.0.3",
1175
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
1176
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
1177
- "dev": true
1178
- },
1179
- "get-stdin": {
1180
- "version": "4.0.1",
1181
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
1182
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
1183
- "dev": true
1184
- },
1185
- "get-value": {
1186
- "version": "2.0.6",
1187
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
1188
- "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
1189
- },
1190
- "getobject": {
1191
- "version": "0.1.0",
1192
- "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
1193
- "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
1194
- "dev": true
1195
- },
1196
- "getpass": {
1197
- "version": "0.1.7",
1198
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
1199
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
1200
- "dev": true,
1201
- "requires": {
1202
- "assert-plus": "^1.0.0"
1203
- }
1204
- },
1205
- "github-from-package": {
1206
- "version": "0.0.0",
1207
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
1208
- "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=",
1209
- "dev": true,
1210
- "optional": true
1211
- },
1212
- "glob": {
1213
- "version": "7.0.6",
1214
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
1215
- "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
1216
- "dev": true,
1217
- "requires": {
1218
- "fs.realpath": "^1.0.0",
1219
- "inflight": "^1.0.4",
1220
- "inherits": "2",
1221
- "minimatch": "^3.0.2",
1222
- "once": "^1.3.0",
1223
- "path-is-absolute": "^1.0.0"
1224
- }
1225
- },
1226
- "global-modules": {
1227
- "version": "1.0.0",
1228
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
1229
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
1230
- "requires": {
1231
- "global-prefix": "^1.0.1",
1232
- "is-windows": "^1.0.1",
1233
- "resolve-dir": "^1.0.0"
1234
- }
1235
- },
1236
- "global-prefix": {
1237
- "version": "1.0.2",
1238
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
1239
- "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
1240
- "requires": {
1241
- "expand-tilde": "^2.0.2",
1242
- "homedir-polyfill": "^1.0.1",
1243
- "ini": "^1.3.4",
1244
- "is-windows": "^1.0.1",
1245
- "which": "^1.2.14"
1246
- }
1247
- },
1248
- "globule": {
1249
- "version": "1.3.1",
1250
- "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
1251
- "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
1252
- "dev": true,
1253
- "requires": {
1254
- "glob": "~7.1.1",
1255
- "lodash": "~4.17.12",
1256
- "minimatch": "~3.0.2"
1257
- },
1258
- "dependencies": {
1259
- "glob": {
1260
- "version": "7.1.6",
1261
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
1262
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
1263
- "dev": true,
1264
- "requires": {
1265
- "fs.realpath": "^1.0.0",
1266
- "inflight": "^1.0.4",
1267
- "inherits": "2",
1268
- "minimatch": "^3.0.4",
1269
- "once": "^1.3.0",
1270
- "path-is-absolute": "^1.0.0"
1271
- }
1272
- }
1273
- }
1274
- },
1275
- "graceful-fs": {
1276
- "version": "4.2.3",
1277
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
1278
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
1279
- "dev": true
1280
- },
1281
- "grunt": {
1282
- "version": "1.1.0",
1283
- "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz",
1284
- "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==",
1285
- "dev": true,
1286
- "requires": {
1287
- "coffeescript": "~1.10.0",
1288
- "dateformat": "~1.0.12",
1289
- "eventemitter2": "~0.4.13",
1290
- "exit": "~0.1.1",
1291
- "findup-sync": "~0.3.0",
1292
- "glob": "~7.0.0",
1293
- "grunt-cli": "~1.2.0",
1294
- "grunt-known-options": "~1.1.0",
1295
- "grunt-legacy-log": "~2.0.0",
1296
- "grunt-legacy-util": "~1.1.1",
1297
- "iconv-lite": "~0.4.13",
1298
- "js-yaml": "~3.13.1",
1299
- "minimatch": "~3.0.2",
1300
- "mkdirp": "~1.0.3",
1301
- "nopt": "~3.0.6",
1302
- "path-is-absolute": "~1.0.0",
1303
- "rimraf": "~2.6.2"
1304
- },
1305
- "dependencies": {
1306
- "findup-sync": {
1307
- "version": "0.3.0",
1308
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
1309
- "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
1310
- "dev": true,
1311
- "requires": {
1312
- "glob": "~5.0.0"
1313
- },
1314
- "dependencies": {
1315
- "glob": {
1316
- "version": "5.0.15",
1317
- "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
1318
- "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
1319
- "dev": true,
1320
- "requires": {
1321
- "inflight": "^1.0.4",
1322
- "inherits": "2",
1323
- "minimatch": "2 || 3",
1324
- "once": "^1.3.0",
1325
- "path-is-absolute": "^1.0.0"
1326
- }
1327
- }
1328
- }
1329
- },
1330
- "grunt-cli": {
1331
- "version": "1.2.0",
1332
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
1333
- "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
1334
- "dev": true,
1335
- "requires": {
1336
- "findup-sync": "~0.3.0",
1337
- "grunt-known-options": "~1.1.0",
1338
- "nopt": "~3.0.6",
1339
- "resolve": "~1.1.0"
1340
- }
1341
- },
1342
- "nopt": {
1343
- "version": "3.0.6",
1344
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
1345
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
1346
- "dev": true,
1347
- "requires": {
1348
- "abbrev": "1"
1349
- }
1350
- },
1351
- "resolve": {
1352
- "version": "1.1.7",
1353
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
1354
- "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
1355
- "dev": true
1356
- }
1357
- }
1358
- },
1359
- "grunt-cli": {
1360
- "version": "1.3.2",
1361
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz",
1362
- "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==",
1363
- "requires": {
1364
- "grunt-known-options": "~1.1.0",
1365
- "interpret": "~1.1.0",
1366
- "liftoff": "~2.5.0",
1367
- "nopt": "~4.0.1",
1368
- "v8flags": "~3.1.1"
1369
- }
1370
- },
1371
- "grunt-contrib-clean": {
1372
- "version": "2.0.0",
1373
- "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-2.0.0.tgz",
1374
- "integrity": "sha512-g5ZD3ORk6gMa5ugZosLDQl3dZO7cI3R14U75hTM+dVLVxdMNJCPVmwf9OUt4v4eWgpKKWWoVK9DZc1amJp4nQw==",
1375
- "dev": true,
1376
- "requires": {
1377
- "async": "^2.6.1",
1378
- "rimraf": "^2.6.2"
1379
- },
1380
- "dependencies": {
1381
- "async": {
1382
- "version": "2.6.3",
1383
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
1384
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
1385
- "dev": true,
1386
- "requires": {
1387
- "lodash": "^4.17.14"
1388
- }
1389
- }
1390
- }
1391
- },
1392
- "grunt-contrib-compress": {
1393
- "version": "1.6.0",
1394
- "resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-1.6.0.tgz",
1395
- "integrity": "sha512-wIFuvk+/Ny4E+OgEfJYFZgoH7KcU/nnNFbYasB7gRvrcRyW6vmTp3Pj8a4rFSR3tbFMjrGvTUszdO6fgLajgZQ==",
1396
- "dev": true,
1397
- "requires": {
1398
- "archiver": "^1.3.0",
1399
- "chalk": "^1.1.1",
1400
- "iltorb": "^2.4.3",
1401
- "lodash": "^4.7.0",
1402
- "pretty-bytes": "^4.0.2",
1403
- "stream-buffers": "^2.1.0"
1404
- },
1405
- "dependencies": {
1406
- "ansi-styles": {
1407
- "version": "2.2.1",
1408
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1409
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1410
- "dev": true
1411
- },
1412
- "chalk": {
1413
- "version": "1.1.3",
1414
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1415
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1416
- "dev": true,
1417
- "requires": {
1418
- "ansi-styles": "^2.2.1",
1419
- "escape-string-regexp": "^1.0.2",
1420
- "has-ansi": "^2.0.0",
1421
- "strip-ansi": "^3.0.0",
1422
- "supports-color": "^2.0.0"
1423
- }
1424
- },
1425
- "supports-color": {
1426
- "version": "2.0.0",
1427
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1428
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1429
- "dev": true
1430
- }
1431
- }
1432
- },
1433
- "grunt-contrib-copy": {
1434
- "version": "1.0.0",
1435
- "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz",
1436
- "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=",
1437
- "dev": true,
1438
- "requires": {
1439
- "chalk": "^1.1.1",
1440
- "file-sync-cmp": "^0.1.0"
1441
- },
1442
- "dependencies": {
1443
- "ansi-styles": {
1444
- "version": "2.2.1",
1445
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1446
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1447
- "dev": true
1448
- },
1449
- "chalk": {
1450
- "version": "1.1.3",
1451
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1452
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1453
- "dev": true,
1454
- "requires": {
1455
- "ansi-styles": "^2.2.1",
1456
- "escape-string-regexp": "^1.0.2",
1457
- "has-ansi": "^2.0.0",
1458
- "strip-ansi": "^3.0.0",
1459
- "supports-color": "^2.0.0"
1460
- }
1461
- },
1462
- "supports-color": {
1463
- "version": "2.0.0",
1464
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1465
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1466
- "dev": true
1467
- }
1468
- }
1469
- },
1470
- "grunt-contrib-cssmin": {
1471
- "version": "3.0.0",
1472
- "resolved": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-3.0.0.tgz",
1473
- "integrity": "sha512-eXpooYmVGKMs/xV7DzTLgJFPVOfMuawPD3x0JwhlH0mumq2NtH3xsxaHxp1Y3NKxp0j0tRhFS6kSBRsz6TuTGg==",
1474
- "dev": true,
1475
- "requires": {
1476
- "chalk": "^2.4.1",
1477
- "clean-css": "~4.2.1",
1478
- "maxmin": "^2.1.0"
1479
- }
1480
- },
1481
- "grunt-known-options": {
1482
- "version": "1.1.1",
1483
- "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
1484
- "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ=="
1485
- },
1486
- "grunt-legacy-log": {
1487
- "version": "2.0.0",
1488
- "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
1489
- "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
1490
- "dev": true,
1491
- "requires": {
1492
- "colors": "~1.1.2",
1493
- "grunt-legacy-log-utils": "~2.0.0",
1494
- "hooker": "~0.2.3",
1495
- "lodash": "~4.17.5"
1496
- }
1497
- },
1498
- "grunt-legacy-log-utils": {
1499
- "version": "2.0.1",
1500
- "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
1501
- "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
1502
- "dev": true,
1503
- "requires": {
1504
- "chalk": "~2.4.1",
1505
- "lodash": "~4.17.10"
1506
- }
1507
- },
1508
- "grunt-legacy-util": {
1509
- "version": "1.1.1",
1510
- "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
1511
- "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
1512
- "dev": true,
1513
- "requires": {
1514
- "async": "~1.5.2",
1515
- "exit": "~0.1.1",
1516
- "getobject": "~0.1.0",
1517
- "hooker": "~0.2.3",
1518
- "lodash": "~4.17.10",
1519
- "underscore.string": "~3.3.4",
1520
- "which": "~1.3.0"
1521
- }
1522
- },
1523
- "grunt-postcss": {
1524
- "version": "0.9.0",
1525
- "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.9.0.tgz",
1526
- "integrity": "sha512-lglLcVaoOIqH0sFv7RqwUKkEFGQwnlqyAKbatxZderwZGV1nDyKHN7gZS9LUiTx1t5GOvRBx0BEalHMyVwFAIA==",
1527
- "dev": true,
1528
- "requires": {
1529
- "chalk": "^2.1.0",
1530
- "diff": "^3.0.0",
1531
- "postcss": "^6.0.11"
1532
- },
1533
- "dependencies": {
1534
- "postcss": {
1535
- "version": "6.0.23",
1536
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
1537
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
1538
- "dev": true,
1539
- "requires": {
1540
- "chalk": "^2.4.1",
1541
- "source-map": "^0.6.1",
1542
- "supports-color": "^5.4.0"
1543
- }
1544
- },
1545
- "source-map": {
1546
- "version": "0.6.1",
1547
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
1548
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
1549
- "dev": true
1550
- }
1551
- }
1552
- },
1553
- "grunt-rtlcss": {
1554
- "version": "2.0.2",
1555
- "resolved": "https://registry.npmjs.org/grunt-rtlcss/-/grunt-rtlcss-2.0.2.tgz",
1556
- "integrity": "sha512-WbI2thnwlF04N+xvJu+NxqEaCyPuLyar196SYhEQFZ2EJHkOS8YYE+Zkh+X9cWhwAtKp7ZEpR/IKXcyQggOIsQ==",
1557
- "dev": true,
1558
- "requires": {
1559
- "chalk": "^1.0.0",
1560
- "rtlcss": "^2.0.0"
1561
- },
1562
- "dependencies": {
1563
- "ansi-styles": {
1564
- "version": "2.2.1",
1565
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1566
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
1567
- "dev": true
1568
- },
1569
- "chalk": {
1570
- "version": "1.1.3",
1571
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1572
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1573
- "dev": true,
1574
- "requires": {
1575
- "ansi-styles": "^2.2.1",
1576
- "escape-string-regexp": "^1.0.2",
1577
- "has-ansi": "^2.0.0",
1578
- "strip-ansi": "^3.0.0",
1579
- "supports-color": "^2.0.0"
1580
- }
1581
- },
1582
- "supports-color": {
1583
- "version": "2.0.0",
1584
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1585
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1586
- "dev": true
1587
- }
1588
- }
1589
- },
1590
- "grunt-text-replace": {
1591
- "version": "0.4.0",
1592
- "resolved": "https://registry.npmjs.org/grunt-text-replace/-/grunt-text-replace-0.4.0.tgz",
1593
- "integrity": "sha1-252c5Z4v5J2id+nbwZXD4Rz7FsI=",
1594
- "dev": true
1595
- },
1596
- "gzip-size": {
1597
- "version": "3.0.0",
1598
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz",
1599
- "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=",
1600
- "dev": true,
1601
- "requires": {
1602
- "duplexer": "^0.1.1"
1603
- }
1604
- },
1605
- "har-schema": {
1606
- "version": "2.0.0",
1607
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
1608
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
1609
- "dev": true
1610
- },
1611
- "har-validator": {
1612
- "version": "5.1.3",
1613
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
1614
- "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
1615
- "dev": true,
1616
- "requires": {
1617
- "ajv": "^6.5.5",
1618
- "har-schema": "^2.0.0"
1619
- }
1620
- },
1621
- "has-ansi": {
1622
- "version": "2.0.0",
1623
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1624
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1625
- "dev": true,
1626
- "requires": {
1627
- "ansi-regex": "^2.0.0"
1628
- }
1629
- },
1630
- "has-flag": {
1631
- "version": "3.0.0",
1632
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
1633
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
1634
- "dev": true
1635
- },
1636
- "has-unicode": {
1637
- "version": "2.0.1",
1638
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
1639
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
1640
- "dev": true
1641
- },
1642
- "has-value": {
1643
- "version": "1.0.0",
1644
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
1645
- "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
1646
- "requires": {
1647
- "get-value": "^2.0.6",
1648
- "has-values": "^1.0.0",
1649
- "isobject": "^3.0.0"
1650
- }
1651
- },
1652
- "has-values": {
1653
- "version": "1.0.0",
1654
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
1655
- "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
1656
- "requires": {
1657
- "is-number": "^3.0.0",
1658
- "kind-of": "^4.0.0"
1659
- },
1660
- "dependencies": {
1661
- "kind-of": {
1662
- "version": "4.0.0",
1663
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
1664
- "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
1665
- "requires": {
1666
- "is-buffer": "^1.1.5"
1667
- }
1668
- }
1669
- }
1670
- },
1671
- "homedir-polyfill": {
1672
- "version": "1.0.3",
1673
- "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
1674
- "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
1675
- "requires": {
1676
- "parse-passwd": "^1.0.0"
1677
- }
1678
- },
1679
- "hooker": {
1680
- "version": "0.2.3",
1681
- "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
1682
- "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
1683
- "dev": true
1684
- },
1685
- "hosted-git-info": {
1686
- "version": "2.8.8",
1687
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
1688
- "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
1689
- "dev": true
1690
- },
1691
- "http-signature": {
1692
- "version": "1.2.0",
1693
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
1694
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
1695
- "dev": true,
1696
- "requires": {
1697
- "assert-plus": "^1.0.0",
1698
- "jsprim": "^1.2.2",
1699
- "sshpk": "^1.7.0"
1700
- }
1701
- },
1702
- "iconv-lite": {
1703
- "version": "0.4.24",
1704
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1705
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1706
- "dev": true,
1707
- "requires": {
1708
- "safer-buffer": ">= 2.1.2 < 3"
1709
- }
1710
- },
1711
- "ieee754": {
1712
- "version": "1.1.13",
1713
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
1714
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
1715
- "dev": true
1716
- },
1717
- "iltorb": {
1718
- "version": "2.4.5",
1719
- "resolved": "https://registry.npmjs.org/iltorb/-/iltorb-2.4.5.tgz",
1720
- "integrity": "sha512-EMCMl3LnnNSZJS5QrxyZmMTaAC4+TJkM5woD+xbpm9RB+mFYCr7C05GFE3TEGCsVQSVHmjX+3sf5AiwsylNInQ==",
1721
- "dev": true,
1722
- "optional": true,
1723
- "requires": {
1724
- "detect-libc": "^1.0.3",
1725
- "nan": "^2.14.0",
1726
- "npmlog": "^4.1.2",
1727
- "prebuild-install": "^5.3.3",
1728
- "which-pm-runs": "^1.0.0"
1729
- }
1730
- },
1731
- "in-publish": {
1732
- "version": "2.0.1",
1733
- "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
1734
- "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
1735
- "dev": true
1736
- },
1737
- "indent-string": {
1738
- "version": "2.1.0",
1739
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
1740
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
1741
- "dev": true,
1742
- "requires": {
1743
- "repeating": "^2.0.0"
1744
- }
1745
- },
1746
- "inflight": {
1747
- "version": "1.0.6",
1748
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1749
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
1750
- "dev": true,
1751
- "requires": {
1752
- "once": "^1.3.0",
1753
- "wrappy": "1"
1754
- }
1755
- },
1756
- "inherits": {
1757
- "version": "2.0.4",
1758
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1759
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1760
- "dev": true
1761
- },
1762
- "ini": {
1763
- "version": "1.3.5",
1764
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
1765
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
1766
- },
1767
- "interpret": {
1768
- "version": "1.1.0",
1769
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
1770
- "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
1771
- },
1772
- "invert-kv": {
1773
- "version": "1.0.0",
1774
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
1775
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
1776
- "dev": true
1777
- },
1778
- "is-absolute": {
1779
- "version": "1.0.0",
1780
- "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
1781
- "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
1782
- "requires": {
1783
- "is-relative": "^1.0.0",
1784
- "is-windows": "^1.0.1"
1785
- }
1786
- },
1787
- "is-accessor-descriptor": {
1788
- "version": "0.1.6",
1789
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
1790
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
1791
- "requires": {
1792
- "kind-of": "^3.0.2"
1793
- },
1794
- "dependencies": {
1795
- "kind-of": {
1796
- "version": "3.2.2",
1797
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
1798
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
1799
- "requires": {
1800
- "is-buffer": "^1.1.5"
1801
- }
1802
- }
1803
- }
1804
- },
1805
- "is-arrayish": {
1806
- "version": "0.2.1",
1807
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1808
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
1809
- "dev": true
1810
- },
1811
- "is-buffer": {
1812
- "version": "1.1.6",
1813
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
1814
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
1815
- },
1816
- "is-data-descriptor": {
1817
- "version": "0.1.4",
1818
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
1819
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
1820
- "requires": {
1821
- "kind-of": "^3.0.2"
1822
- },
1823
- "dependencies": {
1824
- "kind-of": {
1825
- "version": "3.2.2",
1826
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
1827
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
1828
- "requires": {
1829
- "is-buffer": "^1.1.5"
1830
- }
1831
- }
1832
- }
1833
- },
1834
- "is-descriptor": {
1835
- "version": "0.1.6",
1836
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
1837
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
1838
- "requires": {
1839
- "is-accessor-descriptor": "^0.1.6",
1840
- "is-data-descriptor": "^0.1.4",
1841
- "kind-of": "^5.0.0"
1842
- },
1843
- "dependencies": {
1844
- "kind-of": {
1845
- "version": "5.1.0",
1846
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
1847
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
1848
- }
1849
- }
1850
- },
1851
- "is-extendable": {
1852
- "version": "0.1.1",
1853
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
1854
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
1855
- },
1856
- "is-extglob": {
1857
- "version": "2.1.1",
1858
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1859
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
1860
- },
1861
- "is-finite": {
1862
- "version": "1.1.0",
1863
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
1864
- "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
1865
- "dev": true
1866
- },
1867
- "is-fullwidth-code-point": {
1868
- "version": "1.0.0",
1869
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
1870
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
1871
- "dev": true,
1872
- "requires": {
1873
- "number-is-nan": "^1.0.0"
1874
- }
1875
- },
1876
- "is-glob": {
1877
- "version": "3.1.0",
1878
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
1879
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
1880
- "requires": {
1881
- "is-extglob": "^2.1.0"
1882
- }
1883
- },
1884
- "is-number": {
1885
- "version": "3.0.0",
1886
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
1887
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
1888
- "requires": {
1889
- "kind-of": "^3.0.2"
1890
- },
1891
- "dependencies": {
1892
- "kind-of": {
1893
- "version": "3.2.2",
1894
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
1895
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
1896
- "requires": {
1897
- "is-buffer": "^1.1.5"
1898
- }
1899
- }
1900
- }
1901
- },
1902
- "is-plain-object": {
1903
- "version": "2.0.4",
1904
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
1905
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
1906
- "requires": {
1907
- "isobject": "^3.0.1"
1908
- }
1909
- },
1910
- "is-relative": {
1911
- "version": "1.0.0",
1912
- "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
1913
- "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
1914
- "requires": {
1915
- "is-unc-path": "^1.0.0"
1916
- }
1917
- },
1918
- "is-typedarray": {
1919
- "version": "1.0.0",
1920
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1921
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
1922
- "dev": true
1923
- },
1924
- "is-unc-path": {
1925
- "version": "1.0.0",
1926
- "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
1927
- "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
1928
- "requires": {
1929
- "unc-path-regex": "^0.1.2"
1930
- }
1931
- },
1932
- "is-utf8": {
1933
- "version": "0.2.1",
1934
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
1935
- "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
1936
- "dev": true
1937
- },
1938
- "is-windows": {
1939
- "version": "1.0.2",
1940
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
1941
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
1942
- },
1943
- "isarray": {
1944
- "version": "1.0.0",
1945
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
1946
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
1947
- },
1948
- "isexe": {
1949
- "version": "2.0.0",
1950
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1951
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
1952
- },
1953
- "isobject": {
1954
- "version": "3.0.1",
1955
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
1956
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
1957
- },
1958
- "isstream": {
1959
- "version": "0.1.2",
1960
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
1961
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
1962
- "dev": true
1963
- },
1964
- "js-base64": {
1965
- "version": "2.5.2",
1966
- "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
1967
- "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
1968
- "dev": true
1969
- },
1970
- "js-yaml": {
1971
- "version": "3.13.1",
1972
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
1973
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
1974
- "dev": true,
1975
- "requires": {
1976
- "argparse": "^1.0.7",
1977
- "esprima": "^4.0.0"
1978
- }
1979
- },
1980
- "jsbn": {
1981
- "version": "0.1.1",
1982
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
1983
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
1984
- "dev": true
1985
- },
1986
- "json-schema": {
1987
- "version": "0.2.3",
1988
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
1989
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
1990
- "dev": true
1991
- },
1992
- "json-schema-traverse": {
1993
- "version": "0.4.1",
1994
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
1995
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
1996
- "dev": true
1997
- },
1998
- "json-stringify-safe": {
1999
- "version": "5.0.1",
2000
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
2001
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
2002
- "dev": true
2003
- },
2004
- "jsprim": {
2005
- "version": "1.4.1",
2006
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
2007
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
2008
- "dev": true,
2009
- "requires": {
2010
- "assert-plus": "1.0.0",
2011
- "extsprintf": "1.3.0",
2012
- "json-schema": "0.2.3",
2013
- "verror": "1.10.0"
2014
- }
2015
- },
2016
- "kind-of": {
2017
- "version": "6.0.3",
2018
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
2019
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
2020
- },
2021
- "lazystream": {
2022
- "version": "1.0.0",
2023
- "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
2024
- "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
2025
- "dev": true,
2026
- "requires": {
2027
- "readable-stream": "^2.0.5"
2028
- }
2029
- },
2030
- "lcid": {
2031
- "version": "1.0.0",
2032
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
2033
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
2034
- "dev": true,
2035
- "requires": {
2036
- "invert-kv": "^1.0.0"
2037
- }
2038
- },
2039
- "liftoff": {
2040
- "version": "2.5.0",
2041
- "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
2042
- "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
2043
- "requires": {
2044
- "extend": "^3.0.0",
2045
- "findup-sync": "^2.0.0",
2046
- "fined": "^1.0.1",
2047
- "flagged-respawn": "^1.0.0",
2048
- "is-plain-object": "^2.0.4",
2049
- "object.map": "^1.0.0",
2050
- "rechoir": "^0.6.2",
2051
- "resolve": "^1.1.7"
2052
- }
2053
- },
2054
- "load-json-file": {
2055
- "version": "1.1.0",
2056
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
2057
- "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
2058
- "dev": true,
2059
- "requires": {
2060
- "graceful-fs": "^4.1.2",
2061
- "parse-json": "^2.2.0",
2062
- "pify": "^2.0.0",
2063
- "pinkie-promise": "^2.0.0",
2064
- "strip-bom": "^2.0.0"
2065
- }
2066
- },
2067
- "locate-path": {
2068
- "version": "2.0.0",
2069
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
2070
- "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
2071
- "dev": true,
2072
- "requires": {
2073
- "p-locate": "^2.0.0",
2074
- "path-exists": "^3.0.0"
2075
- }
2076
- },
2077
- "lodash": {
2078
- "version": "4.17.15",
2079
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
2080
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
2081
- "dev": true
2082
- },
2083
- "loud-rejection": {
2084
- "version": "1.6.0",
2085
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
2086
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
2087
- "dev": true,
2088
- "requires": {
2089
- "currently-unhandled": "^0.4.1",
2090
- "signal-exit": "^3.0.0"
2091
- }
2092
- },
2093
- "lru-cache": {
2094
- "version": "4.1.5",
2095
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
2096
- "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
2097
- "dev": true,
2098
- "requires": {
2099
- "pseudomap": "^1.0.2",
2100
- "yallist": "^2.1.2"
2101
- }
2102
- },
2103
- "make-iterator": {
2104
- "version": "1.0.1",
2105
- "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
2106
- "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
2107
- "requires": {
2108
- "kind-of": "^6.0.2"
2109
- }
2110
- },
2111
- "map-cache": {
2112
- "version": "0.2.2",
2113
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
2114
- "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
2115
- },
2116
- "map-obj": {
2117
- "version": "1.0.1",
2118
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
2119
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
2120
- "dev": true
2121
- },
2122
- "map-visit": {
2123
- "version": "1.0.0",
2124
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
2125
- "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
2126
- "requires": {
2127
- "object-visit": "^1.0.0"
2128
- }
2129
- },
2130
- "maxmin": {
2131
- "version": "2.1.0",
2132
- "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz",
2133
- "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=",
2134
- "dev": true,
2135
- "requires": {
2136
- "chalk": "^1.0.0",
2137
- "figures": "^1.0.1",
2138
- "gzip-size": "^3.0.0",
2139
- "pretty-bytes": "^3.0.0"
2140
- },
2141
- "dependencies": {
2142
- "ansi-styles": {
2143
- "version": "2.2.1",
2144
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
2145
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
2146
- "dev": true
2147
- },
2148
- "chalk": {
2149
- "version": "1.1.3",
2150
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
2151
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
2152
- "dev": true,
2153
- "requires": {
2154
- "ansi-styles": "^2.2.1",
2155
- "escape-string-regexp": "^1.0.2",
2156
- "has-ansi": "^2.0.0",
2157
- "strip-ansi": "^3.0.0",
2158
- "supports-color": "^2.0.0"
2159
- }
2160
- },
2161
- "pretty-bytes": {
2162
- "version": "3.0.1",
2163
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz",
2164
- "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=",
2165
- "dev": true,
2166
- "requires": {
2167
- "number-is-nan": "^1.0.0"
2168
- }
2169
- },
2170
- "supports-color": {
2171
- "version": "2.0.0",
2172
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
2173
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
2174
- "dev": true
2175
- }
2176
- }
2177
- },
2178
- "meow": {
2179
- "version": "3.7.0",
2180
- "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
2181
- "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
2182
- "dev": true,
2183
- "requires": {
2184
- "camelcase-keys": "^2.0.0",
2185
- "decamelize": "^1.1.2",
2186
- "loud-rejection": "^1.0.0",
2187
- "map-obj": "^1.0.1",
2188
- "minimist": "^1.1.3",
2189
- "normalize-package-data": "^2.3.4",
2190
- "object-assign": "^4.0.1",
2191
- "read-pkg-up": "^1.0.1",
2192
- "redent": "^1.0.0",
2193
- "trim-newlines": "^1.0.0"
2194
- }
2195
- },
2196
- "micromatch": {
2197
- "version": "3.1.10",
2198
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
2199
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
2200
- "requires": {
2201
- "arr-diff": "^4.0.0",
2202
- "array-unique": "^0.3.2",
2203
- "braces": "^2.3.1",
2204
- "define-property": "^2.0.2",
2205
- "extend-shallow": "^3.0.2",
2206
- "extglob": "^2.0.4",
2207
- "fragment-cache": "^0.2.1",
2208
- "kind-of": "^6.0.2",
2209
- "nanomatch": "^1.2.9",
2210
- "object.pick": "^1.3.0",
2211
- "regex-not": "^1.0.0",
2212
- "snapdragon": "^0.8.1",
2213
- "to-regex": "^3.0.2"
2214
- }
2215
- },
2216
- "mime-db": {
2217
- "version": "1.43.0",
2218
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
2219
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
2220
- "dev": true
2221
- },
2222
- "mime-types": {
2223
- "version": "2.1.26",
2224
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
2225
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
2226
- "dev": true,
2227
- "requires": {
2228
- "mime-db": "1.43.0"
2229
- }
2230
- },
2231
- "mimic-response": {
2232
- "version": "2.1.0",
2233
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
2234
- "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
2235
- "dev": true,
2236
- "optional": true
2237
- },
2238
- "minimatch": {
2239
- "version": "3.0.4",
2240
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
2241
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
2242
- "dev": true,
2243
- "requires": {
2244
- "brace-expansion": "^1.1.7"
2245
- }
2246
- },
2247
- "minimist": {
2248
- "version": "1.2.5",
2249
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
2250
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
2251
- "dev": true
2252
- },
2253
- "mixin-deep": {
2254
- "version": "1.3.2",
2255
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
2256
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
2257
- "requires": {
2258
- "for-in": "^1.0.2",
2259
- "is-extendable": "^1.0.1"
2260
- },
2261
- "dependencies": {
2262
- "is-extendable": {
2263
- "version": "1.0.1",
2264
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
2265
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
2266
- "requires": {
2267
- "is-plain-object": "^2.0.4"
2268
- }
2269
- }
2270
- }
2271
- },
2272
- "mkdirp": {
2273
- "version": "1.0.4",
2274
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
2275
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
2276
- "dev": true
2277
- },
2278
- "mkdirp-classic": {
2279
- "version": "0.5.2",
2280
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
2281
- "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==",
2282
- "dev": true,
2283
- "optional": true
2284
- },
2285
- "ms": {
2286
- "version": "2.0.0",
2287
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
2288
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
2289
- },
2290
- "nan": {
2291
- "version": "2.14.0",
2292
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
2293
- "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
2294
- "dev": true
2295
- },
2296
- "nanomatch": {
2297
- "version": "1.2.13",
2298
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
2299
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
2300
- "requires": {
2301
- "arr-diff": "^4.0.0",
2302
- "array-unique": "^0.3.2",
2303
- "define-property": "^2.0.2",
2304
- "extend-shallow": "^3.0.2",
2305
- "fragment-cache": "^0.2.1",
2306
- "is-windows": "^1.0.2",
2307
- "kind-of": "^6.0.2",
2308
- "object.pick": "^1.3.0",
2309
- "regex-not": "^1.0.0",
2310
- "snapdragon": "^0.8.1",
2311
- "to-regex": "^3.0.1"
2312
- }
2313
- },
2314
- "napi-build-utils": {
2315
- "version": "1.0.2",
2316
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
2317
- "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
2318
- "dev": true,
2319
- "optional": true
2320
- },
2321
- "node-abi": {
2322
- "version": "2.15.0",
2323
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
2324
- "integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
2325
- "dev": true,
2326
- "optional": true,
2327
- "requires": {
2328
- "semver": "^5.4.1"
2329
- }
2330
- },
2331
- "node-gyp": {
2332
- "version": "3.8.0",
2333
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
2334
- "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
2335
- "dev": true,
2336
- "requires": {
2337
- "fstream": "^1.0.0",
2338
- "glob": "^7.0.3",
2339
- "graceful-fs": "^4.1.2",
2340
- "mkdirp": "^0.5.0",
2341
- "nopt": "2 || 3",
2342
- "npmlog": "0 || 1 || 2 || 3 || 4",
2343
- "osenv": "0",
2344
- "request": "^2.87.0",
2345
- "rimraf": "2",
2346
- "semver": "~5.3.0",
2347
- "tar": "^2.0.0",
2348
- "which": "1"
2349
- },
2350
- "dependencies": {
2351
- "mkdirp": {
2352
- "version": "0.5.5",
2353
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
2354
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
2355
- "dev": true,
2356
- "requires": {
2357
- "minimist": "^1.2.5"
2358
- }
2359
- },
2360
- "nopt": {
2361
- "version": "3.0.6",
2362
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
2363
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
2364
- "dev": true,
2365
- "requires": {
2366
- "abbrev": "1"
2367
- }
2368
- },
2369
- "semver": {
2370
- "version": "5.3.0",
2371
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
2372
- "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
2373
- "dev": true
2374
- }
2375
- }
2376
- },
2377
- "node-releases": {
2378
- "version": "1.1.53",
2379
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz",
2380
- "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
2381
- "dev": true
2382
- },
2383
- "node-sass": {
2384
- "version": "4.13.1",
2385
- "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
2386
- "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
2387
- "dev": true,
2388
- "requires": {
2389
- "async-foreach": "^0.1.3",
2390
- "chalk": "^1.1.1",
2391
- "cross-spawn": "^3.0.0",
2392
- "gaze": "^1.0.0",
2393
- "get-stdin": "^4.0.1",
2394
- "glob": "^7.0.3",
2395
- "in-publish": "^2.0.0",
2396
- "lodash": "^4.17.15",
2397
- "meow": "^3.7.0",
2398
- "mkdirp": "^0.5.1",
2399
- "nan": "^2.13.2",
2400
- "node-gyp": "^3.8.0",
2401
- "npmlog": "^4.0.0",
2402
- "request": "^2.88.0",
2403
- "sass-graph": "^2.2.4",
2404
- "stdout-stream": "^1.4.0",
2405
- "true-case-path": "^1.0.2"
2406
- },
2407
- "dependencies": {
2408
- "ansi-styles": {
2409
- "version": "2.2.1",
2410
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
2411
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
2412
- "dev": true
2413
- },
2414
- "chalk": {
2415
- "version": "1.1.3",
2416
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
2417
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
2418
- "dev": true,
2419
- "requires": {
2420
- "ansi-styles": "^2.2.1",
2421
- "escape-string-regexp": "^1.0.2",
2422
- "has-ansi": "^2.0.0",
2423
- "strip-ansi": "^3.0.0",
2424
- "supports-color": "^2.0.0"
2425
- }
2426
- },
2427
- "mkdirp": {
2428
- "version": "0.5.5",
2429
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
2430
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
2431
- "dev": true,
2432
- "requires": {
2433
- "minimist": "^1.2.5"
2434
- }
2435
- },
2436
- "supports-color": {
2437
- "version": "2.0.0",
2438
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
2439
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
2440
- "dev": true
2441
- }
2442
- }
2443
- },
2444
- "noop-logger": {
2445
- "version": "0.1.1",
2446
- "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
2447
- "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
2448
- "dev": true,
2449
- "optional": true
2450
- },
2451
- "nopt": {
2452
- "version": "4.0.3",
2453
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
2454
- "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
2455
- "requires": {
2456
- "abbrev": "1",
2457
- "osenv": "^0.1.4"
2458
- }
2459
- },
2460
- "normalize-package-data": {
2461
- "version": "2.5.0",
2462
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
2463
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
2464
- "dev": true,
2465
- "requires": {
2466
- "hosted-git-info": "^2.1.4",
2467
- "resolve": "^1.10.0",
2468
- "semver": "2 || 3 || 4 || 5",
2469
- "validate-npm-package-license": "^3.0.1"
2470
- }
2471
- },
2472
- "normalize-path": {
2473
- "version": "2.1.1",
2474
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
2475
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
2476
- "dev": true,
2477
- "requires": {
2478
- "remove-trailing-separator": "^1.0.1"
2479
- }
2480
- },
2481
- "normalize-range": {
2482
- "version": "0.1.2",
2483
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
2484
- "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
2485
- "dev": true
2486
- },
2487
- "npmlog": {
2488
- "version": "4.1.2",
2489
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
2490
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
2491
- "dev": true,
2492
- "requires": {
2493
- "are-we-there-yet": "~1.1.2",
2494
- "console-control-strings": "~1.1.0",
2495
- "gauge": "~2.7.3",
2496
- "set-blocking": "~2.0.0"
2497
- }
2498
- },
2499
- "num2fraction": {
2500
- "version": "1.2.2",
2501
- "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
2502
- "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
2503
- "dev": true
2504
- },
2505
- "number-is-nan": {
2506
- "version": "1.0.1",
2507
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
2508
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
2509
- "dev": true
2510
- },
2511
- "oauth-sign": {
2512
- "version": "0.9.0",
2513
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
2514
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
2515
- "dev": true
2516
- },
2517
- "object-assign": {
2518
- "version": "4.1.1",
2519
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
2520
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
2521
- "dev": true
2522
- },
2523
- "object-copy": {
2524
- "version": "0.1.0",
2525
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
2526
- "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
2527
- "requires": {
2528
- "copy-descriptor": "^0.1.0",
2529
- "define-property": "^0.2.5",
2530
- "kind-of": "^3.0.3"
2531
- },
2532
- "dependencies": {
2533
- "define-property": {
2534
- "version": "0.2.5",
2535
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
2536
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
2537
- "requires": {
2538
- "is-descriptor": "^0.1.0"
2539
- }
2540
- },
2541
- "kind-of": {
2542
- "version": "3.2.2",
2543
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
2544
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
2545
- "requires": {
2546
- "is-buffer": "^1.1.5"
2547
- }
2548
- }
2549
- }
2550
- },
2551
- "object-visit": {
2552
- "version": "1.0.1",
2553
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
2554
- "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
2555
- "requires": {
2556
- "isobject": "^3.0.0"
2557
- }
2558
- },
2559
- "object.defaults": {
2560
- "version": "1.1.0",
2561
- "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
2562
- "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
2563
- "requires": {
2564
- "array-each": "^1.0.1",
2565
- "array-slice": "^1.0.0",
2566
- "for-own": "^1.0.0",
2567
- "isobject": "^3.0.0"
2568
- }
2569
- },
2570
- "object.map": {
2571
- "version": "1.0.1",
2572
- "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
2573
- "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
2574
- "requires": {
2575
- "for-own": "^1.0.0",
2576
- "make-iterator": "^1.0.0"
2577
- }
2578
- },
2579
- "object.pick": {
2580
- "version": "1.3.0",
2581
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
2582
- "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
2583
- "requires": {
2584
- "isobject": "^3.0.1"
2585
- }
2586
- },
2587
- "once": {
2588
- "version": "1.4.0",
2589
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
2590
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
2591
- "dev": true,
2592
- "requires": {
2593
- "wrappy": "1"
2594
- }
2595
- },
2596
- "os-homedir": {
2597
- "version": "1.0.2",
2598
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
2599
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
2600
- },
2601
- "os-locale": {
2602
- "version": "1.4.0",
2603
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
2604
- "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
2605
- "dev": true,
2606
- "requires": {
2607
- "lcid": "^1.0.0"
2608
- }
2609
- },
2610
- "os-tmpdir": {
2611
- "version": "1.0.2",
2612
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
2613
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
2614
- },
2615
- "osenv": {
2616
- "version": "0.1.5",
2617
- "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
2618
- "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
2619
- "requires": {
2620
- "os-homedir": "^1.0.0",
2621
- "os-tmpdir": "^1.0.0"
2622
- }
2623
- },
2624
- "p-limit": {
2625
- "version": "1.3.0",
2626
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
2627
- "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
2628
- "dev": true,
2629
- "requires": {
2630
- "p-try": "^1.0.0"
2631
- }
2632
- },
2633
- "p-locate": {
2634
- "version": "2.0.0",
2635
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
2636
- "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
2637
- "dev": true,
2638
- "requires": {
2639
- "p-limit": "^1.1.0"
2640
- }
2641
- },
2642
- "p-try": {
2643
- "version": "1.0.0",
2644
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
2645
- "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
2646
- "dev": true
2647
- },
2648
- "parse-filepath": {
2649
- "version": "1.0.2",
2650
- "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
2651
- "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
2652
- "requires": {
2653
- "is-absolute": "^1.0.0",
2654
- "map-cache": "^0.2.0",
2655
- "path-root": "^0.1.1"
2656
- }
2657
- },
2658
- "parse-json": {
2659
- "version": "2.2.0",
2660
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
2661
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
2662
- "dev": true,
2663
- "requires": {
2664
- "error-ex": "^1.2.0"
2665
- }
2666
- },
2667
- "parse-passwd": {
2668
- "version": "1.0.0",
2669
- "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
2670
- "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="
2671
- },
2672
- "pascalcase": {
2673
- "version": "0.1.1",
2674
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
2675
- "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
2676
- },
2677
- "path-exists": {
2678
- "version": "3.0.0",
2679
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
2680
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
2681
- "dev": true
2682
- },
2683
- "path-is-absolute": {
2684
- "version": "1.0.1",
2685
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
2686
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
2687
- "dev": true
2688
- },
2689
- "path-parse": {
2690
- "version": "1.0.6",
2691
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
2692
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
2693
- },
2694
- "path-root": {
2695
- "version": "0.1.1",
2696
- "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
2697
- "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
2698
- "requires": {
2699
- "path-root-regex": "^0.1.0"
2700
- }
2701
- },
2702
- "path-root-regex": {
2703
- "version": "0.1.2",
2704
- "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
2705
- "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0="
2706
- },
2707
- "path-type": {
2708
- "version": "1.1.0",
2709
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
2710
- "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
2711
- "dev": true,
2712
- "requires": {
2713
- "graceful-fs": "^4.1.2",
2714
- "pify": "^2.0.0",
2715
- "pinkie-promise": "^2.0.0"
2716
- }
2717
- },
2718
- "performance-now": {
2719
- "version": "2.1.0",
2720
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
2721
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
2722
- "dev": true
2723
- },
2724
- "pify": {
2725
- "version": "2.3.0",
2726
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
2727
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
2728
- "dev": true
2729
- },
2730
- "pinkie": {
2731
- "version": "2.0.4",
2732
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
2733
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
2734
- "dev": true
2735
- },
2736
- "pinkie-promise": {
2737
- "version": "2.0.1",
2738
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
2739
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
2740
- "dev": true,
2741
- "requires": {
2742
- "pinkie": "^2.0.0"
2743
- }
2744
- },
2745
- "pkg-up": {
2746
- "version": "2.0.0",
2747
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
2748
- "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
2749
- "dev": true,
2750
- "requires": {
2751
- "find-up": "^2.1.0"
2752
- }
2753
- },
2754
- "posix-character-classes": {
2755
- "version": "0.1.1",
2756
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
2757
- "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
2758
- },
2759
- "postcss": {
2760
- "version": "7.0.27",
2761
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
2762
- "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
2763
- "dev": true,
2764
- "requires": {
2765
- "chalk": "^2.4.2",
2766
- "source-map": "^0.6.1",
2767
- "supports-color": "^6.1.0"
2768
- },
2769
- "dependencies": {
2770
- "source-map": {
2771
- "version": "0.6.1",
2772
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
2773
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
2774
- "dev": true
2775
- },
2776
- "supports-color": {
2777
- "version": "6.1.0",
2778
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
2779
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
2780
- "dev": true,
2781
- "requires": {
2782
- "has-flag": "^3.0.0"
2783
- }
2784
- }
2785
- }
2786
- },
2787
- "postcss-flexibility": {
2788
- "version": "2.0.0",
2789
- "resolved": "https://registry.npmjs.org/postcss-flexibility/-/postcss-flexibility-2.0.0.tgz",
2790
- "integrity": "sha1-VhLBuF1lle1ICWaDmSikFhemp2g=",
2791
- "dev": true,
2792
- "requires": {
2793
- "postcss": "^6.0.1"
2794
- },
2795
- "dependencies": {
2796
- "postcss": {
2797
- "version": "6.0.23",
2798
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
2799
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
2800
- "dev": true,
2801
- "requires": {
2802
- "chalk": "^2.4.1",
2803
- "source-map": "^0.6.1",
2804
- "supports-color": "^5.4.0"
2805
- }
2806
- },
2807
- "source-map": {
2808
- "version": "0.6.1",
2809
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
2810
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
2811
- "dev": true
2812
- }
2813
- }
2814
- },
2815
- "postcss-value-parser": {
2816
- "version": "4.0.3",
2817
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
2818
- "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
2819
- "dev": true
2820
- },
2821
- "prebuild-install": {
2822
- "version": "5.3.3",
2823
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
2824
- "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
2825
- "dev": true,
2826
- "optional": true,
2827
- "requires": {
2828
- "detect-libc": "^1.0.3",
2829
- "expand-template": "^2.0.3",
2830
- "github-from-package": "0.0.0",
2831
- "minimist": "^1.2.0",
2832
- "mkdirp": "^0.5.1",
2833
- "napi-build-utils": "^1.0.1",
2834
- "node-abi": "^2.7.0",
2835
- "noop-logger": "^0.1.1",
2836
- "npmlog": "^4.0.1",
2837
- "pump": "^3.0.0",
2838
- "rc": "^1.2.7",
2839
- "simple-get": "^3.0.3",
2840
- "tar-fs": "^2.0.0",
2841
- "tunnel-agent": "^0.6.0",
2842
- "which-pm-runs": "^1.0.0"
2843
- },
2844
- "dependencies": {
2845
- "mkdirp": {
2846
- "version": "0.5.5",
2847
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
2848
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
2849
- "dev": true,
2850
- "optional": true,
2851
- "requires": {
2852
- "minimist": "^1.2.5"
2853
- }
2854
- }
2855
- }
2856
- },
2857
- "pretty-bytes": {
2858
- "version": "4.0.2",
2859
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
2860
- "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
2861
- "dev": true
2862
- },
2863
- "process-nextick-args": {
2864
- "version": "2.0.1",
2865
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
2866
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
2867
- "dev": true
2868
- },
2869
- "pseudomap": {
2870
- "version": "1.0.2",
2871
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
2872
- "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
2873
- "dev": true
2874
- },
2875
- "psl": {
2876
- "version": "1.8.0",
2877
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
2878
- "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
2879
- "dev": true
2880
- },
2881
- "pump": {
2882
- "version": "3.0.0",
2883
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
2884
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
2885
- "dev": true,
2886
- "optional": true,
2887
- "requires": {
2888
- "end-of-stream": "^1.1.0",
2889
- "once": "^1.3.1"
2890
- }
2891
- },
2892
- "punycode": {
2893
- "version": "2.1.1",
2894
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
2895
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
2896
- "dev": true
2897
- },
2898
- "qs": {
2899
- "version": "6.5.2",
2900
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
2901
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
2902
- "dev": true
2903
- },
2904
- "rc": {
2905
- "version": "1.2.8",
2906
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
2907
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
2908
- "dev": true,
2909
- "optional": true,
2910
- "requires": {
2911
- "deep-extend": "^0.6.0",
2912
- "ini": "~1.3.0",
2913
- "minimist": "^1.2.0",
2914
- "strip-json-comments": "~2.0.1"
2915
- }
2916
- },
2917
- "read-pkg": {
2918
- "version": "1.1.0",
2919
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
2920
- "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
2921
- "dev": true,
2922
- "requires": {
2923
- "load-json-file": "^1.0.0",
2924
- "normalize-package-data": "^2.3.2",
2925
- "path-type": "^1.0.0"
2926
- }
2927
- },
2928
- "read-pkg-up": {
2929
- "version": "1.0.1",
2930
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
2931
- "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
2932
- "dev": true,
2933
- "requires": {
2934
- "find-up": "^1.0.0",
2935
- "read-pkg": "^1.0.0"
2936
- },
2937
- "dependencies": {
2938
- "find-up": {
2939
- "version": "1.1.2",
2940
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
2941
- "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
2942
- "dev": true,
2943
- "requires": {
2944
- "path-exists": "^2.0.0",
2945
- "pinkie-promise": "^2.0.0"
2946
- }
2947
- },
2948
- "path-exists": {
2949
- "version": "2.1.0",
2950
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
2951
- "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
2952
- "dev": true,
2953
- "requires": {
2954
- "pinkie-promise": "^2.0.0"
2955
- }
2956
- }
2957
- }
2958
- },
2959
- "readable-stream": {
2960
- "version": "2.3.7",
2961
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
2962
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
2963
- "dev": true,
2964
- "requires": {
2965
- "core-util-is": "~1.0.0",
2966
- "inherits": "~2.0.3",
2967
- "isarray": "~1.0.0",
2968
- "process-nextick-args": "~2.0.0",
2969
- "safe-buffer": "~5.1.1",
2970
- "string_decoder": "~1.1.1",
2971
- "util-deprecate": "~1.0.1"
2972
- }
2973
- },
2974
- "rechoir": {
2975
- "version": "0.6.2",
2976
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
2977
- "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
2978
- "requires": {
2979
- "resolve": "^1.1.6"
2980
- }
2981
- },
2982
- "redent": {
2983
- "version": "1.0.0",
2984
- "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
2985
- "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
2986
- "dev": true,
2987
- "requires": {
2988
- "indent-string": "^2.1.0",
2989
- "strip-indent": "^1.0.1"
2990
- }
2991
- },
2992
- "regex-not": {
2993
- "version": "1.0.2",
2994
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
2995
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
2996
- "requires": {
2997
- "extend-shallow": "^3.0.2",
2998
- "safe-regex": "^1.1.0"
2999
- }
3000
- },
3001
- "remove-trailing-separator": {
3002
- "version": "1.1.0",
3003
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
3004
- "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
3005
- "dev": true
3006
- },
3007
- "repeat-element": {
3008
- "version": "1.1.3",
3009
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
3010
- "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
3011
- },
3012
- "repeat-string": {
3013
- "version": "1.6.1",
3014
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
3015
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
3016
- },
3017
- "repeating": {
3018
- "version": "2.0.1",
3019
- "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
3020
- "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
3021
- "dev": true,
3022
- "requires": {
3023
- "is-finite": "^1.0.0"
3024
- }
3025
- },
3026
- "request": {
3027
- "version": "2.88.2",
3028
- "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
3029
- "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
3030
- "dev": true,
3031
- "requires": {
3032
- "aws-sign2": "~0.7.0",
3033
- "aws4": "^1.8.0",
3034
- "caseless": "~0.12.0",
3035
- "combined-stream": "~1.0.6",
3036
- "extend": "~3.0.2",
3037
- "forever-agent": "~0.6.1",
3038
- "form-data": "~2.3.2",
3039
- "har-validator": "~5.1.3",
3040
- "http-signature": "~1.2.0",
3041
- "is-typedarray": "~1.0.0",
3042
- "isstream": "~0.1.2",
3043
- "json-stringify-safe": "~5.0.1",
3044
- "mime-types": "~2.1.19",
3045
- "oauth-sign": "~0.9.0",
3046
- "performance-now": "^2.1.0",
3047
- "qs": "~6.5.2",
3048
- "safe-buffer": "^5.1.2",
3049
- "tough-cookie": "~2.5.0",
3050
- "tunnel-agent": "^0.6.0",
3051
- "uuid": "^3.3.2"
3052
- }
3053
- },
3054
- "require-directory": {
3055
- "version": "2.1.1",
3056
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
3057
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
3058
- "dev": true
3059
- },
3060
- "require-main-filename": {
3061
- "version": "1.0.1",
3062
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
3063
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
3064
- "dev": true
3065
- },
3066
- "resolve": {
3067
- "version": "1.16.0",
3068
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz",
3069
- "integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==",
3070
- "requires": {
3071
- "path-parse": "^1.0.6"
3072
- }
3073
- },
3074
- "resolve-dir": {
3075
- "version": "1.0.1",
3076
- "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
3077
- "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
3078
- "requires": {
3079
- "expand-tilde": "^2.0.0",
3080
- "global-modules": "^1.0.0"
3081
- }
3082
- },
3083
- "resolve-url": {
3084
- "version": "0.2.1",
3085
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
3086
- "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
3087
- },
3088
- "ret": {
3089
- "version": "0.1.15",
3090
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
3091
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
3092
- },
3093
- "rimraf": {
3094
- "version": "2.6.3",
3095
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
3096
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
3097
- "dev": true,
3098
- "requires": {
3099
- "glob": "^7.1.3"
3100
- },
3101
- "dependencies": {
3102
- "glob": {
3103
- "version": "7.1.6",
3104
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
3105
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
3106
- "dev": true,
3107
- "requires": {
3108
- "fs.realpath": "^1.0.0",
3109
- "inflight": "^1.0.4",
3110
- "inherits": "2",
3111
- "minimatch": "^3.0.4",
3112
- "once": "^1.3.0",
3113
- "path-is-absolute": "^1.0.0"
3114
- }
3115
- }
3116
- }
3117
- },
3118
- "rtlcss": {
3119
- "version": "2.5.0",
3120
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.5.0.tgz",
3121
- "integrity": "sha512-NCVdF45w70/3CQeqVvQ84bu2HN8agNn+CDjw+RxXaiWb7mPOmEvltdd1z4qzm9kin4Jnu9ShFBIx28yvWerZ2g==",
3122
- "dev": true,
3123
- "requires": {
3124
- "chalk": "^2.4.2",
3125
- "findup": "^0.1.5",
3126
- "mkdirp": "^0.5.1",
3127
- "postcss": "^6.0.23",
3128
- "strip-json-comments": "^2.0.0"
3129
- },
3130
- "dependencies": {
3131
- "mkdirp": {
3132
- "version": "0.5.5",
3133
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
3134
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
3135
- "dev": true,
3136
- "requires": {
3137
- "minimist": "^1.2.5"
3138
- }
3139
- },
3140
- "postcss": {
3141
- "version": "6.0.23",
3142
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
3143
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
3144
- "dev": true,
3145
- "requires": {
3146
- "chalk": "^2.4.1",
3147
- "source-map": "^0.6.1",
3148
- "supports-color": "^5.4.0"
3149
- }
3150
- },
3151
- "source-map": {
3152
- "version": "0.6.1",
3153
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
3154
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
3155
- "dev": true
3156
- }
3157
- }
3158
- },
3159
- "safe-buffer": {
3160
- "version": "5.1.2",
3161
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
3162
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
3163
- "dev": true
3164
- },
3165
- "safe-regex": {
3166
- "version": "1.1.0",
3167
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
3168
- "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
3169
- "requires": {
3170
- "ret": "~0.1.10"
3171
- }
3172
- },
3173
- "safer-buffer": {
3174
- "version": "2.1.2",
3175
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
3176
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
3177
- "dev": true
3178
- },
3179
- "sass-graph": {
3180
- "version": "2.2.4",
3181
- "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
3182
- "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
3183
- "dev": true,
3184
- "requires": {
3185
- "glob": "^7.0.0",
3186
- "lodash": "^4.0.0",
3187
- "scss-tokenizer": "^0.2.3",
3188
- "yargs": "^7.0.0"
3189
- }
3190
- },
3191
- "scss-tokenizer": {
3192
- "version": "0.2.3",
3193
- "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
3194
- "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
3195
- "dev": true,
3196
- "requires": {
3197
- "js-base64": "^2.1.8",
3198
- "source-map": "^0.4.2"
3199
- },
3200
- "dependencies": {
3201
- "source-map": {
3202
- "version": "0.4.4",
3203
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
3204
- "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
3205
- "dev": true,
3206
- "requires": {
3207
- "amdefine": ">=0.0.4"
3208
- }
3209
- }
3210
- }
3211
- },
3212
- "semver": {
3213
- "version": "5.7.1",
3214
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
3215
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
3216
- "dev": true
3217
- },
3218
- "set-blocking": {
3219
- "version": "2.0.0",
3220
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
3221
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
3222
- "dev": true
3223
- },
3224
- "set-value": {
3225
- "version": "2.0.1",
3226
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
3227
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
3228
- "requires": {
3229
- "extend-shallow": "^2.0.1",
3230
- "is-extendable": "^0.1.1",
3231
- "is-plain-object": "^2.0.3",
3232
- "split-string": "^3.0.1"
3233
- },
3234
- "dependencies": {
3235
- "extend-shallow": {
3236
- "version": "2.0.1",
3237
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
3238
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
3239
- "requires": {
3240
- "is-extendable": "^0.1.0"
3241
- }
3242
- }
3243
- }
3244
- },
3245
- "signal-exit": {
3246
- "version": "3.0.3",
3247
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
3248
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
3249
- "dev": true
3250
- },
3251
- "simple-concat": {
3252
- "version": "1.0.0",
3253
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
3254
- "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
3255
- "dev": true,
3256
- "optional": true
3257
- },
3258
- "simple-get": {
3259
- "version": "3.1.0",
3260
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
3261
- "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
3262
- "dev": true,
3263
- "optional": true,
3264
- "requires": {
3265
- "decompress-response": "^4.2.0",
3266
- "once": "^1.3.1",
3267
- "simple-concat": "^1.0.0"
3268
- }
3269
- },
3270
- "snapdragon": {
3271
- "version": "0.8.2",
3272
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
3273
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
3274
- "requires": {
3275
- "base": "^0.11.1",
3276
- "debug": "^2.2.0",
3277
- "define-property": "^0.2.5",
3278
- "extend-shallow": "^2.0.1",
3279
- "map-cache": "^0.2.2",
3280
- "source-map": "^0.5.6",
3281
- "source-map-resolve": "^0.5.0",
3282
- "use": "^3.1.0"
3283
- },
3284
- "dependencies": {
3285
- "define-property": {
3286
- "version": "0.2.5",
3287
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
3288
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
3289
- "requires": {
3290
- "is-descriptor": "^0.1.0"
3291
- }
3292
- },
3293
- "extend-shallow": {
3294
- "version": "2.0.1",
3295
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
3296
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
3297
- "requires": {
3298
- "is-extendable": "^0.1.0"
3299
- }
3300
- }
3301
- }
3302
- },
3303
- "snapdragon-node": {
3304
- "version": "2.1.1",
3305
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
3306
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
3307
- "requires": {
3308
- "define-property": "^1.0.0",
3309
- "isobject": "^3.0.0",
3310
- "snapdragon-util": "^3.0.1"
3311
- },
3312
- "dependencies": {
3313
- "define-property": {
3314
- "version": "1.0.0",
3315
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
3316
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
3317
- "requires": {
3318
- "is-descriptor": "^1.0.0"
3319
- }
3320
- },
3321
- "is-accessor-descriptor": {
3322
- "version": "1.0.0",
3323
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
3324
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
3325
- "requires": {
3326
- "kind-of": "^6.0.0"
3327
- }
3328
- },
3329
- "is-data-descriptor": {
3330
- "version": "1.0.0",
3331
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
3332
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
3333
- "requires": {
3334
- "kind-of": "^6.0.0"
3335
- }
3336
- },
3337
- "is-descriptor": {
3338
- "version": "1.0.2",
3339
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
3340
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
3341
- "requires": {
3342
- "is-accessor-descriptor": "^1.0.0",
3343
- "is-data-descriptor": "^1.0.0",
3344
- "kind-of": "^6.0.2"
3345
- }
3346
- }
3347
- }
3348
- },
3349
- "snapdragon-util": {
3350
- "version": "3.0.1",
3351
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
3352
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
3353
- "requires": {
3354
- "kind-of": "^3.2.0"
3355
- },
3356
- "dependencies": {
3357
- "kind-of": {
3358
- "version": "3.2.2",
3359
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
3360
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
3361
- "requires": {
3362
- "is-buffer": "^1.1.5"
3363
- }
3364
- }
3365
- }
3366
- },
3367
- "source-map": {
3368
- "version": "0.5.7",
3369
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
3370
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
3371
- },
3372
- "source-map-resolve": {
3373
- "version": "0.5.3",
3374
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
3375
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
3376
- "requires": {
3377
- "atob": "^2.1.2",
3378
- "decode-uri-component": "^0.2.0",
3379
- "resolve-url": "^0.2.1",
3380
- "source-map-url": "^0.4.0",
3381
- "urix": "^0.1.0"
3382
- }
3383
- },
3384
- "source-map-url": {
3385
- "version": "0.4.0",
3386
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
3387
- "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
3388
- },
3389
- "spdx-correct": {
3390
- "version": "3.1.0",
3391
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
3392
- "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
3393
- "dev": true,
3394
- "requires": {
3395
- "spdx-expression-parse": "^3.0.0",
3396
- "spdx-license-ids": "^3.0.0"
3397
- }
3398
- },
3399
- "spdx-exceptions": {
3400
- "version": "2.2.0",
3401
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
3402
- "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
3403
- "dev": true
3404
- },
3405
- "spdx-expression-parse": {
3406
- "version": "3.0.0",
3407
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
3408
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
3409
- "dev": true,
3410
- "requires": {
3411
- "spdx-exceptions": "^2.1.0",
3412
- "spdx-license-ids": "^3.0.0"
3413
- }
3414
- },
3415
- "spdx-license-ids": {
3416
- "version": "3.0.5",
3417
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
3418
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
3419
- "dev": true
3420
- },
3421
- "split-string": {
3422
- "version": "3.1.0",
3423
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
3424
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
3425
- "requires": {
3426
- "extend-shallow": "^3.0.0"
3427
- }
3428
- },
3429
- "sprintf-js": {
3430
- "version": "1.1.2",
3431
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
3432
- "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
3433
- "dev": true
3434
- },
3435
- "sshpk": {
3436
- "version": "1.16.1",
3437
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
3438
- "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
3439
- "dev": true,
3440
- "requires": {
3441
- "asn1": "~0.2.3",
3442
- "assert-plus": "^1.0.0",
3443
- "bcrypt-pbkdf": "^1.0.0",
3444
- "dashdash": "^1.12.0",
3445
- "ecc-jsbn": "~0.1.1",
3446
- "getpass": "^0.1.1",
3447
- "jsbn": "~0.1.0",
3448
- "safer-buffer": "^2.0.2",
3449
- "tweetnacl": "~0.14.0"
3450
- }
3451
- },
3452
- "static-extend": {
3453
- "version": "0.1.2",
3454
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
3455
- "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
3456
- "requires": {
3457
- "define-property": "^0.2.5",
3458
- "object-copy": "^0.1.0"
3459
- },
3460
- "dependencies": {
3461
- "define-property": {
3462
- "version": "0.2.5",
3463
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
3464
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
3465
- "requires": {
3466
- "is-descriptor": "^0.1.0"
3467
- }
3468
- }
3469
- }
3470
- },
3471
- "stdout-stream": {
3472
- "version": "1.4.1",
3473
- "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
3474
- "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
3475
- "dev": true,
3476
- "requires": {
3477
- "readable-stream": "^2.0.1"
3478
- }
3479
- },
3480
- "stream-buffers": {
3481
- "version": "2.2.0",
3482
- "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz",
3483
- "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=",
3484
- "dev": true
3485
- },
3486
- "string-width": {
3487
- "version": "1.0.2",
3488
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
3489
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
3490
- "dev": true,
3491
- "requires": {
3492
- "code-point-at": "^1.0.0",
3493
- "is-fullwidth-code-point": "^1.0.0",
3494
- "strip-ansi": "^3.0.0"
3495
- }
3496
- },
3497
- "string_decoder": {
3498
- "version": "1.1.1",
3499
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
3500
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
3501
- "dev": true,
3502
- "requires": {
3503
- "safe-buffer": "~5.1.0"
3504
- }
3505
- },
3506
- "strip-ansi": {
3507
- "version": "3.0.1",
3508
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
3509
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
3510
- "dev": true,
3511
- "requires": {
3512
- "ansi-regex": "^2.0.0"
3513
- }
3514
- },
3515
- "strip-bom": {
3516
- "version": "2.0.0",
3517
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
3518
- "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
3519
- "dev": true,
3520
- "requires": {
3521
- "is-utf8": "^0.2.0"
3522
- }
3523
- },
3524
- "strip-indent": {
3525
- "version": "1.0.1",
3526
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
3527
- "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
3528
- "dev": true,
3529
- "requires": {
3530
- "get-stdin": "^4.0.1"
3531
- }
3532
- },
3533
- "strip-json-comments": {
3534
- "version": "2.0.1",
3535
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
3536
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
3537
- "dev": true
3538
- },
3539
- "supports-color": {
3540
- "version": "5.5.0",
3541
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
3542
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
3543
- "dev": true,
3544
- "requires": {
3545
- "has-flag": "^3.0.0"
3546
- }
3547
- },
3548
- "tar": {
3549
- "version": "2.2.2",
3550
- "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
3551
- "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
3552
- "dev": true,
3553
- "requires": {
3554
- "block-stream": "*",
3555
- "fstream": "^1.0.12",
3556
- "inherits": "2"
3557
- }
3558
- },
3559
- "tar-fs": {
3560
- "version": "2.0.1",
3561
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
3562
- "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
3563
- "dev": true,
3564
- "optional": true,
3565
- "requires": {
3566
- "chownr": "^1.1.1",
3567
- "mkdirp-classic": "^0.5.2",
3568
- "pump": "^3.0.0",
3569
- "tar-stream": "^2.0.0"
3570
- },
3571
- "dependencies": {
3572
- "bl": {
3573
- "version": "4.0.2",
3574
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
3575
- "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
3576
- "dev": true,
3577
- "optional": true,
3578
- "requires": {
3579
- "buffer": "^5.5.0",
3580
- "inherits": "^2.0.4",
3581
- "readable-stream": "^3.4.0"
3582
- }
3583
- },
3584
- "readable-stream": {
3585
- "version": "3.6.0",
3586
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
3587
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
3588
- "dev": true,
3589
- "optional": true,
3590
- "requires": {
3591
- "inherits": "^2.0.3",
3592
- "string_decoder": "^1.1.1",
3593
- "util-deprecate": "^1.0.1"
3594
- }
3595
- },
3596
- "tar-stream": {
3597
- "version": "2.1.2",
3598
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
3599
- "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
3600
- "dev": true,
3601
- "optional": true,
3602
- "requires": {
3603
- "bl": "^4.0.1",
3604
- "end-of-stream": "^1.4.1",
3605
- "fs-constants": "^1.0.0",
3606
- "inherits": "^2.0.3",
3607
- "readable-stream": "^3.1.1"
3608
- }
3609
- }
3610
- }
3611
- },
3612
- "tar-stream": {
3613
- "version": "1.6.2",
3614
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
3615
- "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
3616
- "dev": true,
3617
- "requires": {
3618
- "bl": "^1.0.0",
3619
- "buffer-alloc": "^1.2.0",
3620
- "end-of-stream": "^1.0.0",
3621
- "fs-constants": "^1.0.0",
3622
- "readable-stream": "^2.3.0",
3623
- "to-buffer": "^1.1.1",
3624
- "xtend": "^4.0.0"
3625
- }
3626
- },
3627
- "to-buffer": {
3628
- "version": "1.1.1",
3629
- "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
3630
- "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==",
3631
- "dev": true
3632
- },
3633
- "to-object-path": {
3634
- "version": "0.3.0",
3635
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
3636
- "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
3637
- "requires": {
3638
- "kind-of": "^3.0.2"
3639
- },
3640
- "dependencies": {
3641
- "kind-of": {
3642
- "version": "3.2.2",
3643
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
3644
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
3645
- "requires": {
3646
- "is-buffer": "^1.1.5"
3647
- }
3648
- }
3649
- }
3650
- },
3651
- "to-regex": {
3652
- "version": "3.0.2",
3653
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
3654
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
3655
- "requires": {
3656
- "define-property": "^2.0.2",
3657
- "extend-shallow": "^3.0.2",
3658
- "regex-not": "^1.0.2",
3659
- "safe-regex": "^1.1.0"
3660
- }
3661
- },
3662
- "to-regex-range": {
3663
- "version": "2.1.1",
3664
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
3665
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
3666
- "requires": {
3667
- "is-number": "^3.0.0",
3668
- "repeat-string": "^1.6.1"
3669
- }
3670
- },
3671
- "tough-cookie": {
3672
- "version": "2.5.0",
3673
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
3674
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
3675
- "dev": true,
3676
- "requires": {
3677
- "psl": "^1.1.28",
3678
- "punycode": "^2.1.1"
3679
- }
3680
- },
3681
- "trim-newlines": {
3682
- "version": "1.0.0",
3683
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
3684
- "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
3685
- "dev": true
3686
- },
3687
- "true-case-path": {
3688
- "version": "1.0.3",
3689
- "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
3690
- "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
3691
- "dev": true,
3692
- "requires": {
3693
- "glob": "^7.1.2"
3694
- },
3695
- "dependencies": {
3696
- "glob": {
3697
- "version": "7.1.6",
3698
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
3699
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
3700
- "dev": true,
3701
- "requires": {
3702
- "fs.realpath": "^1.0.0",
3703
- "inflight": "^1.0.4",
3704
- "inherits": "2",
3705
- "minimatch": "^3.0.4",
3706
- "once": "^1.3.0",
3707
- "path-is-absolute": "^1.0.0"
3708
- }
3709
- }
3710
- }
3711
- },
3712
- "tunnel-agent": {
3713
- "version": "0.6.0",
3714
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
3715
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
3716
- "dev": true,
3717
- "requires": {
3718
- "safe-buffer": "^5.0.1"
3719
- }
3720
- },
3721
- "tweetnacl": {
3722
- "version": "0.14.5",
3723
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
3724
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
3725
- "dev": true
3726
- },
3727
- "unc-path-regex": {
3728
- "version": "0.1.2",
3729
- "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
3730
- "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
3731
- },
3732
- "underscore.string": {
3733
- "version": "3.3.5",
3734
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
3735
- "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
3736
- "dev": true,
3737
- "requires": {
3738
- "sprintf-js": "^1.0.3",
3739
- "util-deprecate": "^1.0.2"
3740
- }
3741
- },
3742
- "union-value": {
3743
- "version": "1.0.1",
3744
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
3745
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
3746
- "requires": {
3747
- "arr-union": "^3.1.0",
3748
- "get-value": "^2.0.6",
3749
- "is-extendable": "^0.1.1",
3750
- "set-value": "^2.0.1"
3751
- }
3752
- },
3753
- "unset-value": {
3754
- "version": "1.0.0",
3755
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
3756
- "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
3757
- "requires": {
3758
- "has-value": "^0.3.1",
3759
- "isobject": "^3.0.0"
3760
- },
3761
- "dependencies": {
3762
- "has-value": {
3763
- "version": "0.3.1",
3764
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
3765
- "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
3766
- "requires": {
3767
- "get-value": "^2.0.3",
3768
- "has-values": "^0.1.4",
3769
- "isobject": "^2.0.0"
3770
- },
3771
- "dependencies": {
3772
- "isobject": {
3773
- "version": "2.1.0",
3774
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
3775
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
3776
- "requires": {
3777
- "isarray": "1.0.0"
3778
- }
3779
- }
3780
- }
3781
- },
3782
- "has-values": {
3783
- "version": "0.1.4",
3784
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
3785
- "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
3786
- }
3787
- }
3788
- },
3789
- "uri-js": {
3790
- "version": "4.2.2",
3791
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
3792
- "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
3793
- "dev": true,
3794
- "requires": {
3795
- "punycode": "^2.1.0"
3796
- }
3797
- },
3798
- "urix": {
3799
- "version": "0.1.0",
3800
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
3801
- "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
3802
- },
3803
- "use": {
3804
- "version": "3.1.1",
3805
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
3806
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
3807
- },
3808
- "util-deprecate": {
3809
- "version": "1.0.2",
3810
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
3811
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
3812
- "dev": true
3813
- },
3814
- "uuid": {
3815
- "version": "3.4.0",
3816
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
3817
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
3818
- "dev": true
3819
- },
3820
- "v8flags": {
3821
- "version": "3.1.3",
3822
- "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz",
3823
- "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==",
3824
- "requires": {
3825
- "homedir-polyfill": "^1.0.1"
3826
- }
3827
- },
3828
- "validate-npm-package-license": {
3829
- "version": "3.0.4",
3830
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
3831
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
3832
- "dev": true,
3833
- "requires": {
3834
- "spdx-correct": "^3.0.0",
3835
- "spdx-expression-parse": "^3.0.0"
3836
- }
3837
- },
3838
- "verror": {
3839
- "version": "1.10.0",
3840
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
3841
- "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
3842
- "dev": true,
3843
- "requires": {
3844
- "assert-plus": "^1.0.0",
3845
- "core-util-is": "1.0.2",
3846
- "extsprintf": "^1.2.0"
3847
- }
3848
- },
3849
- "walkdir": {
3850
- "version": "0.0.11",
3851
- "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz",
3852
- "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=",
3853
- "dev": true
3854
- },
3855
- "which": {
3856
- "version": "1.3.1",
3857
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
3858
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
3859
- "requires": {
3860
- "isexe": "^2.0.0"
3861
- }
3862
- },
3863
- "which-module": {
3864
- "version": "1.0.0",
3865
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
3866
- "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
3867
- "dev": true
3868
- },
3869
- "which-pm-runs": {
3870
- "version": "1.0.0",
3871
- "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
3872
- "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
3873
- "dev": true,
3874
- "optional": true
3875
- },
3876
- "wide-align": {
3877
- "version": "1.1.3",
3878
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
3879
- "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
3880
- "dev": true,
3881
- "requires": {
3882
- "string-width": "^1.0.2 || 2"
3883
- }
3884
- },
3885
- "wrap-ansi": {
3886
- "version": "2.1.0",
3887
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
3888
- "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
3889
- "dev": true,
3890
- "requires": {
3891
- "string-width": "^1.0.1",
3892
- "strip-ansi": "^3.0.1"
3893
- }
3894
- },
3895
- "wrappy": {
3896
- "version": "1.0.2",
3897
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
3898
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
3899
- "dev": true
3900
- },
3901
- "xtend": {
3902
- "version": "4.0.2",
3903
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
3904
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
3905
- "dev": true
3906
- },
3907
- "y18n": {
3908
- "version": "3.2.1",
3909
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
3910
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
3911
- "dev": true
3912
- },
3913
- "yallist": {
3914
- "version": "2.1.2",
3915
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
3916
- "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
3917
- "dev": true
3918
- },
3919
- "yargs": {
3920
- "version": "7.1.0",
3921
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
3922
- "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
3923
- "dev": true,
3924
- "requires": {
3925
- "camelcase": "^3.0.0",
3926
- "cliui": "^3.2.0",
3927
- "decamelize": "^1.1.1",
3928
- "get-caller-file": "^1.0.1",
3929
- "os-locale": "^1.4.0",
3930
- "read-pkg-up": "^1.0.1",
3931
- "require-directory": "^2.1.1",
3932
- "require-main-filename": "^1.0.1",
3933
- "set-blocking": "^2.0.0",
3934
- "string-width": "^1.0.2",
3935
- "which-module": "^1.0.0",
3936
- "y18n": "^3.2.1",
3937
- "yargs-parser": "^5.0.0"
3938
- },
3939
- "dependencies": {
3940
- "camelcase": {
3941
- "version": "3.0.0",
3942
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
3943
- "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
3944
- "dev": true
3945
- }
3946
- }
3947
- },
3948
- "yargs-parser": {
3949
- "version": "5.0.0",
3950
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
3951
- "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
3952
- "dev": true,
3953
- "requires": {
3954
- "camelcase": "^3.0.0"
3955
- },
3956
- "dependencies": {
3957
- "camelcase": {
3958
- "version": "3.0.0",
3959
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
3960
- "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
3961
- "dev": true
3962
- }
3963
- }
3964
- },
3965
- "zip-stream": {
3966
- "version": "1.2.0",
3967
- "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz",
3968
- "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=",
3969
- "dev": true,
3970
- "requires": {
3971
- "archiver-utils": "^1.3.0",
3972
- "compress-commons": "^1.2.0",
3973
- "lodash": "^4.8.0",
3974
- "readable-stream": "^2.0.0"
3975
- }
3976
- }
3977
- }
3978
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/package.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "name": "bsf-analytics",
3
- "version": "1.0.0",
4
- "description": "BSF Analytics Project.",
5
- "main": "Gruntfile.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git@git.brainstormforce.com:wp-sharks/bsf-analytics.git"
9
- },
10
- "author": "",
11
- "license": "ISC",
12
- "devDependencies": {
13
- "autoprefixer": "^9.6.1",
14
- "grunt": "^1.0.4",
15
- "grunt-contrib-clean": "^2.0.0",
16
- "grunt-contrib-compress": "^1.5.0",
17
- "grunt-contrib-copy": "^1.0.0",
18
- "grunt-contrib-cssmin": "^3.0.0",
19
- "grunt-postcss": "^0.9.0",
20
- "grunt-rtlcss": "^2.0.2",
21
- "grunt-text-replace": "^0.4.0",
22
- "kind-of": ">=6.0.3",
23
- "minimist": ">=1.2.3",
24
- "node-sass": "^4.13.1",
25
- "postcss-flexibility": "^2.0.0"
26
- },
27
- "dependencies": {
28
- "grunt-cli": "^1.3.2"
29
- }
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/phpcs.xml.dist DELETED
@@ -1,19 +0,0 @@
1
- <?xml version="1.0"?>
2
- <ruleset name="WordPress Coding Standards for Plugins">
3
- <description>Generally-applicable sniffs for WordPress plugins</description>
4
- <config name="testVersion" value="5.3-"/>
5
-
6
- <rule ref="WordPress-Core" />
7
- <rule ref="WordPress-Docs" />
8
- <rule ref="WordPress-Extra" />
9
-
10
- <!-- Check all PHP files in directory tree by default. -->
11
- <arg name="extensions" value="php"/>
12
- <file>.</file>
13
-
14
- <!-- Show sniff codes in all reports -->
15
- <arg value="s"/>
16
-
17
- <exclude-pattern>*/node_modules/*</exclude-pattern>
18
- <exclude-pattern>*/vendor/*</exclude-pattern>
19
- </ruleset>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/bsf-analytics/version.json CHANGED
@@ -1,4 +1,4 @@
1
- {
2
- "bsf-analytics-ver": "1.1.1"
3
- }
4
 
1
+ {
2
+ "bsf-analytics-ver": "1.1.1"
3
+ }
4
 
assets/images/cartflows-icon.svg CHANGED
@@ -1,18 +1,18 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Generator: Adobe Illustrator 16.0.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="95px" height="95px" viewBox="0 0 95 95" enable-background="new 0 0 95 95" xml:space="preserve">
6
- <g>
7
- <path fill="#81878c" d="M58.942,5.609c-3.444,3.354-5.587,8.036-5.587,13.22v30.718c0,4.521-3.668,8.188-8.188,8.188H24.574
8
- c-4.52,0-8.188-3.668-8.188-8.188c0-4.52,3.668-8.188,8.188-8.188h22.583V24.974H24.574c-1.399,0-2.763,0.115-4.099,0.34
9
- C8.86,27.26,0,37.377,0,49.547c0,10.7,6.843,19.803,16.386,23.176c1.318,0.466,2.681,0.825,4.09,1.058
10
- c1.336,0.225,2.699,0.341,4.099,0.341h20.592c1.399,0,2.763-0.116,4.099-0.341c1.409-0.232,2.771-0.592,4.09-1.058
11
- C62.897,69.35,69.74,60.247,69.74,49.547v-8.188c9.05,0,16.386-7.337,16.386-16.386H69.74v-6.145c0-1.13,0.915-2.044,2.045-2.044
12
- h4.098c4.996,0,9.525-1.982,12.843-5.212c2.987-2.905,4.996-6.807,5.471-11.174H71.786C66.799,0.399,62.261,2.381,58.942,5.609"/>
13
- <path fill="#81878c" d="M24.575,78.218c-4.523,0-8.191,3.667-8.191,8.191c0,4.523,3.667,8.191,8.191,8.191s8.191-3.668,8.191-8.191
14
- C32.766,81.885,29.098,78.218,24.575,78.218"/>
15
- <path fill="#81878c" d="M49.149,78.218c-4.524,0-8.192,3.667-8.192,8.191c0,4.523,3.667,8.191,8.192,8.191
16
- c4.523,0,8.191-3.668,8.191-8.191C57.34,81.885,53.672,78.218,49.149,78.218"/>
17
- </g>
18
- </svg>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.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="95px" height="95px" viewBox="0 0 95 95" enable-background="new 0 0 95 95" xml:space="preserve">
6
+ <g>
7
+ <path fill="#81878c" d="M58.942,5.609c-3.444,3.354-5.587,8.036-5.587,13.22v30.718c0,4.521-3.668,8.188-8.188,8.188H24.574
8
+ c-4.52,0-8.188-3.668-8.188-8.188c0-4.52,3.668-8.188,8.188-8.188h22.583V24.974H24.574c-1.399,0-2.763,0.115-4.099,0.34
9
+ C8.86,27.26,0,37.377,0,49.547c0,10.7,6.843,19.803,16.386,23.176c1.318,0.466,2.681,0.825,4.09,1.058
10
+ c1.336,0.225,2.699,0.341,4.099,0.341h20.592c1.399,0,2.763-0.116,4.099-0.341c1.409-0.232,2.771-0.592,4.09-1.058
11
+ C62.897,69.35,69.74,60.247,69.74,49.547v-8.188c9.05,0,16.386-7.337,16.386-16.386H69.74v-6.145c0-1.13,0.915-2.044,2.045-2.044
12
+ h4.098c4.996,0,9.525-1.982,12.843-5.212c2.987-2.905,4.996-6.807,5.471-11.174H71.786C66.799,0.399,62.261,2.381,58.942,5.609"/>
13
+ <path fill="#81878c" d="M24.575,78.218c-4.523,0-8.191,3.667-8.191,8.191c0,4.523,3.667,8.191,8.191,8.191s8.191-3.668,8.191-8.191
14
+ C32.766,81.885,29.098,78.218,24.575,78.218"/>
15
+ <path fill="#81878c" d="M49.149,78.218c-4.524,0-8.192,3.667-8.192,8.191c0,4.523,3.667,8.191,8.192,8.191
16
+ c4.523,0,8.191-3.668,8.191-8.191C57.34,81.885,53.672,78.218,49.149,78.218"/>
17
+ </g>
18
+ </svg>
assets/images/cartflows-logo.svg CHANGED
@@ -1,50 +1,50 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Generator: Adobe Illustrator 16.0.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="500px" height="95px" viewBox="0 0 500 95" enable-background="new 0 0 500 95" xml:space="preserve">
6
- <g>
7
- <path fill="#F06335" d="M135.779,68.374c-5.939,0-10.946-1.951-14.934-5.939c-3.989-3.988-5.939-8.995-5.939-15.019
8
- c0-6.025,1.951-11.031,5.939-15.02c3.988-3.988,8.995-6.024,14.934-6.024c6.535,0,11.964,2.376,16.208,7.212l7.211-6.194
9
- c-6.194-7.043-14-10.606-23.42-10.606c-8.74,0-16.207,2.885-22.317,8.74c-6.023,5.854-9.079,13.152-9.079,21.893
10
- c0,8.739,3.056,16.037,9.079,21.892c6.11,5.77,13.577,8.655,22.317,8.655c9.25,0,17.735-3.988,23.505-10.776l-7.127-6.194
11
- C148,65.914,142.483,68.374,135.779,68.374"/>
12
- <path fill="#F06335" d="M199.086,40.372c-1.443-4.157-6.619-7.127-13.661-7.127c-6.025,0-11.117,2.121-15.274,6.448
13
- c-4.073,4.243-6.11,9.504-6.11,15.868s2.037,11.625,6.11,15.952c4.157,4.243,9.249,6.364,15.274,6.364
14
- c7.042,0,12.218-2.97,13.661-7.128v6.194h10.182V34.178h-10.182V40.372z M196.03,64.896c-2.46,2.461-5.6,3.733-9.249,3.733
15
- c-3.648,0-6.619-1.272-9.08-3.733c-2.375-2.461-3.563-5.601-3.563-9.334c0-3.734,1.188-6.873,3.563-9.334
16
- c2.461-2.461,5.432-3.734,9.08-3.734c3.649,0,6.788,1.273,9.249,3.734c2.547,2.461,3.819,5.6,3.819,9.334
17
- C199.85,59.295,198.577,62.435,196.03,64.896"/>
18
- <path fill="#F06335" d="M229.891,40.542v-6.363h-10.098v42.767h10.098V54.458c0-7.043,4.498-11.455,11.541-11.455
19
- c1.527,0,2.971,0.17,4.243,0.424v-9.843c-0.848-0.169-1.867-0.254-3.055-0.254C236.595,33.33,231.928,36.045,229.891,40.542"/>
20
- <polygon fill="#F06335" points="267.654,17.802 257.472,17.802 257.472,34.178 251.955,34.178 251.955,43.427 257.472,43.427
21
- 257.472,76.944 267.654,76.944 267.654,43.427 276.902,43.427 276.902,34.178 267.654,34.178 "/>
22
- <polygon fill="#F06335" points="286.834,76.944 297.439,76.944 297.439,51.827 320.436,51.827 320.436,42.239 297.439,42.239
23
- 297.439,27.39 322.641,27.39 322.641,17.802 286.834,17.802 "/>
24
- <rect x="332.487" y="17.802" fill="#F06335" width="10.183" height="59.143"/>
25
- <path fill="#F06335" d="M374.832,32.905c-6.534,0-12.049,2.206-16.547,6.533c-4.498,4.328-6.789,9.759-6.789,16.123
26
- s2.291,11.795,6.789,16.122s10.013,6.534,16.547,6.534c6.533,0,12.049-2.207,16.547-6.534c4.496-4.327,6.787-9.758,6.787-16.122
27
- s-2.291-11.795-6.787-16.123C386.881,35.111,381.365,32.905,374.832,32.905 M384.25,65.149c-2.545,2.546-5.685,3.819-9.418,3.819
28
- c-3.819,0-6.959-1.273-9.505-3.819c-2.46-2.63-3.733-5.77-3.733-9.588s1.273-6.958,3.733-9.504
29
- c2.546-2.631,5.686-3.903,9.505-3.903c3.733,0,6.873,1.272,9.418,3.903c2.547,2.546,3.818,5.686,3.818,9.504
30
- S386.797,62.52,384.25,65.149"/>
31
- <polygon fill="#F06335" points="443.992,60.822 435.337,34.178 427.615,34.178 418.959,60.822 410.051,34.178 400.461,34.178
32
- 414.717,76.944 422.693,76.944 431.518,49.876 440.344,76.944 448.234,76.944 462.575,34.178 452.902,34.178 "/>
33
- <path fill="#F06335" d="M488.545,51.743c-1.697-0.595-4.074-1.442-5.346-1.782c-1.104-0.424-2.801-1.104-3.479-1.527
34
- c-1.188-0.764-1.952-1.527-1.952-2.97c0-2.291,2.037-3.818,5.176-3.818c3.649,0,6.449,1.188,8.316,3.563l5.686-6.024
35
- c-3.225-4.158-7.807-6.279-13.832-6.279c-4.242,0-7.807,1.188-10.776,3.479c-2.886,2.291-4.327,5.346-4.327,9.333
36
- c0,6.109,3.648,9.589,10.521,12.05c1.443,0.594,3.988,1.357,5.262,1.782c1.271,0.424,3.055,1.018,3.988,1.611
37
- c1.442,0.765,2.459,1.952,2.459,3.395c0,2.631-2.291,4.667-7.636,4.667c-5.176,0-9.333-2.291-11.286-5.346l-6.533,5.516
38
- c2.715,5.346,9.08,8.655,17.65,8.655c11.624,0,17.564-5.685,17.564-13.577C500,58.361,496.436,54.543,488.545,51.743"/>
39
- <path fill="#F06335" d="M58.942,5.609c-3.444,3.354-5.587,8.036-5.587,13.22v30.718c0,4.521-3.668,8.188-8.188,8.188H24.574
40
- c-4.52,0-8.188-3.668-8.188-8.188c0-4.52,3.668-8.188,8.188-8.188h22.583V24.974H24.574c-1.399,0-2.763,0.115-4.099,0.34
41
- C8.86,27.26,0,37.377,0,49.547c0,10.7,6.843,19.803,16.386,23.176c1.318,0.466,2.681,0.825,4.09,1.058
42
- c1.336,0.225,2.699,0.341,4.099,0.341h20.592c1.399,0,2.763-0.116,4.099-0.341c1.409-0.232,2.771-0.592,4.09-1.058
43
- C62.897,69.35,69.74,60.247,69.74,49.547v-8.188c9.05,0,16.386-7.337,16.386-16.386H69.74v-6.145c0-1.13,0.915-2.044,2.045-2.044
44
- h4.098c4.996,0,9.525-1.982,12.843-5.212c2.987-2.905,4.996-6.807,5.471-11.174H71.786C66.799,0.399,62.261,2.381,58.942,5.609"/>
45
- <path fill="#F06335" d="M24.575,78.218c-4.523,0-8.191,3.667-8.191,8.191c0,4.523,3.667,8.191,8.191,8.191s8.191-3.668,8.191-8.191
46
- C32.766,81.885,29.098,78.218,24.575,78.218"/>
47
- <path fill="#F06335" d="M49.149,78.218c-4.524,0-8.192,3.667-8.192,8.191c0,4.523,3.667,8.191,8.192,8.191
48
- c4.523,0,8.191-3.668,8.191-8.191C57.34,81.885,53.672,78.218,49.149,78.218"/>
49
- </g>
50
- </svg>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.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="500px" height="95px" viewBox="0 0 500 95" enable-background="new 0 0 500 95" xml:space="preserve">
6
+ <g>
7
+ <path fill="#F06335" d="M135.779,68.374c-5.939,0-10.946-1.951-14.934-5.939c-3.989-3.988-5.939-8.995-5.939-15.019
8
+ c0-6.025,1.951-11.031,5.939-15.02c3.988-3.988,8.995-6.024,14.934-6.024c6.535,0,11.964,2.376,16.208,7.212l7.211-6.194
9
+ c-6.194-7.043-14-10.606-23.42-10.606c-8.74,0-16.207,2.885-22.317,8.74c-6.023,5.854-9.079,13.152-9.079,21.893
10
+ c0,8.739,3.056,16.037,9.079,21.892c6.11,5.77,13.577,8.655,22.317,8.655c9.25,0,17.735-3.988,23.505-10.776l-7.127-6.194
11
+ C148,65.914,142.483,68.374,135.779,68.374"/>
12
+ <path fill="#F06335" d="M199.086,40.372c-1.443-4.157-6.619-7.127-13.661-7.127c-6.025,0-11.117,2.121-15.274,6.448
13
+ c-4.073,4.243-6.11,9.504-6.11,15.868s2.037,11.625,6.11,15.952c4.157,4.243,9.249,6.364,15.274,6.364
14
+ c7.042,0,12.218-2.97,13.661-7.128v6.194h10.182V34.178h-10.182V40.372z M196.03,64.896c-2.46,2.461-5.6,3.733-9.249,3.733
15
+ c-3.648,0-6.619-1.272-9.08-3.733c-2.375-2.461-3.563-5.601-3.563-9.334c0-3.734,1.188-6.873,3.563-9.334
16
+ c2.461-2.461,5.432-3.734,9.08-3.734c3.649,0,6.788,1.273,9.249,3.734c2.547,2.461,3.819,5.6,3.819,9.334
17
+ C199.85,59.295,198.577,62.435,196.03,64.896"/>
18
+ <path fill="#F06335" d="M229.891,40.542v-6.363h-10.098v42.767h10.098V54.458c0-7.043,4.498-11.455,11.541-11.455
19
+ c1.527,0,2.971,0.17,4.243,0.424v-9.843c-0.848-0.169-1.867-0.254-3.055-0.254C236.595,33.33,231.928,36.045,229.891,40.542"/>
20
+ <polygon fill="#F06335" points="267.654,17.802 257.472,17.802 257.472,34.178 251.955,34.178 251.955,43.427 257.472,43.427
21
+ 257.472,76.944 267.654,76.944 267.654,43.427 276.902,43.427 276.902,34.178 267.654,34.178 "/>
22
+ <polygon fill="#F06335" points="286.834,76.944 297.439,76.944 297.439,51.827 320.436,51.827 320.436,42.239 297.439,42.239
23
+ 297.439,27.39 322.641,27.39 322.641,17.802 286.834,17.802 "/>
24
+ <rect x="332.487" y="17.802" fill="#F06335" width="10.183" height="59.143"/>
25
+ <path fill="#F06335" d="M374.832,32.905c-6.534,0-12.049,2.206-16.547,6.533c-4.498,4.328-6.789,9.759-6.789,16.123
26
+ s2.291,11.795,6.789,16.122s10.013,6.534,16.547,6.534c6.533,0,12.049-2.207,16.547-6.534c4.496-4.327,6.787-9.758,6.787-16.122
27
+ s-2.291-11.795-6.787-16.123C386.881,35.111,381.365,32.905,374.832,32.905 M384.25,65.149c-2.545,2.546-5.685,3.819-9.418,3.819
28
+ c-3.819,0-6.959-1.273-9.505-3.819c-2.46-2.63-3.733-5.77-3.733-9.588s1.273-6.958,3.733-9.504
29
+ c2.546-2.631,5.686-3.903,9.505-3.903c3.733,0,6.873,1.272,9.418,3.903c2.547,2.546,3.818,5.686,3.818,9.504
30
+ S386.797,62.52,384.25,65.149"/>
31
+ <polygon fill="#F06335" points="443.992,60.822 435.337,34.178 427.615,34.178 418.959,60.822 410.051,34.178 400.461,34.178
32
+ 414.717,76.944 422.693,76.944 431.518,49.876 440.344,76.944 448.234,76.944 462.575,34.178 452.902,34.178 "/>
33
+ <path fill="#F06335" d="M488.545,51.743c-1.697-0.595-4.074-1.442-5.346-1.782c-1.104-0.424-2.801-1.104-3.479-1.527
34
+ c-1.188-0.764-1.952-1.527-1.952-2.97c0-2.291,2.037-3.818,5.176-3.818c3.649,0,6.449,1.188,8.316,3.563l5.686-6.024
35
+ c-3.225-4.158-7.807-6.279-13.832-6.279c-4.242,0-7.807,1.188-10.776,3.479c-2.886,2.291-4.327,5.346-4.327,9.333
36
+ c0,6.109,3.648,9.589,10.521,12.05c1.443,0.594,3.988,1.357,5.262,1.782c1.271,0.424,3.055,1.018,3.988,1.611
37
+ c1.442,0.765,2.459,1.952,2.459,3.395c0,2.631-2.291,4.667-7.636,4.667c-5.176,0-9.333-2.291-11.286-5.346l-6.533,5.516
38
+ c2.715,5.346,9.08,8.655,17.65,8.655c11.624,0,17.564-5.685,17.564-13.577C500,58.361,496.436,54.543,488.545,51.743"/>
39
+ <path fill="#F06335" d="M58.942,5.609c-3.444,3.354-5.587,8.036-5.587,13.22v30.718c0,4.521-3.668,8.188-8.188,8.188H24.574
40
+ c-4.52,0-8.188-3.668-8.188-8.188c0-4.52,3.668-8.188,8.188-8.188h22.583V24.974H24.574c-1.399,0-2.763,0.115-4.099,0.34
41
+ C8.86,27.26,0,37.377,0,49.547c0,10.7,6.843,19.803,16.386,23.176c1.318,0.466,2.681,0.825,4.09,1.058
42
+ c1.336,0.225,2.699,0.341,4.099,0.341h20.592c1.399,0,2.763-0.116,4.099-0.341c1.409-0.232,2.771-0.592,4.09-1.058
43
+ C62.897,69.35,69.74,60.247,69.74,49.547v-8.188c9.05,0,16.386-7.337,16.386-16.386H69.74v-6.145c0-1.13,0.915-2.044,2.045-2.044
44
+ h4.098c4.996,0,9.525-1.982,12.843-5.212c2.987-2.905,4.996-6.807,5.471-11.174H71.786C66.799,0.399,62.261,2.381,58.942,5.609"/>
45
+ <path fill="#F06335" d="M24.575,78.218c-4.523,0-8.191,3.667-8.191,8.191c0,4.523,3.667,8.191,8.191,8.191s8.191-3.668,8.191-8.191
46
+ C32.766,81.885,29.098,78.218,24.575,78.218"/>
47
+ <path fill="#F06335" d="M49.149,78.218c-4.524,0-8.192,3.667-8.192,8.191c0,4.523,3.667,8.191,8.192,8.191
48
+ c4.523,0,8.191-3.668,8.191-8.191C57.34,81.885,53.672,78.218,49.149,78.218"/>
49
+ </g>
50
+ </svg>
changelog.txt CHANGED
@@ -1,109 +1,114 @@
1
- Version 1.2.9 - Thursday, 14th January 2021
2
- - New: Added the filter before coupon generation to modify the coupon arguments.
3
- - Improvement: Added the Phone number field in export data.
4
- - Fix: Fixed the get_title on boolean error and PHP 8 notices.
5
- - Fix: Showing wrong product images for variation.
6
-
7
- Version 1.2.8 - Friday, 14th August 2020
8
- - New: Added new option to prevent recovery emails for specific order status.
9
- - Fix: Deprecated the 'woo_ca_exclude_on_hold_order_from_tracking' filter.
10
-
11
- Version 1.2.7 - Tuesday, 16th June 2020
12
- - New: Users can now share [non-personal usage data] to help us test and develop better products.
13
- (https://my.cartflows.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking)
14
-
15
- Version 1.2.6 - Thursday, 21st May 2020
16
- - New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
17
- - Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
18
- - Fix: Coupons generated by plugin were not deleting.
19
- - Fix: Variation/Custom product attributes were excluded from the recovered cart.
20
-
21
- Version 1.2.5 - Wednesday, 11th March 2020
22
- - Improvement: Allowed plugin access to the shop manager.
23
- - Fix: Variable product name not showing in the product table.
24
- - Fix: All orders are not exporting due to the wrong pagination.
25
- - Fix: Not showing the next page's orders.
26
-
27
- Version 1.2.4 - Thursday, 06th February 2020
28
- - New: Added option to export abandoned orders.
29
- - New: Added option to search abandoned orders.
30
- - Improvement: Compatibility with the latest WordPress PHP_CodeSniffer rules.
31
- - Fix: Get id error while sending emails.
32
-
33
- Version 1.2.3 - Thursday, 12th December 2019
34
- - New: Added option to unsubscribe users in bulk.
35
- - New: Added filter 'woo_ca_exclude_on_hold_order_from_tracking' to exclude on hold orders from the tracking.
36
- - New: Added product table shortcode for webhook.
37
- - Improvement: Updated filter 'woo_ca_email_template_table_style' for product table alignment.
38
- - Fix: Sometimes test emails are not sending.
39
-
40
- Version 1.2.2 - Tuesday, 12th November 2019
41
- - Fix: Duplicate order issue for variation products.
42
-
43
- Version 1.2.1 - Tuesday, 5th November 2019
44
- - New: Added delete option for used & expired coupons which will be created now onwards.
45
- - Fix: Sometimes order status remains "abandoned" for initially failed orders.
46
- - Fix: Strings updated for translation.
47
-
48
- Version 1.2.0 - Monday, 14th October 2019
49
- - New: Added support for PPOM products.
50
- - Improvement: Added email activate toggle button on grid.
51
- - Improvement: Added notice on the checkout page for test emails.
52
- - Fix: Zero-value orders getting tracked.
53
- - Fix: Disable tracking for the custom user roles.
54
-
55
- Version 1.1.9 - Thursday, 19th September 2019
56
- - New: Option added to ignore users from cart abandonment process.
57
- - New: Filter added to customize the styling of email template table.
58
- - Improvement: Added compatibility with Razorpay plugin.
59
- - Fix: Email template markup was breaking after save.
60
- - Fix: Failed orders were getting marked as completed.
61
- - Fix: Empty order was getting tracked and email sending for it.
62
- - Fix: Email settings options were swapping value of from and reply-to.
63
-
64
- Version 1.1.8 - Tuesday, 3rd September 2019
65
- - New: Option added to auto-apply coupon on the checkout.
66
- - New: Option added to apply coupon individually.
67
- - New: Option added to create free shipping coupons.
68
-
69
- Version 1.1.7 - Monday, 12th August 2019
70
- - New: Filter added to show the cart total inside the email template.
71
- - New: Filter added to change the cart abandoned time.
72
- - Improvement: Order tracking logic updated for automated payments.
73
- - Improvement: Update report dashboard DateTime format to WordPress format.
74
- - Fix: Broken image in the email template.
75
-
76
- Version 1.1.6 - Friday, 19th July 2019
77
- - New: Bundled product support for email checkout URL.
78
- - Improvement: Added phone number and address while triggering the to webhook.
79
- - Fix: Creating tables and default settings on activation.
80
-
81
- Version 1.1.5 - Tuesday, 9th July 2019
82
- - Fix: Other crons disappearing issue.
83
-
84
- Version 1.1.4 - Tuesday, 9th July 2019
85
- - Fix: Follow up emails were getting sent even after the completion of the order.
86
- - Fix: Email template variable 'Abandoned Product Names' warning issue.
87
-
88
- Version 1.1.3 - Thursday, 27th June 2019
89
- - Improvement: Added checkout link for abandoned cart inside the admin section.
90
- - Fix: Added pagination for reports.
91
- - Fix: Recover report calculations before campaign triggers.
92
- - Fix: Empty cart notice when CartFlows checkout is set global.
93
-
94
- Version 1.1.2 - Wednesday, 12th June 2019
95
- - Fix: Issue of timezone while sending mail through cron.
96
- - Fix: Delete single cart abandonment order.
97
- - Fix: MySql 5.5 support for CURRENT_TIMESTAMP.
98
-
99
- Version 1.1.1 - Thursday, 06th June 2019
100
- - New: Added feature to reschedule emails for Admin.
101
- - Fix: Coupon expiry time issue.
102
- - Fix: Email issue for a user who has an already purchased order.
103
- - Fix: Translatable strings updated.
104
-
105
- Version 1.1.0 - Thursday, 30th May 2019
106
- - Added a view for admin to check email status specific to the particular abandoned user.
107
-
108
- Version 1.0.0 - Monday, 27th May 2019
 
 
 
 
 
109
  - Initial Release
1
+ Version 1.2.10 - Tuesday, 16th February 2021
2
+ - New: Added the option to delete the plugin data on plugin deletion.
3
+ - New: Added the filter before triggering the webhook.
4
+ - Improvement: Showing Parent product image if variation image is not set.
5
+
6
+ Version 1.2.9 - Thursday, 14th January 2021
7
+ - New: Added the filter before coupon generation to modify the coupon arguments.
8
+ - Improvement: Added the Phone number field in export data.
9
+ - Fix: Fixed the get_title on boolean error and PHP 8 notices.
10
+ - Fix: Showing wrong product images for variation.
11
+
12
+ Version 1.2.8 - Friday, 14th August 2020
13
+ - New: Added new option to prevent recovery emails for specific order status.
14
+ - Fix: Deprecated the 'woo_ca_exclude_on_hold_order_from_tracking' filter.
15
+
16
+ Version 1.2.7 - Tuesday, 16th June 2020
17
+ - New: Users can now share [non-personal usage data] to help us test and develop better products.
18
+ (https://my.cartflows.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking)
19
+
20
+ Version 1.2.6 - Thursday, 21st May 2020
21
+ - New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
22
+ - Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
23
+ - Fix: Coupons generated by plugin were not deleting.
24
+ - Fix: Variation/Custom product attributes were excluded from the recovered cart.
25
+
26
+ Version 1.2.5 - Wednesday, 11th March 2020
27
+ - Improvement: Allowed plugin access to the shop manager.
28
+ - Fix: Variable product name not showing in the product table.
29
+ - Fix: All orders are not exporting due to the wrong pagination.
30
+ - Fix: Not showing the next page's orders.
31
+
32
+ Version 1.2.4 - Thursday, 06th February 2020
33
+ - New: Added option to export abandoned orders.
34
+ - New: Added option to search abandoned orders.
35
+ - Improvement: Compatibility with the latest WordPress PHP_CodeSniffer rules.
36
+ - Fix: Get id error while sending emails.
37
+
38
+ Version 1.2.3 - Thursday, 12th December 2019
39
+ - New: Added option to unsubscribe users in bulk.
40
+ - New: Added filter 'woo_ca_exclude_on_hold_order_from_tracking' to exclude on hold orders from the tracking.
41
+ - New: Added product table shortcode for webhook.
42
+ - Improvement: Updated filter 'woo_ca_email_template_table_style' for product table alignment.
43
+ - Fix: Sometimes test emails are not sending.
44
+
45
+ Version 1.2.2 - Tuesday, 12th November 2019
46
+ - Fix: Duplicate order issue for variation products.
47
+
48
+ Version 1.2.1 - Tuesday, 5th November 2019
49
+ - New: Added delete option for used & expired coupons which will be created now onwards.
50
+ - Fix: Sometimes order status remains "abandoned" for initially failed orders.
51
+ - Fix: Strings updated for translation.
52
+
53
+ Version 1.2.0 - Monday, 14th October 2019
54
+ - New: Added support for PPOM products.
55
+ - Improvement: Added email activate toggle button on grid.
56
+ - Improvement: Added notice on the checkout page for test emails.
57
+ - Fix: Zero-value orders getting tracked.
58
+ - Fix: Disable tracking for the custom user roles.
59
+
60
+ Version 1.1.9 - Thursday, 19th September 2019
61
+ - New: Option added to ignore users from cart abandonment process.
62
+ - New: Filter added to customize the styling of email template table.
63
+ - Improvement: Added compatibility with Razorpay plugin.
64
+ - Fix: Email template markup was breaking after save.
65
+ - Fix: Failed orders were getting marked as completed.
66
+ - Fix: Empty order was getting tracked and email sending for it.
67
+ - Fix: Email settings options were swapping value of from and reply-to.
68
+
69
+ Version 1.1.8 - Tuesday, 3rd September 2019
70
+ - New: Option added to auto-apply coupon on the checkout.
71
+ - New: Option added to apply coupon individually.
72
+ - New: Option added to create free shipping coupons.
73
+
74
+ Version 1.1.7 - Monday, 12th August 2019
75
+ - New: Filter added to show the cart total inside the email template.
76
+ - New: Filter added to change the cart abandoned time.
77
+ - Improvement: Order tracking logic updated for automated payments.
78
+ - Improvement: Update report dashboard DateTime format to WordPress format.
79
+ - Fix: Broken image in the email template.
80
+
81
+ Version 1.1.6 - Friday, 19th July 2019
82
+ - New: Bundled product support for email checkout URL.
83
+ - Improvement: Added phone number and address while triggering the to webhook.
84
+ - Fix: Creating tables and default settings on activation.
85
+
86
+ Version 1.1.5 - Tuesday, 9th July 2019
87
+ - Fix: Other crons disappearing issue.
88
+
89
+ Version 1.1.4 - Tuesday, 9th July 2019
90
+ - Fix: Follow up emails were getting sent even after the completion of the order.
91
+ - Fix: Email template variable 'Abandoned Product Names' warning issue.
92
+
93
+ Version 1.1.3 - Thursday, 27th June 2019
94
+ - Improvement: Added checkout link for abandoned cart inside the admin section.
95
+ - Fix: Added pagination for reports.
96
+ - Fix: Recover report calculations before campaign triggers.
97
+ - Fix: Empty cart notice when CartFlows checkout is set global.
98
+
99
+ Version 1.1.2 - Wednesday, 12th June 2019
100
+ - Fix: Issue of timezone while sending mail through cron.
101
+ - Fix: Delete single cart abandonment order.
102
+ - Fix: MySql 5.5 support for CURRENT_TIMESTAMP.
103
+
104
+ Version 1.1.1 - Thursday, 06th June 2019
105
+ - New: Added feature to reschedule emails for Admin.
106
+ - Fix: Coupon expiry time issue.
107
+ - Fix: Email issue for a user who has an already purchased order.
108
+ - Fix: Translatable strings updated.
109
+
110
+ Version 1.1.0 - Thursday, 30th May 2019
111
+ - Added a view for admin to check email status specific to the particular abandoned user.
112
+
113
+ Version 1.0.0 - Monday, 27th May 2019
114
  - Initial Release
classes/class-cartflows-ca-loader.php CHANGED
@@ -1,377 +1,382 @@
1
- <?php
2
- /**
3
- * CartFlows Loader.
4
- *
5
- * @package Woocommerce-Cart-Abandonment-Recovery
6
- */
7
-
8
- if ( ! class_exists( 'CARTFLOWS_CA_Loader' ) ) {
9
-
10
- /**
11
- * Class CARTFLOWS_CA_Loader.
12
- */
13
- final class CARTFLOWS_CA_Loader {
14
-
15
-
16
- /**
17
- * Member Variable
18
- *
19
- * @var instance
20
- */
21
- private static $instance = null;
22
-
23
- /**
24
- * Member Variable
25
- *
26
- * @var utils
27
- */
28
- public $utils = null;
29
-
30
-
31
- /**
32
- * Initiator
33
- */
34
- public static function get_instance() {
35
-
36
- if ( is_null( self::$instance ) ) {
37
-
38
- self::$instance = new self();
39
-
40
- /**
41
- * CartFlows CA loaded.
42
- *
43
- * Fires when Cartflows CA was fully loaded and instantiated.
44
- *
45
- * @since 1.0.0
46
- */
47
- do_action( 'cartflows_ca_loaded' );
48
- }
49
-
50
- return self::$instance;
51
- }
52
-
53
- /**
54
- * Constructor
55
- */
56
- public function __construct() {
57
-
58
- $this->define_constants();
59
-
60
- // Activation hook.
61
- register_activation_hook( CARTFLOWS_CA_FILE, array( $this, 'activation_reset' ) );
62
-
63
- // deActivation hook.
64
- register_deactivation_hook( CARTFLOWS_CA_FILE, array( $this, 'deactivation_reset' ) );
65
-
66
- add_action( 'plugins_loaded', array( $this, 'load_plugin' ), 99 );
67
-
68
- add_action( 'plugins_loaded', array( $this, 'load_cf_textdomain' ) );
69
-
70
- }
71
-
72
- /**
73
- * Defines all constants
74
- *
75
- * @since 1.0.0
76
- */
77
- public function define_constants() {
78
- define( 'CARTFLOWS_CA_BASE', plugin_basename( CARTFLOWS_CA_FILE ) );
79
- define( 'CARTFLOWS_CA_DIR', plugin_dir_path( CARTFLOWS_CA_FILE ) );
80
- define( 'CARTFLOWS_CA_URL', plugins_url( '/', CARTFLOWS_CA_FILE ) );
81
- define( 'CARTFLOWS_CA_VER', '1.2.9' );
82
- define( 'CARTFLOWS_CA_SLUG', 'cartflows_ca' );
83
-
84
- define( 'CARTFLOWS_CA_CART_ABANDONMENT_TABLE', 'cartflows_ca_cart_abandonment' );
85
- define( 'CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE', 'cartflows_ca_email_templates' );
86
- define( 'CARTFLOWS_CA_EMAIL_HISTORY_TABLE', 'cartflows_ca_email_history' );
87
- define( 'CARTFLOWS_CA_EMAIL_TEMPLATE_META_TABLE', 'cartflows_ca_email_templates_meta' );
88
- }
89
-
90
- /**
91
- * Loads plugin files.
92
- *
93
- * @since 1.0.0
94
- *
95
- * @return void
96
- */
97
- public function load_plugin() {
98
-
99
- if ( ! function_exists( 'WC' ) ) {
100
- add_action( 'admin_notices', array( $this, 'fails_to_load' ) );
101
- return;
102
- }
103
-
104
- $this->load_helper_files_components();
105
- $this->load_core_files();
106
- $this->load_core_components();
107
-
108
- /**
109
- * CartFlows Init.
110
- *
111
- * Fires when Cartflows is instantiated.
112
- *
113
- * @since 1.0.0
114
- */
115
- do_action( 'cartflows_ca_init' );
116
- }
117
-
118
-
119
-
120
-
121
- /**
122
- * Fires admin notice when Elementor is not installed and activated.
123
- *
124
- * @since 1.0.0
125
- *
126
- * @return void
127
- */
128
- public function fails_to_load() {
129
-
130
- $screen = get_current_screen();
131
-
132
- if ( isset( $screen->parent_file ) && 'plugins.php' === $screen->parent_file && 'update' === $screen->id ) {
133
- return;
134
- }
135
-
136
- $class = 'notice notice-error';
137
- /* translators: %s: html tags */
138
- $message = sprintf( __( 'The %1$sWooCommerce Cart Abandonment Recovery%2$s plugin requires %1$sWooCommerce%2$s plugin installed & activated.', 'woo-cart-abandonment-recovery' ), '<strong>', '</strong>' );
139
- $plugin = 'woocommerce/woocommerce.php';
140
-
141
- if ( $this->is_woo_installed() ) {
142
- if ( ! current_user_can( 'activate_plugins' ) ) {
143
- return;
144
- }
145
-
146
- $action_url = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $plugin . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . $plugin );
147
- $button_label = __( 'Activate WooCommerce', 'woo-cart-abandonment-recovery' );
148
-
149
- } else {
150
- if ( ! current_user_can( 'install_plugins' ) ) {
151
- return;
152
- }
153
-
154
- $action_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=woocommerce' ), 'install-plugin_woocommerce' );
155
- $button_label = __( 'Install WooCommerce', 'woo-cart-abandonment-recovery' );
156
- }
157
-
158
- $button = '<p><a href="' . $action_url . '" class="button-primary">' . $button_label . '</a></p><p></p>';
159
-
160
- printf( '<div class="%1$s"><p>%2$s</p>%3$s</div>', esc_attr( $class ), wp_kses_post( $message ), wp_kses_post( $button ) );
161
- }
162
-
163
-
164
- /**
165
- * Is woocommerce plugin installed.
166
- *
167
- * @since 1.0.0
168
- *
169
- * @access public
170
- */
171
- public function is_woo_installed() {
172
-
173
- $path = 'woocommerce/woocommerce.php';
174
- $plugins = get_plugins();
175
-
176
- return isset( $plugins[ $path ] );
177
- }
178
-
179
- /**
180
- * Create new database tables for plugin updates.
181
- *
182
- * @since 1.0.0
183
- *
184
- * @return void
185
- */
186
- public function initialize_cart_abandonment_tables() {
187
-
188
- include_once CARTFLOWS_CA_DIR . 'modules/cart-abandonment/class-cartflows-ca-cart-abandonment-db.php';
189
- $db = Cartflows_Ca_Cart_Abandonment_Db::get_instance();
190
- $db->create_tables();
191
- $db->template_table_seeder();
192
- }
193
-
194
-
195
- /**
196
- * Load Helper Files and Components.
197
- *
198
- * @since 1.0.0
199
- *
200
- * @return void
201
- */
202
- public function load_helper_files_components() {
203
-
204
- include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-utils.php';
205
- $this->utils = Cartflows_Ca_Utils::get_instance();
206
- }
207
-
208
- /**
209
- * Load core files.
210
- */
211
- public function load_core_files() {
212
- /* Update compatibility. */
213
- require_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-update.php';
214
-
215
- include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-settings.php';
216
-
217
- if ( is_admin() ) {
218
- require_once CARTFLOWS_CA_DIR . 'lib/notices/class-astra-notices.php';
219
- }
220
-
221
- if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
222
- require_once CARTFLOWS_CA_DIR . '/admin/bsf-analytics/class-bsf-analytics-loader.php';
223
- }
224
-
225
- $bsf_analytics = BSF_Analytics_Loader::get_instance();
226
-
227
- $bsf_analytics->set_entity(
228
- array(
229
- 'cf' => array(
230
- 'product_name' => 'Woocommerce Cart Abandonment Recovery',
231
- 'usage_doc_link' => 'https://my.cartflows.com/usage-tracking/',
232
- 'path' => CARTFLOWS_CA_DIR . 'admin/bsf-analytics',
233
- 'author' => 'CartFlows Inc',
234
- ),
235
- )
236
- );
237
- }
238
-
239
- /**
240
- * Load CartFlows Ca Text Domain.
241
- * This will load the translation textdomain depending on the file priorities.
242
- * 1. Global Languages /wp-content/languages/%plugin-folder-name%/ folder
243
- * 2. Local dorectory /wp-content/plugins/%plugin-folder-name%/languages/ folder
244
- *
245
- * @since 1.0.3
246
- * @return void
247
- */
248
- public function load_cf_textdomain() {
249
-
250
- // Default languages directory for CartFlows Ca.
251
- $lang_dir = CARTFLOWS_CA_DIR . 'languages/';
252
-
253
- /**
254
- * Filters the languages directory path to use for CartFlows Ca.
255
- *
256
- * @param string $lang_dir The languages directory path.
257
- */
258
- $lang_dir = apply_filters( 'carflows_ca_languages_directory', $lang_dir );
259
-
260
- // Traditional WordPress plugin locale filter.
261
- global $wp_version;
262
-
263
- $get_locale = get_locale();
264
-
265
- if ( $wp_version >= 4.7 ) {
266
- $get_locale = get_user_locale();
267
- }
268
-
269
- /**
270
- * Language Locale for CartFlows Ca
271
- *
272
- * @var $get_locale The locale to use.
273
- * Uses get_user_locale()` in WordPress 4.7 or greater,
274
- * otherwise uses `get_locale()`.
275
- */
276
- $locale = apply_filters( 'plugin_locale', $get_locale, 'woo-cart-abandonment-recovery' );
277
- $mofile = sprintf( '%1$s-%2$s.mo', 'woo-cart-abandonment-recovery', $locale );
278
-
279
- // Setup paths to current locale file.
280
- $mofile_local = $lang_dir . $mofile;
281
- $mofile_global = WP_LANG_DIR . '/plugins/' . $mofile;
282
-
283
- if ( file_exists( $mofile_global ) ) {
284
- // Look in global /wp-content/languages/%plugin-folder-name%/ folder.
285
- load_textdomain( 'woo-cart-abandonment-recovery', $mofile_global );
286
- } elseif ( file_exists( $mofile_local ) ) {
287
- // Look in local /wp-content/plugins/%plugin-folder-name%/languages/ folder.
288
- load_textdomain( 'woo-cart-abandonment-recovery', $mofile_local );
289
- } else {
290
- // Load the default language files.
291
- load_plugin_textdomain( 'woo-cart-abandonment-recovery', false, $lang_dir );
292
- }
293
- }
294
- /**
295
- * Load Core Components.
296
- *
297
- * @since 1.0.0
298
- *
299
- * @return void
300
- */
301
- public function load_core_components() {
302
-
303
- /* Cart abandonment templates class */
304
- include_once CARTFLOWS_CA_DIR . 'modules/cart-abandonment/class-cartflows-ca-module-loader.php';
305
-
306
- }
307
-
308
-
309
- /**
310
- * Activation Reset
311
- */
312
- public function activation_reset() {
313
- $this->update_default_settings();
314
- $this->initialize_cart_abandonment_tables();
315
- }
316
-
317
-
318
- /**
319
- * Set the default cart abandonment settings.
320
- */
321
- public function update_default_settings() {
322
-
323
- $current_user = wp_get_current_user();
324
- $email_from = ( isset( $current_user->user_firstname ) && ! empty( $current_user->user_firstname ) ) ? $current_user->user_firstname . ' ' . $current_user->user_lastname : 'Admin';
325
- $default_settings = array(
326
- 'wcf_ca_status' => 'on',
327
- 'wcf_ca_gdpr_status' => 'off',
328
- 'wcf_ca_coupon_code_status' => 'off',
329
- 'wcf_ca_zapier_tracking_status' => 'off',
330
- 'wcf_ca_cut_off_time' => 15,
331
- 'wcf_ca_from_name' => $email_from,
332
- 'wcf_ca_from_email' => $current_user->user_email,
333
- 'wcf_ca_reply_email' => $current_user->user_email,
334
- 'wcf_ca_discount_type' => 'percent',
335
- 'wcf_ca_coupon_amount' => 10,
336
- 'wcf_ca_zapier_cart_abandoned_webhook' => '',
337
- 'wcf_ca_gdpr_message' => 'Your email & cart are saved so we can send email reminders about this order.',
338
- 'wcf_ca_coupon_expiry' => 0,
339
- 'wcf_ca_coupon_expiry_unit' => 'hours',
340
- 'wcf_ca_excludes_orders' => array( 'processing', 'completed' ),
341
-
342
- );
343
-
344
- foreach ( $default_settings as $option_key => $option_value ) {
345
- if ( ! get_option( $option_key ) ) {
346
- update_option( $option_key, $option_value );
347
- }
348
- }
349
- }
350
-
351
- /**
352
- * Deactivation Reset
353
- */
354
- public function deactivation_reset() {
355
- wp_clear_scheduled_hook( 'cartflows_ca_update_order_status_action' );
356
- }
357
- }
358
-
359
- /**
360
- * Prepare if class 'CARTFLOWS_CA_Loader' exist.
361
- * Kicking this off by calling 'get_instance()' method
362
- */
363
- CARTFLOWS_CA_Loader::get_instance();
364
- }
365
-
366
-
367
- if ( ! function_exists( 'wcf_ca' ) ) {
368
- /**
369
- * Get global class.
370
- *
371
- * @return object
372
- */
373
- function wcf_ca() {
374
- return CARTFLOWS_CA_Loader::get_instance();
375
- }
376
- }
377
-
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CartFlows Loader.
4
+ *
5
+ * @package Woocommerce-Cart-Abandonment-Recovery
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ if ( ! class_exists( 'CARTFLOWS_CA_Loader' ) ) {
13
+
14
+ /**
15
+ * Class CARTFLOWS_CA_Loader.
16
+ */
17
+ final class CARTFLOWS_CA_Loader {
18
+
19
+
20
+ /**
21
+ * Member Variable
22
+ *
23
+ * @var instance
24
+ */
25
+ private static $instance = null;
26
+
27
+ /**
28
+ * Member Variable
29
+ *
30
+ * @var utils
31
+ */
32
+ public $utils = null;
33
+
34
+
35
+ /**
36
+ * Initiator
37
+ */
38
+ public static function get_instance() {
39
+
40
+ if ( is_null( self::$instance ) ) {
41
+
42
+ self::$instance = new self();
43
+
44
+ /**
45
+ * CartFlows CA loaded.
46
+ *
47
+ * Fires when Cartflows CA was fully loaded and instantiated.
48
+ *
49
+ * @since 1.0.0
50
+ */
51
+ do_action( 'cartflows_ca_loaded' );
52
+ }
53
+
54
+ return self::$instance;
55
+ }
56
+
57
+ /**
58
+ * Constructor
59
+ */
60
+ public function __construct() {
61
+
62
+ $this->define_constants();
63
+
64
+ // Activation hook.
65
+ register_activation_hook( CARTFLOWS_CA_FILE, array( $this, 'activation_reset' ) );
66
+
67
+ // deActivation hook.
68
+ register_deactivation_hook( CARTFLOWS_CA_FILE, array( $this, 'deactivation_reset' ) );
69
+
70
+ add_action( 'plugins_loaded', array( $this, 'load_plugin' ), 99 );
71
+
72
+ add_action( 'plugins_loaded', array( $this, 'load_cf_textdomain' ) );
73
+
74
+ }
75
+
76
+ /**
77
+ * Defines all constants
78
+ *
79
+ * @since 1.0.0
80
+ */
81
+ public function define_constants() {
82
+ define( 'CARTFLOWS_CA_BASE', plugin_basename( CARTFLOWS_CA_FILE ) );
83
+ define( 'CARTFLOWS_CA_DIR', plugin_dir_path( CARTFLOWS_CA_FILE ) );
84
+ define( 'CARTFLOWS_CA_URL', plugins_url( '/', CARTFLOWS_CA_FILE ) );
85
+ define( 'CARTFLOWS_CA_VER', '1.2.10' );
86
+ define( 'CARTFLOWS_CA_SLUG', 'cartflows_ca' );
87
+
88
+ define( 'CARTFLOWS_CA_CART_ABANDONMENT_TABLE', 'cartflows_ca_cart_abandonment' );
89
+ define( 'CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE', 'cartflows_ca_email_templates' );
90
+ define( 'CARTFLOWS_CA_EMAIL_HISTORY_TABLE', 'cartflows_ca_email_history' );
91
+ define( 'CARTFLOWS_CA_EMAIL_TEMPLATE_META_TABLE', 'cartflows_ca_email_templates_meta' );
92
+ }
93
+
94
+ /**
95
+ * Loads plugin files.
96
+ *
97
+ * @since 1.0.0
98
+ *
99
+ * @return void
100
+ */
101
+ public function load_plugin() {
102
+
103
+ if ( ! function_exists( 'WC' ) ) {
104
+ add_action( 'admin_notices', array( $this, 'fails_to_load' ) );
105
+ return;
106
+ }
107
+
108
+ $this->load_helper_files_components();
109
+ $this->load_core_files();
110
+ $this->load_core_components();
111
+
112
+ /**
113
+ * CartFlows Init.
114
+ *
115
+ * Fires when Cartflows is instantiated.
116
+ *
117
+ * @since 1.0.0
118
+ */
119
+ do_action( 'cartflows_ca_init' );
120
+ }
121
+
122
+
123
+
124
+
125
+ /**
126
+ * Fires admin notice when Elementor is not installed and activated.
127
+ *
128
+ * @since 1.0.0
129
+ *
130
+ * @return void
131
+ */
132
+ public function fails_to_load() {
133
+
134
+ $screen = get_current_screen();
135
+
136
+ if ( isset( $screen->parent_file ) && 'plugins.php' === $screen->parent_file && 'update' === $screen->id ) {
137
+ return;
138
+ }
139
+
140
+ $class = 'notice notice-error';
141
+ /* translators: %s: html tags */
142
+ $message = sprintf( __( 'The %1$sWooCommerce Cart Abandonment Recovery%2$s plugin requires %1$sWooCommerce%2$s plugin installed & activated.', 'woo-cart-abandonment-recovery' ), '<strong>', '</strong>' );
143
+ $plugin = 'woocommerce/woocommerce.php';
144
+
145
+ if ( $this->is_woo_installed() ) {
146
+ if ( ! current_user_can( 'activate_plugins' ) ) {
147
+ return;
148
+ }
149
+
150
+ $action_url = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $plugin . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . $plugin );
151
+ $button_label = __( 'Activate WooCommerce', 'woo-cart-abandonment-recovery' );
152
+
153
+ } else {
154
+ if ( ! current_user_can( 'install_plugins' ) ) {
155
+ return;
156
+ }
157
+
158
+ $action_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=woocommerce' ), 'install-plugin_woocommerce' );
159
+ $button_label = __( 'Install WooCommerce', 'woo-cart-abandonment-recovery' );
160
+ }
161
+
162
+ $button = '<p><a href="' . $action_url . '" class="button-primary">' . $button_label . '</a></p><p></p>';
163
+
164
+ printf( '<div class="%1$s"><p>%2$s</p>%3$s</div>', esc_attr( $class ), wp_kses_post( $message ), wp_kses_post( $button ) );
165
+ }
166
+
167
+
168
+ /**
169
+ * Is woocommerce plugin installed.
170
+ *
171
+ * @since 1.0.0
172
+ *
173
+ * @access public
174
+ */
175
+ public function is_woo_installed() {
176
+
177
+ $path = 'woocommerce/woocommerce.php';
178
+ $plugins = get_plugins();
179
+
180
+ return isset( $plugins[ $path ] );
181
+ }
182
+
183
+ /**
184
+ * Create new database tables for plugin updates.
185
+ *
186
+ * @since 1.0.0
187
+ *
188
+ * @return void
189
+ */
190
+ public function initialize_cart_abandonment_tables() {
191
+
192
+ include_once CARTFLOWS_CA_DIR . 'modules/cart-abandonment/class-cartflows-ca-cart-abandonment-db.php';
193
+ $db = Cartflows_Ca_Cart_Abandonment_Db::get_instance();
194
+ $db->create_tables();
195
+ $db->template_table_seeder();
196
+ }
197
+
198
+
199
+ /**
200
+ * Load Helper Files and Components.
201
+ *
202
+ * @since 1.0.0
203
+ *
204
+ * @return void
205
+ */
206
+ public function load_helper_files_components() {
207
+
208
+ include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-utils.php';
209
+ $this->utils = Cartflows_Ca_Utils::get_instance();
210
+ }
211
+
212
+ /**
213
+ * Load core files.
214
+ */
215
+ public function load_core_files() {
216
+ /* Update compatibility. */
217
+ require_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-update.php';
218
+
219
+ include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-settings.php';
220
+
221
+ if ( is_admin() ) {
222
+ require_once CARTFLOWS_CA_DIR . 'lib/notices/class-astra-notices.php';
223
+ }
224
+
225
+ if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
226
+ require_once CARTFLOWS_CA_DIR . '/admin/bsf-analytics/class-bsf-analytics-loader.php';
227
+ }
228
+
229
+ $bsf_analytics = BSF_Analytics_Loader::get_instance();
230
+
231
+ $bsf_analytics->set_entity(
232
+ array(
233
+ 'cf' => array(
234
+ 'product_name' => 'Woocommerce Cart Abandonment Recovery',
235
+ 'usage_doc_link' => 'https://my.cartflows.com/usage-tracking/',
236
+ 'path' => CARTFLOWS_CA_DIR . 'admin/bsf-analytics',
237
+ 'author' => 'CartFlows Inc',
238
+ ),
239
+ )
240
+ );
241
+ }
242
+
243
+ /**
244
+ * Load CartFlows Ca Text Domain.
245
+ * This will load the translation textdomain depending on the file priorities.
246
+ * 1. Global Languages /wp-content/languages/%plugin-folder-name%/ folder
247
+ * 2. Local dorectory /wp-content/plugins/%plugin-folder-name%/languages/ folder
248
+ *
249
+ * @since 1.0.3
250
+ * @return void
251
+ */
252
+ public function load_cf_textdomain() {
253
+
254
+ // Default languages directory for CartFlows Ca.
255
+ $lang_dir = CARTFLOWS_CA_DIR . 'languages/';
256
+
257
+ /**
258
+ * Filters the languages directory path to use for CartFlows Ca.
259
+ *
260
+ * @param string $lang_dir The languages directory path.
261
+ */
262
+ $lang_dir = apply_filters( 'carflows_ca_languages_directory', $lang_dir );
263
+
264
+ // Traditional WordPress plugin locale filter.
265
+ global $wp_version;
266
+
267
+ $get_locale = get_locale();
268
+
269
+ if ( $wp_version >= 4.7 ) {
270
+ $get_locale = get_user_locale();
271
+ }
272
+
273
+ /**
274
+ * Language Locale for CartFlows Ca
275
+ *
276
+ * @var $get_locale The locale to use.
277
+ * Uses get_user_locale()` in WordPress 4.7 or greater,
278
+ * otherwise uses `get_locale()`.
279
+ */
280
+ $locale = apply_filters( 'plugin_locale', $get_locale, 'woo-cart-abandonment-recovery' );
281
+ $mofile = sprintf( '%1$s-%2$s.mo', 'woo-cart-abandonment-recovery', $locale );
282
+
283
+ // Setup paths to current locale file.
284
+ $mofile_local = $lang_dir . $mofile;
285
+ $mofile_global = WP_LANG_DIR . '/plugins/' . $mofile;
286
+
287
+ if ( file_exists( $mofile_global ) ) {
288
+ // Look in global /wp-content/languages/%plugin-folder-name%/ folder.
289
+ load_textdomain( 'woo-cart-abandonment-recovery', $mofile_global );
290
+ } elseif ( file_exists( $mofile_local ) ) {
291
+ // Look in local /wp-content/plugins/%plugin-folder-name%/languages/ folder.
292
+ load_textdomain( 'woo-cart-abandonment-recovery', $mofile_local );
293
+ } else {
294
+ // Load the default language files.
295
+ load_plugin_textdomain( 'woo-cart-abandonment-recovery', false, $lang_dir );
296
+ }
297
+ }
298
+ /**
299
+ * Load Core Components.
300
+ *
301
+ * @since 1.0.0
302
+ *
303
+ * @return void
304
+ */
305
+ public function load_core_components() {
306
+
307
+ /* Cart abandonment templates class */
308
+ include_once CARTFLOWS_CA_DIR . 'modules/cart-abandonment/class-cartflows-ca-module-loader.php';
309
+
310
+ }
311
+
312
+
313
+ /**
314
+ * Activation Reset
315
+ */
316
+ public function activation_reset() {
317
+ $this->update_default_settings();
318
+ $this->initialize_cart_abandonment_tables();
319
+ }
320
+
321
+
322
+ /**
323
+ * Set the default cart abandonment settings.
324
+ */
325
+ public function update_default_settings() {
326
+
327
+ $current_user = wp_get_current_user();
328
+ $email_from = ( isset( $current_user->user_firstname ) && ! empty( $current_user->user_firstname ) ) ? $current_user->user_firstname . ' ' . $current_user->user_lastname : 'Admin';
329
+ $default_settings = array(
330
+ 'wcf_ca_status' => 'on',
331
+ 'wcf_ca_gdpr_status' => 'off',
332
+ 'wcf_ca_coupon_code_status' => 'off',
333
+ 'wcf_ca_zapier_tracking_status' => 'off',
334
+ 'wcf_ca_delete_plugin_data' => 'off',
335
+ 'wcf_ca_cut_off_time' => 15,
336
+ 'wcf_ca_from_name' => $email_from,
337
+ 'wcf_ca_from_email' => $current_user->user_email,
338
+ 'wcf_ca_reply_email' => $current_user->user_email,
339
+ 'wcf_ca_discount_type' => 'percent',
340
+ 'wcf_ca_coupon_amount' => 10,
341
+ 'wcf_ca_zapier_cart_abandoned_webhook' => '',
342
+ 'wcf_ca_gdpr_message' => 'Your email & cart are saved so we can send email reminders about this order.',
343
+ 'wcf_ca_coupon_expiry' => 0,
344
+ 'wcf_ca_coupon_expiry_unit' => 'hours',
345
+ 'wcf_ca_excludes_orders' => array( 'processing', 'completed' ),
346
+
347
+ );
348
+
349
+ foreach ( $default_settings as $option_key => $option_value ) {
350
+ if ( ! get_option( $option_key ) ) {
351
+ update_option( $option_key, $option_value );
352
+ }
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Deactivation Reset
358
+ */
359
+ public function deactivation_reset() {
360
+ wp_clear_scheduled_hook( 'cartflows_ca_update_order_status_action' );
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Prepare if class 'CARTFLOWS_CA_Loader' exist.
366
+ * Kicking this off by calling 'get_instance()' method
367
+ */
368
+ CARTFLOWS_CA_Loader::get_instance();
369
+ }
370
+
371
+
372
+ if ( ! function_exists( 'wcf_ca' ) ) {
373
+ /**
374
+ * Get global class.
375
+ *
376
+ * @return object
377
+ */
378
+ function wcf_ca() {
379
+ return CARTFLOWS_CA_Loader::get_instance();
380
+ }
381
+ }
382
+
classes/class-cartflows-ca-settings.php CHANGED
@@ -1,775 +1,819 @@
1
- <?php
2
- /**
3
- * Settings.
4
- *
5
- * @package Woocommerce-Cart-Abandonment-Recovery
6
- */
7
-
8
- /**
9
- * Class Cartflows_Ca_Utils.
10
- */
11
- class Cartflows_Ca_Settings {
12
-
13
-
14
- /**
15
- * Member Variable
16
- *
17
- * @var instance
18
- */
19
- private static $instance;
20
-
21
-
22
- /**
23
- * Cartflows_Ca_Settings constructor.
24
- */
25
- public function __construct() {
26
- add_action( 'admin_init', array( $this, 'wcf_initialize_settings' ) );
27
- add_filter( 'plugin_action_links_' . CARTFLOWS_CA_BASE, array( $this, 'add_action_links' ), 999 );
28
- }
29
-
30
-
31
- /**
32
- * Adding action links for plugin list page.
33
- *
34
- * @param array $links links.
35
- * @return array
36
- */
37
- public function add_action_links( $links ) {
38
- $mylinks = array(
39
- '<a href="' . admin_url( 'admin.php?page=' . WCF_CA_PAGE_NAME ) . '">Settings</a>',
40
- );
41
-
42
- return array_merge( $mylinks, $links );
43
- }
44
- /**
45
- * Add new settings for cart abandonment settings.
46
- *
47
- * @since 1.1.5
48
- */
49
- public function wcf_initialize_settings() {
50
-
51
- // Start: Settings for cart abandonment.
52
- add_settings_section(
53
- WCF_CA_GENERAL_SETTINGS_SECTION,
54
- __( 'Cart Abandonment Settings', 'woo-cart-abandonment-recovery' ),
55
- array( $this, 'wcf_cart_abandonment_options_callback' ),
56
- WCF_CA_PAGE_NAME
57
- );
58
-
59
- add_settings_field(
60
- 'wcf_ca_status',
61
- __( 'Enable Tracking', 'woo-cart-abandonment-recovery' ),
62
- array( $this, 'wcf_ca_status_callback' ),
63
- WCF_CA_PAGE_NAME,
64
- WCF_CA_GENERAL_SETTINGS_SECTION,
65
- array( __( 'Start capturing abandoned carts. <br/><br/> <span class="description"><strong>Note:</strong> Cart will be considered abandoned if order is not completed in <strong>15 minutes</strong>.</span>', 'woo-cart-abandonment-recovery' ) )
66
- );
67
-
68
- register_setting(
69
- WCF_CA_SETTINGS_OPTION_GROUP,
70
- 'wcf_ca_status'
71
- );
72
-
73
- add_settings_field(
74
- 'wcf_ca_ignore_users',
75
- __( 'Disable Tracking For', 'woo-cart-abandonment-recovery' ),
76
- array( $this, 'wcf_ca_ignore_users_callback' ),
77
- WCF_CA_PAGE_NAME,
78
- WCF_CA_GENERAL_SETTINGS_SECTION,
79
- array( '<br><span class="description"><strong>Note:</strong>' . __( ' It will ignore selected users from abandonment process when they logged in, and hence they can not receive mail for cart abandoned by themselves.', 'woo-cart-abandonment-recovery' ) . '</span>' )
80
- );
81
-
82
- register_setting(
83
- WCF_CA_SETTINGS_OPTION_GROUP,
84
- 'wcf_ca_ignore_users'
85
- );
86
-
87
- add_settings_field(
88
- 'wcf_ca_excludes_orders',
89
- __( 'Exclude email sending For', 'woo-cart-abandonment-recovery' ),
90
- array( $this, 'wcf_ca_exclude_orders_callback' ),
91
- WCF_CA_PAGE_NAME,
92
- WCF_CA_GENERAL_SETTINGS_SECTION,
93
- array( '<br><span class="description"><strong>Note:</strong>' . __( ' It will not send future recovery emails to selected order status and will mark as recovered.', 'woo-cart-abandonment-recovery' ) . '</span>' )
94
- );
95
-
96
- register_setting(
97
- WCF_CA_SETTINGS_OPTION_GROUP,
98
- 'wcf_ca_excludes_orders'
99
- );
100
-
101
- add_settings_field(
102
- 'wcar_email_admin_on_recovery',
103
- __( 'Notify recovery to admin', 'woo-cart-abandonment-recovery' ),
104
- array( $this, 'wcar_email_admin_on_recovery' ),
105
- WCF_CA_PAGE_NAME,
106
- WCF_CA_GENERAL_SETTINGS_SECTION,
107
- array( __( 'This option will send an email to admin on new order recovery.', 'woo-cart-abandonment-recovery' ) )
108
- );
109
-
110
- register_setting(
111
- WCF_CA_SETTINGS_OPTION_GROUP,
112
- 'wcar_email_admin_on_recovery'
113
- );
114
-
115
- // End: General Settings for cart abandonment.
116
- // Start: Delete coupons settings for cart abandonment.
117
-
118
- add_settings_section(
119
- WCF_CA_COUPONS_SETTINGS_SECTION,
120
- __( 'Coupons Settings', 'woo-cart-abandonment-recovery' ),
121
- array( $this, 'wcf_cart_abandonment_options_callback' ),
122
- WCF_CA_PAGE_NAME
123
- );
124
-
125
- add_settings_field(
126
- 'wcf_ca_auto_delete_coupons',
127
- __( 'Delete Coupons Automatically', 'woo-cart-abandonment-recovery' ),
128
- array( $this, 'wcf_ca_auto_delete_coupons_callback' ),
129
- WCF_CA_PAGE_NAME,
130
- WCF_CA_COUPONS_SETTINGS_SECTION,
131
- array( __( 'Delete coupons automatically on weekly basis.<br><span class="description"><br/><strong>Note:</strong> This option will set a weekly cron to delete all <strong>expired</strong> and <strong>used</strong> coupons automatically in the background.</p>', 'woo-cart-abandonment-recovery' ) )
132
- );
133
-
134
- register_setting(
135
- WCF_CA_SETTINGS_OPTION_GROUP,
136
- 'wcf_ca_auto_delete_coupons'
137
- );
138
-
139
- add_settings_field(
140
- 'wcf_ca_delete_coupons',
141
- __( 'Delete Coupons Manually', 'woo-cart-abandonment-recovery' ),
142
- array( $this, 'wcf_ca_delete_coupons_callback' ),
143
- WCF_CA_PAGE_NAME,
144
- WCF_CA_COUPONS_SETTINGS_SECTION,
145
- array( '<br><span class="description"> ' . __( '<br><strong>Note:</strong> This will delete all <strong>expired</strong> and <strong>used</strong> coupons that were created by Woo Cart Abandonment Recovery.</p>', 'woo-cart-abandonment-recovery' ) )
146
- );
147
-
148
- register_setting(
149
- WCF_CA_SETTINGS_OPTION_GROUP,
150
- 'wcf_ca_delete_coupons'
151
- );
152
-
153
- // End: Delete coupons settings for cart abandonment.
154
- // Start: Settings for email templates.
155
- add_settings_section(
156
- WCF_CA_EMAIL_SETTINGS_SECTION,
157
- __( 'Email Settings', 'woo-cart-abandonment-recovery' ),
158
- array( $this, 'wcf_cart_abandonment_options_callback' ),
159
- WCF_CA_PAGE_NAME
160
- );
161
-
162
- add_settings_field(
163
- 'wcf_ca_from_name',
164
- __( '"From" Name', 'woo-cart-abandonment-recovery' ),
165
- array( $this, 'wcf_ca_from_name_callback' ),
166
- WCF_CA_PAGE_NAME,
167
- WCF_CA_EMAIL_SETTINGS_SECTION,
168
- array( __( 'Name will appear in email sent.', 'woo-cart-abandonment-recovery' ) )
169
- );
170
-
171
- add_settings_field(
172
- 'wcf_ca_from_email',
173
- __( '"From" Address', 'woo-cart-abandonment-recovery' ),
174
- array( $this, 'wcf_ca_from_email_callback' ),
175
- WCF_CA_PAGE_NAME,
176
- WCF_CA_EMAIL_SETTINGS_SECTION,
177
- array( __( 'Email which send from.', 'woo-cart-abandonment-recovery' ) )
178
- );
179
-
180
- add_settings_field(
181
- 'wcf_ca_reply_email',
182
- __( '"Reply To" Address', 'woo-cart-abandonment-recovery' ),
183
- array( $this, 'wcf_ca_reply_email_callback' ),
184
- WCF_CA_PAGE_NAME,
185
- WCF_CA_EMAIL_SETTINGS_SECTION,
186
- array( __( 'When a user clicks reply, which email address should that reply be sent to?', 'woo-cart-abandonment-recovery' ) )
187
- );
188
-
189
- register_setting(
190
- WCF_CA_SETTINGS_OPTION_GROUP,
191
- 'wcf_ca_from_name'
192
- );
193
-
194
- register_setting(
195
- WCF_CA_SETTINGS_OPTION_GROUP,
196
- 'wcf_ca_from_email',
197
- array( $this, 'wcf_ca_from_email_validation' )
198
- );
199
-
200
- register_setting(
201
- WCF_CA_SETTINGS_OPTION_GROUP,
202
- 'wcf_ca_reply_email',
203
- array( $this, 'wcf_ca_reply_email_validation' )
204
- );
205
- // End: Settings for email templates.
206
- // Start: Settings for coupon code.
207
- add_settings_field(
208
- 'wcf_ca_zapier_tracking_status',
209
- __( 'Enable Webhook', 'woo-cart-abandonment-recovery' ),
210
- array( $this, 'wcf_ca_zapier_tracking_status_callback' ),
211
- WCF_CA_PAGE_NAME,
212
- WCF_CA_ZAPIER_SETTINGS_SECTION,
213
- array( __( 'Allows you to trigger webhook automatically upon cart abandonment and recovery.', 'woo-cart-abandonment-recovery' ) )
214
- );
215
-
216
- add_settings_field(
217
- 'wcf_ca_zapier_cart_abandoned_webhook',
218
- __( 'Webhook URL', 'woo-cart-abandonment-recovery' ),
219
- array( $this, 'wcf_ca_zapier_cart_abandoned_webhook_callback' ),
220
- WCF_CA_PAGE_NAME,
221
- WCF_CA_ZAPIER_SETTINGS_SECTION,
222
- array( '', 'woo-cart-abandonment-recovery' )
223
- );
224
-
225
- register_setting(
226
- WCF_CA_SETTINGS_OPTION_GROUP,
227
- 'wcf_ca_zapier_tracking_status'
228
- );
229
-
230
- register_setting(
231
- WCF_CA_SETTINGS_OPTION_GROUP,
232
- 'wcf_ca_zapier_cart_abandoned_webhook'
233
- );
234
-
235
- add_settings_section(
236
- WCF_CA_ZAPIER_SETTINGS_SECTION,
237
- __( 'Coupon Code Settings', 'woo-cart-abandonment-recovery' ),
238
- array( $this, 'wcf_cart_abandonment_options_callback' ),
239
- WCF_CA_PAGE_NAME
240
- );
241
-
242
- add_settings_field(
243
- 'wcf_ca_coupon_code_status',
244
- __( 'Create Coupon Code', 'woo-cart-abandonment-recovery' ),
245
- array( $this, 'wcf_ca_coupon_code_status_callback' ),
246
- WCF_CA_PAGE_NAME,
247
- WCF_CA_ZAPIER_SETTINGS_SECTION,
248
- array( __( 'Auto-create the special coupon for the abandoned cart to send over the emails.', 'woo-cart-abandonment-recovery' ) )
249
- );
250
-
251
- add_settings_field(
252
- 'wcf_ca_discount_type',
253
- __( 'Discount Type', 'woo-cart-abandonment-recovery' ),
254
- array( $this, 'wcf_ca_discount_type_callback' ),
255
- WCF_CA_PAGE_NAME,
256
- WCF_CA_ZAPIER_SETTINGS_SECTION,
257
- array( '', 'woo-cart-abandonment-recovery' )
258
- );
259
-
260
- add_settings_field(
261
- 'wcf_ca_coupon_amount',
262
- __( 'Coupon Amount', 'woo-cart-abandonment-recovery' ),
263
- array( $this, 'wcf_ca_coupon_amount_callback' ),
264
- WCF_CA_PAGE_NAME,
265
- WCF_CA_ZAPIER_SETTINGS_SECTION,
266
- array( '', 'woo-cart-abandonment-recovery' )
267
- );
268
-
269
- add_settings_field(
270
- 'wcf_ca_coupon_expiry',
271
- __( 'Coupon Expires After', 'woo-cart-abandonment-recovery' ),
272
- array( $this, 'wcf_ca_coupon_expiry_callback' ),
273
- WCF_CA_PAGE_NAME,
274
- WCF_CA_ZAPIER_SETTINGS_SECTION,
275
- array( __( '<br/><br/> <span class="description"><strong>Note: </strong>. Enter zero (0) to restrict coupon from expiring.</span>', 'woo-cart-abandonment-recovery' ) )
276
- );
277
-
278
- register_setting(
279
- WCF_CA_SETTINGS_OPTION_GROUP,
280
- 'wcf_ca_coupon_expiry'
281
- );
282
- register_setting(
283
- WCF_CA_SETTINGS_OPTION_GROUP,
284
- 'wcf_ca_coupon_expiry_unit'
285
- );
286
-
287
- register_setting(
288
- WCF_CA_SETTINGS_OPTION_GROUP,
289
- 'wcf_ca_coupon_code_status'
290
- );
291
-
292
- register_setting(
293
- WCF_CA_SETTINGS_OPTION_GROUP,
294
- 'wcf_ca_discount_type'
295
- );
296
-
297
- register_setting(
298
- WCF_CA_SETTINGS_OPTION_GROUP,
299
- 'wcf_ca_coupon_amount',
300
- array( $this, 'wcf_ca_coupon_amount_validation' )
301
- );
302
- // End: Settings for coupon code.
303
- // Start: Settings for Zapier.
304
- add_settings_section(
305
- WCF_CA_ZAPIER_SETTINGS_SECTION,
306
- __( 'Webhook Settings', 'woo-cart-abandonment-recovery' ),
307
- array( $this, 'wcf_cart_abandonment_options_callback' ),
308
- WCF_CA_PAGE_NAME
309
- );
310
-
311
- // End: Settings for webhook.
312
- // Start: GDPR Settings.
313
- add_settings_section(
314
- WCF_CA_GDPR_SETTINGS_SECTION,
315
- __( 'GDPR Settings', 'woo-cart-abandonment-recovery' ),
316
- array( $this, 'wcf_cart_abandonment_options_callback' ),
317
- WCF_CA_PAGE_NAME
318
- );
319
-
320
- add_settings_field(
321
- 'wcf_ca_gdpr_status',
322
- __( 'Enable GDPR Integration', 'woo-cart-abandonment-recovery' ),
323
- array( $this, 'wcf_ca_gdpr_status_callback' ),
324
- WCF_CA_PAGE_NAME,
325
- WCF_CA_GDPR_SETTINGS_SECTION,
326
- array( __( 'Ask confirmation from the user before tracking data. <br/><br/> <span class="description"><strong>Note:</strong> By checking this, it will show up confirmation text below the email id on checkout page.</span>', 'woo-cart-abandonment-recovery' ) )
327
- );
328
-
329
- add_settings_field(
330
- 'wcf_ca_gdpr_message',
331
- __( 'GDPR Message', 'woo-cart-abandonment-recovery' ),
332
- array( $this, 'wcf_ca_gdpr_message_callback' ),
333
- WCF_CA_PAGE_NAME,
334
- WCF_CA_GDPR_SETTINGS_SECTION,
335
- array( '', 'woo-cart-abandonment-recovery' )
336
- );
337
-
338
- register_setting(
339
- WCF_CA_SETTINGS_OPTION_GROUP,
340
- 'wcf_ca_gdpr_status'
341
- );
342
- register_setting(
343
- WCF_CA_SETTINGS_OPTION_GROUP,
344
- 'wcf_ca_gdpr_message'
345
- );
346
-
347
- }
348
-
349
- /**
350
- * Callback for cart abandonment status.
351
- *
352
- * @param array $args args.
353
- * @since 1.1.5
354
- */
355
- public function wcf_ca_coupon_code_status_callback( $args ) {
356
- $wcf_ca_coupon_code_status = get_option( 'wcf_ca_coupon_code_status' );
357
- $html = '';
358
- printf(
359
- '<input type="checkbox" id="wcf_ca_coupon_code_status" name="wcf_ca_coupon_code_status" value="on"
360
- ' . checked( 'on', $wcf_ca_coupon_code_status, false ) . ' />'
361
- );
362
- $html .= '<label for="wcf_ca_coupon_code_status"> ' . $args[0] . '</label>';
363
- echo wp_kses_post( $html );
364
- }
365
-
366
-
367
- /**
368
- * Callback for cart abandonment cut off time.
369
- *
370
- * @param array $args args.
371
- * @since 1.1.5
372
- */
373
- public function wcf_ca_zapier_cart_abandoned_webhook_callback( $args ) {
374
- $wcf_ca_zapier_cart_abandoned_webhook = get_option( 'wcf_ca_zapier_cart_abandoned_webhook' );
375
- echo '<input type="text" class="wcf-ca-trigger-input" id="wcf_ca_zapier_cart_abandoned_webhook" name="wcf_ca_zapier_cart_abandoned_webhook" value="' . esc_attr( sanitize_text_field( $wcf_ca_zapier_cart_abandoned_webhook ) ) . '" />';
376
- echo '<button id="wcf_ca_trigger_web_hook_abandoned_btn" type="button" class="button"> Trigger Sample </button>';
377
- echo '<span style="margin-left: 10px;" id="wcf_ca_abandoned_btn_message"></span>';
378
- $html = '<label for="wcf_ca_zapier_cart_abandoned_webhook"> ' . $args[0] . '</label>';
379
- echo wp_kses_post( $html );
380
- }
381
-
382
-
383
- /**
384
- * Callback for cart abandonment status.
385
- *
386
- * @param array $args args.
387
- * @since 1.1.5
388
- */
389
- public function wcf_ca_zapier_tracking_status_callback( $args ) {
390
- $wcf_ca_zapier_tracking_status = get_option( 'wcf_ca_zapier_tracking_status' );
391
-
392
- $html = '';
393
- printf(
394
- '<input type="checkbox" id="wcf_ca_zapier_tracking_status" name="wcf_ca_zapier_tracking_status" value="on"
395
- ' . checked( 'on', $wcf_ca_zapier_tracking_status, false ) . ' />'
396
- );
397
- $html .= '<label for="wcf_ca_zapier_tracking_status"> ' . $args[0] . '</label>';
398
- echo wp_kses_post( $html );
399
- }
400
-
401
- /**
402
- * Callback for send email to admin.
403
- *
404
- * @param array $args args.
405
- * @since 1.1.5
406
- */
407
- public function wcar_email_admin_on_recovery( $args ) {
408
- $email_admin_on_recovery = get_option( 'wcar_email_admin_on_recovery' );
409
-
410
- $html = '';
411
- printf(
412
- '<input type="checkbox" id="wcar_email_admin_on_recovery" name="wcar_email_admin_on_recovery" value="on"
413
- ' . checked( 'on', $email_admin_on_recovery, false ) . ' />'
414
- );
415
- $html .= '<label for="wcar_email_admin_on_recovery"> ' . $args[0] . '</label>';
416
- echo wp_kses_post( $html );
417
- }
418
-
419
- /**
420
- * Callback for cart abandonment cut off time.
421
- *
422
- * @param array $args args.
423
- * @since 1.1.5
424
- */
425
- public function wcf_ca_coupon_amount_callback( $args ) {
426
- $wcf_ca_coupon_amount = get_option( 'wcf_ca_coupon_amount' );
427
- printf(
428
- '<input type="number" class="wcf-ca-trigger-input wcf-ca-email-inputs" id="wcf_ca_coupon_amount" name="wcf_ca_coupon_amount" value="%s" />',
429
- isset( $wcf_ca_coupon_amount ) ? esc_attr( $wcf_ca_coupon_amount ) : ''
430
- );
431
- $html = '<label for="wcf_ca_coupon_amount"> ' . $args[0] . '</label>';
432
- echo wp_kses_post( $html );
433
- }
434
-
435
- /**
436
- * Callback for cart abandonment cut off time.
437
- *
438
- * @param array $args args.
439
- * @since 1.1.5
440
- */
441
- public function wcf_ca_coupon_expiry_callback( $args ) {
442
- $wcf_ca_coupon_expiry = intval( get_option( 'wcf_ca_coupon_expiry' ) );
443
- printf(
444
- '<input type="number" class="wcf-ca-trigger-input wcf-ca-coupon-inputs" id="wcf_ca_coupon_expiry" name="wcf_ca_coupon_expiry" value="%s" autocomplete="off" />',
445
- isset( $wcf_ca_coupon_expiry ) ? esc_attr( $wcf_ca_coupon_expiry ) : ''
446
- );
447
-
448
- $coupon_expiry_unit = get_option( 'wcf_ca_coupon_expiry_unit' );
449
- $items = array(
450
- 'hours' => __( 'Hour(s)', 'woo-cart-abandonment-recovery' ),
451
- 'days' => __( 'Day(s)', 'woo-cart-abandonment-recovery' ),
452
- );
453
- echo "<select id='wcf_ca_coupon_expiry_unit' name='wcf_ca_coupon_expiry_unit'>";
454
- foreach ( $items as $key => $item ) {
455
- $selected = ( $coupon_expiry_unit === $key ) ? 'selected="selected"' : '';
456
- echo "<option value='$key' $selected>$item</option>"; //phpcs:ignore
457
- }
458
- echo '</select>';
459
-
460
- $html = '<label for="wcf_ca_coupon_expiry_unit"> ' . $args[0] . '</label>';
461
- echo wp_kses_post( $html );
462
- }
463
-
464
-
465
-
466
- /**
467
- * Callback for cart abandonment cut off time.
468
- *
469
- * @param array $args args.
470
- * @since 1.1.5
471
- */
472
- public function wcf_ca_gdpr_message_callback( $args ) {
473
- $wcf_ca_gdpr_message = get_option( 'wcf_ca_gdpr_message' );
474
-
475
- printf(
476
- '<textarea rows="2" cols="60" id="wcf_ca_gdpr_message" name="wcf_ca_gdpr_message" spellcheck="false">%s</textarea>',
477
- isset( $wcf_ca_gdpr_message ) ? esc_attr( $wcf_ca_gdpr_message ) : ''
478
- );
479
- $html = '<label for="wcf_ca_gdpr_message"> ' . $args[0] . '</label>';
480
- echo wp_kses_post( $html );
481
- }
482
-
483
- /**
484
- * Callback for cart abandonment cut off time.
485
- *
486
- * @param array $args args.
487
- * @since 1.1.5
488
- */
489
- public function wcf_ca_discount_type_callback( $args ) {
490
-
491
- $discount_type = get_option( 'wcf_ca_discount_type' );
492
- $items = array(
493
- 'percent' => 'Percentage discount',
494
- 'fixed_cart' => 'Fixed cart discount',
495
- );
496
- echo "<select id='wcf_ca_discount_type' name='wcf_ca_discount_type'>";
497
- foreach ( $items as $key => $item ) {
498
- $selected = ( $discount_type === $key ) ? 'selected="selected"' : '';
499
- echo "<option value='$key' $selected>$item</option>"; //phpcs:ignore
500
- }
501
- echo '</select>';
502
- }
503
-
504
- /**
505
- * Validation for cart abandonment `cut-off` settings.
506
- *
507
- * @param array $input input.
508
- * @since 1.1.5
509
- */
510
- public function wcf_ca_coupon_amount_validation( $input ) {
511
-
512
- $output = '';
513
- if ( ( is_numeric( $input ) && $input >= 1 ) ) {
514
- $output = stripslashes( $input );
515
- } else {
516
- add_settings_error(
517
- 'wcf_ca_coupon_amount',
518
- 'error found',
519
- __( 'Coupon code should be numeric and has to be greater than or equals to 1.', 'woo-cart-abandonment-recovery' )
520
- );
521
- }
522
- return $output;
523
- }
524
-
525
- /**
526
- * Callback for cart abandonment options.
527
- *
528
- * @since 1.1.5
529
- */
530
- public function wcf_cart_abandonment_options_callback() {
531
- echo '<hr/>';
532
- }
533
-
534
-
535
- /**
536
- * Callback for cart abandonment status.
537
- *
538
- * @param array $args args.
539
- * @since 1.1.5
540
- */
541
- public function wcf_ca_status_callback( $args ) {
542
- $wcf_ca_status = get_option( 'wcf_ca_status' );
543
- $html = '';
544
- printf(
545
- '<input type="checkbox" id="wcf_ca_status" name="wcf_ca_status" value="on"
546
- ' . checked( 'on', $wcf_ca_status, false ) . ' />'
547
- );
548
- $html .= '<label for="wcf_ca_status"> ' . $args[0] . '</label>';
549
- echo wp_kses_post( $html );
550
- }
551
-
552
- /**
553
- * Callback for ignore users from tracking cart.
554
- *
555
- * @param array $args args.
556
- * @since 1.1.5
557
- */
558
- public function wcf_ca_ignore_users_callback( $args ) {
559
-
560
- $wcf_ca_ignore_users = get_option( 'wcf_ca_ignore_users' );
561
- $html = '';
562
- $roles_obj = new WP_Roles();
563
- $roles_names_array = $roles_obj->get_names();
564
- $roles_names_array = array_diff( $roles_names_array, ( is_array( 'Customer' ) ? $value : array( 'Customer' ) ) );
565
- ?>
566
- <p class="wcf_ca_ignore_users" name="wcf_ca_ignore_users" multiple="multiple">
567
- <?php
568
- foreach ( $roles_names_array as $role_name ) {
569
- ?>
570
- <input type="checkbox" name="wcf_ca_ignore_users[]"
571
- <?php
572
- if ( ! empty( $wcf_ca_ignore_users ) ) {
573
- foreach ( $wcf_ca_ignore_users as $user ) {
574
- checked( $user, $role_name );
575
- }
576
- }
577
- ?>
578
- value="<?php echo esc_attr( $role_name ); ?>">
579
- <?php
580
- echo esc_attr( $role_name );
581
- echo '<br> ';
582
- }
583
- ?>
584
- </p>
585
- <?php
586
- $html .= '<span for="wcf_ca_ignore_users"> ' . $args[0] . '</span>';
587
- echo wp_kses_post( $html );
588
- }
589
-
590
- /**
591
- * Callback for ignore users from tracking cart.
592
- *
593
- * @param array $args args.
594
- * @since 1.1.5
595
- */
596
- public function wcf_ca_exclude_orders_callback( $args ) {
597
- $wcf_ca_excludes_orders = get_option( 'wcf_ca_excludes_orders' );
598
-
599
- $html = '';
600
- $order_status = wc_get_order_statuses();
601
- $new_order_status = str_replace( 'wc-', '', array_keys( $order_status ) );
602
- $order_status = array_combine( $new_order_status, $order_status );
603
- $order_status = \array_diff( $order_status, array( 'Refunded', 'Draft', 'Cancelled' ) );
604
- ?>
605
- <p class="wcf-ca-excludes-orders" name="wcf-ca-excludes-orders">
606
- <?php
607
- foreach ( $order_status as $key => $value ) {
608
- ?>
609
- <input type="checkbox" name="wcf_ca_excludes_orders[]"
610
- <?php
611
- if ( ! empty( $wcf_ca_excludes_orders ) && in_array( $key, $wcf_ca_excludes_orders, true ) ) {
612
- checked( true, true );
613
- }
614
- ?>
615
- value="<?php echo esc_attr( $key ); ?>">
616
- <?php
617
- echo esc_attr( $value );
618
- echo '<br> ';
619
- }
620
- ?>
621
- </p>
622
-
623
- <?php
624
- $html .= '<span for="wcf_ca_excludes_orders"> ' . $args[0] . '</span>';
625
- echo wp_kses_post( $html );
626
- }
627
- /**
628
- * Delete coupons.
629
- *
630
- * @param array $args args.
631
- */
632
- public function wcf_ca_auto_delete_coupons_callback( $args ) {
633
- $wcf_ca_auto_delete_coupons = get_option( 'wcf_ca_auto_delete_coupons' );
634
- $html = '';
635
- printf(
636
- '<input type="checkbox" id="wcf_ca_auto_delete_coupons" name="wcf_ca_auto_delete_coupons" value="on"
637
- ' . checked( 'on', $wcf_ca_auto_delete_coupons, false ) . ' />'
638
- );
639
- $html .= '<span for="wcf_ca_auto_delete_coupons"> ' . $args[0] . '</span>';
640
- echo wp_kses_post( $html );
641
- }
642
-
643
- /**
644
- * Delete coupons.
645
- *
646
- * @param array $args args.
647
- */
648
- public function wcf_ca_delete_coupons_callback( $args ) {
649
- ?>
650
-
651
- <input type="button" class="button-secondary" id="wcf_ca_delete_coupons" value="<?php esc_html_e( 'Delete', 'woo-cart-abandonment-recovery' ); ?>" >
652
- <span class="spinner wcf-ca-spinner"></span>
653
- <span class="wcf-ca-response-msg"></span>
654
- <?php
655
- $html = '';
656
- $html .= '<span for="wcf_ca_delete_coupons"> ' . $args[0] . '</span>';
657
- echo wp_kses_post( $html );
658
- }
659
-
660
- /**
661
- * Callback for cart abandonment status.
662
- *
663
- * @param array $args args.
664
- * @since 1.1.5
665
- */
666
- public function wcf_ca_gdpr_status_callback( $args ) {
667
- $wcf_ca_gdpr_status = get_option( 'wcf_ca_gdpr_status' );
668
- $html = '';
669
- printf(
670
- '<input type="checkbox" id="wcf_ca_gdpr_status" name="wcf_ca_gdpr_status" value="on"
671
- ' . checked( 'on', $wcf_ca_gdpr_status, false ) . ' />'
672
- );
673
- $html .= '<label for="wcf_ca_gdpr_status"> ' . $args[0] . '</label>';
674
- echo wp_kses_post( $html );
675
- }
676
-
677
- /**
678
- * Callback for email from name.
679
- *
680
- * @param array $args Arguments.
681
- */
682
- public static function wcf_ca_from_name_callback( $args ) {
683
- $wcf_ca_from_name = get_option( 'wcf_ca_from_name' );
684
- printf(
685
- '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_from_name" name="wcf_ca_from_name" value="%s" />',
686
- isset( $wcf_ca_from_name ) ? esc_attr( $wcf_ca_from_name ) : ''
687
- );
688
- $html = '<label for="wcf_ca_from_name"> ' . $args[0] . '</label>';
689
- echo wp_kses_post( $html );
690
- }
691
-
692
- /**
693
- * Callback for email from.
694
- *
695
- * @param array $args Arguments.
696
- */
697
- public static function wcf_ca_from_email_callback( $args ) {
698
- $wcf_ca_from_email = get_option( 'wcf_ca_from_email' );
699
- printf(
700
- '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_from_email" name="wcf_ca_from_email" value="%s" />',
701
- isset( $wcf_ca_from_email ) ? esc_attr( $wcf_ca_from_email ) : ''
702
- );
703
- $html = '<label for="wcf_ca_from_email"> ' . $args[0] . '</label>';
704
- echo wp_kses_post( $html );
705
- }
706
-
707
- /**
708
- * Callback for email reply.
709
- *
710
- * @param array $args Arguments.
711
- * @since 3.5
712
- */
713
- public static function wcf_ca_reply_email_callback( $args ) {
714
- $wcf_ca_reply_email = get_option( 'wcf_ca_reply_email' );
715
- printf(
716
- '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_reply_email" name="wcf_ca_reply_email" value="%s" />',
717
- isset( $wcf_ca_reply_email ) ? esc_attr( $wcf_ca_reply_email ) : ''
718
- );
719
-
720
- $html = '<label for="wcf_ca_reply_email"> ' . $args[0] . '</label>';
721
- echo wp_kses_post( $html );
722
- }
723
-
724
-
725
- /**
726
- * Validation for email.
727
- *
728
- * @param array $input input.
729
- * @since 1.1.5
730
- */
731
- public function wcf_ca_from_email_validation( $input ) {
732
-
733
- if ( $input && ! is_email( $input ) ) {
734
- add_settings_error(
735
- 'wcf_ca_from_email',
736
- 'error found',
737
- __( 'Invalid email "From" address field', 'woo-cart-abandonment-recovery' )
738
- );
739
- }
740
- return sanitize_email( $input );
741
- }
742
-
743
- /**
744
- * Validation for reply email.
745
- *
746
- * @param array $input input.
747
- * @since 1.1.5
748
- */
749
- public function wcf_ca_reply_email_validation( $input ) {
750
-
751
- if ( $input && ! is_email( $input ) ) {
752
- add_settings_error(
753
- 'wcf_ca_reply_email',
754
- 'error found',
755
- __( 'Invalid email "Reply" address field', 'woo-cart-abandonment-recovery' )
756
- );
757
- }
758
- return sanitize_email( $input );
759
- }
760
-
761
- /**
762
- * Initiator
763
- */
764
- public static function get_instance() {
765
- if ( ! isset( self::$instance ) ) {
766
- self::$instance = new self();
767
- }
768
- return self::$instance;
769
- }
770
-
771
-
772
-
773
-
774
- }
775
- Cartflows_Ca_Settings::get_instance();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Settings.
4
+ *
5
+ * @package Woocommerce-Cart-Abandonment-Recovery
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ /**
13
+ * Class Cartflows_Ca_Utils.
14
+ */
15
+ class Cartflows_Ca_Settings {
16
+
17
+
18
+ /**
19
+ * Member Variable
20
+ *
21
+ * @var instance
22
+ */
23
+ private static $instance;
24
+
25
+
26
+ /**
27
+ * Cartflows_Ca_Settings constructor.
28
+ */
29
+ public function __construct() {
30
+ add_action( 'admin_init', array( $this, 'wcf_initialize_settings' ) );
31
+ add_filter( 'plugin_action_links_' . CARTFLOWS_CA_BASE, array( $this, 'add_action_links' ), 999 );
32
+ }
33
+
34
+
35
+ /**
36
+ * Adding action links for plugin list page.
37
+ *
38
+ * @param array $links links.
39
+ * @return array
40
+ */
41
+ public function add_action_links( $links ) {
42
+ $mylinks = array(
43
+ '<a href="' . admin_url( 'admin.php?page=' . WCF_CA_PAGE_NAME ) . '">Settings</a>',
44
+ );
45
+
46
+ return array_merge( $mylinks, $links );
47
+ }
48
+ /**
49
+ * Add new settings for cart abandonment settings.
50
+ *
51
+ * @since 1.1.5
52
+ */
53
+ public function wcf_initialize_settings() {
54
+
55
+ // Start: Settings for cart abandonment.
56
+ add_settings_section(
57
+ WCF_CA_GENERAL_SETTINGS_SECTION,
58
+ __( 'Cart Abandonment Settings', 'woo-cart-abandonment-recovery' ),
59
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
60
+ WCF_CA_PAGE_NAME
61
+ );
62
+
63
+ add_settings_field(
64
+ 'wcf_ca_status',
65
+ __( 'Enable Tracking', 'woo-cart-abandonment-recovery' ),
66
+ array( $this, 'wcf_ca_status_callback' ),
67
+ WCF_CA_PAGE_NAME,
68
+ WCF_CA_GENERAL_SETTINGS_SECTION,
69
+ array( __( 'Start capturing abandoned carts. <br/><br/> <span class="description"><strong>Note:</strong> Cart will be considered abandoned if order is not completed in <strong>15 minutes</strong>.</span>', 'woo-cart-abandonment-recovery' ) )
70
+ );
71
+
72
+ register_setting(
73
+ WCF_CA_SETTINGS_OPTION_GROUP,
74
+ 'wcf_ca_status'
75
+ );
76
+
77
+ add_settings_field(
78
+ 'wcf_ca_ignore_users',
79
+ __( 'Disable Tracking For', 'woo-cart-abandonment-recovery' ),
80
+ array( $this, 'wcf_ca_ignore_users_callback' ),
81
+ WCF_CA_PAGE_NAME,
82
+ WCF_CA_GENERAL_SETTINGS_SECTION,
83
+ array( '<br><span class="description"><strong>Note:</strong>' . __( ' It will ignore selected users from abandonment process when they logged in, and hence they can not receive mail for cart abandoned by themselves.', 'woo-cart-abandonment-recovery' ) . '</span>' )
84
+ );
85
+
86
+ register_setting(
87
+ WCF_CA_SETTINGS_OPTION_GROUP,
88
+ 'wcf_ca_ignore_users'
89
+ );
90
+
91
+ add_settings_field(
92
+ 'wcf_ca_excludes_orders',
93
+ __( 'Exclude email sending For', 'woo-cart-abandonment-recovery' ),
94
+ array( $this, 'wcf_ca_exclude_orders_callback' ),
95
+ WCF_CA_PAGE_NAME,
96
+ WCF_CA_GENERAL_SETTINGS_SECTION,
97
+ array( '<br><span class="description"><strong>Note:</strong>' . __( ' It will not send future recovery emails to selected order status and will mark as recovered.', 'woo-cart-abandonment-recovery' ) . '</span>' )
98
+ );
99
+
100
+ register_setting(
101
+ WCF_CA_SETTINGS_OPTION_GROUP,
102
+ 'wcf_ca_excludes_orders'
103
+ );
104
+
105
+ add_settings_field(
106
+ 'wcar_email_admin_on_recovery',
107
+ __( 'Notify recovery to admin', 'woo-cart-abandonment-recovery' ),
108
+ array( $this, 'wcar_email_admin_on_recovery' ),
109
+ WCF_CA_PAGE_NAME,
110
+ WCF_CA_GENERAL_SETTINGS_SECTION,
111
+ array( __( 'This option will send an email to admin on new order recovery.', 'woo-cart-abandonment-recovery' ) )
112
+ );
113
+
114
+ register_setting(
115
+ WCF_CA_SETTINGS_OPTION_GROUP,
116
+ 'wcar_email_admin_on_recovery'
117
+ );
118
+
119
+ // End: General Settings for cart abandonment.
120
+ // Start: Delete coupons settings for cart abandonment.
121
+
122
+ add_settings_section(
123
+ WCF_CA_COUPONS_SETTINGS_SECTION,
124
+ __( 'Coupons Settings', 'woo-cart-abandonment-recovery' ),
125
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
126
+ WCF_CA_PAGE_NAME
127
+ );
128
+
129
+ add_settings_field(
130
+ 'wcf_ca_auto_delete_coupons',
131
+ __( 'Delete Coupons Automatically', 'woo-cart-abandonment-recovery' ),
132
+ array( $this, 'wcf_ca_auto_delete_coupons_callback' ),
133
+ WCF_CA_PAGE_NAME,
134
+ WCF_CA_COUPONS_SETTINGS_SECTION,
135
+ array( __( 'Delete coupons automatically on weekly basis.<br><span class="description"><br/><strong>Note:</strong> This option will set a weekly cron to delete all <strong>expired</strong> and <strong>used</strong> coupons automatically in the background.</p>', 'woo-cart-abandonment-recovery' ) )
136
+ );
137
+
138
+ register_setting(
139
+ WCF_CA_SETTINGS_OPTION_GROUP,
140
+ 'wcf_ca_auto_delete_coupons'
141
+ );
142
+
143
+ add_settings_field(
144
+ 'wcf_ca_delete_coupons',
145
+ __( 'Delete Coupons Manually', 'woo-cart-abandonment-recovery' ),
146
+ array( $this, 'wcf_ca_delete_coupons_callback' ),
147
+ WCF_CA_PAGE_NAME,
148
+ WCF_CA_COUPONS_SETTINGS_SECTION,
149
+ array( '<br><span class="description"> ' . __( '<br><strong>Note:</strong> This will delete all <strong>expired</strong> and <strong>used</strong> coupons that were created by Woo Cart Abandonment Recovery.</p>', 'woo-cart-abandonment-recovery' ) )
150
+ );
151
+
152
+ register_setting(
153
+ WCF_CA_SETTINGS_OPTION_GROUP,
154
+ 'wcf_ca_delete_coupons'
155
+ );
156
+
157
+ // End: Delete coupons settings for cart abandonment.
158
+ // Start: Settings for email templates.
159
+ add_settings_section(
160
+ WCF_CA_EMAIL_SETTINGS_SECTION,
161
+ __( 'Email Settings', 'woo-cart-abandonment-recovery' ),
162
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
163
+ WCF_CA_PAGE_NAME
164
+ );
165
+
166
+ add_settings_field(
167
+ 'wcf_ca_from_name',
168
+ __( '"From" Name', 'woo-cart-abandonment-recovery' ),
169
+ array( $this, 'wcf_ca_from_name_callback' ),
170
+ WCF_CA_PAGE_NAME,
171
+ WCF_CA_EMAIL_SETTINGS_SECTION,
172
+ array( __( 'Name will appear in email sent.', 'woo-cart-abandonment-recovery' ) )
173
+ );
174
+
175
+ add_settings_field(
176
+ 'wcf_ca_from_email',
177
+ __( '"From" Address', 'woo-cart-abandonment-recovery' ),
178
+ array( $this, 'wcf_ca_from_email_callback' ),
179
+ WCF_CA_PAGE_NAME,
180
+ WCF_CA_EMAIL_SETTINGS_SECTION,
181
+ array( __( 'Email which send from.', 'woo-cart-abandonment-recovery' ) )
182
+ );
183
+
184
+ add_settings_field(
185
+ 'wcf_ca_reply_email',
186
+ __( '"Reply To" Address', 'woo-cart-abandonment-recovery' ),
187
+ array( $this, 'wcf_ca_reply_email_callback' ),
188
+ WCF_CA_PAGE_NAME,
189
+ WCF_CA_EMAIL_SETTINGS_SECTION,
190
+ array( __( 'When a user clicks reply, which email address should that reply be sent to?', 'woo-cart-abandonment-recovery' ) )
191
+ );
192
+
193
+ register_setting(
194
+ WCF_CA_SETTINGS_OPTION_GROUP,
195
+ 'wcf_ca_from_name'
196
+ );
197
+
198
+ register_setting(
199
+ WCF_CA_SETTINGS_OPTION_GROUP,
200
+ 'wcf_ca_from_email',
201
+ array( $this, 'wcf_ca_from_email_validation' )
202
+ );
203
+
204
+ register_setting(
205
+ WCF_CA_SETTINGS_OPTION_GROUP,
206
+ 'wcf_ca_reply_email',
207
+ array( $this, 'wcf_ca_reply_email_validation' )
208
+ );
209
+ // End: Settings for email templates.
210
+ // Start: Settings for coupon code.
211
+ add_settings_field(
212
+ 'wcf_ca_zapier_tracking_status',
213
+ __( 'Enable Webhook', 'woo-cart-abandonment-recovery' ),
214
+ array( $this, 'wcf_ca_zapier_tracking_status_callback' ),
215
+ WCF_CA_PAGE_NAME,
216
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
217
+ array( __( 'Allows you to trigger webhook automatically upon cart abandonment and recovery.', 'woo-cart-abandonment-recovery' ) )
218
+ );
219
+
220
+ add_settings_field(
221
+ 'wcf_ca_zapier_cart_abandoned_webhook',
222
+ __( 'Webhook URL', 'woo-cart-abandonment-recovery' ),
223
+ array( $this, 'wcf_ca_zapier_cart_abandoned_webhook_callback' ),
224
+ WCF_CA_PAGE_NAME,
225
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
226
+ array( '', 'woo-cart-abandonment-recovery' )
227
+ );
228
+
229
+ register_setting(
230
+ WCF_CA_SETTINGS_OPTION_GROUP,
231
+ 'wcf_ca_zapier_tracking_status'
232
+ );
233
+
234
+ register_setting(
235
+ WCF_CA_SETTINGS_OPTION_GROUP,
236
+ 'wcf_ca_zapier_cart_abandoned_webhook'
237
+ );
238
+
239
+ add_settings_section(
240
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
241
+ __( 'Coupon Code Settings', 'woo-cart-abandonment-recovery' ),
242
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
243
+ WCF_CA_PAGE_NAME
244
+ );
245
+
246
+ add_settings_field(
247
+ 'wcf_ca_coupon_code_status',
248
+ __( 'Create Coupon Code', 'woo-cart-abandonment-recovery' ),
249
+ array( $this, 'wcf_ca_coupon_code_status_callback' ),
250
+ WCF_CA_PAGE_NAME,
251
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
252
+ array( __( 'Auto-create the special coupon for the abandoned cart to send over the emails.', 'woo-cart-abandonment-recovery' ) )
253
+ );
254
+
255
+ add_settings_field(
256
+ 'wcf_ca_discount_type',
257
+ __( 'Discount Type', 'woo-cart-abandonment-recovery' ),
258
+ array( $this, 'wcf_ca_discount_type_callback' ),
259
+ WCF_CA_PAGE_NAME,
260
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
261
+ array( '', 'woo-cart-abandonment-recovery' )
262
+ );
263
+
264
+ add_settings_field(
265
+ 'wcf_ca_coupon_amount',
266
+ __( 'Coupon Amount', 'woo-cart-abandonment-recovery' ),
267
+ array( $this, 'wcf_ca_coupon_amount_callback' ),
268
+ WCF_CA_PAGE_NAME,
269
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
270
+ array( '', 'woo-cart-abandonment-recovery' )
271
+ );
272
+
273
+ add_settings_field(
274
+ 'wcf_ca_coupon_expiry',
275
+ __( 'Coupon Expires After', 'woo-cart-abandonment-recovery' ),
276
+ array( $this, 'wcf_ca_coupon_expiry_callback' ),
277
+ WCF_CA_PAGE_NAME,
278
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
279
+ array( __( '<br/><br/> <span class="description"><strong>Note: </strong>. Enter zero (0) to restrict coupon from expiring.</span>', 'woo-cart-abandonment-recovery' ) )
280
+ );
281
+
282
+ register_setting(
283
+ WCF_CA_SETTINGS_OPTION_GROUP,
284
+ 'wcf_ca_coupon_expiry'
285
+ );
286
+ register_setting(
287
+ WCF_CA_SETTINGS_OPTION_GROUP,
288
+ 'wcf_ca_coupon_expiry_unit'
289
+ );
290
+
291
+ register_setting(
292
+ WCF_CA_SETTINGS_OPTION_GROUP,
293
+ 'wcf_ca_coupon_code_status'
294
+ );
295
+
296
+ register_setting(
297
+ WCF_CA_SETTINGS_OPTION_GROUP,
298
+ 'wcf_ca_discount_type'
299
+ );
300
+
301
+ register_setting(
302
+ WCF_CA_SETTINGS_OPTION_GROUP,
303
+ 'wcf_ca_coupon_amount',
304
+ array( $this, 'wcf_ca_coupon_amount_validation' )
305
+ );
306
+ // End: Settings for coupon code.
307
+ // Start: Settings for Zapier.
308
+ add_settings_section(
309
+ WCF_CA_ZAPIER_SETTINGS_SECTION,
310
+ __( 'Webhook Settings', 'woo-cart-abandonment-recovery' ),
311
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
312
+ WCF_CA_PAGE_NAME
313
+ );
314
+
315
+ // End: Settings for webhook.
316
+ // Start: GDPR Settings.
317
+ add_settings_section(
318
+ WCF_CA_GDPR_SETTINGS_SECTION,
319
+ __( 'GDPR Settings', 'woo-cart-abandonment-recovery' ),
320
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
321
+ WCF_CA_PAGE_NAME
322
+ );
323
+
324
+ add_settings_field(
325
+ 'wcf_ca_gdpr_status',
326
+ __( 'Enable GDPR Integration', 'woo-cart-abandonment-recovery' ),
327
+ array( $this, 'wcf_ca_gdpr_status_callback' ),
328
+ WCF_CA_PAGE_NAME,
329
+ WCF_CA_GDPR_SETTINGS_SECTION,
330
+ array( __( 'Ask confirmation from the user before tracking data. <br/><br/> <span class="description"><strong>Note:</strong> By checking this, it will show up confirmation text below the email id on checkout page.</span>', 'woo-cart-abandonment-recovery' ) )
331
+ );
332
+
333
+ add_settings_field(
334
+ 'wcf_ca_gdpr_message',
335
+ __( 'GDPR Message', 'woo-cart-abandonment-recovery' ),
336
+ array( $this, 'wcf_ca_gdpr_message_callback' ),
337
+ WCF_CA_PAGE_NAME,
338
+ WCF_CA_GDPR_SETTINGS_SECTION,
339
+ array( '', 'woo-cart-abandonment-recovery' )
340
+ );
341
+
342
+ register_setting(
343
+ WCF_CA_SETTINGS_OPTION_GROUP,
344
+ 'wcf_ca_gdpr_status'
345
+ );
346
+ register_setting(
347
+ WCF_CA_SETTINGS_OPTION_GROUP,
348
+ 'wcf_ca_gdpr_message'
349
+ );
350
+
351
+ // End: GDPR Settings.
352
+ // Start: Plugin settings.
353
+
354
+ add_settings_section(
355
+ WCF_CA_PLUGIN_SETTINGS_SECTION,
356
+ __( 'Plugin Settings', 'woo-cart-abandonment-recovery' ),
357
+ array( $this, 'wcf_cart_abandonment_options_callback' ),
358
+ WCF_CA_PAGE_NAME
359
+ );
360
+ register_setting(
361
+ WCF_CA_SETTINGS_OPTION_GROUP,
362
+ 'wcf_ca_delete_plugin_data'
363
+ );
364
+ add_settings_field(
365
+ 'wcf_ca_delete_plugin_data',
366
+ __( 'Delete Plugin Data', 'woo-cart-abandonment-recovery' ),
367
+ array( $this, 'wcf_ca_delete_plugin_data_callback' ),
368
+ WCF_CA_PAGE_NAME,
369
+ WCF_CA_PLUGIN_SETTINGS_SECTION,
370
+ array( __( 'Enabling this option will delete the plugin data while deleting the Plugin.', 'woo-cart-abandonment-recovery' ) )
371
+ );
372
+
373
+ }
374
+
375
+ /**
376
+ * Callback for cart abandonment status.
377
+ *
378
+ * @param array $args args.
379
+ * @since 1.1.5
380
+ */
381
+ public function wcf_ca_delete_plugin_data_callback( $args ) {
382
+ $wcf_ca_delete_plugin_data = get_option( 'wcf_ca_delete_plugin_data' );
383
+ $html = '';
384
+ printf(
385
+ '<input type="checkbox" id="wcf_ca_delete_plugin_data" name="wcf_ca_delete_plugin_data" value="on"
386
+ ' . checked( 'on', $wcf_ca_delete_plugin_data, false ) . ' />'
387
+ );
388
+ $html .= '<label for="wcf_ca_delete_plugin_data"> ' . $args[0] . '</label>';
389
+ echo wp_kses_post( $html );
390
+ }
391
+
392
+
393
+ /**
394
+ * Callback for cart abandonment status.
395
+ *
396
+ * @param array $args args.
397
+ * @since 1.1.5
398
+ */
399
+ public function wcf_ca_coupon_code_status_callback( $args ) {
400
+ $wcf_ca_coupon_code_status = get_option( 'wcf_ca_coupon_code_status' );
401
+ $html = '';
402
+ printf(
403
+ '<input type="checkbox" id="wcf_ca_coupon_code_status" name="wcf_ca_coupon_code_status" value="on"
404
+ ' . checked( 'on', $wcf_ca_coupon_code_status, false ) . ' />'
405
+ );
406
+ $html .= '<label for="wcf_ca_coupon_code_status"> ' . $args[0] . '</label>';
407
+ echo wp_kses_post( $html );
408
+ }
409
+
410
+
411
+ /**
412
+ * Callback for cart abandonment cut off time.
413
+ *
414
+ * @param array $args args.
415
+ * @since 1.1.5
416
+ */
417
+ public function wcf_ca_zapier_cart_abandoned_webhook_callback( $args ) {
418
+ $wcf_ca_zapier_cart_abandoned_webhook = get_option( 'wcf_ca_zapier_cart_abandoned_webhook' );
419
+ echo '<input type="text" class="wcf-ca-trigger-input" id="wcf_ca_zapier_cart_abandoned_webhook" name="wcf_ca_zapier_cart_abandoned_webhook" value="' . esc_attr( sanitize_text_field( $wcf_ca_zapier_cart_abandoned_webhook ) ) . '" />';
420
+ echo '<button id="wcf_ca_trigger_web_hook_abandoned_btn" type="button" class="button"> Trigger Sample </button>';
421
+ echo '<span style="margin-left: 10px;" id="wcf_ca_abandoned_btn_message"></span>';
422
+ $html = '<label for="wcf_ca_zapier_cart_abandoned_webhook"> ' . $args[0] . '</label>';
423
+ echo wp_kses_post( $html );
424
+ }
425
+
426
+
427
+ /**
428
+ * Callback for cart abandonment status.
429
+ *
430
+ * @param array $args args.
431
+ * @since 1.1.5
432
+ */
433
+ public function wcf_ca_zapier_tracking_status_callback( $args ) {
434
+ $wcf_ca_zapier_tracking_status = get_option( 'wcf_ca_zapier_tracking_status' );
435
+
436
+ $html = '';
437
+ printf(
438
+ '<input type="checkbox" id="wcf_ca_zapier_tracking_status" name="wcf_ca_zapier_tracking_status" value="on"
439
+ ' . checked( 'on', $wcf_ca_zapier_tracking_status, false ) . ' />'
440
+ );
441
+ $html .= '<label for="wcf_ca_zapier_tracking_status"> ' . $args[0] . '</label>';
442
+ echo wp_kses_post( $html );
443
+ }
444
+
445
+ /**
446
+ * Callback for send email to admin.
447
+ *
448
+ * @param array $args args.
449
+ * @since 1.1.5
450
+ */
451
+ public function wcar_email_admin_on_recovery( $args ) {
452
+ $email_admin_on_recovery = get_option( 'wcar_email_admin_on_recovery' );
453
+
454
+ $html = '';
455
+ printf(
456
+ '<input type="checkbox" id="wcar_email_admin_on_recovery" name="wcar_email_admin_on_recovery" value="on"
457
+ ' . checked( 'on', $email_admin_on_recovery, false ) . ' />'
458
+ );
459
+ $html .= '<label for="wcar_email_admin_on_recovery"> ' . $args[0] . '</label>';
460
+ echo wp_kses_post( $html );
461
+ }
462
+
463
+ /**
464
+ * Callback for cart abandonment cut off time.
465
+ *
466
+ * @param array $args args.
467
+ * @since 1.1.5
468
+ */
469
+ public function wcf_ca_coupon_amount_callback( $args ) {
470
+ $wcf_ca_coupon_amount = get_option( 'wcf_ca_coupon_amount' );
471
+ printf(
472
+ '<input type="number" class="wcf-ca-trigger-input wcf-ca-email-inputs" id="wcf_ca_coupon_amount" name="wcf_ca_coupon_amount" value="%s" />',
473
+ isset( $wcf_ca_coupon_amount ) ? esc_attr( $wcf_ca_coupon_amount ) : ''
474
+ );
475
+ $html = '<label for="wcf_ca_coupon_amount"> ' . $args[0] . '</label>';
476
+ echo wp_kses_post( $html );
477
+ }
478
+
479
+ /**
480
+ * Callback for cart abandonment cut off time.
481
+ *
482
+ * @param array $args args.
483
+ * @since 1.1.5
484
+ */
485
+ public function wcf_ca_coupon_expiry_callback( $args ) {
486
+ $wcf_ca_coupon_expiry = intval( get_option( 'wcf_ca_coupon_expiry' ) );
487
+ printf(
488
+ '<input type="number" class="wcf-ca-trigger-input wcf-ca-coupon-inputs" id="wcf_ca_coupon_expiry" name="wcf_ca_coupon_expiry" value="%s" autocomplete="off" />',
489
+ isset( $wcf_ca_coupon_expiry ) ? esc_attr( $wcf_ca_coupon_expiry ) : ''
490
+ );
491
+
492
+ $coupon_expiry_unit = get_option( 'wcf_ca_coupon_expiry_unit' );
493
+ $items = array(
494
+ 'hours' => __( 'Hour(s)', 'woo-cart-abandonment-recovery' ),
495
+ 'days' => __( 'Day(s)', 'woo-cart-abandonment-recovery' ),
496
+ );
497
+ echo "<select id='wcf_ca_coupon_expiry_unit' name='wcf_ca_coupon_expiry_unit'>";
498
+ foreach ( $items as $key => $item ) {
499
+ $selected = ( $coupon_expiry_unit === $key ) ? 'selected="selected"' : '';
500
+ echo "<option value='$key' $selected>$item</option>"; //phpcs:ignore
501
+ }
502
+ echo '</select>';
503
+
504
+ $html = '<label for="wcf_ca_coupon_expiry_unit"> ' . $args[0] . '</label>';
505
+ echo wp_kses_post( $html );
506
+ }
507
+
508
+
509
+
510
+ /**
511
+ * Callback for cart abandonment cut off time.
512
+ *
513
+ * @param array $args args.
514
+ * @since 1.1.5
515
+ */
516
+ public function wcf_ca_gdpr_message_callback( $args ) {
517
+ $wcf_ca_gdpr_message = get_option( 'wcf_ca_gdpr_message' );
518
+
519
+ printf(
520
+ '<textarea rows="2" cols="60" id="wcf_ca_gdpr_message" name="wcf_ca_gdpr_message" spellcheck="false">%s</textarea>',
521
+ isset( $wcf_ca_gdpr_message ) ? esc_attr( $wcf_ca_gdpr_message ) : ''
522
+ );
523
+ $html = '<label for="wcf_ca_gdpr_message"> ' . $args[0] . '</label>';
524
+ echo wp_kses_post( $html );
525
+ }
526
+
527
+ /**
528
+ * Callback for cart abandonment cut off time.
529
+ *
530
+ * @param array $args args.
531
+ * @since 1.1.5
532
+ */
533
+ public function wcf_ca_discount_type_callback( $args ) {
534
+
535
+ $discount_type = get_option( 'wcf_ca_discount_type' );
536
+ $items = array(
537
+ 'percent' => 'Percentage discount',
538
+ 'fixed_cart' => 'Fixed cart discount',
539
+ );
540
+ echo "<select id='wcf_ca_discount_type' name='wcf_ca_discount_type'>";
541
+ foreach ( $items as $key => $item ) {
542
+ $selected = ( $discount_type === $key ) ? 'selected="selected"' : '';
543
+ echo "<option value='$key' $selected>$item</option>"; //phpcs:ignore
544
+ }
545
+ echo '</select>';
546
+ }
547
+
548
+ /**
549
+ * Validation for cart abandonment `cut-off` settings.
550
+ *
551
+ * @param array $input input.
552
+ * @since 1.1.5
553
+ */
554
+ public function wcf_ca_coupon_amount_validation( $input ) {
555
+
556
+ $output = '';
557
+ if ( ( is_numeric( $input ) && $input >= 1 ) ) {
558
+ $output = stripslashes( $input );
559
+ } else {
560
+ add_settings_error(
561
+ 'wcf_ca_coupon_amount',
562
+ 'error found',
563
+ __( 'Coupon code should be numeric and has to be greater than or equals to 1.', 'woo-cart-abandonment-recovery' )
564
+ );
565
+ }
566
+ return $output;
567
+ }
568
+
569
+ /**
570
+ * Callback for cart abandonment options.
571
+ *
572
+ * @since 1.1.5
573
+ */
574
+ public function wcf_cart_abandonment_options_callback() {
575
+ echo '<hr/>';
576
+ }
577
+
578
+
579
+ /**
580
+ * Callback for cart abandonment status.
581
+ *
582
+ * @param array $args args.
583
+ * @since 1.1.5
584
+ */
585
+ public function wcf_ca_status_callback( $args ) {
586
+ $wcf_ca_status = get_option( 'wcf_ca_status' );
587
+ $html = '';
588
+ printf(
589
+ '<input type="checkbox" id="wcf_ca_status" name="wcf_ca_status" value="on"
590
+ ' . checked( 'on', $wcf_ca_status, false ) . ' />'
591
+ );
592
+ $html .= '<label for="wcf_ca_status"> ' . $args[0] . '</label>';
593
+ echo wp_kses_post( $html );
594
+ }
595
+
596
+ /**
597
+ * Callback for ignore users from tracking cart.
598
+ *
599
+ * @param array $args args.
600
+ * @since 1.1.5
601
+ */
602
+ public function wcf_ca_ignore_users_callback( $args ) {
603
+
604
+ $wcf_ca_ignore_users = get_option( 'wcf_ca_ignore_users' );
605
+ $html = '';
606
+ $roles_obj = new WP_Roles();
607
+ $roles_names_array = $roles_obj->get_names();
608
+ $roles_names_array = array_diff( $roles_names_array, ( is_array( 'Customer' ) ? $value : array( 'Customer' ) ) );
609
+ ?>
610
+ <p class="wcf_ca_ignore_users" name="wcf_ca_ignore_users" multiple="multiple">
611
+ <?php
612
+ foreach ( $roles_names_array as $role_name ) {
613
+ ?>
614
+ <input type="checkbox" name="wcf_ca_ignore_users[]"
615
+ <?php
616
+ if ( ! empty( $wcf_ca_ignore_users ) ) {
617
+ foreach ( $wcf_ca_ignore_users as $user ) {
618
+ checked( $user, $role_name );
619
+ }
620
+ }
621
+ ?>
622
+ value="<?php echo esc_attr( $role_name ); ?>">
623
+ <?php
624
+ echo esc_attr( $role_name );
625
+ echo '<br> ';
626
+ }
627
+ ?>
628
+ </p>
629
+ <?php
630
+ $html .= '<span for="wcf_ca_ignore_users"> ' . $args[0] . '</span>';
631
+ echo wp_kses_post( $html );
632
+ }
633
+
634
+ /**
635
+ * Callback for ignore users from tracking cart.
636
+ *
637
+ * @param array $args args.
638
+ * @since 1.1.5
639
+ */
640
+ public function wcf_ca_exclude_orders_callback( $args ) {
641
+ $wcf_ca_excludes_orders = get_option( 'wcf_ca_excludes_orders' );
642
+
643
+ $html = '';
644
+ $order_status = wc_get_order_statuses();
645
+ $new_order_status = str_replace( 'wc-', '', array_keys( $order_status ) );
646
+ $order_status = array_combine( $new_order_status, $order_status );
647
+ $order_status = \array_diff( $order_status, array( 'Refunded', 'Draft', 'Cancelled' ) );
648
+ ?>
649
+ <p class="wcf-ca-excludes-orders" name="wcf-ca-excludes-orders">
650
+ <?php
651
+ foreach ( $order_status as $key => $value ) {
652
+ ?>
653
+ <input type="checkbox" name="wcf_ca_excludes_orders[]"
654
+ <?php
655
+ if ( ! empty( $wcf_ca_excludes_orders ) && in_array( $key, $wcf_ca_excludes_orders, true ) ) {
656
+ checked( true, true );
657
+ }
658
+ ?>
659
+ value="<?php echo esc_attr( $key ); ?>">
660
+ <?php
661
+ echo esc_attr( $value );
662
+ echo '<br> ';
663
+ }
664
+ ?>
665
+ </p>
666
+
667
+ <?php
668
+ $html .= '<span for="wcf_ca_excludes_orders"> ' . $args[0] . '</span>';
669
+ echo wp_kses_post( $html );
670
+ }
671
+ /**
672
+ * Delete coupons.
673
+ *
674
+ * @param array $args args.
675
+ */
676
+ public function wcf_ca_auto_delete_coupons_callback( $args ) {
677
+ $wcf_ca_auto_delete_coupons = get_option( 'wcf_ca_auto_delete_coupons' );
678
+ $html = '';
679
+ printf(
680
+ '<input type="checkbox" id="wcf_ca_auto_delete_coupons" name="wcf_ca_auto_delete_coupons" value="on"
681
+ ' . checked( 'on', $wcf_ca_auto_delete_coupons, false ) . ' />'
682
+ );
683
+ $html .= '<span for="wcf_ca_auto_delete_coupons"> ' . $args[0] . '</span>';
684
+ echo wp_kses_post( $html );
685
+ }
686
+
687
+ /**
688
+ * Delete coupons.
689
+ *
690
+ * @param array $args args.
691
+ */
692
+ public function wcf_ca_delete_coupons_callback( $args ) {
693
+ ?>
694
+
695
+ <input type="button" class="button-secondary" id="wcf_ca_delete_coupons" value="<?php esc_html_e( 'Delete', 'woo-cart-abandonment-recovery' ); ?>" >
696
+ <span class="spinner wcf-ca-spinner"></span>
697
+ <span class="wcf-ca-response-msg"></span>
698
+ <?php
699
+ $html = '';
700
+ $html .= '<span for="wcf_ca_delete_coupons"> ' . $args[0] . '</span>';
701
+ echo wp_kses_post( $html );
702
+ }
703
+
704
+ /**
705
+ * Callback for cart abandonment status.
706
+ *
707
+ * @param array $args args.
708
+ * @since 1.1.5
709
+ */
710
+ public function wcf_ca_gdpr_status_callback( $args ) {
711
+ $wcf_ca_gdpr_status = get_option( 'wcf_ca_gdpr_status' );
712
+ $html = '';
713
+ printf(
714
+ '<input type="checkbox" id="wcf_ca_gdpr_status" name="wcf_ca_gdpr_status" value="on"
715
+ ' . checked( 'on', $wcf_ca_gdpr_status, false ) . ' />'
716
+ );
717
+ $html .= '<label for="wcf_ca_gdpr_status"> ' . $args[0] . '</label>';
718
+ echo wp_kses_post( $html );
719
+ }
720
+
721
+ /**
722
+ * Callback for email from name.
723
+ *
724
+ * @param array $args Arguments.
725
+ */
726
+ public static function wcf_ca_from_name_callback( $args ) {
727
+ $wcf_ca_from_name = get_option( 'wcf_ca_from_name' );
728
+ printf(
729
+ '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_from_name" name="wcf_ca_from_name" value="%s" />',
730
+ isset( $wcf_ca_from_name ) ? esc_attr( $wcf_ca_from_name ) : ''
731
+ );
732
+ $html = '<label for="wcf_ca_from_name"> ' . $args[0] . '</label>';
733
+ echo wp_kses_post( $html );
734
+ }
735
+
736
+ /**
737
+ * Callback for email from.
738
+ *
739
+ * @param array $args Arguments.
740
+ */
741
+ public static function wcf_ca_from_email_callback( $args ) {
742
+ $wcf_ca_from_email = get_option( 'wcf_ca_from_email' );
743
+ printf(
744
+ '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_from_email" name="wcf_ca_from_email" value="%s" />',
745
+ isset( $wcf_ca_from_email ) ? esc_attr( $wcf_ca_from_email ) : ''
746
+ );
747
+ $html = '<label for="wcf_ca_from_email"> ' . $args[0] . '</label>';
748
+ echo wp_kses_post( $html );
749
+ }
750
+
751
+ /**
752
+ * Callback for email reply.
753
+ *
754
+ * @param array $args Arguments.
755
+ * @since 3.5
756
+ */
757
+ public static function wcf_ca_reply_email_callback( $args ) {
758
+ $wcf_ca_reply_email = get_option( 'wcf_ca_reply_email' );
759
+ printf(
760
+ '<input class="wcf-ca-trigger-input wcf-ca-email-inputs" type="text" id="wcf_ca_reply_email" name="wcf_ca_reply_email" value="%s" />',
761
+ isset( $wcf_ca_reply_email ) ? esc_attr( $wcf_ca_reply_email ) : ''
762
+ );
763
+
764
+ $html = '<label for="wcf_ca_reply_email"> ' . $args[0] . '</label>';
765
+ echo wp_kses_post( $html );
766
+ }
767
+
768
+
769
+ /**
770
+ * Validation for email.
771
+ *
772
+ * @param array $input input.
773
+ * @since 1.1.5
774
+ */
775
+ public function wcf_ca_from_email_validation( $input ) {
776
+
777
+ if ( $input && ! is_email( $input ) ) {
778
+ add_settings_error(
779
+ 'wcf_ca_from_email',
780
+ 'error found',
781
+ __( 'Invalid email "From" address field', 'woo-cart-abandonment-recovery' )
782
+ );
783
+ }
784
+ return sanitize_email( $input );
785
+ }
786
+
787
+ /**
788
+ * Validation for reply email.
789
+ *
790
+ * @param array $input input.
791
+ * @since 1.1.5
792
+ */
793
+ public function wcf_ca_reply_email_validation( $input ) {
794
+
795
+ if ( $input && ! is_email( $input ) ) {
796
+ add_settings_error(
797
+ 'wcf_ca_reply_email',
798
+ 'error found',
799
+ __( 'Invalid email "Reply" address field', 'woo-cart-abandonment-recovery' )
800
+ );
801
+ }
802
+ return sanitize_email( $input );
803
+ }
804
+
805
+ /**
806
+ * Initiator
807
+ */
808
+ public static function get_instance() {
809
+ if ( ! isset( self::$instance ) ) {
810
+ self::$instance = new self();
811
+ }
812
+ return self::$instance;
813
+ }
814
+
815
+
816
+
817
+
818
+ }
819
+ Cartflows_Ca_Settings::get_instance();
classes/class-cartflows-ca-update.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  if ( ! class_exists( 'Cartflows_Ca_Update' ) ) :
9
 
10
  /**
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  if ( ! class_exists( 'Cartflows_Ca_Update' ) ) :
13
 
14
  /**
languages/woo-cart-abandonment-recovery.pot CHANGED
@@ -1,885 +1,897 @@
1
- # Copyright (C) 2021 CartFlows Inc
2
- # This file is distributed under the same license as the WooCommerce Cart Abandonment Recovery package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: WooCommerce Cart Abandonment Recovery 1.2.9\n"
6
- "Report-Msgid-Bugs-To: "
7
- "https://wordpress.org/support/plugin/woo-cart-abandonment-recovery\n"
8
- "POT-Creation-Date: 2021-01-14 06:26:22+00:00\n"
9
- "MIME-Version: 1.0\n"
10
- "Content-Type: text/plain; charset=utf-8\n"
11
- "Content-Transfer-Encoding: 8bit\n"
12
- "PO-Revision-Date: 2021-MO-DA HO:MI+ZONE\n"
13
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
- "Language-Team: LANGUAGE <LL@li.org>\n"
15
- "Language: en\n"
16
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17
- "X-Poedit-Country: United States\n"
18
- "X-Poedit-SourceCharset: UTF-8\n"
19
- "X-Poedit-KeywordsList: "
20
- "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
21
- "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
22
- "X-Poedit-Basepath: ../\n"
23
- "X-Poedit-SearchPath-0: .\n"
24
- "X-Poedit-Bookmarks: \n"
25
- "X-Textdomain-Support: yes\n"
26
- "X-Generator: grunt-wp-i18n 1.0.3\n"
27
-
28
- #: admin/bsf-analytics/class-bsf-analytics.php:216
29
- #. translators: %s product name
30
- msgid ""
31
- "Want to help make <strong>%1s</strong> even more awesome? Allow us to "
32
- "collect non-sensitive diagnostic data and usage information. "
33
- msgstr ""
34
-
35
- #: admin/bsf-analytics/class-bsf-analytics.php:219
36
- msgid "This will be applicable for all sites from the network."
37
- msgstr ""
38
-
39
- #: admin/bsf-analytics/class-bsf-analytics.php:243
40
- #. translators: %s usage doc link
41
- msgid " Know More."
42
- msgstr ""
43
-
44
- #: admin/bsf-analytics/class-bsf-analytics.php:251
45
- msgid "Yes! Allow it"
46
- msgstr ""
47
-
48
- #: admin/bsf-analytics/class-bsf-analytics.php:260
49
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:539
50
- msgid "No Thanks"
51
- msgstr ""
52
-
53
- #: admin/bsf-analytics/class-bsf-analytics.php:364
54
- msgid "Usage Tracking"
55
- msgstr ""
56
-
57
- #: admin/bsf-analytics/class-bsf-analytics.php:408
58
- #. translators: %s Product title
59
- msgid "Allow %s products to track non-sensitive usage tracking data."
60
- msgstr ""
61
-
62
- #: admin/bsf-analytics/class-bsf-analytics.php:411
63
- msgid " This will be applicable for all sites from the network."
64
- msgstr ""
65
-
66
- #: admin/bsf-analytics/class-bsf-analytics.php:416
67
- msgid "Learn More."
68
- msgstr ""
69
-
70
- #: classes/class-cartflows-ca-loader.php:138
71
- #. translators: %s: html tags
72
- msgid ""
73
- "The %1$sWooCommerce Cart Abandonment Recovery%2$s plugin requires "
74
- "%1$sWooCommerce%2$s plugin installed & activated."
75
- msgstr ""
76
-
77
- #: classes/class-cartflows-ca-loader.php:147
78
- msgid "Activate WooCommerce"
79
- msgstr ""
80
-
81
- #: classes/class-cartflows-ca-loader.php:155
82
- msgid "Install WooCommerce"
83
- msgstr ""
84
-
85
- #: classes/class-cartflows-ca-settings.php:54
86
- msgid "Cart Abandonment Settings"
87
- msgstr ""
88
-
89
- #: classes/class-cartflows-ca-settings.php:61
90
- msgid "Enable Tracking"
91
- msgstr ""
92
-
93
- #: classes/class-cartflows-ca-settings.php:65
94
- msgid ""
95
- "Start capturing abandoned carts. <br/><br/> <span "
96
- "class=\"description\"><strong>Note:</strong> Cart will be considered "
97
- "abandoned if order is not completed in <strong>15 minutes</strong>.</span>"
98
- msgstr ""
99
-
100
- #: classes/class-cartflows-ca-settings.php:75
101
- msgid "Disable Tracking For"
102
- msgstr ""
103
-
104
- #: classes/class-cartflows-ca-settings.php:79
105
- msgid ""
106
- " It will ignore selected users from abandonment process when they logged "
107
- "in, and hence they can not receive mail for cart abandoned by themselves."
108
- msgstr ""
109
-
110
- #: classes/class-cartflows-ca-settings.php:89
111
- msgid "Exclude email sending For"
112
- msgstr ""
113
-
114
- #: classes/class-cartflows-ca-settings.php:93
115
- msgid ""
116
- " It will not send future recovery emails to selected order status and will "
117
- "mark as recovered."
118
- msgstr ""
119
-
120
- #: classes/class-cartflows-ca-settings.php:103
121
- msgid "Notify recovery to admin"
122
- msgstr ""
123
-
124
- #: classes/class-cartflows-ca-settings.php:107
125
- msgid "This option will send an email to admin on new order recovery."
126
- msgstr ""
127
-
128
- #: classes/class-cartflows-ca-settings.php:120
129
- msgid "Coupons Settings"
130
- msgstr ""
131
-
132
- #: classes/class-cartflows-ca-settings.php:127
133
- msgid "Delete Coupons Automatically"
134
- msgstr ""
135
-
136
- #: classes/class-cartflows-ca-settings.php:131
137
- msgid ""
138
- "Delete coupons automatically on weekly basis.<br><span "
139
- "class=\"description\"><br/><strong>Note:</strong> This option will set a "
140
- "weekly cron to delete all <strong>expired</strong> and "
141
- "<strong>used</strong> coupons automatically in the background.</p>"
142
- msgstr ""
143
-
144
- #: classes/class-cartflows-ca-settings.php:141
145
- msgid "Delete Coupons Manually"
146
- msgstr ""
147
-
148
- #: classes/class-cartflows-ca-settings.php:145
149
- msgid ""
150
- "<br><strong>Note:</strong> This will delete all <strong>expired</strong> "
151
- "and <strong>used</strong> coupons that were created by Woo Cart Abandonment "
152
- "Recovery.</p>"
153
- msgstr ""
154
-
155
- #: classes/class-cartflows-ca-settings.php:157
156
- msgid "Email Settings"
157
- msgstr ""
158
-
159
- #: classes/class-cartflows-ca-settings.php:164
160
- msgid "\"From\" Name"
161
- msgstr ""
162
-
163
- #: classes/class-cartflows-ca-settings.php:168
164
- msgid "Name will appear in email sent."
165
- msgstr ""
166
-
167
- #: classes/class-cartflows-ca-settings.php:173
168
- msgid "\"From\" Address"
169
- msgstr ""
170
-
171
- #: classes/class-cartflows-ca-settings.php:177
172
- msgid "Email which send from."
173
- msgstr ""
174
-
175
- #: classes/class-cartflows-ca-settings.php:182
176
- msgid "\"Reply To\" Address"
177
- msgstr ""
178
-
179
- #: classes/class-cartflows-ca-settings.php:186
180
- msgid "When a user clicks reply, which email address should that reply be sent to?"
181
- msgstr ""
182
-
183
- #: classes/class-cartflows-ca-settings.php:209
184
- msgid "Enable Webhook"
185
- msgstr ""
186
-
187
- #: classes/class-cartflows-ca-settings.php:213
188
- msgid ""
189
- "Allows you to trigger webhook automatically upon cart abandonment and "
190
- "recovery."
191
- msgstr ""
192
-
193
- #: classes/class-cartflows-ca-settings.php:218
194
- msgid "Webhook URL"
195
- msgstr ""
196
-
197
- #: classes/class-cartflows-ca-settings.php:237
198
- msgid "Coupon Code Settings"
199
- msgstr ""
200
-
201
- #: classes/class-cartflows-ca-settings.php:244
202
- msgid "Create Coupon Code"
203
- msgstr ""
204
-
205
- #: classes/class-cartflows-ca-settings.php:248
206
- msgid ""
207
- "Auto-create the special coupon for the abandoned cart to send over the "
208
- "emails."
209
- msgstr ""
210
-
211
- #: classes/class-cartflows-ca-settings.php:253
212
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:533
213
- msgid "Discount Type"
214
- msgstr ""
215
-
216
- #: classes/class-cartflows-ca-settings.php:262
217
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:565
218
- msgid "Coupon Amount"
219
- msgstr ""
220
-
221
- #: classes/class-cartflows-ca-settings.php:271
222
- msgid "Coupon Expires After"
223
- msgstr ""
224
-
225
- #: classes/class-cartflows-ca-settings.php:275
226
- msgid ""
227
- "<br/><br/> <span class=\"description\"><strong>Note: </strong>. Enter zero "
228
- "(0) to restrict coupon from expiring.</span>"
229
- msgstr ""
230
-
231
- #: classes/class-cartflows-ca-settings.php:306
232
- msgid "Webhook Settings"
233
- msgstr ""
234
-
235
- #: classes/class-cartflows-ca-settings.php:315
236
- msgid "GDPR Settings"
237
- msgstr ""
238
-
239
- #: classes/class-cartflows-ca-settings.php:322
240
- msgid "Enable GDPR Integration"
241
- msgstr ""
242
-
243
- #: classes/class-cartflows-ca-settings.php:326
244
- msgid ""
245
- "Ask confirmation from the user before tracking data. <br/><br/> <span "
246
- "class=\"description\"><strong>Note:</strong> By checking this, it will show "
247
- "up confirmation text below the email id on checkout page.</span>"
248
- msgstr ""
249
-
250
- #: classes/class-cartflows-ca-settings.php:331
251
- msgid "GDPR Message"
252
- msgstr ""
253
-
254
- #: classes/class-cartflows-ca-settings.php:450
255
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:603
256
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:699
257
- msgid "Hour(s)"
258
- msgstr ""
259
-
260
- #: classes/class-cartflows-ca-settings.php:451
261
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:604
262
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:700
263
- msgid "Day(s)"
264
- msgstr ""
265
-
266
- #: classes/class-cartflows-ca-settings.php:519
267
- msgid "Coupon code should be numeric and has to be greater than or equals to 1."
268
- msgstr ""
269
-
270
- #: classes/class-cartflows-ca-settings.php:651
271
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:82
272
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:134
273
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:84
274
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:118
275
- msgid "Delete"
276
- msgstr ""
277
-
278
- #: classes/class-cartflows-ca-settings.php:737
279
- msgid "Invalid email \"From\" address field"
280
- msgstr ""
281
-
282
- #: classes/class-cartflows-ca-settings.php:755
283
- msgid "Invalid email \"Reply\" address field"
284
- msgstr ""
285
-
286
- #: lib/notices/class-astra-notices.php:120
287
- msgid "WordPress Nonce not validated."
288
- msgstr ""
289
-
290
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:81
291
- msgid "View"
292
- msgstr ""
293
-
294
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:86
295
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:138
296
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1793
297
- msgid "Unsubscribe"
298
- msgstr ""
299
-
300
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:234
301
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1935
302
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1985
303
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:171
304
- msgid "Name"
305
- msgstr ""
306
-
307
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:235
308
- msgid "Email"
309
- msgstr ""
310
-
311
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:236
312
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2007
313
- msgid "Cart Total"
314
- msgstr ""
315
-
316
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:237
317
- msgid "Order Status"
318
- msgstr ""
319
-
320
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:238
321
- msgid "Time"
322
- msgstr ""
323
-
324
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:206
325
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1036
326
- msgid "This order was abandoned & subsequently recovered."
327
- msgstr ""
328
-
329
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:231
330
- msgid "Mail has been sent successfully!"
331
- msgstr ""
332
-
333
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:233
334
- msgid "Mail sending failed!"
335
- msgstr ""
336
-
337
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:272
338
- msgid "Every Fifteen Minutes"
339
- msgstr ""
340
-
341
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:346
342
- msgid "You have successfully unsubscribed from our email list."
343
- msgstr ""
344
-
345
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:346
346
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:162
347
- msgid "Unsubscribed"
348
- msgstr ""
349
-
350
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:497
351
- msgid ""
352
- "This checkout page is generated by WooCommerce Cart Abandonment Recovery "
353
- "plugin from test mail."
354
- msgstr ""
355
-
356
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:540
357
- msgid "You won't receive further emails from us, thank you!"
358
- msgstr ""
359
-
360
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1194
361
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1195
362
- msgid "Cart Abandonment"
363
- msgstr ""
364
-
365
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1220
366
- msgid "Items deleted: %d"
367
- msgstr ""
368
-
369
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1237
370
- msgid "User(s) unsubscribed successfully!"
371
- msgstr ""
372
-
373
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1298
374
- msgid ""
375
- "Do you really want to delete the used and expired coupons created by Cart "
376
- "Abandonment Plugin?"
377
- msgstr ""
378
-
379
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1299
380
- msgid "Do you really want to export orders?"
381
- msgstr ""
382
-
383
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1303
384
- msgid "No such order is found."
385
- msgstr ""
386
-
387
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1322
388
- msgid "View Report"
389
- msgstr ""
390
-
391
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1460
392
- msgid "Report"
393
- msgstr ""
394
-
395
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1476
396
- msgid "Follow-Up Emails"
397
- msgstr ""
398
-
399
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1492
400
- msgid "Settings"
401
- msgstr ""
402
-
403
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1754
404
- msgid "there"
405
- msgstr ""
406
-
407
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1927
408
- msgid "Cart Total ( Cart Total + Shipping + Tax )"
409
- msgstr ""
410
-
411
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1934
412
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1984
413
- msgid "Item"
414
- msgstr ""
415
-
416
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1936
417
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1986
418
- msgid "Quantity"
419
- msgstr ""
420
-
421
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1937
422
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1987
423
- msgid "Price"
424
- msgstr ""
425
-
426
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1938
427
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1988
428
- msgid "Line Subtotal"
429
- msgstr ""
430
-
431
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1994
432
- msgid "Discount"
433
- msgstr ""
434
-
435
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1998
436
- msgid "Other"
437
- msgstr ""
438
-
439
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2003
440
- msgid "Shipping"
441
- msgstr ""
442
-
443
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2155
444
- #. translators: %1$s: Coupons Deleted, %2$s: Deleted coupons count'.
445
- msgid "%1$s: %2$d"
446
- msgstr ""
447
-
448
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:72
449
- msgid "Edit"
450
- msgstr ""
451
-
452
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:96
453
- msgid "Clone"
454
- msgstr ""
455
-
456
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:183
457
- msgid "Template Name"
458
- msgstr ""
459
-
460
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:184
461
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:81
462
- msgid "Email Subject"
463
- msgstr ""
464
-
465
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:185
466
- msgid "Trigger After"
467
- msgstr ""
468
-
469
- #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:186
470
- msgid "Activate Template"
471
- msgstr ""
472
-
473
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:128
474
- msgid "Admin Firstname"
475
- msgstr ""
476
-
477
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:129
478
- msgid "Admin Company"
479
- msgstr ""
480
-
481
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:130
482
- msgid "Abandoned Product Details Table"
483
- msgstr ""
484
-
485
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:131
486
- msgid "Abandoned Product Names"
487
- msgstr ""
488
-
489
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:132
490
- msgid "Cart Checkout URL"
491
- msgstr ""
492
-
493
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:133
494
- msgid "Coupon Code"
495
- msgstr ""
496
-
497
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:134
498
- msgid "Customer First Name"
499
- msgstr ""
500
-
501
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:135
502
- msgid "Customer Last Name"
503
- msgstr ""
504
-
505
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:136
506
- msgid "Customer Full Name"
507
- msgstr ""
508
-
509
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:137
510
- msgid "Cart Abandonment Date"
511
- msgstr ""
512
-
513
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:138
514
- msgid "Site URL"
515
- msgstr ""
516
-
517
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:139
518
- msgid "Unsubscribe Link"
519
- msgstr ""
520
-
521
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:159
522
- msgid "Something went wrong"
523
- msgstr ""
524
-
525
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:166
526
- msgid "Activated"
527
- msgstr ""
528
-
529
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:169
530
- msgid "Deactivated"
531
- msgstr ""
532
-
533
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:228
534
- msgid "The Email Template has been successfully added."
535
- msgstr ""
536
-
537
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:238
538
- msgid "The Email Template has been cloned successfully."
539
- msgstr ""
540
-
541
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:248
542
- msgid "The Email Template has been successfully deleted."
543
- msgstr ""
544
-
545
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:257
546
- msgid "The Email Template has been successfully updated."
547
- msgstr ""
548
-
549
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:267
550
- msgid "Default Email Templates has been restored successfully."
551
- msgstr ""
552
-
553
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:434
554
- msgid "Activate Template now?"
555
- msgstr ""
556
-
557
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:454
558
- msgid "Template Name:"
559
- msgstr ""
560
-
561
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:469
562
- msgid "Email Subject:"
563
- msgstr ""
564
-
565
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:484
566
- msgid "Email Body:"
567
- msgstr ""
568
-
569
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:513
570
- msgid "Create Coupon"
571
- msgstr ""
572
-
573
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:526
574
- msgid "Allows you to send new coupon only for this template."
575
- msgstr ""
576
-
577
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:583
578
- msgid "Coupon expiry date"
579
- msgstr ""
580
-
581
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:613
582
- msgid "Enter zero (0) to restrict coupon from expiring"
583
- msgstr ""
584
-
585
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:619
586
- msgid "Free Shipping"
587
- msgstr ""
588
-
589
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:632
590
- msgid ""
591
- "Allows you to grant free shipping. A free shipping method must be enabled "
592
- "in your shipping zone and be set to require \"a valid free shipping "
593
- "coupon\". "
594
- msgstr ""
595
-
596
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:639
597
- msgid "Individual use only"
598
- msgstr ""
599
-
600
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:653
601
- msgid ""
602
- "Check this box if the coupon cannot be used in conjunction with other "
603
- "coupons."
604
- msgstr ""
605
-
606
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:660
607
- msgid "Auto Apply Coupon"
608
- msgstr ""
609
-
610
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:674
611
- msgid " Automatically add the coupon to the cart at the checkout."
612
- msgstr ""
613
-
614
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:680
615
- msgid "Send This Email"
616
- msgstr ""
617
-
618
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:698
619
- msgid "Minute(s)"
620
- msgstr ""
621
-
622
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:713
623
- msgid "after cart is abandoned."
624
- msgstr ""
625
-
626
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:723
627
- msgid "Send Test Email To:"
628
- msgstr ""
629
-
630
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:727
631
- msgid "Send a test email"
632
- msgstr ""
633
-
634
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:740
635
- msgid "Save Changes"
636
- msgstr ""
637
-
638
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:742
639
- msgid "Update Changes"
640
- msgstr ""
641
-
642
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:1048
643
- msgid "Create New Template"
644
- msgstr ""
645
-
646
- #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:1051
647
- msgid " Restore Default Templates"
648
- msgstr ""
649
-
650
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:27
651
- msgid "Back to Reports"
652
- msgstr ""
653
-
654
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:38
655
- msgid "Email Details:"
656
- msgstr ""
657
-
658
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:48
659
- msgid ""
660
- "All new activated emails will be reschedule for this abandoned order. New "
661
- "emails will be sent to user according to schedule time."
662
- msgstr ""
663
-
664
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:54
665
- msgid "Are your sure?"
666
- msgstr ""
667
-
668
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:58
669
- msgid "Reschedule"
670
- msgstr ""
671
-
672
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:62
673
- msgid "Close"
674
- msgstr ""
675
-
676
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:67
677
- msgid "Do you really want to reschedule emails?"
678
- msgstr ""
679
-
680
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:67
681
- msgid "Reschedule Emails"
682
- msgstr ""
683
-
684
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:73
685
- msgid " No Email Scheduled."
686
- msgstr ""
687
-
688
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:80
689
- msgid "Scheduled Template"
690
- msgstr ""
691
-
692
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:82
693
- msgid "Email Coupon"
694
- msgstr ""
695
-
696
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:83
697
- msgid "Email Sent"
698
- msgstr ""
699
-
700
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:84
701
- msgid "Scheduled At"
702
- msgstr ""
703
-
704
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:113
705
- msgid "The email has been unsubscribed and won't be sent further."
706
- msgstr ""
707
-
708
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:116
709
- msgid "Email is in the queue and will be sent at the scheduled time."
710
- msgstr ""
711
-
712
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:121
713
- msgid "The email has been sent."
714
- msgstr ""
715
-
716
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:125
717
- msgid ""
718
- "The email has been unscheduled due to the complete order and won't be sent "
719
- "further."
720
- msgstr ""
721
-
722
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:158
723
- msgid "User Address Details:"
724
- msgstr ""
725
-
726
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:170
727
- msgid "Billing Address"
728
- msgstr ""
729
-
730
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:174
731
- msgid "Email address"
732
- msgstr ""
733
-
734
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:179
735
- msgid "Phone"
736
- msgstr ""
737
-
738
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:184
739
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:204
740
- msgid "Address 1:"
741
- msgstr ""
742
-
743
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:187
744
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:207
745
- msgid "Address 2:"
746
- msgstr ""
747
-
748
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:190
749
- msgid "Country, City:"
750
- msgstr ""
751
-
752
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:193
753
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:213
754
- msgid "State:"
755
- msgstr ""
756
-
757
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:197
758
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:219
759
- msgid "Postcode:"
760
- msgstr ""
761
-
762
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:202
763
- msgid "Shipping Address"
764
- msgstr ""
765
-
766
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:210
767
- msgid "City:"
768
- msgstr ""
769
-
770
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:216
771
- msgid "Country:"
772
- msgstr ""
773
-
774
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:227
775
- msgid "Checkout Link"
776
- msgstr ""
777
-
778
- #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:242
779
- msgid "User Order Details:"
780
- msgstr ""
781
-
782
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:14
783
- msgid "Today"
784
- msgstr ""
785
-
786
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:18
787
- msgid "Yesterday"
788
- msgstr ""
789
-
790
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:22
791
- msgid "Last Week"
792
- msgstr ""
793
-
794
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:26
795
- msgid "Last Month"
796
- msgstr ""
797
-
798
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:35
799
- msgid "Custom Filter"
800
- msgstr ""
801
-
802
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:46
803
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:108
804
- msgid "Recoverable Orders"
805
- msgstr ""
806
-
807
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:50
808
- msgid "Total Recoverable Orders."
809
- msgstr ""
810
-
811
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:55
812
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:111
813
- msgid "Recovered Orders"
814
- msgstr ""
815
-
816
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:57
817
- msgid "Total Recovered Orders."
818
- msgstr ""
819
-
820
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:62
821
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:114
822
- msgid "Lost Orders"
823
- msgstr ""
824
-
825
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:65
826
- msgid "Total Lost Orders."
827
- msgstr ""
828
-
829
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:74
830
- msgid "Recoverable Revenue"
831
- msgstr ""
832
-
833
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:79
834
- msgid "Total Recoverable Revenue."
835
- msgstr ""
836
-
837
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:84
838
- msgid "Recovered Revenue"
839
- msgstr ""
840
-
841
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:90
842
- msgid "Total Recovered Revenue."
843
- msgstr ""
844
-
845
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:95
846
- msgid "Recovery Rate"
847
- msgstr ""
848
-
849
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:97
850
- msgid "Total Percentage Of Recovered Orders After Abandonment."
851
- msgstr ""
852
-
853
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:124
854
- msgid "Search by email"
855
- msgstr ""
856
-
857
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:125
858
- msgid "Search Orders"
859
- msgstr ""
860
-
861
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:152
862
- msgid "No Orders Found."
863
- msgstr ""
864
-
865
- #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-tabs.php:10
866
- msgid "WooCommerce Cart Abandonment Recovery "
867
- msgstr ""
868
-
869
- #. Plugin Name of the plugin/theme
870
- msgid "WooCommerce Cart Abandonment Recovery"
871
- msgstr ""
872
-
873
- #. Author URI of the plugin/theme
874
- msgid "https://cartflows.com/"
875
- msgstr ""
876
-
877
- #. Description of the plugin/theme
878
- msgid ""
879
- "Recover your lost revenue. Capture email address of users on the checkout "
880
- "page and send follow up emails if they don't complete the purchase."
881
- msgstr ""
882
-
883
- #. Author of the plugin/theme
884
- msgid "CartFlows Inc"
 
 
 
 
 
 
 
 
 
 
 
 
885
  msgstr ""
1
+ # Copyright (C) 2021 CartFlows Inc
2
+ # This file is distributed under the same license as the WooCommerce Cart Abandonment Recovery package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WooCommerce Cart Abandonment Recovery 1.2.10\n"
6
+ "Report-Msgid-Bugs-To: "
7
+ "https://wordpress.org/support/plugin/woo-cart-abandonment-recovery\n"
8
+ "POT-Creation-Date: 2021-02-15 12:16:02+00:00\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=utf-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "PO-Revision-Date: 2021-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: en\n"
16
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17
+ "X-Poedit-Country: United States\n"
18
+ "X-Poedit-SourceCharset: UTF-8\n"
19
+ "X-Poedit-KeywordsList: "
20
+ "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
21
+ "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
22
+ "X-Poedit-Basepath: ../\n"
23
+ "X-Poedit-SearchPath-0: .\n"
24
+ "X-Poedit-Bookmarks: \n"
25
+ "X-Textdomain-Support: yes\n"
26
+ "X-Generator: grunt-wp-i18n 1.0.3\n"
27
+
28
+ #: admin/bsf-analytics/class-bsf-analytics.php:216
29
+ #. translators: %s product name
30
+ msgid ""
31
+ "Want to help make <strong>%1s</strong> even more awesome? Allow us to "
32
+ "collect non-sensitive diagnostic data and usage information. "
33
+ msgstr ""
34
+
35
+ #: admin/bsf-analytics/class-bsf-analytics.php:219
36
+ msgid "This will be applicable for all sites from the network."
37
+ msgstr ""
38
+
39
+ #: admin/bsf-analytics/class-bsf-analytics.php:243
40
+ #. translators: %s usage doc link
41
+ msgid " Know More."
42
+ msgstr ""
43
+
44
+ #: admin/bsf-analytics/class-bsf-analytics.php:251
45
+ msgid "Yes! Allow it"
46
+ msgstr ""
47
+
48
+ #: admin/bsf-analytics/class-bsf-analytics.php:260
49
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:543
50
+ msgid "No Thanks"
51
+ msgstr ""
52
+
53
+ #: admin/bsf-analytics/class-bsf-analytics.php:364
54
+ msgid "Usage Tracking"
55
+ msgstr ""
56
+
57
+ #: admin/bsf-analytics/class-bsf-analytics.php:408
58
+ #. translators: %s Product title
59
+ msgid "Allow %s products to track non-sensitive usage tracking data."
60
+ msgstr ""
61
+
62
+ #: admin/bsf-analytics/class-bsf-analytics.php:411
63
+ msgid " This will be applicable for all sites from the network."
64
+ msgstr ""
65
+
66
+ #: admin/bsf-analytics/class-bsf-analytics.php:416
67
+ msgid "Learn More."
68
+ msgstr ""
69
+
70
+ #: classes/class-cartflows-ca-loader.php:142
71
+ #. translators: %s: html tags
72
+ msgid ""
73
+ "The %1$sWooCommerce Cart Abandonment Recovery%2$s plugin requires "
74
+ "%1$sWooCommerce%2$s plugin installed & activated."
75
+ msgstr ""
76
+
77
+ #: classes/class-cartflows-ca-loader.php:151
78
+ msgid "Activate WooCommerce"
79
+ msgstr ""
80
+
81
+ #: classes/class-cartflows-ca-loader.php:159
82
+ msgid "Install WooCommerce"
83
+ msgstr ""
84
+
85
+ #: classes/class-cartflows-ca-settings.php:58
86
+ msgid "Cart Abandonment Settings"
87
+ msgstr ""
88
+
89
+ #: classes/class-cartflows-ca-settings.php:65
90
+ msgid "Enable Tracking"
91
+ msgstr ""
92
+
93
+ #: classes/class-cartflows-ca-settings.php:69
94
+ msgid ""
95
+ "Start capturing abandoned carts. <br/><br/> <span "
96
+ "class=\"description\"><strong>Note:</strong> Cart will be considered "
97
+ "abandoned if order is not completed in <strong>15 minutes</strong>.</span>"
98
+ msgstr ""
99
+
100
+ #: classes/class-cartflows-ca-settings.php:79
101
+ msgid "Disable Tracking For"
102
+ msgstr ""
103
+
104
+ #: classes/class-cartflows-ca-settings.php:83
105
+ msgid ""
106
+ " It will ignore selected users from abandonment process when they logged "
107
+ "in, and hence they can not receive mail for cart abandoned by themselves."
108
+ msgstr ""
109
+
110
+ #: classes/class-cartflows-ca-settings.php:93
111
+ msgid "Exclude email sending For"
112
+ msgstr ""
113
+
114
+ #: classes/class-cartflows-ca-settings.php:97
115
+ msgid ""
116
+ " It will not send future recovery emails to selected order status and will "
117
+ "mark as recovered."
118
+ msgstr ""
119
+
120
+ #: classes/class-cartflows-ca-settings.php:107
121
+ msgid "Notify recovery to admin"
122
+ msgstr ""
123
+
124
+ #: classes/class-cartflows-ca-settings.php:111
125
+ msgid "This option will send an email to admin on new order recovery."
126
+ msgstr ""
127
+
128
+ #: classes/class-cartflows-ca-settings.php:124
129
+ msgid "Coupons Settings"
130
+ msgstr ""
131
+
132
+ #: classes/class-cartflows-ca-settings.php:131
133
+ msgid "Delete Coupons Automatically"
134
+ msgstr ""
135
+
136
+ #: classes/class-cartflows-ca-settings.php:135
137
+ msgid ""
138
+ "Delete coupons automatically on weekly basis.<br><span "
139
+ "class=\"description\"><br/><strong>Note:</strong> This option will set a "
140
+ "weekly cron to delete all <strong>expired</strong> and "
141
+ "<strong>used</strong> coupons automatically in the background.</p>"
142
+ msgstr ""
143
+
144
+ #: classes/class-cartflows-ca-settings.php:145
145
+ msgid "Delete Coupons Manually"
146
+ msgstr ""
147
+
148
+ #: classes/class-cartflows-ca-settings.php:149
149
+ msgid ""
150
+ "<br><strong>Note:</strong> This will delete all <strong>expired</strong> "
151
+ "and <strong>used</strong> coupons that were created by Woo Cart Abandonment "
152
+ "Recovery.</p>"
153
+ msgstr ""
154
+
155
+ #: classes/class-cartflows-ca-settings.php:161
156
+ msgid "Email Settings"
157
+ msgstr ""
158
+
159
+ #: classes/class-cartflows-ca-settings.php:168
160
+ msgid "\"From\" Name"
161
+ msgstr ""
162
+
163
+ #: classes/class-cartflows-ca-settings.php:172
164
+ msgid "Name will appear in email sent."
165
+ msgstr ""
166
+
167
+ #: classes/class-cartflows-ca-settings.php:177
168
+ msgid "\"From\" Address"
169
+ msgstr ""
170
+
171
+ #: classes/class-cartflows-ca-settings.php:181
172
+ msgid "Email which send from."
173
+ msgstr ""
174
+
175
+ #: classes/class-cartflows-ca-settings.php:186
176
+ msgid "\"Reply To\" Address"
177
+ msgstr ""
178
+
179
+ #: classes/class-cartflows-ca-settings.php:190
180
+ msgid "When a user clicks reply, which email address should that reply be sent to?"
181
+ msgstr ""
182
+
183
+ #: classes/class-cartflows-ca-settings.php:213
184
+ msgid "Enable Webhook"
185
+ msgstr ""
186
+
187
+ #: classes/class-cartflows-ca-settings.php:217
188
+ msgid ""
189
+ "Allows you to trigger webhook automatically upon cart abandonment and "
190
+ "recovery."
191
+ msgstr ""
192
+
193
+ #: classes/class-cartflows-ca-settings.php:222
194
+ msgid "Webhook URL"
195
+ msgstr ""
196
+
197
+ #: classes/class-cartflows-ca-settings.php:241
198
+ msgid "Coupon Code Settings"
199
+ msgstr ""
200
+
201
+ #: classes/class-cartflows-ca-settings.php:248
202
+ msgid "Create Coupon Code"
203
+ msgstr ""
204
+
205
+ #: classes/class-cartflows-ca-settings.php:252
206
+ msgid ""
207
+ "Auto-create the special coupon for the abandoned cart to send over the "
208
+ "emails."
209
+ msgstr ""
210
+
211
+ #: classes/class-cartflows-ca-settings.php:257
212
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:538
213
+ msgid "Discount Type"
214
+ msgstr ""
215
+
216
+ #: classes/class-cartflows-ca-settings.php:266
217
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:570
218
+ msgid "Coupon Amount"
219
+ msgstr ""
220
+
221
+ #: classes/class-cartflows-ca-settings.php:275
222
+ msgid "Coupon Expires After"
223
+ msgstr ""
224
+
225
+ #: classes/class-cartflows-ca-settings.php:279
226
+ msgid ""
227
+ "<br/><br/> <span class=\"description\"><strong>Note: </strong>. Enter zero "
228
+ "(0) to restrict coupon from expiring.</span>"
229
+ msgstr ""
230
+
231
+ #: classes/class-cartflows-ca-settings.php:310
232
+ msgid "Webhook Settings"
233
+ msgstr ""
234
+
235
+ #: classes/class-cartflows-ca-settings.php:319
236
+ msgid "GDPR Settings"
237
+ msgstr ""
238
+
239
+ #: classes/class-cartflows-ca-settings.php:326
240
+ msgid "Enable GDPR Integration"
241
+ msgstr ""
242
+
243
+ #: classes/class-cartflows-ca-settings.php:330
244
+ msgid ""
245
+ "Ask confirmation from the user before tracking data. <br/><br/> <span "
246
+ "class=\"description\"><strong>Note:</strong> By checking this, it will show "
247
+ "up confirmation text below the email id on checkout page.</span>"
248
+ msgstr ""
249
+
250
+ #: classes/class-cartflows-ca-settings.php:335
251
+ msgid "GDPR Message"
252
+ msgstr ""
253
+
254
+ #: classes/class-cartflows-ca-settings.php:356
255
+ msgid "Plugin Settings"
256
+ msgstr ""
257
+
258
+ #: classes/class-cartflows-ca-settings.php:366
259
+ msgid "Delete Plugin Data"
260
+ msgstr ""
261
+
262
+ #: classes/class-cartflows-ca-settings.php:370
263
+ msgid "Enabling this option will delete the plugin data while deleting the Plugin."
264
+ msgstr ""
265
+
266
+ #: classes/class-cartflows-ca-settings.php:494
267
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:608
268
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:704
269
+ msgid "Hour(s)"
270
+ msgstr ""
271
+
272
+ #: classes/class-cartflows-ca-settings.php:495
273
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:609
274
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:705
275
+ msgid "Day(s)"
276
+ msgstr ""
277
+
278
+ #: classes/class-cartflows-ca-settings.php:563
279
+ msgid "Coupon code should be numeric and has to be greater than or equals to 1."
280
+ msgstr ""
281
+
282
+ #: classes/class-cartflows-ca-settings.php:695
283
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:86
284
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:138
285
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:88
286
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:122
287
+ msgid "Delete"
288
+ msgstr ""
289
+
290
+ #: classes/class-cartflows-ca-settings.php:781
291
+ msgid "Invalid email \"From\" address field"
292
+ msgstr ""
293
+
294
+ #: classes/class-cartflows-ca-settings.php:799
295
+ msgid "Invalid email \"Reply\" address field"
296
+ msgstr ""
297
+
298
+ #: lib/notices/class-astra-notices.php:120
299
+ msgid "WordPress Nonce not validated."
300
+ msgstr ""
301
+
302
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:85
303
+ msgid "View"
304
+ msgstr ""
305
+
306
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:90
307
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:142
308
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1799
309
+ msgid "Unsubscribe"
310
+ msgstr ""
311
+
312
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:238
313
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1943
314
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1995
315
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:176
316
+ msgid "Name"
317
+ msgstr ""
318
+
319
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:239
320
+ msgid "Email"
321
+ msgstr ""
322
+
323
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:240
324
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2017
325
+ msgid "Cart Total"
326
+ msgstr ""
327
+
328
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:241
329
+ msgid "Order Status"
330
+ msgstr ""
331
+
332
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:242
333
+ msgid "Time"
334
+ msgstr ""
335
+
336
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:210
337
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1042
338
+ msgid "This order was abandoned & subsequently recovered."
339
+ msgstr ""
340
+
341
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:235
342
+ msgid "Mail has been sent successfully!"
343
+ msgstr ""
344
+
345
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:237
346
+ msgid "Mail sending failed!"
347
+ msgstr ""
348
+
349
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:276
350
+ msgid "Every Fifteen Minutes"
351
+ msgstr ""
352
+
353
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:350
354
+ msgid "You have successfully unsubscribed from our email list."
355
+ msgstr ""
356
+
357
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:350
358
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:167
359
+ msgid "Unsubscribed"
360
+ msgstr ""
361
+
362
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:501
363
+ msgid ""
364
+ "This checkout page is generated by WooCommerce Cart Abandonment Recovery "
365
+ "plugin from test mail."
366
+ msgstr ""
367
+
368
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:544
369
+ msgid "You won't receive further emails from us, thank you!"
370
+ msgstr ""
371
+
372
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1200
373
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1201
374
+ msgid "Cart Abandonment"
375
+ msgstr ""
376
+
377
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1226
378
+ msgid "Items deleted: %d"
379
+ msgstr ""
380
+
381
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1243
382
+ msgid "User(s) unsubscribed successfully!"
383
+ msgstr ""
384
+
385
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1304
386
+ msgid ""
387
+ "Do you really want to delete the used and expired coupons created by Cart "
388
+ "Abandonment Plugin?"
389
+ msgstr ""
390
+
391
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1305
392
+ msgid "Do you really want to export orders?"
393
+ msgstr ""
394
+
395
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1309
396
+ msgid "No such order is found."
397
+ msgstr ""
398
+
399
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1328
400
+ msgid "View Report"
401
+ msgstr ""
402
+
403
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1466
404
+ msgid "Report"
405
+ msgstr ""
406
+
407
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1482
408
+ msgid "Follow-Up Emails"
409
+ msgstr ""
410
+
411
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1498
412
+ msgid "Settings"
413
+ msgstr ""
414
+
415
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1760
416
+ msgid "there"
417
+ msgstr ""
418
+
419
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1935
420
+ msgid "Cart Total ( Cart Total + Shipping + Tax )"
421
+ msgstr ""
422
+
423
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1942
424
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1994
425
+ msgid "Item"
426
+ msgstr ""
427
+
428
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1944
429
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1996
430
+ msgid "Quantity"
431
+ msgstr ""
432
+
433
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1945
434
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1997
435
+ msgid "Price"
436
+ msgstr ""
437
+
438
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1946
439
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:1998
440
+ msgid "Line Subtotal"
441
+ msgstr ""
442
+
443
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2004
444
+ msgid "Discount"
445
+ msgstr ""
446
+
447
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2008
448
+ msgid "Other"
449
+ msgstr ""
450
+
451
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2013
452
+ msgid "Shipping"
453
+ msgstr ""
454
+
455
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:2165
456
+ #. translators: %1$s: Coupons Deleted, %2$s: Deleted coupons count'.
457
+ msgid "%1$s: %2$d"
458
+ msgstr ""
459
+
460
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:76
461
+ msgid "Edit"
462
+ msgstr ""
463
+
464
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:100
465
+ msgid "Clone"
466
+ msgstr ""
467
+
468
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:187
469
+ msgid "Template Name"
470
+ msgstr ""
471
+
472
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:188
473
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:86
474
+ msgid "Email Subject"
475
+ msgstr ""
476
+
477
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:189
478
+ msgid "Trigger After"
479
+ msgstr ""
480
+
481
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates-table.php:190
482
+ msgid "Activate Template"
483
+ msgstr ""
484
+
485
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:132
486
+ msgid "Admin Firstname"
487
+ msgstr ""
488
+
489
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:133
490
+ msgid "Admin Company"
491
+ msgstr ""
492
+
493
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:134
494
+ msgid "Abandoned Product Details Table"
495
+ msgstr ""
496
+
497
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:135
498
+ msgid "Abandoned Product Names"
499
+ msgstr ""
500
+
501
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:136
502
+ msgid "Cart Checkout URL"
503
+ msgstr ""
504
+
505
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:137
506
+ msgid "Coupon Code"
507
+ msgstr ""
508
+
509
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:138
510
+ msgid "Customer First Name"
511
+ msgstr ""
512
+
513
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:139
514
+ msgid "Customer Last Name"
515
+ msgstr ""
516
+
517
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:140
518
+ msgid "Customer Full Name"
519
+ msgstr ""
520
+
521
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:141
522
+ msgid "Cart Abandonment Date"
523
+ msgstr ""
524
+
525
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:142
526
+ msgid "Site URL"
527
+ msgstr ""
528
+
529
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:143
530
+ msgid "Unsubscribe Link"
531
+ msgstr ""
532
+
533
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:163
534
+ msgid "Something went wrong"
535
+ msgstr ""
536
+
537
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:170
538
+ msgid "Activated"
539
+ msgstr ""
540
+
541
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:173
542
+ msgid "Deactivated"
543
+ msgstr ""
544
+
545
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:233
546
+ msgid "The Email Template has been successfully added."
547
+ msgstr ""
548
+
549
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:243
550
+ msgid "The Email Template has been cloned successfully."
551
+ msgstr ""
552
+
553
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:253
554
+ msgid "The Email Template has been successfully deleted."
555
+ msgstr ""
556
+
557
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:262
558
+ msgid "The Email Template has been successfully updated."
559
+ msgstr ""
560
+
561
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:272
562
+ msgid "Default Email Templates has been restored successfully."
563
+ msgstr ""
564
+
565
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:439
566
+ msgid "Activate Template now?"
567
+ msgstr ""
568
+
569
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:459
570
+ msgid "Template Name:"
571
+ msgstr ""
572
+
573
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:474
574
+ msgid "Email Subject:"
575
+ msgstr ""
576
+
577
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:489
578
+ msgid "Email Body:"
579
+ msgstr ""
580
+
581
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:518
582
+ msgid "Create Coupon"
583
+ msgstr ""
584
+
585
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:531
586
+ msgid "Allows you to send new coupon only for this template."
587
+ msgstr ""
588
+
589
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:588
590
+ msgid "Coupon expiry date"
591
+ msgstr ""
592
+
593
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:618
594
+ msgid "Enter zero (0) to restrict coupon from expiring"
595
+ msgstr ""
596
+
597
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:624
598
+ msgid "Free Shipping"
599
+ msgstr ""
600
+
601
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:637
602
+ msgid ""
603
+ "Allows you to grant free shipping. A free shipping method must be enabled "
604
+ "in your shipping zone and be set to require \"a valid free shipping "
605
+ "coupon\". "
606
+ msgstr ""
607
+
608
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:644
609
+ msgid "Individual use only"
610
+ msgstr ""
611
+
612
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:658
613
+ msgid ""
614
+ "Check this box if the coupon cannot be used in conjunction with other "
615
+ "coupons."
616
+ msgstr ""
617
+
618
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:665
619
+ msgid "Auto Apply Coupon"
620
+ msgstr ""
621
+
622
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:679
623
+ msgid " Automatically add the coupon to the cart at the checkout."
624
+ msgstr ""
625
+
626
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:685
627
+ msgid "Send This Email"
628
+ msgstr ""
629
+
630
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:703
631
+ msgid "Minute(s)"
632
+ msgstr ""
633
+
634
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:718
635
+ msgid "after cart is abandoned."
636
+ msgstr ""
637
+
638
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:728
639
+ msgid "Send Test Email To:"
640
+ msgstr ""
641
+
642
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:732
643
+ msgid "Send a test email"
644
+ msgstr ""
645
+
646
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:745
647
+ msgid "Save Changes"
648
+ msgstr ""
649
+
650
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:747
651
+ msgid "Update Changes"
652
+ msgstr ""
653
+
654
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:1053
655
+ msgid "Create New Template"
656
+ msgstr ""
657
+
658
+ #: modules/cart-abandonment/class-cartflows-ca-email-templates.php:1056
659
+ msgid " Restore Default Templates"
660
+ msgstr ""
661
+
662
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:31
663
+ msgid "Back to Reports"
664
+ msgstr ""
665
+
666
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:42
667
+ msgid "Email Details:"
668
+ msgstr ""
669
+
670
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:52
671
+ msgid ""
672
+ "All new activated emails will be reschedule for this abandoned order. New "
673
+ "emails will be sent to user according to schedule time."
674
+ msgstr ""
675
+
676
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:59
677
+ msgid "Are your sure?"
678
+ msgstr ""
679
+
680
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:63
681
+ msgid "Reschedule"
682
+ msgstr ""
683
+
684
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:67
685
+ msgid "Close"
686
+ msgstr ""
687
+
688
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:72
689
+ msgid "Do you really want to reschedule emails?"
690
+ msgstr ""
691
+
692
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:72
693
+ msgid "Reschedule Emails"
694
+ msgstr ""
695
+
696
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:78
697
+ msgid " No Email Scheduled."
698
+ msgstr ""
699
+
700
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:85
701
+ msgid "Scheduled Template"
702
+ msgstr ""
703
+
704
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:87
705
+ msgid "Email Coupon"
706
+ msgstr ""
707
+
708
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:88
709
+ msgid "Email Sent"
710
+ msgstr ""
711
+
712
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:89
713
+ msgid "Scheduled At"
714
+ msgstr ""
715
+
716
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:118
717
+ msgid "The email has been unsubscribed and won't be sent further."
718
+ msgstr ""
719
+
720
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:121
721
+ msgid "Email is in the queue and will be sent at the scheduled time."
722
+ msgstr ""
723
+
724
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:126
725
+ msgid "The email has been sent."
726
+ msgstr ""
727
+
728
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:130
729
+ msgid ""
730
+ "The email has been unscheduled due to the complete order and won't be sent "
731
+ "further."
732
+ msgstr ""
733
+
734
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:163
735
+ msgid "User Address Details:"
736
+ msgstr ""
737
+
738
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:175
739
+ msgid "Billing Address"
740
+ msgstr ""
741
+
742
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:179
743
+ msgid "Email address"
744
+ msgstr ""
745
+
746
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:184
747
+ msgid "Phone"
748
+ msgstr ""
749
+
750
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:189
751
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:209
752
+ msgid "Address 1:"
753
+ msgstr ""
754
+
755
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:192
756
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:212
757
+ msgid "Address 2:"
758
+ msgstr ""
759
+
760
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:195
761
+ msgid "Country, City:"
762
+ msgstr ""
763
+
764
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:198
765
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:218
766
+ msgid "State:"
767
+ msgstr ""
768
+
769
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:202
770
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:224
771
+ msgid "Postcode:"
772
+ msgstr ""
773
+
774
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:207
775
+ msgid "Shipping Address"
776
+ msgstr ""
777
+
778
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:215
779
+ msgid "City:"
780
+ msgstr ""
781
+
782
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:221
783
+ msgid "Country:"
784
+ msgstr ""
785
+
786
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:232
787
+ msgid "Checkout Link"
788
+ msgstr ""
789
+
790
+ #: modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php:247
791
+ msgid "User Order Details:"
792
+ msgstr ""
793
+
794
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:18
795
+ msgid "Today"
796
+ msgstr ""
797
+
798
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:22
799
+ msgid "Yesterday"
800
+ msgstr ""
801
+
802
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:26
803
+ msgid "Last Week"
804
+ msgstr ""
805
+
806
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:30
807
+ msgid "Last Month"
808
+ msgstr ""
809
+
810
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:39
811
+ msgid "Custom Filter"
812
+ msgstr ""
813
+
814
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:50
815
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:112
816
+ msgid "Recoverable Orders"
817
+ msgstr ""
818
+
819
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:54
820
+ msgid "Total Recoverable Orders."
821
+ msgstr ""
822
+
823
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:59
824
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:115
825
+ msgid "Recovered Orders"
826
+ msgstr ""
827
+
828
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:61
829
+ msgid "Total Recovered Orders."
830
+ msgstr ""
831
+
832
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:66
833
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:118
834
+ msgid "Lost Orders"
835
+ msgstr ""
836
+
837
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:69
838
+ msgid "Total Lost Orders."
839
+ msgstr ""
840
+
841
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:78
842
+ msgid "Recoverable Revenue"
843
+ msgstr ""
844
+
845
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:83
846
+ msgid "Total Recoverable Revenue."
847
+ msgstr ""
848
+
849
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:88
850
+ msgid "Recovered Revenue"
851
+ msgstr ""
852
+
853
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:94
854
+ msgid "Total Recovered Revenue."
855
+ msgstr ""
856
+
857
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:99
858
+ msgid "Recovery Rate"
859
+ msgstr ""
860
+
861
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:101
862
+ msgid "Total Percentage Of Recovered Orders After Abandonment."
863
+ msgstr ""
864
+
865
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:128
866
+ msgid "Search by email"
867
+ msgstr ""
868
+
869
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:129
870
+ msgid "Search Orders"
871
+ msgstr ""
872
+
873
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php:156
874
+ msgid "No Orders Found."
875
+ msgstr ""
876
+
877
+ #: modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-tabs.php:14
878
+ msgid "WooCommerce Cart Abandonment Recovery "
879
+ msgstr ""
880
+
881
+ #. Plugin Name of the plugin/theme
882
+ msgid "WooCommerce Cart Abandonment Recovery"
883
+ msgstr ""
884
+
885
+ #. Author URI of the plugin/theme
886
+ msgid "https://cartflows.com/"
887
+ msgstr ""
888
+
889
+ #. Description of the plugin/theme
890
+ msgid ""
891
+ "Recover your lost revenue. Capture email address of users on the checkout "
892
+ "page and send follow up emails if they don't complete the purchase."
893
+ msgstr ""
894
+
895
+ #. Author of the plugin/theme
896
+ msgid "CartFlows Inc"
897
  msgstr ""
lib/notices/class-astra-notices.php CHANGED
@@ -1,360 +1,360 @@
1
- <?php
2
- /**
3
- * Astra Sites Notices
4
- *
5
- * Closing notice on click on `astra-notice-close` class.
6
- *
7
- * If notice has the data attribute `data-repeat-notice-after="%2$s"` then notice close for that SPECIFIC TIME.
8
- * If notice has NO data attribute `data-repeat-notice-after="%2$s"` then notice close for the CURRENT USER FOREVER.
9
- *
10
- * > Create custom close notice link in the notice markup. E.g.
11
- * `<a href="#" data-repeat-notice-after="<?php echo MONTH_IN_SECONDS; ?>" class="astra-notice-close">`
12
- * It close the notice for 30 days.
13
- *
14
- * @package Astra Sites
15
- * @since 1.4.0
16
- */
17
-
18
- if ( ! defined( 'ABSPATH' ) ) {
19
- exit; // Exit if accessed directly.
20
- }
21
-
22
- if ( ! class_exists( 'Astra_Notices' ) ) :
23
-
24
- /**
25
- * Astra_Notices
26
- *
27
- * @since 1.4.0
28
- */
29
- class Astra_Notices {
30
-
31
- /**
32
- * Notices
33
- *
34
- * @access private
35
- * @var array Notices.
36
- * @since 1.4.0
37
- */
38
- private static $version = '1.1.5';
39
-
40
- /**
41
- * Notices
42
- *
43
- * @access private
44
- * @var array Notices.
45
- * @since 1.4.0
46
- */
47
- private static $notices = array();
48
-
49
- /**
50
- * Instance
51
- *
52
- * @access private
53
- * @var object Class object.
54
- * @since 1.4.0
55
- */
56
- private static $instance;
57
-
58
- /**
59
- * Initiator
60
- *
61
- * @since 1.4.0
62
- * @return object initialized object of class.
63
- */
64
- public static function get_instance() {
65
- if ( ! isset( self::$instance ) ) {
66
- self::$instance = new self();
67
- }
68
- return self::$instance;
69
- }
70
-
71
- /**
72
- * Constructor
73
- *
74
- * @since 1.4.0
75
- */
76
- public function __construct() {
77
- add_action( 'admin_notices', array( $this, 'show_notices' ), 30 );
78
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
79
- add_action( 'wp_ajax_astra-notice-dismiss', array( $this, 'dismiss_notice' ) );
80
- add_filter( 'wp_kses_allowed_html', array( $this, 'add_data_attributes' ), 10, 2 );
81
- }
82
-
83
- /**
84
- * Filters and Returns a list of allowed tags and attributes for a given context.
85
- *
86
- * @param Array $allowedposttags Array of allowed tags.
87
- * @param String $context Context type (explicit).
88
- * @since 1.4.0
89
- * @return Array
90
- */
91
- public function add_data_attributes( $allowedposttags, $context ) {
92
- $allowedposttags['a']['data-repeat-notice-after'] = true;
93
-
94
- return $allowedposttags;
95
- }
96
-
97
- /**
98
- * Add Notice.
99
- *
100
- * @since 1.4.0
101
- * @param array $args Notice arguments.
102
- * @return void
103
- */
104
- public static function add_notice( $args = array() ) {
105
- self::$notices[] = $args;
106
- }
107
-
108
- /**
109
- * Dismiss Notice.
110
- *
111
- * @since 1.4.0
112
- * @return void
113
- */
114
- public function dismiss_notice() {
115
- $notice_id = ( isset( $_POST['notice_id'] ) ) ? sanitize_key( $_POST['notice_id'] ) : '';
116
- $repeat_notice_after = ( isset( $_POST['repeat_notice_after'] ) ) ? absint( $_POST['repeat_notice_after'] ) : '';
117
- $nonce = ( isset( $_POST['nonce'] ) ) ? sanitize_key( $_POST['nonce'] ) : '';
118
-
119
- if ( false === wp_verify_nonce( $nonce, 'astra-notices' ) ) {
120
- wp_send_json_error( esc_html_e( 'WordPress Nonce not validated.', 'woo-cart-abandonment-recovery' ) );
121
- }
122
-
123
- // Valid inputs?
124
- if ( ! empty( $notice_id ) ) {
125
-
126
- if ( ! empty( $repeat_notice_after ) ) {
127
- set_transient( $notice_id, true, $repeat_notice_after );
128
- } else {
129
- update_user_meta( get_current_user_id(), $notice_id, 'notice-dismissed' );
130
- }
131
-
132
- wp_send_json_success();
133
- }
134
-
135
- wp_send_json_error();
136
- }
137
-
138
- /**
139
- * Enqueue Scripts.
140
- *
141
- * @since 1.4.0
142
- * @return void
143
- */
144
- public function enqueue_scripts() {
145
- wp_register_script( 'astra-notices', self::_get_uri() . 'notices.js', array( 'jquery' ), self::$version, true );
146
- wp_localize_script(
147
- 'astra-notices',
148
- 'astraNotices',
149
- array(
150
- '_notice_nonce' => wp_create_nonce( 'astra-notices' ),
151
- )
152
- );
153
- }
154
-
155
- /**
156
- * Rating priority sort
157
- *
158
- * @since 1.5.2
159
- * @param array $array1 array one.
160
- * @param array $array2 array two.
161
- * @return array
162
- */
163
- public function sort_notices( $array1, $array2 ) {
164
- if ( ! isset( $array1['priority'] ) ) {
165
- $array1['priority'] = 10;
166
- }
167
- if ( ! isset( $array2['priority'] ) ) {
168
- $array2['priority'] = 10;
169
- }
170
-
171
- return $array1['priority'] - $array2['priority'];
172
- }
173
-
174
- /**
175
- * Notice Types
176
- *
177
- * @since 1.4.0
178
- * @return void
179
- */
180
- public function show_notices() {
181
-
182
- $defaults = array(
183
- 'id' => '', // Optional, Notice ID. If empty it set `astra-notices-id-<$array-index>`.
184
- 'type' => 'info', // Optional, Notice type. Default `info`. Expected [info, warning, notice, error].
185
- 'message' => '', // Optional, Message.
186
- 'show_if' => true, // Optional, Show notice on custom condition. E.g. 'show_if' => if( is_admin() ) ? true, false, .
187
- 'repeat-notice-after' => '', // Optional, Dismiss-able notice time. It'll auto show after given time.
188
- 'display-notice-after' => false, // Optional, Dismiss-able notice time. It'll auto show after given time.
189
- 'class' => '', // Optional, Additional notice wrapper class.
190
- 'priority' => 10, // Priority of the notice.
191
- 'display-with-other-notices' => true, // Should the notice be displayed if other notices are being displayed from Astra_Notices.
192
- 'is_dismissible' => true,
193
- );
194
-
195
- // Count for the notices that are rendered.
196
- $notices_displayed = 0;
197
-
198
- // sort the array with priority.
199
- usort( self::$notices, array( $this, 'sort_notices' ) );
200
-
201
- foreach ( self::$notices as $key => $notice ) {
202
-
203
- $notice = wp_parse_args( $notice, $defaults );
204
-
205
- $notice['id'] = self::get_notice_id( $notice, $key );
206
-
207
- $notice['classes'] = self::get_wrap_classes( $notice );
208
-
209
- // Notices visible after transient expire.
210
- if ( isset( $notice['show_if'] ) && true === $notice['show_if'] ) {
211
-
212
- // don't display the notice if it is not supposed to be displayed with other notices.
213
- if ( 0 !== $notices_displayed && false === $notice['display-with-other-notices'] ) {
214
- continue;
215
- }
216
-
217
- if ( self::is_expired( $notice ) ) {
218
-
219
- self::markup( $notice );
220
- ++$notices_displayed;
221
- }
222
- }
223
- }
224
-
225
- }
226
-
227
- /**
228
- * Markup Notice.
229
- *
230
- * @since 1.4.0
231
- * @param array $notice Notice markup.
232
- * @return void
233
- */
234
- public static function markup( $notice = array() ) {
235
-
236
- wp_enqueue_script( 'astra-notices' );
237
-
238
- do_action( 'astra_notice_before_markup' );
239
-
240
- do_action( "astra_notice_before_markup_{$notice['id']}" );
241
-
242
- ?>
243
- <div id="<?php echo esc_attr( $notice['id'] ); ?>" class="<?php echo esc_attr( $notice['classes'] ); ?>" data-repeat-notice-after="<?php echo esc_attr( $notice['repeat-notice-after'] ); ?>">
244
- <div class="notice-container">
245
- <?php do_action( "astra_notice_inside_markup_{$notice['id']}" ); ?>
246
- <?php echo wp_kses_post( $notice['message'] ); ?>
247
- </div>
248
- </div>
249
- <?php
250
-
251
- do_action( "astra_notice_after_markup_{$notice['id']}" );
252
-
253
- do_action( 'astra_notice_after_markup' );
254
-
255
- }
256
-
257
- /**
258
- * Notice classes.
259
- *
260
- * @since 1.4.0
261
- *
262
- * @param array $notice Notice arguments.
263
- * @return array Notice wrapper classes.
264
- */
265
- private static function get_wrap_classes( $notice ) {
266
- $classes = array( 'astra-notice', 'notice' );
267
-
268
- if ( $notice['is_dismissible'] ) {
269
- $classes[] = 'is-dismissible';
270
- }
271
-
272
- $classes[] = $notice['class'];
273
- if ( isset( $notice['type'] ) && '' !== $notice['type'] ) {
274
- $classes[] = 'notice-' . $notice['type'];
275
- }
276
-
277
- return esc_attr( implode( ' ', $classes ) );
278
- }
279
-
280
- /**
281
- * Get Notice ID.
282
- *
283
- * @since 1.4.0
284
- *
285
- * @param array $notice Notice arguments.
286
- * @param int $key Notice array index.
287
- * @return string Notice id.
288
- */
289
- private static function get_notice_id( $notice, $key ) {
290
- if ( isset( $notice['id'] ) && ! empty( $notice['id'] ) ) {
291
- return $notice['id'];
292
- }
293
-
294
- return 'astra-notices-id-' . $key;
295
- }
296
-
297
- /**
298
- * Is notice expired?
299
- *
300
- * @since 1.4.0
301
- *
302
- * @param array $notice Notice arguments.
303
- * @return boolean
304
- */
305
- private static function is_expired( $notice ) {
306
- $transient_status = get_transient( $notice['id'] );
307
-
308
- if ( false === $transient_status ) {
309
-
310
- if ( isset( $notice['display-notice-after'] ) && false !== $notice['display-notice-after'] ) {
311
-
312
- if ( 'delayed-notice' !== get_user_meta( get_current_user_id(), $notice['id'], true ) &&
313
- 'notice-dismissed' !== get_user_meta( get_current_user_id(), $notice['id'], true ) ) {
314
- set_transient( $notice['id'], 'delayed-notice', $notice['display-notice-after'] );
315
- update_user_meta( get_current_user_id(), $notice['id'], 'delayed-notice' );
316
-
317
- return false;
318
- }
319
- }
320
-
321
- // Check the user meta status if current notice is dismissed or delay completed.
322
- $meta_status = get_user_meta( get_current_user_id(), $notice['id'], true );
323
-
324
- if ( empty( $meta_status ) || 'delayed-notice' === $meta_status ) {
325
- return true;
326
- }
327
- }
328
-
329
- return false;
330
- }
331
-
332
- /**
333
- * Get URI
334
- *
335
- * @return mixed URL.
336
- */
337
- public static function _get_uri() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
338
- $path = wp_normalize_path( dirname( __FILE__ ) );
339
- $theme_dir = wp_normalize_path( get_template_directory() );
340
- $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
341
-
342
- if ( strpos( $path, $theme_dir ) !== false ) {
343
- return trailingslashit( get_template_directory_uri() . str_replace( $theme_dir, '', $path ) );
344
- } elseif ( strpos( $path, $plugin_dir ) !== false ) {
345
- return plugin_dir_url( __FILE__ );
346
- } elseif ( strpos( $path, dirname( plugin_basename( __FILE__ ) ) ) !== false ) {
347
- return plugin_dir_url( __FILE__ );
348
- }
349
-
350
- return; // phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired
351
- }
352
-
353
- }
354
-
355
- /**
356
- * Kicking this off by calling 'get_instance()' method
357
- */
358
- Astra_Notices::get_instance();
359
-
360
- endif;
1
+ <?php
2
+ /**
3
+ * Astra Sites Notices
4
+ *
5
+ * Closing notice on click on `astra-notice-close` class.
6
+ *
7
+ * If notice has the data attribute `data-repeat-notice-after="%2$s"` then notice close for that SPECIFIC TIME.
8
+ * If notice has NO data attribute `data-repeat-notice-after="%2$s"` then notice close for the CURRENT USER FOREVER.
9
+ *
10
+ * > Create custom close notice link in the notice markup. E.g.
11
+ * `<a href="#" data-repeat-notice-after="<?php echo MONTH_IN_SECONDS; ?>" class="astra-notice-close">`
12
+ * It close the notice for 30 days.
13
+ *
14
+ * @package Astra Sites
15
+ * @since 1.4.0
16
+ */
17
+
18
+ if ( ! defined( 'ABSPATH' ) ) {
19
+ exit; // Exit if accessed directly.
20
+ }
21
+
22
+ if ( ! class_exists( 'Astra_Notices' ) ) :
23
+
24
+ /**
25
+ * Astra_Notices
26
+ *
27
+ * @since 1.4.0
28
+ */
29
+ class Astra_Notices {
30
+
31
+ /**
32
+ * Notices
33
+ *
34
+ * @access private
35
+ * @var array Notices.
36
+ * @since 1.4.0
37
+ */
38
+ private static $version = '1.1.5';
39
+
40
+ /**
41
+ * Notices
42
+ *
43
+ * @access private
44
+ * @var array Notices.
45
+ * @since 1.4.0
46
+ */
47
+ private static $notices = array();
48
+
49
+ /**
50
+ * Instance
51
+ *
52
+ * @access private
53
+ * @var object Class object.
54
+ * @since 1.4.0
55
+ */
56
+ private static $instance;
57
+
58
+ /**
59
+ * Initiator
60
+ *
61
+ * @since 1.4.0
62
+ * @return object initialized object of class.
63
+ */
64
+ public static function get_instance() {
65
+ if ( ! isset( self::$instance ) ) {
66
+ self::$instance = new self();
67
+ }
68
+ return self::$instance;
69
+ }
70
+
71
+ /**
72
+ * Constructor
73
+ *
74
+ * @since 1.4.0
75
+ */
76
+ public function __construct() {
77
+ add_action( 'admin_notices', array( $this, 'show_notices' ), 30 );
78
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
79
+ add_action( 'wp_ajax_astra-notice-dismiss', array( $this, 'dismiss_notice' ) );
80
+ add_filter( 'wp_kses_allowed_html', array( $this, 'add_data_attributes' ), 10, 2 );
81
+ }
82
+
83
+ /**
84
+ * Filters and Returns a list of allowed tags and attributes for a given context.
85
+ *
86
+ * @param Array $allowedposttags Array of allowed tags.
87
+ * @param String $context Context type (explicit).
88
+ * @since 1.4.0
89
+ * @return Array
90
+ */
91
+ public function add_data_attributes( $allowedposttags, $context ) {
92
+ $allowedposttags['a']['data-repeat-notice-after'] = true;
93
+
94
+ return $allowedposttags;
95
+ }
96
+
97
+ /**
98
+ * Add Notice.
99
+ *
100
+ * @since 1.4.0
101
+ * @param array $args Notice arguments.
102
+ * @return void
103
+ */
104
+ public static function add_notice( $args = array() ) {
105
+ self::$notices[] = $args;
106
+ }
107
+
108
+ /**
109
+ * Dismiss Notice.
110
+ *
111
+ * @since 1.4.0
112
+ * @return void
113
+ */
114
+ public function dismiss_notice() {
115
+ $notice_id = ( isset( $_POST['notice_id'] ) ) ? sanitize_key( $_POST['notice_id'] ) : '';
116
+ $repeat_notice_after = ( isset( $_POST['repeat_notice_after'] ) ) ? absint( $_POST['repeat_notice_after'] ) : '';
117
+ $nonce = ( isset( $_POST['nonce'] ) ) ? sanitize_key( $_POST['nonce'] ) : '';
118
+
119
+ if ( false === wp_verify_nonce( $nonce, 'astra-notices' ) ) {
120
+ wp_send_json_error( esc_html_e( 'WordPress Nonce not validated.', 'woo-cart-abandonment-recovery' ) );
121
+ }
122
+
123
+ // Valid inputs?
124
+ if ( ! empty( $notice_id ) ) {
125
+
126
+ if ( ! empty( $repeat_notice_after ) ) {
127
+ set_transient( $notice_id, true, $repeat_notice_after );
128
+ } else {
129
+ update_user_meta( get_current_user_id(), $notice_id, 'notice-dismissed' );
130
+ }
131
+
132
+ wp_send_json_success();
133
+ }
134
+
135
+ wp_send_json_error();
136
+ }
137
+
138
+ /**
139
+ * Enqueue Scripts.
140
+ *
141
+ * @since 1.4.0
142
+ * @return void
143
+ */
144
+ public function enqueue_scripts() {
145
+ wp_register_script( 'astra-notices', self::_get_uri() . 'notices.js', array( 'jquery' ), self::$version, true );
146
+ wp_localize_script(
147
+ 'astra-notices',
148
+ 'astraNotices',
149
+ array(
150
+ '_notice_nonce' => wp_create_nonce( 'astra-notices' ),
151
+ )
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Rating priority sort
157
+ *
158
+ * @since 1.5.2
159
+ * @param array $array1 array one.
160
+ * @param array $array2 array two.
161
+ * @return array
162
+ */
163
+ public function sort_notices( $array1, $array2 ) {
164
+ if ( ! isset( $array1['priority'] ) ) {
165
+ $array1['priority'] = 10;
166
+ }
167
+ if ( ! isset( $array2['priority'] ) ) {
168
+ $array2['priority'] = 10;
169
+ }
170
+
171
+ return $array1['priority'] - $array2['priority'];
172
+ }
173
+
174
+ /**
175
+ * Notice Types
176
+ *
177
+ * @since 1.4.0
178
+ * @return void
179
+ */
180
+ public function show_notices() {
181
+
182
+ $defaults = array(
183
+ 'id' => '', // Optional, Notice ID. If empty it set `astra-notices-id-<$array-index>`.
184
+ 'type' => 'info', // Optional, Notice type. Default `info`. Expected [info, warning, notice, error].
185
+ 'message' => '', // Optional, Message.
186
+ 'show_if' => true, // Optional, Show notice on custom condition. E.g. 'show_if' => if( is_admin() ) ? true, false, .
187
+ 'repeat-notice-after' => '', // Optional, Dismiss-able notice time. It'll auto show after given time.
188
+ 'display-notice-after' => false, // Optional, Dismiss-able notice time. It'll auto show after given time.
189
+ 'class' => '', // Optional, Additional notice wrapper class.
190
+ 'priority' => 10, // Priority of the notice.
191
+ 'display-with-other-notices' => true, // Should the notice be displayed if other notices are being displayed from Astra_Notices.
192
+ 'is_dismissible' => true,
193
+ );
194
+
195
+ // Count for the notices that are rendered.
196
+ $notices_displayed = 0;
197
+
198
+ // sort the array with priority.
199
+ usort( self::$notices, array( $this, 'sort_notices' ) );
200
+
201
+ foreach ( self::$notices as $key => $notice ) {
202
+
203
+ $notice = wp_parse_args( $notice, $defaults );
204
+
205
+ $notice['id'] = self::get_notice_id( $notice, $key );
206
+
207
+ $notice['classes'] = self::get_wrap_classes( $notice );
208
+
209
+ // Notices visible after transient expire.
210
+ if ( isset( $notice['show_if'] ) && true === $notice['show_if'] ) {
211
+
212
+ // don't display the notice if it is not supposed to be displayed with other notices.
213
+ if ( 0 !== $notices_displayed && false === $notice['display-with-other-notices'] ) {
214
+ continue;
215
+ }
216
+
217
+ if ( self::is_expired( $notice ) ) {
218
+
219
+ self::markup( $notice );
220
+ ++$notices_displayed;
221
+ }
222
+ }
223
+ }
224
+
225
+ }
226
+
227
+ /**
228
+ * Markup Notice.
229
+ *
230
+ * @since 1.4.0
231
+ * @param array $notice Notice markup.
232
+ * @return void
233
+ */
234
+ public static function markup( $notice = array() ) {
235
+
236
+ wp_enqueue_script( 'astra-notices' );
237
+
238
+ do_action( 'astra_notice_before_markup' );
239
+
240
+ do_action( "astra_notice_before_markup_{$notice['id']}" );
241
+
242
+ ?>
243
+ <div id="<?php echo esc_attr( $notice['id'] ); ?>" class="<?php echo esc_attr( $notice['classes'] ); ?>" data-repeat-notice-after="<?php echo esc_attr( $notice['repeat-notice-after'] ); ?>">
244
+ <div class="notice-container">
245
+ <?php do_action( "astra_notice_inside_markup_{$notice['id']}" ); ?>
246
+ <?php echo wp_kses_post( $notice['message'] ); ?>
247
+ </div>
248
+ </div>
249
+ <?php
250
+
251
+ do_action( "astra_notice_after_markup_{$notice['id']}" );
252
+
253
+ do_action( 'astra_notice_after_markup' );
254
+
255
+ }
256
+
257
+ /**
258
+ * Notice classes.
259
+ *
260
+ * @since 1.4.0
261
+ *
262
+ * @param array $notice Notice arguments.
263
+ * @return array Notice wrapper classes.
264
+ */
265
+ private static function get_wrap_classes( $notice ) {
266
+ $classes = array( 'astra-notice', 'notice' );
267
+
268
+ if ( $notice['is_dismissible'] ) {
269
+ $classes[] = 'is-dismissible';
270
+ }
271
+
272
+ $classes[] = $notice['class'];
273
+ if ( isset( $notice['type'] ) && '' !== $notice['type'] ) {
274
+ $classes[] = 'notice-' . $notice['type'];
275
+ }
276
+
277
+ return esc_attr( implode( ' ', $classes ) );
278
+ }
279
+
280
+ /**
281
+ * Get Notice ID.
282
+ *
283
+ * @since 1.4.0
284
+ *
285
+ * @param array $notice Notice arguments.
286
+ * @param int $key Notice array index.
287
+ * @return string Notice id.
288
+ */
289
+ private static function get_notice_id( $notice, $key ) {
290
+ if ( isset( $notice['id'] ) && ! empty( $notice['id'] ) ) {
291
+ return $notice['id'];
292
+ }
293
+
294
+ return 'astra-notices-id-' . $key;
295
+ }
296
+
297
+ /**
298
+ * Is notice expired?
299
+ *
300
+ * @since 1.4.0
301
+ *
302
+ * @param array $notice Notice arguments.
303
+ * @return boolean
304
+ */
305
+ private static function is_expired( $notice ) {
306
+ $transient_status = get_transient( $notice['id'] );
307
+
308
+ if ( false === $transient_status ) {
309
+
310
+ if ( isset( $notice['display-notice-after'] ) && false !== $notice['display-notice-after'] ) {
311
+
312
+ if ( 'delayed-notice' !== get_user_meta( get_current_user_id(), $notice['id'], true ) &&
313
+ 'notice-dismissed' !== get_user_meta( get_current_user_id(), $notice['id'], true ) ) {
314
+ set_transient( $notice['id'], 'delayed-notice', $notice['display-notice-after'] );
315
+ update_user_meta( get_current_user_id(), $notice['id'], 'delayed-notice' );
316
+
317
+ return false;
318
+ }
319
+ }
320
+
321
+ // Check the user meta status if current notice is dismissed or delay completed.
322
+ $meta_status = get_user_meta( get_current_user_id(), $notice['id'], true );
323
+
324
+ if ( empty( $meta_status ) || 'delayed-notice' === $meta_status ) {
325
+ return true;
326
+ }
327
+ }
328
+
329
+ return false;
330
+ }
331
+
332
+ /**
333
+ * Get URI
334
+ *
335
+ * @return mixed URL.
336
+ */
337
+ public static function _get_uri() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
338
+ $path = wp_normalize_path( dirname( __FILE__ ) );
339
+ $theme_dir = wp_normalize_path( get_template_directory() );
340
+ $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
341
+
342
+ if ( strpos( $path, $theme_dir ) !== false ) {
343
+ return trailingslashit( get_template_directory_uri() . str_replace( $theme_dir, '', $path ) );
344
+ } elseif ( strpos( $path, $plugin_dir ) !== false ) {
345
+ return plugin_dir_url( __FILE__ );
346
+ } elseif ( strpos( $path, dirname( plugin_basename( __FILE__ ) ) ) !== false ) {
347
+ return plugin_dir_url( __FILE__ );
348
+ }
349
+
350
+ return; // phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired
351
+ }
352
+
353
+ }
354
+
355
+ /**
356
+ * Kicking this off by calling 'get_instance()' method
357
+ */
358
+ Astra_Notices::get_instance();
359
+
360
+ endif;
modules/cart-abandonment/assets/js/cart-abandonment-tracking.js CHANGED
@@ -1,167 +1,167 @@
1
- (function ($) {
2
-
3
- var timer;
4
- var wcf_cart_abandonment = {
5
-
6
- init: function () {
7
-
8
- if ( CartFlowsProCAVars._show_gdpr_message && ! $("#wcf_cf_gdpr_message_block").length ) {
9
- $("#billing_email").after("<span id='wcf_cf_gdpr_message_block'> <span style='font-size: xx-small'> "+ CartFlowsProCAVars._gdpr_message +" <a style='cursor: pointer' id='wcf_ca_gdpr_no_thanks'> "+ CartFlowsProCAVars._gdpr_nothanks_msg +" </a></span></span>");
10
- }
11
-
12
- $(document).on(
13
- 'keyup keypress change',
14
- '#billing_email, #billing_phone, input.input-text, textarea.input-text, select',
15
- this._getCheckoutData
16
- );
17
-
18
- $("#wcf_ca_gdpr_no_thanks").click( function () {
19
- wcf_cart_abandonment._set_cookie();
20
- } );
21
-
22
- $( document.body ).on( 'updated_checkout', function(){
23
- wcf_cart_abandonment._getCheckoutData();
24
- });
25
-
26
- $(document).on('ready', function(e) {
27
- setTimeout(function() {
28
- wcf_cart_abandonment._getCheckoutData();
29
- }, 800);
30
- });
31
- },
32
-
33
- _set_cookie: function() {
34
-
35
-
36
- var data = {
37
- 'wcf_ca_skip_track_data': true,
38
- 'action': 'cartflows_skip_cart_tracking_gdpr',
39
- 'security': CartFlowsProCAVars._gdpr_nonce,
40
- };
41
-
42
- jQuery.post(
43
- CartFlowsProCAVars.ajaxurl,data,
44
- function (response) {
45
-
46
- if(response.success) {
47
- $("#wcf_cf_gdpr_message_block").empty().append("<span style='font-size: xx-small'>" + CartFlowsProCAVars._gdpr_after_no_thanks_msg + "</span>").delay(5000).fadeOut();
48
- }
49
-
50
- }
51
- );
52
-
53
- },
54
-
55
- _validate_email: function (value) {
56
- var valid = true;
57
- if (value.indexOf('@') == -1) {
58
- valid = false;
59
- } else {
60
- var parts = value.split('@');
61
- var domain = parts[1];
62
- if (domain.indexOf('.') == -1) {
63
- valid = false;
64
- } else {
65
- var domainParts = domain.split('.');
66
- var ext = domainParts[1];
67
- if (ext.length > 14 || ext.length < 2) {
68
- valid = false;
69
- }
70
- }
71
- }
72
- return valid;
73
- },
74
-
75
- _getCheckoutData: function () {
76
-
77
- var wcf_phone = jQuery("#billing_phone").val();
78
- var wcf_email = jQuery("#billing_email").val();
79
-
80
- if( typeof wcf_email === 'undefined' ){
81
- return ;
82
- }
83
-
84
- var atposition = wcf_email.indexOf("@");
85
- var dotposition = wcf_email.lastIndexOf(".");
86
-
87
-
88
- if (typeof wcf_phone === 'undefined' || wcf_phone === null) { //If phone number field does not exist on the Checkout form
89
- wcf_phone = '';
90
- }
91
-
92
- clearTimeout(timer);
93
-
94
- if (!(atposition < 1 || dotposition < atposition + 2 || dotposition + 2 >= wcf_email.length) || wcf_phone.length >= 1) { //Checking if the email field is valid or phone number is longer than 1 digit
95
- //If Email or Phone valid
96
- var wcf_name = jQuery("#billing_first_name").val();
97
- var wcf_surname = jQuery("#billing_last_name").val();
98
- var wcf_phone = jQuery("#billing_phone").val();
99
- var wcf_country = jQuery("#billing_country").val();
100
- var wcf_city = jQuery("#billing_city").val();
101
-
102
- //Other fields used for "Remember user input" function
103
- var wcf_billing_company = jQuery("#billing_company").val();
104
- var wcf_billing_address_1 = jQuery("#billing_address_1").val();
105
- var wcf_billing_address_2 = jQuery("#billing_address_2").val();
106
- var wcf_billing_state = jQuery("#billing_state").val();
107
- var wcf_billing_postcode = jQuery("#billing_postcode").val();
108
- var wcf_shipping_first_name = jQuery("#shipping_first_name").val();
109
- var wcf_shipping_last_name = jQuery("#shipping_last_name").val();
110
- var wcf_shipping_company = jQuery("#shipping_company").val();
111
- var wcf_shipping_country = jQuery("#shipping_country").val();
112
- var wcf_shipping_address_1 = jQuery("#shipping_address_1").val();
113
- var wcf_shipping_address_2 = jQuery("#shipping_address_2").val();
114
- var wcf_shipping_city = jQuery("#shipping_city").val();
115
- var wcf_shipping_state = jQuery("#shipping_state").val();
116
- var wcf_shipping_postcode = jQuery("#shipping_postcode").val();
117
- var wcf_order_comments = jQuery("#order_comments").val();
118
-
119
- var data = {
120
- action: "cartflows_save_cart_abandonment_data",
121
- wcf_email: wcf_email,
122
- wcf_name: wcf_name,
123
- wcf_surname: wcf_surname,
124
- wcf_phone: wcf_phone,
125
- wcf_country: wcf_country,
126
- wcf_city: wcf_city,
127
- wcf_billing_company: wcf_billing_company,
128
- wcf_billing_address_1: wcf_billing_address_1,
129
- wcf_billing_address_2: wcf_billing_address_2,
130
- wcf_billing_state: wcf_billing_state,
131
- wcf_billing_postcode: wcf_billing_postcode,
132
- wcf_shipping_first_name: wcf_shipping_first_name,
133
- wcf_shipping_last_name: wcf_shipping_last_name,
134
- wcf_shipping_company: wcf_shipping_company,
135
- wcf_shipping_country: wcf_shipping_country,
136
- wcf_shipping_address_1: wcf_shipping_address_1,
137
- wcf_shipping_address_2: wcf_shipping_address_2,
138
- wcf_shipping_city: wcf_shipping_city,
139
- wcf_shipping_state: wcf_shipping_state,
140
- wcf_shipping_postcode: wcf_shipping_postcode,
141
- wcf_order_comments: wcf_order_comments,
142
- security: CartFlowsProCAVars._nonce,
143
- wcf_post_id: CartFlowsProCAVars._post_id,
144
- }
145
-
146
- timer = setTimeout(
147
- function () {
148
- if (wcf_cart_abandonment._validate_email(data.wcf_email)) {
149
- jQuery.post(
150
- CartFlowsProCAVars.ajaxurl, data, //Ajaxurl coming from localized script and contains the link to wp-admin/admin-ajax.php file that handles AJAX requests on Wordpress
151
- function (response) {
152
- // success response
153
- }
154
- );
155
- }
156
- }, 500
157
- );
158
- } else {
159
- //console.log("Not a valid e-mail or phone address");
160
- }
161
- }
162
-
163
- }
164
-
165
- wcf_cart_abandonment.init();
166
-
167
  })(jQuery);
1
+ (function ($) {
2
+
3
+ var timer;
4
+ var wcf_cart_abandonment = {
5
+
6
+ init: function () {
7
+
8
+ if ( CartFlowsProCAVars._show_gdpr_message && ! $("#wcf_cf_gdpr_message_block").length ) {
9
+ $("#billing_email").after("<span id='wcf_cf_gdpr_message_block'> <span style='font-size: xx-small'> "+ CartFlowsProCAVars._gdpr_message +" <a style='cursor: pointer' id='wcf_ca_gdpr_no_thanks'> "+ CartFlowsProCAVars._gdpr_nothanks_msg +" </a></span></span>");
10
+ }
11
+
12
+ $(document).on(
13
+ 'keyup keypress change',
14
+ '#billing_email, #billing_phone, input.input-text, textarea.input-text, select',
15
+ this._getCheckoutData
16
+ );
17
+
18
+ $("#wcf_ca_gdpr_no_thanks").click( function () {
19
+ wcf_cart_abandonment._set_cookie();
20
+ } );
21
+
22
+ $( document.body ).on( 'updated_checkout', function(){
23
+ wcf_cart_abandonment._getCheckoutData();
24
+ });
25
+
26
+ $(document).on('ready', function(e) {
27
+ setTimeout(function() {
28
+ wcf_cart_abandonment._getCheckoutData();
29
+ }, 800);
30
+ });
31
+ },
32
+
33
+ _set_cookie: function() {
34
+
35
+
36
+ var data = {
37
+ 'wcf_ca_skip_track_data': true,
38
+ 'action': 'cartflows_skip_cart_tracking_gdpr',
39
+ 'security': CartFlowsProCAVars._gdpr_nonce,
40
+ };
41
+
42
+ jQuery.post(
43
+ CartFlowsProCAVars.ajaxurl,data,
44
+ function (response) {
45
+
46
+ if(response.success) {
47
+ $("#wcf_cf_gdpr_message_block").empty().append("<span style='font-size: xx-small'>" + CartFlowsProCAVars._gdpr_after_no_thanks_msg + "</span>").delay(5000).fadeOut();
48
+ }
49
+
50
+ }
51
+ );
52
+
53
+ },
54
+
55
+ _validate_email: function (value) {
56
+ var valid = true;
57
+ if (value.indexOf('@') == -1) {
58
+ valid = false;
59
+ } else {
60
+ var parts = value.split('@');
61
+ var domain = parts[1];
62
+ if (domain.indexOf('.') == -1) {
63
+ valid = false;
64
+ } else {
65
+ var domainParts = domain.split('.');
66
+ var ext = domainParts[1];
67
+ if (ext.length > 14 || ext.length < 2) {
68
+ valid = false;
69
+ }
70
+ }
71
+ }
72
+ return valid;
73
+ },
74
+
75
+ _getCheckoutData: function () {
76
+
77
+ var wcf_phone = jQuery("#billing_phone").val();
78
+ var wcf_email = jQuery("#billing_email").val();
79
+
80
+ if( typeof wcf_email === 'undefined' ){
81
+ return ;
82
+ }
83
+
84
+ var atposition = wcf_email.indexOf("@");
85
+ var dotposition = wcf_email.lastIndexOf(".");
86
+
87
+
88
+ if (typeof wcf_phone === 'undefined' || wcf_phone === null) { //If phone number field does not exist on the Checkout form
89
+ wcf_phone = '';
90
+ }
91
+
92
+ clearTimeout(timer);
93
+
94
+ if (!(atposition < 1 || dotposition < atposition + 2 || dotposition + 2 >= wcf_email.length) || wcf_phone.length >= 1) { //Checking if the email field is valid or phone number is longer than 1 digit
95
+ //If Email or Phone valid
96
+ var wcf_name = jQuery("#billing_first_name").val();
97
+ var wcf_surname = jQuery("#billing_last_name").val();
98
+ var wcf_phone = jQuery("#billing_phone").val();
99
+ var wcf_country = jQuery("#billing_country").val();
100
+ var wcf_city = jQuery("#billing_city").val();
101
+
102
+ //Other fields used for "Remember user input" function
103
+ var wcf_billing_company = jQuery("#billing_company").val();
104
+ var wcf_billing_address_1 = jQuery("#billing_address_1").val();
105
+ var wcf_billing_address_2 = jQuery("#billing_address_2").val();
106
+ var wcf_billing_state = jQuery("#billing_state").val();
107
+ var wcf_billing_postcode = jQuery("#billing_postcode").val();
108
+ var wcf_shipping_first_name = jQuery("#shipping_first_name").val();
109
+ var wcf_shipping_last_name = jQuery("#shipping_last_name").val();
110
+ var wcf_shipping_company = jQuery("#shipping_company").val();
111
+ var wcf_shipping_country = jQuery("#shipping_country").val();
112
+ var wcf_shipping_address_1 = jQuery("#shipping_address_1").val();
113
+ var wcf_shipping_address_2 = jQuery("#shipping_address_2").val();
114
+ var wcf_shipping_city = jQuery("#shipping_city").val();
115
+ var wcf_shipping_state = jQuery("#shipping_state").val();
116
+ var wcf_shipping_postcode = jQuery("#shipping_postcode").val();
117
+ var wcf_order_comments = jQuery("#order_comments").val();
118
+
119
+ var data = {
120
+ action: "cartflows_save_cart_abandonment_data",
121
+ wcf_email: wcf_email,
122
+ wcf_name: wcf_name,
123
+ wcf_surname: wcf_surname,
124
+ wcf_phone: wcf_phone,
125
+ wcf_country: wcf_country,
126
+ wcf_city: wcf_city,
127
+ wcf_billing_company: wcf_billing_company,
128
+ wcf_billing_address_1: wcf_billing_address_1,
129
+ wcf_billing_address_2: wcf_billing_address_2,
130
+ wcf_billing_state: wcf_billing_state,
131
+ wcf_billing_postcode: wcf_billing_postcode,
132
+ wcf_shipping_first_name: wcf_shipping_first_name,
133
+ wcf_shipping_last_name: wcf_shipping_last_name,
134
+ wcf_shipping_company: wcf_shipping_company,
135
+ wcf_shipping_country: wcf_shipping_country,
136
+ wcf_shipping_address_1: wcf_shipping_address_1,
137
+ wcf_shipping_address_2: wcf_shipping_address_2,
138
+ wcf_shipping_city: wcf_shipping_city,
139
+ wcf_shipping_state: wcf_shipping_state,
140
+ wcf_shipping_postcode: wcf_shipping_postcode,
141
+ wcf_order_comments: wcf_order_comments,
142
+ security: CartFlowsProCAVars._nonce,
143
+ wcf_post_id: CartFlowsProCAVars._post_id,
144
+ }
145
+
146
+ timer = setTimeout(
147
+ function () {
148
+ if (wcf_cart_abandonment._validate_email(data.wcf_email)) {
149
+ jQuery.post(
150
+ CartFlowsProCAVars.ajaxurl, data, //Ajaxurl coming from localized script and contains the link to wp-admin/admin-ajax.php file that handles AJAX requests on Wordpress
151
+ function (response) {
152
+ // success response
153
+ }
154
+ );
155
+ }
156
+ }, 500
157
+ );
158
+ } else {
159
+ //console.log("Not a valid e-mail or phone address");
160
+ }
161
+ }
162
+
163
+ }
164
+
165
+ wcf_cart_abandonment.init();
166
+
167
  })(jQuery);
modules/cart-abandonment/class-cartflows-ca-cart-abandonment-db.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  /**
9
  * Cart Abandonment DB class.
10
  */
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  /**
13
  * Cart Abandonment DB class.
14
  */
modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  if ( ! class_exists( 'WP_List_Table' ) ) {
9
  include_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
10
  }
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  if ( ! class_exists( 'WP_List_Table' ) ) {
13
  include_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
14
  }
modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php CHANGED
@@ -1,2266 +1,2276 @@
1
- <?php
2
- /**
3
- * Cart Abandonment
4
- *
5
- * @package Woocommerce-Cart-Abandonment-Recovery
6
- */
7
-
8
- /**
9
- * Cart abandonment tracking class.
10
- */
11
- class Cartflows_Ca_Cart_Abandonment {
12
-
13
-
14
-
15
- /**
16
- * Member Variable
17
- *
18
- * @var object instance
19
- */
20
- private static $instance;
21
-
22
- /**
23
- * Initiator
24
- */
25
- public static function get_instance() {
26
- if ( ! isset( self::$instance ) ) {
27
- self::$instance = new self();
28
- }
29
- return self::$instance;
30
- }
31
-
32
- /**
33
- * Constructor function that initializes required actions and hooks.
34
- */
35
- public function __construct() {
36
-
37
- $this->define_cart_abandonment_constants();
38
-
39
- // Adding menu to view cart abandonment report.
40
- add_action( 'admin_menu', array( $this, 'abandoned_cart_tracking_menu' ), 999 );
41
-
42
- // Adding the styles and scripts for the cart abandonment.
43
- add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_cart_abandonment_script' ), 20 );
44
-
45
- if ( wcf_ca()->utils->is_cart_abandonment_tracking_enabled() && ! isset( $_COOKIE['wcf_ca_skip_track_data'] ) ) {
46
-
47
- // Add script to track the cart abandonment.
48
- add_action( 'woocommerce_after_checkout_form', array( $this, 'cart_abandonment_tracking_script' ) );
49
-
50
- // Store user details from the current checkout page.
51
- add_action( 'wp_ajax_cartflows_save_cart_abandonment_data', array( $this, 'save_cart_abandonment_data' ) );
52
- add_action( 'wp_ajax_nopriv_cartflows_save_cart_abandonment_data', array( $this, 'save_cart_abandonment_data' ) );
53
-
54
- // GDPR actions.
55
- add_action( 'wp_ajax_cartflows_skip_cart_tracking_gdpr', array( $this, 'skip_cart_tracking_by_gdpr' ) );
56
- add_action( 'wp_ajax_nopriv_cartflows_skip_cart_tracking_gdpr', array( $this, 'skip_cart_tracking_by_gdpr' ) );
57
-
58
- // Delete the stored cart abandonment data once order gets created.
59
- add_action( 'woocommerce_new_order', array( $this, 'delete_cart_abandonment_data' ) );
60
- add_action( 'woocommerce_thankyou', array( $this, 'delete_cart_abandonment_data' ) );
61
- add_action( 'woocommerce_order_status_changed', array( $this, 'wcf_ca_update_order_status' ), 999, 3 );
62
-
63
- // Adding filter to restore the data if recreating abandonment order.
64
- add_filter( 'wp', array( $this, 'restore_cart_abandonment_data' ), 10 );
65
- add_filter( 'wp', array( $this, 'unsubscribe_cart_abandonment_emails' ), 10 );
66
-
67
- add_action( 'wp_ajax_wcf_ca_preview_email_send', array( $this, 'send_preview_email' ) );
68
-
69
- // Delete coupons.
70
- add_action( 'wp_ajax_wcf_ca_delete_garbage_coupons', array( $this, 'delete_used_and_expired_coupons' ) );
71
-
72
- $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
73
- $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
74
- if ( WCF_CA_PAGE_NAME === $page ) {
75
- // Adding filter to add new button to add custom fields.
76
- add_filter( 'mce_buttons', array( $this, 'wcf_filter_mce_button' ) );
77
- add_filter( 'mce_external_plugins', array( $this, 'wcf_filter_mce_plugin' ), 9 );
78
- }
79
-
80
- add_filter( 'cron_schedules', array( $this, 'cartflows_ca_update_order_status_action' ) ); //phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
81
-
82
- // Schedule an action if it's not already scheduled.
83
- if ( ! wp_next_scheduled( 'cartflows_ca_update_order_status_action' ) ) {
84
- wp_schedule_event( time(), 'every_fifteen_minutes', 'cartflows_ca_update_order_status_action' );
85
- }
86
-
87
- // Adding notice to checkout page to inform about test email checkout page.
88
- add_action( 'woocommerce_before_checkout_form', array( $this, 'test_email_checkout_page' ), 9 );
89
-
90
- add_action( 'cartflows_ca_update_order_status_action', array( $this, 'update_order_status' ) );
91
-
92
- }
93
-
94
- }
95
-
96
- /**
97
- * This function will send the email to the store admin when any abandoned cart email recovered.
98
- *
99
- * @param int | string $order_id Order id.
100
- * @param string $wcar_old_status Old status of the order.
101
- * @param string $wcar_new_status New status of the order.
102
- */
103
- public function wcar_send_successful_recovery_email_to_admin( $order_id, $wcar_old_status, $wcar_new_status ) {
104
- global $woocommerce;
105
-
106
- if ( in_array( $wcar_old_status, array( 'pending', 'failed', 'on-hold' ), true ) &&
107
- in_array( $wcar_new_status, array( 'processing', 'completed' ), true )
108
- ) {
109
- $user_id = get_current_user_id();
110
- $order = wc_get_order( $order_id );
111
- if ( version_compare( $woocommerce->version, '3.0.0', '>=' ) ) {
112
- $user_id = $order->get_user_id();
113
- } else {
114
- $user_id = $order->user_id;
115
- }
116
-
117
- $is_recoverd = $this->wcar_check_order_is_recovered( $order_id );
118
-
119
- if ( $is_recoverd ) {
120
- $order = wc_get_order( $order_id );
121
- $email_heading = __( 'New Customer Order - Recovered Order ID: ' . $order_id . '', 'woo-cart-abandonment-recovery' ); //phpcs:ignore
122
- $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
123
- $email_subject = __( 'New Customer Order - Recovered Order ID: ' . $order_id . '', 'woo-cart-abandonment-recovery' ); //phpcs:ignore
124
- $user_email = get_option( 'admin_email' );
125
- $headers[] = 'From: Admin <' . $user_email . '>';
126
- $headers[] = 'Content-Type: text/html';
127
-
128
- ob_start();
129
- wc_get_template(
130
- 'emails/admin-new-order.php',
131
- array(
132
- 'order' => $order,
133
- 'email_heading' => $email_heading,
134
- 'sent_to_admin' => false,
135
- 'plain_text' => false,
136
- 'email' => true,
137
- )
138
- );
139
-
140
- $email_body = ob_get_clean();
141
- wc_mail( $user_email, $email_subject, $email_body, $headers );
142
- }
143
- }
144
- }
145
-
146
- /**
147
- * This function will check if cart is recoverd from woocommerce and WCAR.
148
- *
149
- * @param int $order_id order id.
150
- */
151
- public function wcar_check_order_is_recovered( $order_id ) {
152
-
153
- global $wpdb;
154
- $order = wc_get_order( $order_id );
155
- $email = $order->get_billing_email();
156
- $cart_abandonment_table_name = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
157
- $wcar_status = $wpdb->get_var($wpdb->prepare( "SELECT `order_status` FROM {$cart_abandonment_table_name} WHERE `email` = %s", $email )); // phpcs:ignore
158
- $woo_status = $order->get_status();
159
-
160
- if ( 'completed' === $wcar_status && in_array( $woo_status, array( 'completed', 'processing' ), true ) ) {
161
- return true;
162
- }
163
- return false;
164
- }
165
-
166
- /**
167
- * Update the Order status.
168
- *
169
- * @param integer $order_id order id.
170
- * @param string $old_order_status old order status.
171
- * @param string $new_order_status new order status.
172
- */
173
- public function wcf_ca_update_order_status( $order_id, $old_order_status, $new_order_status ) {
174
-
175
- $acceptable_order_statuses = $this->get_acceptable_order_statuses();
176
-
177
- $exclude_on_hold_order = apply_filters_deprecated( 'woo_ca_exclude_on_hold_order_from_tracking', array( false ), '1.2.8', 'New Option is introduced instead of this filter' );
178
-
179
- if ( $exclude_on_hold_order & ! ( in_array( 'on-hold', $acceptable_order_statuses, true ) ) ) {
180
- array_push( $acceptable_order_statuses, 'on-hold' );
181
- }
182
-
183
- if ( ( WCF_CART_FAILED_ORDER === $new_order_status ) ) {
184
- return;
185
- }
186
-
187
- if ( $order_id && in_array( $new_order_status, $acceptable_order_statuses, true ) ) {
188
-
189
- $order = wc_get_order( $order_id );
190
-
191
- $order_email = $order->get_billing_email();
192
- $captured_data = ( WCF_CART_FAILED_ORDER === $new_order_status ) ? $this->get_tracked_data_without_status( $order_email ) : $this->get_captured_data_by_email( $order_email );
193
-
194
- if ( $captured_data && is_object( $captured_data ) ) {
195
- $capture_status = $captured_data->order_status;
196
- global $wpdb;
197
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
198
-
199
- if ( ( WCF_CART_NORMAL_ORDER === $capture_status ) ) {
200
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $captured_data->session_id ) ) );
201
- }
202
-
203
- if ( ( WCF_CART_ABANDONED_ORDER === $capture_status || WCF_CART_LOST_ORDER === $capture_status ) ) {
204
- $this->skip_future_emails_when_order_is_completed( sanitize_key( $captured_data->session_id ) );
205
- $this->trigger_zapier_webhook( $captured_data->session_id, WCF_CART_COMPLETED_ORDER );
206
- $note = __( 'This order was abandoned & subsequently recovered.', 'woo-cart-abandonment-recovery' );
207
- $order->add_order_note( $note );
208
- $order->save();
209
- if ( WC()->session ) {
210
- WC()->session->__unset( 'wcf_session_id' );
211
- }
212
- }
213
- }
214
- $wcar_email_admin_recovery = get_option( 'wcar_email_admin_on_recovery' );
215
- if ( 'on' === $wcar_email_admin_recovery ) {
216
- $this->wcar_send_successful_recovery_email_to_admin( $order_id, $old_order_status, $new_order_status );
217
- }
218
- }
219
-
220
- }
221
-
222
-
223
- /**
224
- * Send preview emails.
225
- */
226
- public function send_preview_email() {
227
-
228
- check_ajax_referer( WCF_EMAIL_TEMPLATES_NONCE, 'security' );
229
- $mail_result = $this->send_email_templates( null, true );
230
- if ( $mail_result ) {
231
- wp_send_json_success( __( 'Mail has been sent successfully!', 'woo-cart-abandonment-recovery' ) );
232
- } else {
233
- wp_send_json_error( __( 'Mail sending failed!', 'woo-cart-abandonment-recovery' ) );
234
- }
235
- }
236
-
237
-
238
- /**
239
- * Delete tracked data and set cookie for the user.
240
- */
241
- public function skip_cart_tracking_by_gdpr() {
242
- check_ajax_referer( 'cartflows_skip_cart_tracking_gdpr', 'security' );
243
-
244
- global $wpdb;
245
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
246
-
247
- $session_id = WC()->session->get( 'wcf_session_id' );
248
- if ( $session_id ) {
249
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
250
- }
251
-
252
- setcookie( 'wcf_ca_skip_track_data', 'true', 0, '/' );
253
- wp_send_json_success();
254
- }
255
-
256
-
257
- /**
258
- * Create custom schedule.
259
- *
260
- * @param array $schedules schedules.
261
- * @return mixed
262
- */
263
- public function cartflows_ca_update_order_status_action( $schedules ) {
264
-
265
- /**
266
- * Add filter to change the cron interval time to uodate order status.
267
- */
268
- $cron_time = apply_filters( 'woo_ca_update_order_cron_interval', 15 );
269
-
270
- $schedules['every_fifteen_minutes'] = array(
271
- 'interval' => $cron_time * MINUTE_IN_SECONDS,
272
- 'display' => __( 'Every Fifteen Minutes', 'woo-cart-abandonment-recovery' ),
273
- );
274
-
275
- return $schedules;
276
- }
277
-
278
- /**
279
- * Generate new coupon code for abandoned cart.
280
- *
281
- * @param string $discount_type discount type.
282
- * @param float $amount amount.
283
- * @param string $expiry expiry.
284
- * @param string $free_shipping is free shipping.
285
- * @param string $individual_use use coupon individual.
286
- */
287
- public function generate_coupon_code( $discount_type, $amount, $expiry = '', $free_shipping = 'no', $individual_use = 'no' ) {
288
-
289
- $coupon_code = '';
290
-
291
- $coupon_code = wp_generate_password( 8, false, false );
292
-
293
- $new_coupon_id = wp_insert_post(
294
- array(
295
- 'post_title' => $coupon_code,
296
- 'post_content' => '',
297
- 'post_status' => 'publish',
298
- 'post_author' => 1,
299
- 'post_type' => 'shop_coupon',
300
- )
301
- );
302
-
303
- $coupon_post_data = array(
304
- 'discount_type' => $discount_type,
305
- 'description' => WCF_CA_COUPON_DESCRIPTION,
306
- 'coupon_amount' => $amount,
307
- 'individual_use' => $individual_use,
308
- 'product_ids' => '',
309
- 'exclude_product_ids' => '',
310
- 'usage_limit' => '1',
311
- 'usage_count' => '0',
312
- 'date_expires' => $expiry,
313
- 'apply_before_tax' => 'yes',
314
- 'free_shipping' => $free_shipping,
315
- 'coupon_generated_by' => WCF_CA_COUPON_GENERATED_BY,
316
- );
317
-
318
- $coupon_post_data = apply_filters( 'woo_ca_generate_coupon', $coupon_post_data );
319
-
320
- foreach ( $coupon_post_data as $key => $value ) {
321
- update_post_meta( $new_coupon_id, $key, $value );
322
- }
323
-
324
- return $coupon_code;
325
- }
326
-
327
- /**
328
- * Unsubscribe the user from the mailing list.
329
- */
330
- public function unsubscribe_cart_abandonment_emails() {
331
-
332
- $unsubscribe = filter_input( INPUT_GET, 'unsubscribe', FILTER_VALIDATE_BOOLEAN );
333
- $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
334
- if ( $unsubscribe && $this->is_valid_token( $wcf_ac_token ) ) {
335
- $token_data = $this->wcf_decode_token( $wcf_ac_token );
336
- if ( isset( $token_data['wcf_session_id'] ) ) {
337
- $session_id = $token_data['wcf_session_id'];
338
-
339
- global $wpdb;
340
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
341
- $wpdb->update(
342
- $cart_abandonment_table,
343
- array( 'unsubscribed' => true ),
344
- array( 'session_id' => $session_id )
345
- );
346
- wp_die( esc_html__( 'You have successfully unsubscribed from our email list.', 'woo-cart-abandonment-recovery' ), esc_html__( 'Unsubscribed', 'woo-cart-abandonment-recovery' ) );
347
-
348
- }
349
- }
350
-
351
- }
352
-
353
-
354
- /**
355
- * Link JS to mce button.
356
- *
357
- * @param array $plugins mce pluggins.
358
- * @return mixed
359
- */
360
- public function wcf_filter_mce_plugin( $plugins ) {
361
- $plugins['cartflows_ac'] = CARTFLOWS_CA_URL . 'admin/assets/js/admin-mce.js';
362
- return $plugins;
363
- }
364
-
365
- /**
366
- * Register button.
367
- *
368
- * @param array $buttons mce buttons.
369
- * @return mixed
370
- */
371
- public function wcf_filter_mce_button( $buttons ) {
372
- array_push( $buttons, 'cartflows_ac' );
373
- return $buttons;
374
- }
375
-
376
- /**
377
- * Initialise all the constants
378
- */
379
- public function define_cart_abandonment_constants() {
380
- define( 'CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR', CARTFLOWS_CA_DIR . 'modules/cart-abandonment/' );
381
- define( 'CARTFLOWS_CART_ABANDONMENT_TRACKING_URL', CARTFLOWS_CA_URL . 'modules/cart-abandonment/' );
382
- define( 'WCF_CART_ABANDONED_ORDER', 'abandoned' );
383
- define( 'WCF_CART_COMPLETED_ORDER', 'completed' );
384
- define( 'WCF_CART_LOST_ORDER', 'lost' );
385
- define( 'WCF_CART_NORMAL_ORDER', 'normal' );
386
- define( 'WCF_CART_FAILED_ORDER', 'failed' );
387
- define( 'CARTFLOWS_ZAPIER_ACTION_AFTER_TIME', 1800 );
388
-
389
- define( 'WCF_ACTION_ABANDONED_CARTS', 'abandoned_carts' );
390
- define( 'WCF_ACTION_RECOVERED_CARTS', 'recovered_carts' );
391
- define( 'WCF_ACTION_LOST_CARTS', 'lost_carts' );
392
- define( 'WCF_ACTION_SETTINGS', 'settings' );
393
- define( 'WCF_ACTION_REPORTS', 'reports' );
394
-
395
- define( 'WCF_SUB_ACTION_REPORTS_VIEW', 'view' );
396
- define( 'WCF_SUB_ACTION_REPORTS_RESCHEDULE', 'reschedule' );
397
-
398
- define( 'WCF_DEFAULT_CUT_OFF_TIME', 15 );
399
- define( 'WCF_DEFAULT_COUPON_AMOUNT', 10 );
400
-
401
- define( 'WCF_CA_DATETIME_FORMAT', 'Y-m-d H:i:s' );
402
-
403
- define( 'WCF_CA_COUPON_DESCRIPTION', 'This coupon is for abandoned cart email templates.' );
404
- define( 'WCF_CA_COUPON_GENERATED_BY', 'woo-cart-abandonment-recovery' );
405
- }
406
-
407
- /**
408
- * Restore cart abandonemnt data on checkout page.
409
- *
410
- * @param array $fields checkout fields values.
411
- * @return array field values
412
- */
413
- public function restore_cart_abandonment_data( $fields = array() ) {
414
- global $woocommerce;
415
- $result = array();
416
- // Restore only of user is not logged in.
417
- $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
418
- if ( $this->is_valid_token( $wcf_ac_token ) ) {
419
-
420
- // Check if `wcf_restore_token` exists to restore cart data.
421
- $token_data = $this->wcf_decode_token( $wcf_ac_token );
422
- if ( is_array( $token_data ) && isset( $token_data['wcf_session_id'] ) ) {
423
- $result = $this->get_checkout_details( $token_data['wcf_session_id'] );
424
- if ( isset( $result ) && WCF_CART_ABANDONED_ORDER === $result->order_status || WCF_CART_LOST_ORDER === $result->order_status ) {
425
- WC()->session->set( 'wcf_session_id', $token_data['wcf_session_id'] );
426
- }
427
- }
428
-
429
- if ( $result ) {
430
- $cart_content = unserialize( $result->cart_contents );
431
-
432
- if ( $cart_content ) {
433
- $woocommerce->cart->empty_cart();
434
- wc_clear_notices();
435
- foreach ( $cart_content as $cart_item ) {
436
-
437
- $cart_item_data = array();
438
- $variation_data = array();
439
- $id = $cart_item['product_id'];
440
- $qty = $cart_item['quantity'];
441
-
442
- // Skip bundled products when added main product.
443
- if ( isset( $cart_item['bundled_by'] ) ) {
444
- continue;
445
- }
446
-
447
- if ( isset( $cart_item['variation'] ) ) {
448
- foreach ( $cart_item['variation'] as $key => $value ) {
449
- $variation_data[ $key ] = $value;
450
- }
451
- }
452
-
453
- $cart_item_data = $cart_item;
454
-
455
- $woocommerce->cart->add_to_cart( $id, $qty, $cart_item['variation_id'], $variation_data, $cart_item_data );
456
- }
457
-
458
- if ( isset( $token_data['wcf_coupon_code'] ) && ! $woocommerce->cart->applied_coupons ) {
459
- $woocommerce->cart->add_discount( $token_data['wcf_coupon_code'] );
460
- }
461
- }
462
- $other_fields = unserialize( $result->other_fields );
463
-
464
- $parts = explode( ',', $other_fields['wcf_location'] );
465
- if ( count( $parts ) > 1 ) {
466
- $country = $parts[0];
467
- $city = trim( $parts[1] );
468
- } else {
469
- $country = $parts[0];
470
- $city = '';
471
- }
472
-
473
- foreach ( $other_fields as $key => $value ) {
474
- $key = str_replace( 'wcf_', '', $key );
475
- $_POST[ $key ] = sanitize_text_field( $value );
476
- }
477
- $_POST['billing_first_name'] = sanitize_text_field( $other_fields['wcf_first_name'] );
478
- $_POST['billing_last_name'] = sanitize_text_field( $other_fields['wcf_last_name'] );
479
- $_POST['billing_phone'] = sanitize_text_field( $other_fields['wcf_phone_number'] );
480
- $_POST['billing_email'] = sanitize_email( $result->email );
481
- $_POST['billing_city'] = sanitize_text_field( $city );
482
- $_POST['billing_country'] = sanitize_text_field( $country );
483
-
484
- }
485
- }
486
- return $fields;
487
- }
488
-
489
- /**
490
- * Add notice to inform user about test email checkout page.
491
- */
492
- public function test_email_checkout_page() {
493
-
494
- $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
495
- $token_data = $this->wcf_decode_token( $wcf_ac_token );
496
- if ( is_checkout() && ! is_wc_endpoint_url() && isset( $token_data['wcf_preview_email'] ) && $token_data['wcf_preview_email'] ) {
497
- wc_print_notice( __( 'This checkout page is generated by WooCommerce Cart Abandonment Recovery plugin from test mail.', 'woo-cart-abandonment-recovery' ), 'notice' );
498
- }
499
- }
500
-
501
-
502
- /**
503
- * Load cart abandonemnt tracking script.
504
- *
505
- * @return void
506
- */
507
- public function cart_abandonment_tracking_script() {
508
-
509
- $wcf_ca_ignore_users = get_option( 'wcf_ca_ignore_users' );
510
- $current_user = wp_get_current_user();
511
- $roles = $current_user->roles;
512
- $role = array_shift( $roles );
513
- if ( ! empty( $wcf_ca_ignore_users ) ) {
514
- foreach ( $wcf_ca_ignore_users as $user ) {
515
- $user = strtolower( $user );
516
- $role = preg_replace( '/_/', ' ', $role );
517
- if ( $role === $user ) {
518
- return;
519
- }
520
- }
521
- }
522
-
523
- global $post;
524
- wp_enqueue_script(
525
- 'cartflows-cart-abandonment-tracking',
526
- CARTFLOWS_CART_ABANDONMENT_TRACKING_URL . 'assets/js/cart-abandonment-tracking.js',
527
- array( 'jquery' ),
528
- CARTFLOWS_CA_VER,
529
- true
530
- );
531
-
532
- $vars = array(
533
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
534
- '_nonce' => wp_create_nonce( 'cartflows_save_cart_abandonment_data' ),
535
- '_gdpr_nonce' => wp_create_nonce( 'cartflows_skip_cart_tracking_gdpr' ),
536
- '_post_id' => get_the_ID(),
537
- '_show_gdpr_message' => ( wcf_ca()->utils->is_gdpr_enabled() && ! isset( $_COOKIE['wcf_ca_skip_track_data'] ) ),
538
- '_gdpr_message' => get_option( 'wcf_ca_gdpr_message' ),
539
- '_gdpr_nothanks_msg' => __( 'No Thanks', 'woo-cart-abandonment-recovery' ),
540
- '_gdpr_after_no_thanks_msg' => __( 'You won\'t receive further emails from us, thank you!', 'woo-cart-abandonment-recovery' ),
541
- 'enable_ca_tracking' => true,
542
- );
543
-
544
- wp_localize_script( 'cartflows-cart-abandonment-tracking', 'CartFlowsProCAVars', $vars );
545
-
546
- }
547
-
548
- /**
549
- * Validate the token before use.
550
- *
551
- * @param string $token token form the url.
552
- * @return bool
553
- */
554
- public function is_valid_token( $token ) {
555
- $is_valid = false;
556
- $token_data = $this->wcf_decode_token( $token );
557
- if ( is_array( $token_data ) && array_key_exists( 'wcf_session_id', $token_data ) ) {
558
- $result = $this->get_checkout_details( $token_data['wcf_session_id'] );
559
- if ( isset( $result ) ) {
560
- $is_valid = true;
561
- }
562
- }
563
- return $is_valid;
564
- }
565
-
566
- /**
567
- * Check before emails actually send to user.
568
- *
569
- * @param array $email_data email_data.
570
- * @param array $current_cart_data cart data.
571
- * @return bool
572
- */
573
- public function check_if_already_purchased_by_email_product_ids( $email_data, $current_cart_data ) {
574
-
575
- global $wpdb;
576
- $current_cart_data = unserialize( $current_cart_data );
577
-
578
- // Fetch products & variations.
579
- $products = array_values( wp_list_pluck( $current_cart_data, 'product_id' ) );
580
- $variations = array_values( wp_list_pluck( $current_cart_data, 'variation_id' ) );
581
- $current_products = array_unique( array_merge( $products, $variations ) );
582
-
583
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
584
-
585
- $orders = wc_get_orders(
586
- array(
587
- 'billing_email' => $email_data->email,
588
- 'status' => array( 'processing', 'completed' ),
589
- 'date_after' => gmdate(
590
- 'Y-m-d h:i:s',
591
- strtotime( '-30 days' )
592
- ),
593
- )
594
- );
595
- $need_to_send_email = true;
596
-
597
- foreach ( $orders as $order ) {
598
- $order = wc_get_order( $order->get_id() );
599
- $items = $order->get_items();
600
- foreach ( $items as $item ) {
601
- $product_id = $item->get_product_id();
602
- if ( in_array( $product_id, $current_products, true ) ) {
603
- /**
604
- * Remove duplicate captured order for tracking.
605
- */
606
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $email_data->session_id ) ) );
607
- $need_to_send_email = false;
608
- break;
609
- }
610
- }
611
- }
612
- return $need_to_send_email;
613
- }
614
-
615
- /**
616
- * Execute Zapier webhook for further action inside Zapier.
617
- *
618
- * @since 1.0.0
619
- */
620
- public function update_order_status() {
621
-
622
- global $wpdb;
623
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
624
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
625
- $minutes = wcf_ca()->utils->get_cart_abandonment_tracking_cut_off_time();
626
-
627
- /**
628
- * Delete abandoned cart orders if empty.
629
- */
630
- $this->delete_empty_abandoned_order();
631
-
632
- $wp_current_datetime = current_time( WCF_CA_DATETIME_FORMAT );
633
- $abandoned_ids = $wpdb->get_results(
634
- $wpdb->prepare('SELECT `session_id` FROM `' . $cart_abandonment_table . '` WHERE `order_status` = %s AND ADDDATE( `time`, INTERVAL %d MINUTE) <= %s', WCF_CART_NORMAL_ORDER, $minutes, $wp_current_datetime ), ARRAY_A // phpcs:ignore
635
- );
636
-
637
- foreach ( $abandoned_ids as $session_id ) {
638
-
639
- if ( isset( $session_id['session_id'] ) ) {
640
-
641
- $current_session_id = $session_id['session_id'];
642
- $this->schedule_emails( $current_session_id );
643
-
644
- $coupon_code = '';
645
- $wcf_ca_coupon_code_status = get_option( 'wcf_ca_coupon_code_status' );
646
-
647
- if ( 'on' === $wcf_ca_coupon_code_status ) {
648
- $discount_type = get_option( 'wcf_ca_discount_type' );
649
- $discount_type = $discount_type ? $discount_type : 'percent';
650
- $amount = get_option( 'wcf_ca_coupon_amount' );
651
- $amount = $amount ? $amount : WCF_DEFAULT_COUPON_AMOUNT;
652
- $coupon_expiry_date = get_option( 'wcf_ca_coupon_expiry' );
653
- $coupon_expiry_unit = get_option( 'wcf_ca_coupon_expiry_unit' );
654
- $coupon_expiry_date = $coupon_expiry_date ? strtotime( $wp_current_datetime . ' +' . $coupon_expiry_date . ' ' . $coupon_expiry_unit ) : '';
655
- $free_shipping_coupon = get_option( 'wcf_ca_free_shipping_coupon' );
656
- $free_shipping = ( isset( $free_shipping_coupon ) && ( $free_shipping_coupon->meta_value ) ) ? 'yes' : 'no';
657
-
658
- $individual_use_only = get_option( 'wcf_ca_individual_use_only' );
659
- $individual_use = ( isset( $individual_use_only ) && ( $individual_use_only->meta_value ) ) ? 'yes' : 'no';
660
-
661
- $coupon_code = $this->generate_coupon_code( $discount_type, $amount, $coupon_expiry_date, $free_shipping, $individual_use );
662
- }
663
-
664
- $wpdb->update(
665
- $cart_abandonment_table,
666
- array(
667
- 'order_status' => WCF_CART_ABANDONED_ORDER,
668
- 'coupon_code' => $coupon_code,
669
- ),
670
- array( 'session_id' => $current_session_id )
671
- );
672
-
673
- $this->trigger_zapier_webhook( $current_session_id, WCF_CART_ABANDONED_ORDER );
674
- }
675
- }
676
-
677
- /**
678
- * Send scheduled emails.
679
- */
680
- $this->send_emails_to_callback();
681
-
682
- // Update order status to lost after campaign complete.
683
- // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
684
- $wpdb->query(
685
- $wpdb->prepare(
686
- "UPDATE $cart_abandonment_table as ca SET order_status = 'lost' WHERE ca.order_status = %s AND DATE(ca.time) <= DATE_SUB( %s , INTERVAL 30 DAY)
687
- AND ( (SELECT count(*) FROM $email_history_table WHERE ca_session_id = ca.session_id ) =
688
- (SELECT count(*) FROM $email_history_table WHERE ca_session_id = ca.session_id AND email_sent = 1) )",
689
- WCF_CART_ABANDONED_ORDER,
690
- $wp_current_datetime
691
- )
692
- );
693
- // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
694
-
695
- /**
696
- * Delete garbage coupons.
697
- */
698
- $wcf_ca_auto_delete_coupons = get_option( 'wcf_ca_auto_delete_coupons' );
699
-
700
- if ( isset( $wcf_ca_auto_delete_coupons ) && 'on' === $wcf_ca_auto_delete_coupons ) {
701
- $this->delete_used_and_expired_coupons();
702
- }
703
-
704
- }
705
-
706
- /**
707
- * Send zapier webhook.
708
- *
709
- * @param string $session_id session id.
710
- * @param string $order_status order status.
711
- */
712
- public function trigger_zapier_webhook( $session_id, $order_status ) {
713
-
714
- $checkout_details = $this->get_checkout_details( $session_id );
715
-
716
- if ( $checkout_details && wcf_ca()->utils->is_zapier_trigger_enabled() ) {
717
- $trigger_details = array();
718
- $url = get_option( 'wcf_ca_zapier_cart_abandoned_webhook' );
719
-
720
- $other_details = unserialize( $checkout_details->other_fields );
721
- $trigger_details['first_name'] = $other_details['wcf_first_name'];
722
- $trigger_details['last_name'] = $other_details['wcf_last_name'];
723
- $trigger_details['phone_number'] = $other_details['wcf_phone_number'];
724
- $trigger_details['billing_address'] = $other_details['wcf_billing_company'] . ' ' . $other_details['wcf_billing_address_1'] . ', ' . $other_details['wcf_billing_state'] . ', ' . $other_details['wcf_location'] . ', ' . $other_details['wcf_billing_postcode'];
725
- $trigger_details['billing_address'] = trim( $trigger_details['billing_address'], ', ' );
726
- $trigger_details['shipping_address'] = $other_details['wcf_shipping_company'] . ' ' . $other_details['wcf_shipping_address_1'] . ', ' . $other_details['wcf_shipping_city'] . ', ' . $other_details['wcf_shipping_state'] . ', ' . $other_details['wcf_shipping_postcode'];
727
- $trigger_details['shipping_address'] = trim( $trigger_details['shipping_address'], ', ' );
728
- $trigger_details['email'] = $checkout_details->email;
729
- $token_data = array( 'wcf_session_id' => $checkout_details->session_id );
730
- $trigger_details['checkout_url'] = $this->get_checkout_url( $checkout_details->checkout_id, $token_data );
731
- $trigger_details['product_names'] = $this->get_comma_separated_products( $checkout_details->cart_contents );
732
- $trigger_details['coupon_code'] = $checkout_details->coupon_code;
733
- $trigger_details['order_status'] = $order_status;
734
- $trigger_details['cart_total'] = $checkout_details->cart_total;
735
- $trigger_details['product_table'] = $this->get_email_product_block( $checkout_details->cart_contents, $checkout_details->cart_total );
736
-
737
- $parameters = http_build_query( $trigger_details );
738
-
739
- wp_remote_post(
740
- $url,
741
- array(
742
- 'body' => $parameters,
743
- 'timeout' => '5',
744
- 'redirection' => '5',
745
- 'httpversion' => '1.0',
746
- 'blocking' => true,
747
- 'headers' => array(),
748
- 'cookies' => array(),
749
- )
750
- );
751
-
752
- }
753
- }
754
-
755
-
756
- /**
757
- * Sanitize post array.
758
- *
759
- * @return array
760
- */
761
- public function sanitize_post_data() {
762
-
763
- $input_post_values = array(
764
- 'wcf_billing_company' => array(
765
- 'default' => '',
766
- 'sanitize' => FILTER_SANITIZE_STRING,
767
- ),
768
- 'wcf_email' => array(
769
- 'default' => '',
770
- 'sanitize' => FILTER_SANITIZE_EMAIL,
771
- ),
772
- 'wcf_billing_address_1' => array(
773
- 'default' => '',
774
- 'sanitize' => FILTER_SANITIZE_STRING,
775
- ),
776
- 'wcf_billing_address_2' => array(
777
- 'default' => '',
778
- 'sanitize' => FILTER_SANITIZE_STRING,
779
- ),
780
- 'wcf_billing_state' => array(
781
- 'default' => '',
782
- 'sanitize' => FILTER_SANITIZE_STRING,
783
- ),
784
- 'wcf_billing_postcode' => array(
785
- 'default' => '',
786
- 'sanitize' => FILTER_SANITIZE_STRING,
787
- ),
788
- 'wcf_shipping_first_name' => array(
789
- 'default' => '',
790
- 'sanitize' => FILTER_SANITIZE_STRING,
791
- ),
792
- 'wcf_shipping_last_name' => array(
793
- 'default' => '',
794
- 'sanitize' => FILTER_SANITIZE_STRING,
795
- ),
796
- 'wcf_shipping_company' => array(
797
- 'default' => '',
798
- 'sanitize' => FILTER_SANITIZE_STRING,
799
- ),
800
- 'wcf_shipping_country' => array(
801
- 'default' => '',
802
- 'sanitize' => FILTER_SANITIZE_STRING,
803
- ),
804
- 'wcf_shipping_address_1' => array(
805
- 'default' => '',
806
- 'sanitize' => FILTER_SANITIZE_STRING,
807
- ),
808
- 'wcf_shipping_address_2' => array(
809
- 'default' => '',
810
- 'sanitize' => FILTER_SANITIZE_STRING,
811
- ),
812
- 'wcf_shipping_city' => array(
813
- 'default' => '',
814
- 'sanitize' => FILTER_SANITIZE_STRING,
815
- ),
816
- 'wcf_shipping_state' => array(
817
- 'default' => '',
818
- 'sanitize' => FILTER_SANITIZE_STRING,
819
- ),
820
- 'wcf_shipping_postcode' => array(
821
- 'default' => '',
822
- 'sanitize' => FILTER_SANITIZE_STRING,
823
- ),
824
- 'wcf_order_comments' => array(
825
- 'default' => '',
826
- 'sanitize' => FILTER_SANITIZE_STRING,
827
- ),
828
- 'wcf_name' => array(
829
- 'default' => '',
830
- 'sanitize' => FILTER_SANITIZE_STRING,
831
- ),
832
- 'wcf_surname' => array(
833
- 'default' => '',
834
- 'sanitize' => FILTER_SANITIZE_STRING,
835
- ),
836
- 'wcf_phone' => array(
837
- 'default' => '',
838
- 'sanitize' => FILTER_SANITIZE_STRING,
839
- ),
840
- 'wcf_country' => array(
841
- 'default' => '',
842
- 'sanitize' => FILTER_SANITIZE_STRING,
843
- ),
844
- 'wcf_city' => array(
845
- 'default' => '',
846
- 'sanitize' => FILTER_SANITIZE_STRING,
847
- ),
848
- 'wcf_post_id' => array(
849
- 'default' => 0,
850
- 'sanitize' => FILTER_SANITIZE_NUMBER_INT,
851
- ),
852
- );
853
-
854
- $sanitized_post = array();
855
- foreach ( $input_post_values as $key => $input_post_value ) {
856
-
857
- if ( isset( $_POST[ $key ] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
858
- $sanitized_post[ $key ] = filter_input( INPUT_POST, $key, $input_post_value['sanitize'] );
859
- } else {
860
- $sanitized_post[ $key ] = $input_post_value['default'];
861
- }
862
- }
863
- return $sanitized_post;
864
-
865
- }
866
-
867
-
868
- /**
869
- * Save cart abandonment tracking and schedule new event.
870
- *
871
- * @since 1.0.0
872
- */
873
- public function save_cart_abandonment_data() {
874
- check_ajax_referer( 'cartflows_save_cart_abandonment_data', 'security' );
875
- $post_data = $this->sanitize_post_data();
876
- if ( isset( $post_data['wcf_email'] ) ) {
877
- $user_email = sanitize_email( $post_data['wcf_email'] );
878
- global $wpdb;
879
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
880
-
881
- // Verify if email is already exists.
882
- $session_id = WC()->session->get( 'wcf_session_id' );
883
- $session_checkout_details = null;
884
- if ( isset( $session_id ) ) {
885
- $session_checkout_details = $this->get_checkout_details( $session_id );
886
- } else {
887
- $session_checkout_details = $this->get_checkout_details_by_email( $user_email );
888
- if ( $session_checkout_details ) {
889
- $session_id = $session_checkout_details->session_id;
890
- WC()->session->set( 'wcf_session_id', $session_id );
891
- } else {
892
- $session_id = md5( uniqid( wp_rand(), true ) );
893
- }
894
- }
895
-
896
- $checkout_details = $this->prepare_abandonment_data( $post_data );
897
-
898
- if ( isset( $session_checkout_details ) && WCF_CART_COMPLETED_ORDER === $session_checkout_details->order_status ) {
899
- WC()->session->__unset( 'wcf_session_id' );
900
- $session_id = md5( uniqid( wp_rand(), true ) );
901
- }
902
-
903
- if ( isset( $checkout_details['cart_total'] ) && $checkout_details['cart_total'] > 0 ) {
904
-
905
- if ( ( ! is_null( $session_id ) ) && ! is_null( $session_checkout_details ) ) {
906
-
907
- // Updating row in the Database where users Session id = same as prevously saved in Session.
908
- $wpdb->update(
909
- $cart_abandonment_table,
910
- $checkout_details,
911
- array( 'session_id' => $session_id )
912
- );
913
-
914
- } else {
915
-
916
- $checkout_details['session_id'] = sanitize_text_field( $session_id );
917
- // Inserting row into Database.
918
- $wpdb->insert(
919
- $cart_abandonment_table,
920
- $checkout_details
921
- );
922
-
923
- // Storing session_id in WooCommerce session.
924
- WC()->session->set( 'wcf_session_id', $session_id );
925
-
926
- }
927
- } else {
928
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
929
- }
930
-
931
- wp_send_json_success();
932
- }
933
- }
934
-
935
-
936
- /**
937
- * Prepare cart data to save for abandonment.
938
- *
939
- * @param array $post_data post data.
940
- * @return array
941
- */
942
- public function prepare_abandonment_data( $post_data = array() ) {
943
-
944
- if ( function_exists( 'WC' ) ) {
945
-
946
- // Retrieving cart total value and currency.
947
- $cart_total = WC()->cart->total;
948
-
949
- // Retrieving cart products and their quantities.
950
- $products = WC()->cart->get_cart();
951
- $current_time = current_time( WCF_CA_DATETIME_FORMAT );
952
- $other_fields = array(
953
- 'wcf_billing_company' => $post_data['wcf_billing_company'],
954
- 'wcf_billing_address_1' => $post_data['wcf_billing_address_1'],
955
- 'wcf_billing_address_2' => $post_data['wcf_billing_address_2'],
956
- 'wcf_billing_state' => $post_data['wcf_billing_state'],
957
- 'wcf_billing_postcode' => $post_data['wcf_billing_postcode'],
958
- 'wcf_shipping_first_name' => $post_data['wcf_shipping_first_name'],
959
- 'wcf_shipping_last_name' => $post_data['wcf_shipping_last_name'],
960
- 'wcf_shipping_company' => $post_data['wcf_shipping_company'],
961
- 'wcf_shipping_country' => $post_data['wcf_shipping_country'],
962
- 'wcf_shipping_address_1' => $post_data['wcf_shipping_address_1'],
963
- 'wcf_shipping_address_2' => $post_data['wcf_shipping_address_2'],
964
- 'wcf_shipping_city' => $post_data['wcf_shipping_city'],
965
- 'wcf_shipping_state' => $post_data['wcf_shipping_state'],
966
- 'wcf_shipping_postcode' => $post_data['wcf_shipping_postcode'],
967
- 'wcf_order_comments' => $post_data['wcf_order_comments'],
968
- 'wcf_first_name' => $post_data['wcf_name'],
969
- 'wcf_last_name' => $post_data['wcf_surname'],
970
- 'wcf_phone_number' => $post_data['wcf_phone'],
971
- 'wcf_location' => $post_data['wcf_country'] . ', ' . $post_data['wcf_city'],
972
- );
973
-
974
- $checkout_details = array(
975
- 'email' => $post_data['wcf_email'],
976
- 'cart_contents' => serialize( $products ),
977
- 'cart_total' => sanitize_text_field( $cart_total ),
978
- 'time' => sanitize_text_field( $current_time ),
979
- 'other_fields' => serialize( $other_fields ),
980
- 'checkout_id' => $post_data['wcf_post_id'],
981
- );
982
- }
983
- return $checkout_details;
984
- }
985
-
986
- /**
987
- * Get the acceptable order statuses.
988
- */
989
- public function get_acceptable_order_statuses() {
990
-
991
- $acceptable_order_statuses = get_option( 'wcf_ca_excludes_orders' );
992
- $acceptable_order_statuses = array_map( 'strtolower', $acceptable_order_statuses );
993
-
994
- return $acceptable_order_statuses;
995
- }
996
-
997
- /**
998
- * Deletes cart abandonment tracking and scheduled event.
999
- *
1000
- * @param int $order_id Order ID.
1001
- * @since 1.0.0
1002
- */
1003
- public function delete_cart_abandonment_data( $order_id ) {
1004
-
1005
- $acceptable_order_statuses = $this->get_acceptable_order_statuses();
1006
-
1007
- $order = wc_get_order( $order_id );
1008
- $order_status = $order->get_status();
1009
- if ( ! in_array( $order_status, $acceptable_order_statuses, true ) ) {
1010
- // Proceed if order status in completed or processing.
1011
- return;
1012
- }
1013
-
1014
- global $wpdb;
1015
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1016
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1017
-
1018
- if ( isset( WC()->session ) ) {
1019
- $session_id = WC()->session->get( 'wcf_session_id' );
1020
-
1021
- if ( isset( $session_id ) ) {
1022
- $checkout_details = $this->get_checkout_details( $session_id );
1023
-
1024
- $has_mail_sent = count( $this->fetch_scheduled_emails( $session_id, true ) );
1025
-
1026
- if ( ! $has_mail_sent ) {
1027
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
1028
- } else {
1029
- if ( $checkout_details && ( WCF_CART_ABANDONED_ORDER === $checkout_details->order_status || WCF_CART_LOST_ORDER === $checkout_details->order_status ) ) {
1030
-
1031
- $this->skip_future_emails_when_order_is_completed( $session_id );
1032
-
1033
- $this->trigger_zapier_webhook( $session_id, WCF_CART_COMPLETED_ORDER );
1034
-
1035
- $order = wc_get_order( $order_id );
1036
- $note = __( 'This order was abandoned & subsequently recovered.', 'woo-cart-abandonment-recovery' );
1037
- $order->add_order_note( $note );
1038
- $order->save();
1039
-
1040
- } elseif ( WCF_CART_COMPLETED_ORDER !== $checkout_details->order_status ) {
1041
- // Normal checkout.
1042
-
1043
- $billing_email = filter_input( INPUT_POST, 'billing_email', FILTER_SANITIZE_EMAIL );
1044
-
1045
- if ( $billing_email ) {
1046
- $order_data = $this->get_captured_data_by_email( $billing_email );
1047
-
1048
- if ( ! is_null( $order_data ) ) {
1049
- $existing_cart_contents = unserialize( $order_data->cart_contents );
1050
- $order_cart_contents = unserialize( $checkout_details->cart_contents );
1051
- $existing_cart_products = array_keys( (array) $existing_cart_contents );
1052
- $order_cart_products = array_keys( (array) $order_cart_contents );
1053
- if ( $this->check_if_similar_cart( $existing_cart_products, $order_cart_products ) ) {
1054
- $this->skip_future_emails_when_order_is_completed( $order_data->session_id );
1055
- }
1056
- }
1057
- }
1058
- $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
1059
- }
1060
- }
1061
- }
1062
- if ( WC()->session ) {
1063
- WC()->session->__unset( 'wcf_session_id' );
1064
- }
1065
- }
1066
- }
1067
-
1068
- /**
1069
- * Unschedule future emails for completed orders.
1070
- *
1071
- * @param string $session_id session id.
1072
- * @param bool $skip_complete skip update query.
1073
- */
1074
- public function skip_future_emails_when_order_is_completed( $session_id, $skip_complete = false ) {
1075
-
1076
- global $wpdb;
1077
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1078
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1079
-
1080
- if ( ! $skip_complete ) {
1081
- $wpdb->update(
1082
- $cart_abandonment_table,
1083
- array(
1084
- 'order_status' => WCF_CART_COMPLETED_ORDER,
1085
- ),
1086
- array(
1087
- 'session_id' => sanitize_key( $session_id ),
1088
- )
1089
- );
1090
- }
1091
-
1092
- $wpdb->update(
1093
- $email_history_table,
1094
- array( 'email_sent' => -1 ),
1095
- array(
1096
- 'ca_session_id' => $session_id,
1097
- 'email_sent' => 0,
1098
- )
1099
- );
1100
- }
1101
-
1102
- /**
1103
- * Compare cart if similar products.
1104
- *
1105
- * @param array $cart_a cart_a.
1106
- * @param array $cart_b cart_b.
1107
- * @return bool
1108
- */
1109
- public function check_if_similar_cart( $cart_a, $cart_b ) {
1110
- return (
1111
- is_array( $cart_a )
1112
- && is_array( $cart_b )
1113
- && count( $cart_a ) === count( $cart_b )
1114
- && array_diff( $cart_a, $cart_b ) === array_diff( $cart_b, $cart_a )
1115
- );
1116
- }
1117
-
1118
-
1119
- /**
1120
- * Get the checkout details for the user.
1121
- *
1122
- * @param string $wcf_session_id checkout page session id.
1123
- * @since 1.0.0
1124
- */
1125
- public function get_checkout_details( $wcf_session_id ) {
1126
- global $wpdb;
1127
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1128
- $result = $wpdb->get_row(
1129
- $wpdb->prepare('SELECT * FROM `' . $cart_abandonment_table . '` WHERE session_id = %s', $wcf_session_id ) // phpcs:ignore
1130
- );
1131
- return $result;
1132
- }
1133
-
1134
- /**
1135
- * Get the checkout details for the user.
1136
- *
1137
- * @param string $email user email.
1138
- * @since 1.0.0
1139
- */
1140
- public function get_checkout_details_by_email( $email ) {
1141
- global $wpdb;
1142
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1143
- $result = $wpdb->get_row(
1144
- $wpdb->prepare('SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s AND `order_status` IN ( %s, %s )', $email, WCF_CART_ABANDONED_ORDER, WCF_CART_NORMAL_ORDER ) // phpcs:ignore
1145
- );
1146
- return $result;
1147
- }
1148
-
1149
-
1150
- /**
1151
- * Get the checkout details for the user.
1152
- *
1153
- * @param string $value value.
1154
- * @since 1.0.0
1155
- */
1156
- public function get_captured_data_by_email( $value ) {
1157
- global $wpdb;
1158
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1159
- $result = $wpdb->get_row(
1160
- $wpdb->prepare(
1161
- 'SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s AND `order_status` IN (%s, %s) ORDER BY `time` DESC LIMIT 1', $value, WCF_CART_ABANDONED_ORDER, WCF_CART_LOST_ORDER ) // phpcs:ignore
1162
- );
1163
- return $result;
1164
- }
1165
-
1166
-
1167
- /**
1168
- * Get the checkout details for the user.
1169
- *
1170
- * @param string $value value.
1171
- * @since 1.0.0
1172
- */
1173
- public function get_tracked_data_without_status( $value ) {
1174
- global $wpdb;
1175
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1176
- $result = $wpdb->get_row(
1177
- $wpdb->prepare(
1178
- 'SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s LIMIT 1', $value ) // phpcs:ignore
1179
- );
1180
- return $result;
1181
- }
1182
-
1183
- /**
1184
- * Add submenu to admin menu.
1185
- *
1186
- * @since 1.1.5
1187
- */
1188
- public function abandoned_cart_tracking_menu() {
1189
-
1190
- $capability = current_user_can( 'manage_woocommerce' ) ? 'manage_woocommerce' : 'manage_options';
1191
-
1192
- add_submenu_page(
1193
- 'woocommerce',
1194
- __( 'Cart Abandonment', 'woo-cart-abandonment-recovery' ),
1195
- __( 'Cart Abandonment', 'woo-cart-abandonment-recovery' ),
1196
- $capability,
1197
- WCF_CA_PAGE_NAME,
1198
- array( $this, 'render_abandoned_cart_tracking' )
1199
- );
1200
- }
1201
-
1202
- /**
1203
- * Render table view for cart abandonment tracking.
1204
- *
1205
- * @since 1.1.5
1206
- */
1207
- public function render_abandoned_cart_tracking() {
1208
-
1209
- $wcf_list_table = Cartflows_Ca_Cart_Abandonment_Table::get_instance();
1210
-
1211
- if ( 'delete' === $wcf_list_table->current_action() ) {
1212
-
1213
- $ids = array();
1214
- if ( isset( $_REQUEST['id'] ) && is_array( $_REQUEST['id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1215
- $ids = array_map( 'intval', $_REQUEST['id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1216
- }
1217
- $deleted_row_count = empty( $ids ) ? 1 : count( $ids );
1218
-
1219
- $wcf_list_table->process_bulk_action();
1220
- $message = '<div class="notice notice-success is-dismissible" id="message"><p>' . sprintf( __( 'Items deleted: %d', 'woo-cart-abandonment-recovery' ), $deleted_row_count ) . '</p></div>'; // phpcs:ignore
1221
- set_transient( 'wcf_ca_show_message', $message, 5 );
1222
- if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
1223
- wp_safe_redirect( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) );
1224
- }
1225
- } elseif ( 'unsubscribe' === $wcf_list_table->current_action() ) {
1226
-
1227
- global $wpdb;
1228
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1229
- $id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT );
1230
-
1231
- $wpdb->update(
1232
- $cart_abandonment_table,
1233
- array( 'unsubscribed' => true ),
1234
- array( 'id' => $id )
1235
- );
1236
- $wcf_list_table->process_bulk_action();
1237
- $message = '<div class="notice notice-success is-dismissible" id="message"><p>' . sprintf( __( 'User(s) unsubscribed successfully!', 'woo-cart-abandonment-recovery' ) ) . '</p></div>'; // phpcs:ignore
1238
- set_transient( 'wcf_ca_show_message', $message, 5 );
1239
- if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
1240
- wp_safe_redirect( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) );
1241
- }
1242
- }
1243
- ?>
1244
-
1245
- <?php
1246
- include_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-cart-abandonment-tabs.php';
1247
- ?>
1248
- <?php
1249
- }
1250
-
1251
- /**
1252
- * Count abandoned carts
1253
- *
1254
- * @since 1.1.5
1255
- */
1256
- public function abandoned_cart_count() {
1257
- global $wpdb;
1258
- $cart_abandonment_table_name = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1259
-
1260
- $query = $wpdb->prepare( "SELECT COUNT(`id`) FROM {$cart_abandonment_table_name} WHERE `order_status` = %s", WCF_CART_ABANDONED_ORDER ); // phpcs:ignore
1261
- $total_items = $wpdb->get_var( $query ); // phpcs:ignore
1262
- return $total_items;
1263
- }
1264
-
1265
- /**
1266
- * Load analytics scripts.
1267
- */
1268
- public function load_admin_cart_abandonment_script() {
1269
-
1270
- $wcar_page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
1271
-
1272
- if ( ! ( WCF_CA_PAGE_NAME === $wcar_page ) ) {
1273
- return;
1274
- }
1275
-
1276
- // Styles.
1277
- wp_enqueue_style( 'cartflows-cart-abandonment-admin', CARTFLOWS_CA_URL . 'admin/assets/css/admin-cart-abandonment.css', array(), CARTFLOWS_CA_VER );
1278
-
1279
- wp_enqueue_script(
1280
- 'cartflows-ca-email-tmpl-settings',
1281
- CARTFLOWS_CA_URL . 'admin/assets/js/admin-email-templates.js',
1282
- array( 'jquery' ),
1283
- CARTFLOWS_CA_VER,
1284
- false
1285
- );
1286
-
1287
- if ( WCF_CA_PAGE_NAME === $wcar_page ) {
1288
- $filter_table = filter_input( INPUT_GET, 'filter_table', FILTER_SANITIZE_STRING );
1289
- $from_date = filter_input( INPUT_GET, 'from_date', FILTER_SANITIZE_STRING );
1290
- $to_date = filter_input( INPUT_GET, 'to_date', FILTER_SANITIZE_STRING );
1291
- }
1292
-
1293
- $vars = array(
1294
- 'url' => 'admin-ajax.php',
1295
-
1296
- // For delete coupons.
1297
- '_delete_coupon_nonce' => wp_create_nonce( 'wcf_ca_delete_garbage_coupons' ),
1298
- '_confirm_msg' => __( 'Do you really want to delete the used and expired coupons created by Cart Abandonment Plugin?', 'woo-cart-abandonment-recovery' ),
1299
- '_confirm_msg_export' => __( 'Do you really want to export orders?', 'woo-cart-abandonment-recovery' ),
1300
-
1301
- // For Search orders.
1302
- '_search_button_nonce' => wp_create_nonce( 'wcf_ca_search_orders' ),
1303
- '_result_msg' => __( 'No such order is found.', 'woo-cart-abandonment-recovery' ),
1304
-
1305
- );
1306
- wp_localize_script( 'cartflows-ca-email-tmpl-settings', 'wcf_ca_localized_vars', $vars );
1307
- }
1308
-
1309
-
1310
- /**
1311
- * Render Cart abandonment display button beside title.
1312
- */
1313
- public function setup_cart_abandonment_button() {
1314
-
1315
- if ( ! Cartflows_Admin::is_flow_edit_admin() ) {
1316
- return;
1317
- }
1318
-
1319
- $reports_btn_markup = '<style>.wrap{ position:relative;}</style>';
1320
- $reports_btn_markup .= "<div class='wcf-reports-button-wrap'>";
1321
- $reports_btn_markup .= "<button class='wcf-cart-abandonment-reports-popup button button-secondary'>";
1322
- $reports_btn_markup .= esc_html__( 'View Report', 'woo-cart-abandonment-recovery' );
1323
- $reports_btn_markup .= '</button>';
1324
- $reports_btn_markup .= '</div>';
1325
-
1326
- echo wp_kses_post( $reports_btn_markup );
1327
-
1328
- }
1329
-
1330
- /**
1331
- * Get start and end date for given interval.
1332
- *
1333
- * @param string $interval interval .
1334
- * @return array
1335
- */
1336
- public function get_start_end_by_interval( $interval ) {
1337
-
1338
- if ( 'today' === $interval ) {
1339
- $start_date = gmdate( 'Y-m-d' );
1340
- $end_date = gmdate( 'Y-m-d' );
1341
- } else {
1342
-
1343
- $days = $interval;
1344
-
1345
- $start_date = gmdate( 'Y-m-d', strtotime( '-' . $days . ' days' ) );
1346
- $end_date = gmdate( 'Y-m-d' );
1347
- }
1348
-
1349
- return array(
1350
- 'start' => $start_date,
1351
- 'end' => $end_date,
1352
- );
1353
- }
1354
-
1355
-
1356
- /**
1357
- * Get Attributable revenue.
1358
- * Represents the revenue generated by this campaign.
1359
- *
1360
- * @param string $from_date from date.
1361
- * @param string $to_date to date.
1362
- * @param string $type abondened|completed.
1363
- */
1364
- public function get_report_by_type( $from_date, $to_date, $type = WCF_CART_ABANDONED_ORDER ) {
1365
- global $wpdb;
1366
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1367
- $minutes = wcf_ca()->utils->get_cart_abandonment_tracking_cut_off_time();
1368
- $attributable_revenue = $wpdb->get_row(
1369
- $wpdb->prepare( "SELECT SUM(`cart_total`) as revenue, count('*') as no_of_orders FROM {$cart_abandonment_table} WHERE `order_status` = %s AND DATE(`time`) >= %s AND DATE(`time`) <= %s ", $type, $from_date, $to_date ), // phpcs:ignore
1370
- ARRAY_A
1371
- );
1372
- return $attributable_revenue;
1373
- }
1374
-
1375
-
1376
- /**
1377
- * Get checkout url.
1378
- *
1379
- * @param integer $post_id post id.
1380
- * @param string $token_data token data.
1381
- * @return string
1382
- */
1383
- public function get_checkout_url( $post_id, $token_data ) {
1384
-
1385
- $token = $this->wcf_generate_token( (array) $token_data );
1386
- $checkout_url = get_permalink( $post_id ) . '?wcf_ac_token=' . $token;
1387
- return esc_url( $checkout_url );
1388
- }
1389
-
1390
- /**
1391
- * Geberate the token for the given data.
1392
- *
1393
- * @param array $data data.
1394
- */
1395
- public function wcf_generate_token( $data ) {
1396
- return urlencode( base64_encode( http_build_query( $data ) ) );
1397
- }
1398
-
1399
- /**
1400
- * Decode and get the original contents.
1401
- *
1402
- * @param string $token token.
1403
- */
1404
- public function wcf_decode_token( $token ) {
1405
- $token = sanitize_text_field( $token );
1406
- parse_str( base64_decode( urldecode( $token ) ), $token );
1407
- return $token;
1408
- }
1409
-
1410
- /**
1411
- * Render Cart abandonment tabs.
1412
- *
1413
- * @since 1.1.5
1414
- */
1415
- public function wcf_display_tabs() {
1416
-
1417
- $wcar_action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
1418
- $sub_action = filter_input( INPUT_GET, 'sub_action', FILTER_SANITIZE_STRING );
1419
-
1420
- if ( ! $wcar_action ) {
1421
- $wcar_action = WCF_ACTION_REPORTS;
1422
- $active_settings = '';
1423
- $active_reports = '';
1424
- $active_email_templates = '';
1425
- }
1426
-
1427
- switch ( $wcar_action ) {
1428
- case WCF_ACTION_SETTINGS:
1429
- $active_settings = 'nav-tab-active';
1430
- break;
1431
- case WCF_ACTION_REPORTS:
1432
- $active_reports = 'nav-tab-active';
1433
- break;
1434
- case WCF_ACTION_EMAIL_TEMPLATES:
1435
- $active_email_templates = 'nav-tab-active';
1436
- break;
1437
- default:
1438
- $active_reports = 'nav-tab-active';
1439
- break;
1440
- }
1441
- // phpcs:disable
1442
- ?>
1443
-
1444
-
1445
- <div class="nav-tab-wrapper woo-nav-tab-wrapper">
1446
-
1447
- <?php
1448
- $url = add_query_arg( array(
1449
- 'page' => WCF_CA_PAGE_NAME,
1450
- 'action' => WCF_ACTION_REPORTS
1451
- ), admin_url( '/admin.php' ) )
1452
- ?>
1453
- <a href="<?php echo $url; ?>"
1454
- class="nav-tab
1455
- <?php
1456
- if ( isset( $active_reports ) ) {
1457
- echo $active_reports;}
1458
- ?>
1459
- ">
1460
- <?php _e( 'Report', 'woo-cart-abandonment-recovery' ); ?>
1461
- </a>
1462
-
1463
- <?php
1464
- $url = add_query_arg( array(
1465
- 'page' => WCF_CA_PAGE_NAME,
1466
- 'action' => WCF_ACTION_EMAIL_TEMPLATES
1467
- ), admin_url( '/admin.php' ) )
1468
- ?>
1469
- <a href="<?php echo $url; ?>"
1470
- class="nav-tab
1471
- <?php
1472
- if ( isset( $active_email_templates ) ) {
1473
- echo $active_email_templates;}
1474
- ?>
1475
- ">
1476
- <?php _e( 'Follow-Up Emails', 'woo-cart-abandonment-recovery' ); ?>
1477
- </a>
1478
-
1479
- <?php
1480
- $url = add_query_arg( array(
1481
- 'page' => WCF_CA_PAGE_NAME,
1482
- 'action' => WCF_ACTION_SETTINGS
1483
- ), admin_url( '/admin.php' ) )
1484
- ?>
1485
- <a href="<?php echo $url; ?>"
1486
- class="nav-tab
1487
- <?php
1488
- if ( isset( $active_settings ) ) {
1489
- echo $active_settings;}
1490
- ?>
1491
- ">
1492
- <?php _e( 'Settings', 'woo-cart-abandonment-recovery' ); ?>
1493
- </a>
1494
-
1495
- </div>
1496
- <?php
1497
- // phpcs:enable
1498
- }
1499
-
1500
- /**
1501
- * Render Cart abandonment settings.
1502
- *
1503
- * @since 1.1.5
1504
- */
1505
- public function wcf_display_settings() {
1506
- ?>
1507
-
1508
- <form method="post" action="options.php">
1509
- <?php settings_fields( WCF_CA_SETTINGS_OPTION_GROUP ); ?>
1510
- <?php do_settings_sections( WCF_CA_PAGE_NAME ); ?>
1511
- <?php submit_button(); ?>
1512
- </form>
1513
-
1514
- <?php
1515
- }
1516
-
1517
- /**
1518
- * Render Cart abandonment reports.
1519
- *
1520
- * @since 1.1.5
1521
- */
1522
- public function wcf_display_reports() {
1523
-
1524
- $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
1525
- $filter_table = filter_input( INPUT_GET, 'filter_table', FILTER_SANITIZE_STRING );
1526
-
1527
- if ( ! $filter ) {
1528
- $filter = 'last_month';
1529
- }
1530
- if ( ! $filter_table ) {
1531
- $filter_table = WCF_CART_ABANDONED_ORDER;
1532
- }
1533
-
1534
- $from_date = filter_input( INPUT_GET, 'from_date', FILTER_SANITIZE_STRING );
1535
- $to_date = filter_input( INPUT_GET, 'to_date', FILTER_SANITIZE_STRING );
1536
- $export_data = filter_input( INPUT_GET, 'export_data', FILTER_VALIDATE_BOOLEAN );
1537
-
1538
- switch ( $filter ) {
1539
-
1540
- case 'yesterday':
1541
- $to_date = gmdate( 'Y-m-d', strtotime( '-1 days' ) );
1542
- $from_date = $to_date;
1543
- break;
1544
- case 'today':
1545
- $to_date = gmdate( 'Y-m-d' );
1546
- $from_date = $to_date;
1547
- break;
1548
- case 'last_week':
1549
- $from_date = gmdate( 'Y-m-d', strtotime( '-7 days' ) );
1550
- $to_date = gmdate( 'Y-m-d' );
1551
- break;
1552
- case 'last_month':
1553
- $from_date = gmdate( 'Y-m-d', strtotime( '-1 months' ) );
1554
- $to_date = gmdate( 'Y-m-d' );
1555
- break;
1556
- case 'custom':
1557
- $to_date = $to_date ? $to_date : gmdate( 'Y-m-d' );
1558
- $from_date = $from_date ? $from_date : $to_date;
1559
- break;
1560
-
1561
- }
1562
-
1563
- $abandoned_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_ABANDONED_ORDER );
1564
- $recovered_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_COMPLETED_ORDER );
1565
- $lost_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_LOST_ORDER );
1566
-
1567
- $wcf_list_table = Cartflows_Ca_Cart_Abandonment_Table::get_instance();
1568
- $wcf_list_table->prepare_items( $filter_table, $from_date, $to_date );
1569
-
1570
- if ( $export_data ) {
1571
-
1572
- $this->download_send_headers();
1573
- echo $this->array2csv( $wcf_list_table->items ); //phpcs:ignore
1574
- die;
1575
-
1576
- }
1577
-
1578
- $conversion_rate = 0;
1579
- $total_orders = ( $recovered_report['no_of_orders'] + $abandoned_report['no_of_orders'] + $lost_report['no_of_orders'] );
1580
- if ( $total_orders ) {
1581
- $conversion_rate = ( $recovered_report['no_of_orders'] / $total_orders ) * 100;
1582
- }
1583
-
1584
- global $woocommerce;
1585
- $conversion_rate = number_format_i18n( $conversion_rate, 2 );
1586
- $currency_symbol = get_woocommerce_currency_symbol();
1587
- require_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-cart-abandonment-reports.php';
1588
-
1589
- }
1590
-
1591
-
1592
- /**
1593
- * Show report details for specific order.
1594
- */
1595
- public function wcf_display_report_details() {
1596
-
1597
- $sesson_id = filter_input( INPUT_GET, 'session_id', FILTER_SANITIZE_STRING );
1598
-
1599
- if ( $sesson_id ) {
1600
- $details = $this->get_checkout_details( $sesson_id );
1601
- $user_details = (object) unserialize( $details->other_fields );
1602
- $scheduled_emails = $this->fetch_scheduled_emails( $sesson_id );
1603
-
1604
- require_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-ca-single-report-details.php';
1605
- }
1606
-
1607
- }
1608
-
1609
- /**
1610
- * Check and show warning message if cart abandonment is disabled.
1611
- */
1612
- public function wcf_show_warning_ca() {
1613
- $settings_url = add_query_arg(
1614
- array(
1615
- 'page' => WCF_CA_PAGE_NAME,
1616
- 'action' => WCF_ACTION_SETTINGS,
1617
- ),
1618
- admin_url( '/admin.php' )
1619
- );
1620
-
1621
- if ( ! wcf_ca()->utils->is_cart_abandonment_tracking_enabled() ) {
1622
- ?>
1623
- <div class="notice notice-warning is-dismissible">
1624
- <p>
1625
- <?php echo __('Looks like abandonment tracking is disabled! Please enable it from <a href=' . esc_url($settings_url) . '> <strong>settings</strong></a>.', 'woo-cart-abandonment-recovery'); // phpcs:ignore
1626
- ?>
1627
- </p>
1628
- </div>
1629
- <?php
1630
- }
1631
- }
1632
-
1633
- /**
1634
- * Callback trigger event to send the emails.
1635
- */
1636
- public function send_emails_to_callback() {
1637
-
1638
- global $wpdb;
1639
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1640
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1641
- $email_template_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE;
1642
-
1643
- $current_time = current_time( WCF_CA_DATETIME_FORMAT );
1644
- // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
1645
- $emails_send_to = $wpdb->get_results(
1646
- $wpdb->prepare(
1647
- 'SELECT *, EHT.id as email_history_id, ETT.id as email_template_id FROM ' . $email_history_table . ' as EHT
1648
- INNER JOIN ' . $cart_abandonment_table . ' as CAT ON EHT.`ca_session_id` = CAT.`session_id`
1649
- INNER JOIN ' . $email_template_table . ' as ETT ON ETT.`id` = EHT.`template_id`
1650
- WHERE CAT.`order_status` = %s AND CAT.unsubscribed = 0 AND EHT.`email_sent` = 0 AND EHT.`scheduled_time` <= %s',
1651
- WCF_CART_ABANDONED_ORDER,
1652
- $current_time
1653
- )
1654
- );
1655
- // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
1656
- foreach ( $emails_send_to as $email_send_to ) {
1657
- $email_result = $this->send_email_templates( $email_send_to );
1658
- if ( $email_result ) {
1659
- $wpdb->update(
1660
- $email_history_table,
1661
- array( 'email_sent' => true ),
1662
- array( 'id' => $email_send_to->email_history_id )
1663
- );
1664
- }
1665
- }
1666
- }
1667
-
1668
-
1669
- /**
1670
- * Create a dummy object for the preview email.
1671
- *
1672
- * @return stdClass
1673
- */
1674
- public function create_dummy_session_for_preview_email() {
1675
-
1676
- $email_data = new stdClass();
1677
- $current_user = wp_get_current_user();
1678
- $email_data->email_template_id = null;
1679
- $email_data->checkout_id = wc_get_page_id( 'checkout' );
1680
- $email_data->session_id = 'dummy-session-id';
1681
- $email_send_to = filter_input( INPUT_POST, 'email_send_to', FILTER_SANITIZE_EMAIL );
1682
- $email_data->email = $email_send_to ? $email_send_to : $current_user->user_email;
1683
- $email_data->email_body = filter_input( INPUT_POST, 'email_body', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
1684
- $email_data->email_subject = filter_input( INPUT_POST, 'email_subject', FILTER_SANITIZE_STRING );
1685
- $email_data->email_body = html_entity_decode( $email_data->email_body, ENT_COMPAT, 'UTF-8' );
1686
- $email_data->other_fields = serialize(
1687
- array(
1688
- 'wcf_first_name' => $current_user->user_firstname,
1689
- 'wcf_last_name' => $current_user->user_lastname,
1690
- )
1691
- );
1692
- if ( ! WC()->cart->get_cart_contents_count() ) {
1693
- $args = array(
1694
- 'posts_per_page' => 1,
1695
- 'orderby' => 'rand',
1696
- 'post_type' => 'product',
1697
- 'meta_query' => array( //phpcs:ignore
1698
- // Exclude out of stock products.
1699
- array(
1700
- 'key' => '_stock_status',
1701
- 'value' => 'outofstock',
1702
- 'compare' => 'NOT IN',
1703
- ),
1704
- ),
1705
- 'tax_query' => array( //phpcs:ignore
1706
- array(
1707
- 'taxonomy' => 'product_type',
1708
- 'field' => 'slug',
1709
- 'terms' => 'simple',
1710
- ),
1711
- ),
1712
- );
1713
-
1714
- $random_products = get_posts( $args );
1715
- if ( ! empty( $random_products ) ) {
1716
- $random_product = reset( $random_products );
1717
- WC()->cart->add_to_cart( $random_product->ID );
1718
- }
1719
- }
1720
-
1721
- $email_data->cart_total = WC()->cart->total + floatval( WC()->cart->get_cart_shipping_total() );
1722
- $email_data->cart_contents = serialize( WC()->cart->get_cart() );
1723
- $email_data->time = current_time( WCF_CA_DATETIME_FORMAT );
1724
- return $email_data;
1725
- }
1726
-
1727
- /**
1728
- * Callback function to send email templates.
1729
- *
1730
- * @param array $email_data email data .
1731
- * @param boolean $preview_email preview email.
1732
- * @since 1.0.0
1733
- */
1734
- public function send_email_templates( $email_data, $preview_email = false ) {
1735
-
1736
- if ( $preview_email ) {
1737
- $email_data = $this->create_dummy_session_for_preview_email();
1738
- }
1739
-
1740
- if ( filter_var( $email_data->email, FILTER_VALIDATE_EMAIL ) ) {
1741
- if ( ! $preview_email ) {
1742
- if ( ! $this->check_if_already_purchased_by_email_product_ids( $email_data, $email_data->cart_contents ) ) {
1743
- return false;
1744
- }
1745
- }
1746
-
1747
- $other_fields = unserialize( $email_data->other_fields );
1748
-
1749
- $from_email_name = get_option( 'wcf_ca_from_name' );
1750
- $reply_name_preview = get_option( 'wcf_ca_reply_email' );
1751
- $from_email_preview = get_option( 'wcf_ca_from_email' );
1752
-
1753
- $user_first_name = ucfirst( $other_fields['wcf_first_name'] );
1754
- $user_first_name = $user_first_name ? $user_first_name : __( 'there', 'woo-cart-abandonment-recovery' );
1755
- $user_last_name = ucfirst( $other_fields['wcf_last_name'] );
1756
- $user_full_name = trim( $user_first_name . ' ' . $user_last_name );
1757
-
1758
- $subject_email_preview = stripslashes( html_entity_decode( $email_data->email_subject, ENT_QUOTES, 'UTF-8' ) );
1759
- $subject_email_preview = convert_smilies( $subject_email_preview );
1760
- $subject_email_preview = str_replace( '{{customer.firstname}}', $user_first_name, $subject_email_preview );
1761
- $body_email_preview = convert_smilies( $email_data->email_body );
1762
- $body_email_preview = str_replace( '{{customer.firstname}}', $user_first_name, $body_email_preview );
1763
- $body_email_preview = str_replace( '{{customer.lastname}}', $user_last_name, $body_email_preview );
1764
- $body_email_preview = str_replace( '{{customer.fullname}}', $user_full_name, $body_email_preview );
1765
-
1766
- $email_instance = Cartflows_Ca_Email_Templates::get_instance();
1767
- if ( $preview_email ) {
1768
- $coupon_code = 'DUMMY-COUPON';
1769
- } else {
1770
- $override_global_coupon = $email_instance->get_email_template_meta_by_key( $email_data->email_template_id, 'override_global_coupon' );
1771
- if ( $override_global_coupon->meta_value ) {
1772
- $email_history = $email_instance->get_email_history_by_id( $email_data->email_history_id );
1773
- $coupon_code = $email_history->coupon_code;
1774
- } else {
1775
- $coupon_code = $email_data->coupon_code;
1776
- }
1777
- }
1778
-
1779
- $auto_apply_coupon = $email_instance->get_email_template_meta_by_key( $email_data->email_template_id, 'auto_coupon' );
1780
-
1781
- $token_data = array(
1782
- 'wcf_session_id' => $email_data->session_id,
1783
- 'wcf_coupon_code' => isset( $auto_apply_coupon ) && $auto_apply_coupon->meta_value ? $coupon_code : null,
1784
- 'wcf_preview_email' => $preview_email ? true : false,
1785
- );
1786
-
1787
- $checkout_url = $this->get_checkout_url( $email_data->checkout_id, $token_data );
1788
-
1789
- $body_email_preview = str_replace( '{{cart.coupon_code}}', $coupon_code, $body_email_preview );
1790
-
1791
- $current_time_stamp = $email_data->time;
1792
- $body_email_preview = str_replace( '{{cart.abandoned_date}}', $current_time_stamp, $body_email_preview );
1793
- $unsubscribe_element = '<a target="_blank" style="color: lightgray" href="' . $checkout_url . '&unsubscribe=true ">' . __( 'Unsubscribe', 'woo-cart-abandonment-recovery' ) . '</a>';
1794
- $body_email_preview = str_replace( '{{cart.unsubscribe}}', $unsubscribe_element, $body_email_preview );
1795
- $body_email_preview = str_replace( 'http://{{cart.checkout_url}}', '{{cart.checkout_url}}', $body_email_preview );
1796
- $body_email_preview = str_replace( 'https://{{cart.checkout_url}}', '{{cart.checkout_url}}', $body_email_preview );
1797
- $body_email_preview = str_replace( '{{cart.checkout_url}}', $checkout_url, $body_email_preview );
1798
- $host = wp_parse_url( get_site_url() );
1799
- $body_email_preview = str_replace( '{{site.url}}', $host['host'], $body_email_preview );
1800
-
1801
- if ( false !== strpos( $body_email_preview, '{{cart.product.names}}' ) ) {
1802
- $body_email_preview = str_replace( '{{cart.product.names}}', $this->get_comma_separated_products( $email_data->cart_contents ), $body_email_preview );
1803
- }
1804
-
1805
- $admin_user = get_users(
1806
- array(
1807
- 'role' => 'Administrator',
1808
- 'number' => 1,
1809
- )
1810
- );
1811
- $admin_user = reset( $admin_user );
1812
- $admin_first_name = $admin_user->user_firstname ? $admin_user->user_firstname : 'Admin';
1813
- $body_email_preview = str_replace( '{{admin.firstname}}', $admin_first_name, $body_email_preview );
1814
- $body_email_preview = str_replace( '{{admin.company}}', get_bloginfo( 'name' ), $body_email_preview );
1815
-
1816
- $headers = 'From: ' . $from_email_name . ' <' . $from_email_preview . '>' . "\r\n";
1817
- $headers .= 'Content-Type: text/html' . "\r\n";
1818
- $headers .= 'Reply-To: ' . $reply_name_preview . ' ' . "\r\n";
1819
- $var = $this->get_email_product_block( $email_data->cart_contents, $email_data->cart_total );
1820
-
1821
- $body_email_preview = str_replace( '{{cart.product.table}}', $var, $body_email_preview );
1822
- $body_email_preview = wpautop( $body_email_preview );
1823
- $mail_result = wp_mail( $email_data->email, $subject_email_preview, stripslashes( $body_email_preview ), $headers );
1824
- if ( $mail_result ) {
1825
- return true;
1826
- } else {
1827
- // Retry sending mail.
1828
- $mail_result = wp_mail( $email_data->email, $subject_email_preview, stripslashes( $body_email_preview ), $headers );
1829
- if ( ! $preview_email ) {
1830
- return true;
1831
- }
1832
- return false;
1833
- }
1834
- } else {
1835
- return false;
1836
- }
1837
-
1838
- }
1839
-
1840
- /**
1841
- * Generate comma separated products.
1842
- *
1843
- * @param object $cart_contents user cart details.
1844
- */
1845
- public function get_comma_separated_products( $cart_contents ) {
1846
- $cart_comma_string = '';
1847
- if ( ! $cart_contents ) {
1848
- return $cart_comma_string;
1849
- }
1850
- $cart_data = unserialize( $cart_contents );
1851
-
1852
- $cart_length = count( $cart_data );
1853
- $index = 0;
1854
- foreach ( $cart_data as $key => $product ) {
1855
-
1856
- if ( ! isset( $product['product_id'] ) ) {
1857
- continue;
1858
- }
1859
-
1860
- $cart_product = wc_get_product( $product['product_id'] );
1861
-
1862
- if ( $cart_product ) {
1863
- $cart_comma_string = $cart_comma_string . $cart_product->get_title();
1864
- if ( ( $cart_length - 2 ) === $index ) {
1865
- $cart_comma_string = $cart_comma_string . ' & ';
1866
- } elseif ( ( $cart_length - 1 ) !== $index ) {
1867
- $cart_comma_string = $cart_comma_string . ', ';
1868
- }
1869
- $index++;
1870
- }
1871
- }
1872
- return $cart_comma_string;
1873
-
1874
- }
1875
-
1876
- /**
1877
- * Generate the view for email product cart block.
1878
- *
1879
- * @param object $cart_contents user cart contents details.
1880
- * @param float $cart_total user cart total.
1881
- * @return string
1882
- */
1883
- public function get_email_product_block( $cart_contents, $cart_total ) {
1884
-
1885
- $cart_items = unserialize( $cart_contents );
1886
-
1887
- if ( ! is_array( $cart_items ) || ! count( $cart_items ) ) {
1888
- return;
1889
- }
1890
-
1891
- $currency_symbol = get_woocommerce_currency_symbol();
1892
- $tr = '';
1893
- $style = array(
1894
- 'product_image' => array(
1895
- 'style' => 'height: 42px; width: 42px;',
1896
- ),
1897
- 'table' => array(
1898
- 'style' => 'color: #636363; border: 1px solid #e5e5e5;',
1899
- 'attribute' => 'align= left;',
1900
- ),
1901
- );
1902
-
1903
- $style_filter = apply_filters( 'woo_ca_email_template_table_style', $style );
1904
- $product_image_style = isset( $style_filter['product_image']['style'] ) ? $style_filter['product_image']['style'] : '';
1905
- $style = isset( $style_filter['table']['style'] ) ? $style_filter['table']['style'] : '';
1906
-
1907
- foreach ( $cart_items as $cart_item ) {
1908
-
1909
- if ( isset( $cart_item['product_id'] ) && isset( $cart_item['quantity'] ) && isset( $cart_item['line_total'] ) ) {
1910
- $id = 0 !== $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
1911
- $tr = $tr . '<tr style=' . $style . ' align="center">
1912
- <td style="' . $style . '"><img class="demo_img" style="' . $product_image_style . '" src="' . esc_url( get_the_post_thumbnail_url( $id ) ) . '"></td>
1913
- <td style="' . $style . '">' . get_the_title( $id ) . '</td>
1914
- <td style="' . $style . '"> ' . $cart_item['quantity'] . ' </td>
1915
- <td style="' . $style . '">' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1916
- <td style="' . $style . '" >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1917
- </tr> ';
1918
- }
1919
- }
1920
-
1921
- /**
1922
- * Add filter to toggle the Cart Total row.
1923
- */
1924
- $enable_cart_total = apply_filters( 'woo_ca_recovery_enable_cart_total', false );
1925
- if ( $enable_cart_total ) {
1926
- $tr = $tr . '<tr style="' . $style . '" align="center">
1927
- <td colspan="4" style="' . $style . '"> ' . __( 'Cart Total ( Cart Total + Shipping + Tax )', 'woo-cart-abandonment-recovery' ) . ' </td>
1928
- <td style="' . $style . '" >' . $currency_symbol . number_format_i18n( $cart_total, 2 ) . '</td>
1929
- </tr> ';
1930
- }
1931
-
1932
- return '<table ' . $style_filter['table']['attribute'] . ' cellpadding="10" cellspacing="0" style="float: none; border: 1px solid #e5e5e5;">
1933
- <tr align="center">
1934
- <th style="' . $style . '">' . __( 'Item', 'woo-cart-abandonment-recovery' ) . '</th>
1935
- <th style="' . $style . '">' . __( 'Name', 'woo-cart-abandonment-recovery' ) . '</th>
1936
- <th style="' . $style . '">' . __( 'Quantity', 'woo-cart-abandonment-recovery' ) . '</th>
1937
- <th style="' . $style . '">' . __( 'Price', 'woo-cart-abandonment-recovery' ) . '</th>
1938
- <th style="' . $style . '">' . __( 'Line Subtotal', 'woo-cart-abandonment-recovery' ) . '</th>
1939
- </tr> ' . $tr . '
1940
- </table>';
1941
- }
1942
-
1943
- /**
1944
- * Generate the view for admin product cart block.
1945
- *
1946
- * @param object $cart_contents user cart contents details.
1947
- * @param float $cart_total user cart total.
1948
- * @return string
1949
- */
1950
- public function get_admin_product_block( $cart_contents, $cart_total ) {
1951
-
1952
- $cart_items = unserialize( $cart_contents );
1953
-
1954
- if ( ! is_array( $cart_items ) || ! count( $cart_items ) ) {
1955
- return;
1956
- }
1957
-
1958
- $currency_symbol = get_woocommerce_currency_symbol();
1959
- $tr = '';
1960
- $total = 0;
1961
- $discount = 0;
1962
- $tax = 0;
1963
-
1964
- foreach ( $cart_items as $cart_item ) {
1965
-
1966
- if ( isset( $cart_item['product_id'] ) && isset( $cart_item['quantity'] ) && isset( $cart_item['line_total'] ) && isset( $cart_item['line_subtotal'] ) ) {
1967
- $id = 0 !== $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
1968
- $discount = number_format_i18n( $discount + ( $cart_item['line_subtotal'] - $cart_item['line_total'] ), 2 );
1969
- $total = number_format_i18n( $total + $cart_item['line_subtotal'], 2 );
1970
- $tax = number_format_i18n( $tax + $cart_item['line_tax'], 2 );
1971
- $tr = $tr . '<tr align="center">
1972
- <td ><img class="demo_img" width="42" height="42" src=" ' . esc_url( get_the_post_thumbnail_url( $id ) ) . ' "/></td>
1973
- <td >' . get_the_title( $id ) . '</td>
1974
- <td > ' . $cart_item['quantity'] . ' </td>
1975
- <td >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1976
- <td >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1977
- </tr> ';
1978
- }
1979
- }
1980
-
1981
- return '<table align="left" cellspacing="0" class="widefat fixed striped posts">
1982
- <thead>
1983
- <tr align="center">
1984
- <th >' . __( 'Item', 'woo-cart-abandonment-recovery' ) . '</th>
1985
- <th >' . __( 'Name', 'woo-cart-abandonment-recovery' ) . '</th>
1986
- <th >' . __( 'Quantity', 'woo-cart-abandonment-recovery' ) . '</th>
1987
- <th >' . __( 'Price', 'woo-cart-abandonment-recovery' ) . '</th>
1988
- <th >' . __( 'Line Subtotal', 'woo-cart-abandonment-recovery' ) . '</th>
1989
- </tr>
1990
- </thead>
1991
- <tbody>
1992
- ' . $tr . '
1993
- <tr align="center" id="wcf-ca-discount">
1994
- <td colspan="4" >' . __( 'Discount', 'woo-cart-abandonment-recovery' ) . '</td>
1995
- <td>' . $currency_symbol . ( $discount ) . '</td>
1996
- </tr>
1997
- <tr align="center" id="wcf-ca-other">
1998
- <td colspan="4" >' . __( 'Other', 'woo-cart-abandonment-recovery' ) . '</td>
1999
- <td>' . $currency_symbol . ( $tax ) . '</td>
2000
- </tr>
2001
-
2002
- <tr align="center" id="wcf-ca-shipping">
2003
- <td colspan="4" >' . __( 'Shipping', 'woo-cart-abandonment-recovery' ) . '</td>
2004
- <td>' . $currency_symbol . number_format_i18n( $discount + ( $cart_total - $total ) - $tax, 2 ) . '</td>
2005
- </tr>
2006
- <tr align="center" id="wcf-ca-cart-total">
2007
- <td colspan="4" >' . __( 'Cart Total', 'woo-cart-abandonment-recovery' ) . '</td>
2008
- <td>' . $currency_symbol . $cart_total . '</td>
2009
- </tr>
2010
- </tbody>
2011
- </table>';
2012
- }
2013
-
2014
- /**
2015
- * Schedule events for the abadoned carts to send emails.
2016
- *
2017
- * @param integer $session_id user session id.
2018
- * @param boolean $force_reschedule force reschedule.
2019
- */
2020
- public function schedule_emails( $session_id, $force_reschedule = false ) {
2021
-
2022
- $checkout_details = $this->get_checkout_details( $session_id );
2023
-
2024
- if ( ( $checkout_details->unsubscribed ) || ( WCF_CART_COMPLETED_ORDER === $checkout_details->order_status ) ) {
2025
- return;
2026
- }
2027
-
2028
- $scheduled_emails = $this->fetch_scheduled_emails( $session_id );
2029
- $scheduled_templates = array_column( $scheduled_emails, 'template_id' ); //phpcs:ignore
2030
- $scheduled_time_from = $checkout_details->time;
2031
-
2032
- if ( $force_reschedule ) {
2033
- $scheduled_time_from = current_time( WCF_CA_DATETIME_FORMAT );
2034
- }
2035
-
2036
- $email_tmpl = Cartflows_Ca_Email_Templates::get_instance();
2037
- $templates = $email_tmpl->fetch_all_active_templates();
2038
-
2039
- global $wpdb;
2040
-
2041
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
2042
-
2043
- foreach ( $templates as $template ) {
2044
-
2045
- if ( false !== array_search( $template->id, $scheduled_templates, true ) && false === $force_reschedule ) {
2046
- continue;
2047
- }
2048
-
2049
- $timestamp_str = '+' . $template->frequency . ' ' . $template->frequency_unit . 'S';
2050
- $scheduled_time = gmdate( WCF_CA_DATETIME_FORMAT, strtotime( $scheduled_time_from . $timestamp_str ) );
2051
- $discount_type = $email_tmpl->get_email_template_meta_by_key( $template->id, 'discount_type' );
2052
- $discount_type = isset( $discount_type->meta_value ) ? $discount_type->meta_value : '';
2053
- $amount = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_amount' );
2054
- $amount = isset( $amount->meta_value ) ? $amount->meta_value : '';
2055
-
2056
- $coupon_expiry_date = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_expiry_date' );
2057
- $coupon_expiry_unit = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_expiry_unit' );
2058
- $coupon_expiry_date = isset( $coupon_expiry_date->meta_value ) ? $coupon_expiry_date->meta_value : '';
2059
- $coupon_expiry_unit = isset( $coupon_expiry_unit->meta_value ) ? $coupon_expiry_unit->meta_value : 'hours';
2060
-
2061
- $coupon_expiry_date = $coupon_expiry_date ? strtotime( $scheduled_time . ' +' . $coupon_expiry_date . ' ' . $coupon_expiry_unit ) : '';
2062
-
2063
- $free_shipping_coupon = $email_tmpl->get_email_template_meta_by_key( $template->id, 'free_shipping_coupon' );
2064
- $free_shipping = ( isset( $free_shipping_coupon ) && ( $free_shipping_coupon->meta_value ) ) ? 'yes' : 'no';
2065
-
2066
- $individual_use_only = $email_tmpl->get_email_template_meta_by_key( $template->id, 'individual_use_only' );
2067
- $individual_use = ( isset( $individual_use_only ) && ( $individual_use_only->meta_value ) ) ? 'yes' : 'no';
2068
-
2069
- $override_global_coupon = $email_tmpl->get_email_template_meta_by_key( $template->id, 'override_global_coupon' );
2070
-
2071
- $new_coupon_code = '';
2072
- if ( $override_global_coupon->meta_value ) {
2073
- $new_coupon_code = $this->generate_coupon_code( $discount_type, $amount, $coupon_expiry_date, $free_shipping, $individual_use );
2074
- }
2075
-
2076
- $wpdb->replace(
2077
- $email_history_table,
2078
- array(
2079
- 'template_id' => $template->id,
2080
- 'ca_session_id' => $checkout_details->session_id,
2081
- 'coupon_code' => $new_coupon_code,
2082
- 'scheduled_time' => $scheduled_time,
2083
- )
2084
- );
2085
- }
2086
- }
2087
-
2088
- /**
2089
- * Fetch all the scheduled emails with templates for the specific session.
2090
- *
2091
- * @param string $session_id session id.
2092
- * @param boolean $fetch_sent sfetch sent emails.
2093
- * @return array|object|null
2094
- */
2095
- public function fetch_scheduled_emails( $session_id, $fetch_sent = false ) {
2096
- global $wpdb;
2097
- $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
2098
- $email_template_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE;
2099
-
2100
- $query = $wpdb->prepare("SELECT * FROM $email_history_table as eht INNER JOIN $email_template_table as ett ON eht.template_id = ett.id WHERE ca_session_id = %s", sanitize_text_field($session_id)); // phpcs:ignore
2101
-
2102
- if ( $fetch_sent ) {
2103
- $query .= ' AND email_sent = 1';
2104
- }
2105
-
2106
- $result = $wpdb->get_results( $query ); // phpcs:ignore
2107
- return $result;
2108
- }
2109
-
2110
- /**
2111
- * Delete orders from cart abandonment table whose cart total is zero and order status is abandoned.
2112
- */
2113
- public function delete_empty_abandoned_order() {
2114
- global $wpdb;
2115
-
2116
- $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
2117
-
2118
- $where = array(
2119
- 'cart_total' => 0,
2120
- );
2121
-
2122
- $wpdb->delete( $cart_abandonment_table, $where );
2123
- }
2124
-
2125
-
2126
- /**
2127
- * Check if transient is set for delete garbage coupons.
2128
- */
2129
- public function delete_used_and_expired_coupons() {
2130
- $is_ajax_request = wp_doing_ajax();
2131
- $is_transient_set = false;
2132
- global $wpdb;
2133
- if ( $is_ajax_request ) {
2134
- check_ajax_referer( 'wcf_ca_delete_garbage_coupons', 'security' );
2135
- } else {
2136
- $is_transient_set = get_transient( 'woocommerce_ca_delete_garbage_coupons' );
2137
- }
2138
-
2139
- if ( false === $is_transient_set || $is_ajax_request ) {
2140
- $coupons = $this->delete_garbage_coupons();
2141
- $coupon_count = count( $coupons );
2142
-
2143
- if ( $coupon_count ) {
2144
- $coupons_post_ids = implode( ',', wp_list_pluck( $coupons, 'ID' ) );
2145
- $wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE post_id IN(" . $coupons_post_ids . ')' );//phpcs:ignore
2146
- $wpdb->query( "DELETE FROM {$wpdb->prefix}posts WHERE ID IN(" . $coupons_post_ids . ')' );//phpcs:ignore
2147
- }
2148
-
2149
- if ( ! $is_ajax_request ) {
2150
- set_transient( 'woocommerce_ca_delete_garbage_coupons', $coupons, WEEK_IN_SECONDS );
2151
- return;
2152
- }
2153
-
2154
- // translators: %1$s: Coupons Deleted, %2$s: Deleted coupons count'.
2155
- wp_send_json_success( sprintf( __( '%1$s: %2$d', 'woo-cart-abandonment-recovery' ), 'Coupons Deleted', $coupon_count ) );
2156
-
2157
- }
2158
- }
2159
-
2160
-
2161
- /**
2162
- * Set transient and delete garbage coupons.
2163
- */
2164
- public function delete_garbage_coupons() {
2165
-
2166
- global $wpdb;
2167
-
2168
- $coupon_generated_by = WCF_CA_COUPON_GENERATED_BY;
2169
- $timestamp = time();
2170
- $post_type = 'shop_coupon';
2171
- $coupons = $wpdb->get_results(
2172
- $wpdb->prepare(
2173
- "SELECT ID, coupon_code, usage_limit, total_usaged, expiry_date FROM (
2174
- SELECT p.ID,
2175
- p.post_title AS coupon_code,
2176
- Max(CASE WHEN pm.meta_key = 'date_expires' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS expiry_date,
2177
- Max(CASE WHEN pm.meta_key = 'usage_limit' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS usage_limit,
2178
- Max(CASE WHEN pm.meta_key = 'usage_count' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS total_usaged,
2179
-
2180
- Max(CASE WHEN pm.meta_key = 'coupon_generated_by' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS coupon_generated_by
2181
- FROM {$wpdb->prefix}posts AS p
2182
- INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
2183
- WHERE p.`post_type` = %s
2184
-
2185
- GROUP BY p.ID
2186
- ) AS final_res WHERE coupon_generated_by IS NOT NULL AND coupon_generated_by = %s AND ( ( usage_limit = total_usaged ) OR ( expiry_date <= %d AND expiry_date != '') )",
2187
- $post_type,
2188
- $coupon_generated_by,
2189
- $timestamp
2190
- )
2191
- );
2192
- return $coupons;
2193
- }
2194
-
2195
- /**
2196
- * Send headers to export orders to csv format.
2197
- */
2198
- private function download_send_headers() {
2199
- $now = gmdate( 'Y-m-d-H-i-s' );
2200
- $filename = 'woo-cart-abandonment-recovery-export-' . $now . '.csv';
2201
-
2202
- header( 'Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate' );
2203
- header( "Last-Modified: {$now} GMT" );
2204
-
2205
- // force download.
2206
- header( 'Content-Type: application/force-download' );
2207
- header( 'Content-Type: application/octet-stream' );
2208
- header( 'Content-Type: application/download' );
2209
-
2210
- // disposition / encoding on response body.
2211
- header( "Content-Disposition: attachment;filename={$filename}" );
2212
- header( 'Content-Transfer-Encoding: binary' );
2213
- }
2214
-
2215
- /**
2216
- * Convert users data to csv format.
2217
- *
2218
- * @param array $user_data users data.
2219
- */
2220
- private function array2csv( array $user_data ) {
2221
- if ( empty( $user_data ) ) {
2222
- return;
2223
- }
2224
- ob_clean();
2225
- ob_start();
2226
- $data_file = fopen( 'php://output', 'w' );
2227
- fputcsv(
2228
- $data_file,
2229
- array(
2230
- 'First-Name',
2231
- 'Last-Name',
2232
- 'Email',
2233
- 'Phone',
2234
- 'Products',
2235
- 'Cart-Total in ' . get_woocommerce_currency(),
2236
- 'Order-Status',
2237
- 'Unsubscribed',
2238
- 'Coupon-Code',
2239
- )
2240
- );
2241
- foreach ( $user_data as $data ) {
2242
- $name = unserialize( $data['other_fields'] );
2243
- $checkout_details = $this->get_checkout_details( $data['session_id'] );
2244
- $cart_data = $this->get_comma_separated_products( $checkout_details->cart_contents );
2245
- fputcsv(
2246
- $data_file,
2247
- array(
2248
- $name['wcf_first_name'],
2249
- $name['wcf_last_name'],
2250
- $data['email'],
2251
- $name['wcf_phone_number'],
2252
- $cart_data,
2253
- $data['cart_total'],
2254
- $data['order_status'],
2255
- $data['unsubscribed'] ? 'Yes' : 'No',
2256
- $data['coupon_code'],
2257
- )
2258
- );
2259
-
2260
- }
2261
- fclose( $data_file ); //phpcs:ignore
2262
- return ob_get_clean();
2263
- }
2264
- }
2265
-
2266
- Cartflows_Ca_Cart_Abandonment::get_instance();
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cart Abandonment
4
+ *
5
+ * @package Woocommerce-Cart-Abandonment-Recovery
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ /**
13
+ * Cart abandonment tracking class.
14
+ */
15
+ class Cartflows_Ca_Cart_Abandonment {
16
+
17
+
18
+
19
+ /**
20
+ * Member Variable
21
+ *
22
+ * @var object instance
23
+ */
24
+ private static $instance;
25
+
26
+ /**
27
+ * Initiator
28
+ */
29
+ public static function get_instance() {
30
+ if ( ! isset( self::$instance ) ) {
31
+ self::$instance = new self();
32
+ }
33
+ return self::$instance;
34
+ }
35
+
36
+ /**
37
+ * Constructor function that initializes required actions and hooks.
38
+ */
39
+ public function __construct() {
40
+
41
+ $this->define_cart_abandonment_constants();
42
+
43
+ // Adding menu to view cart abandonment report.
44
+ add_action( 'admin_menu', array( $this, 'abandoned_cart_tracking_menu' ), 999 );
45
+
46
+ // Adding the styles and scripts for the cart abandonment.
47
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_cart_abandonment_script' ), 20 );
48
+
49
+ if ( wcf_ca()->utils->is_cart_abandonment_tracking_enabled() && ! isset( $_COOKIE['wcf_ca_skip_track_data'] ) ) {
50
+
51
+ // Add script to track the cart abandonment.
52
+ add_action( 'woocommerce_after_checkout_form', array( $this, 'cart_abandonment_tracking_script' ) );
53
+
54
+ // Store user details from the current checkout page.
55
+ add_action( 'wp_ajax_cartflows_save_cart_abandonment_data', array( $this, 'save_cart_abandonment_data' ) );
56
+ add_action( 'wp_ajax_nopriv_cartflows_save_cart_abandonment_data', array( $this, 'save_cart_abandonment_data' ) );
57
+
58
+ // GDPR actions.
59
+ add_action( 'wp_ajax_cartflows_skip_cart_tracking_gdpr', array( $this, 'skip_cart_tracking_by_gdpr' ) );
60
+ add_action( 'wp_ajax_nopriv_cartflows_skip_cart_tracking_gdpr', array( $this, 'skip_cart_tracking_by_gdpr' ) );
61
+
62
+ // Delete the stored cart abandonment data once order gets created.
63
+ add_action( 'woocommerce_new_order', array( $this, 'delete_cart_abandonment_data' ) );
64
+ add_action( 'woocommerce_thankyou', array( $this, 'delete_cart_abandonment_data' ) );
65
+ add_action( 'woocommerce_order_status_changed', array( $this, 'wcf_ca_update_order_status' ), 999, 3 );
66
+
67
+ // Adding filter to restore the data if recreating abandonment order.
68
+ add_filter( 'wp', array( $this, 'restore_cart_abandonment_data' ), 10 );
69
+ add_filter( 'wp', array( $this, 'unsubscribe_cart_abandonment_emails' ), 10 );
70
+
71
+ add_action( 'wp_ajax_wcf_ca_preview_email_send', array( $this, 'send_preview_email' ) );
72
+
73
+ // Delete coupons.
74
+ add_action( 'wp_ajax_wcf_ca_delete_garbage_coupons', array( $this, 'delete_used_and_expired_coupons' ) );
75
+
76
+ $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
77
+ $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
78
+ if ( WCF_CA_PAGE_NAME === $page ) {
79
+ // Adding filter to add new button to add custom fields.
80
+ add_filter( 'mce_buttons', array( $this, 'wcf_filter_mce_button' ) );
81
+ add_filter( 'mce_external_plugins', array( $this, 'wcf_filter_mce_plugin' ), 9 );
82
+ }
83
+
84
+ add_filter( 'cron_schedules', array( $this, 'cartflows_ca_update_order_status_action' ) ); //phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
85
+
86
+ // Schedule an action if it's not already scheduled.
87
+ if ( ! wp_next_scheduled( 'cartflows_ca_update_order_status_action' ) ) {
88
+ wp_schedule_event( time(), 'every_fifteen_minutes', 'cartflows_ca_update_order_status_action' );
89
+ }
90
+
91
+ // Adding notice to checkout page to inform about test email checkout page.
92
+ add_action( 'woocommerce_before_checkout_form', array( $this, 'test_email_checkout_page' ), 9 );
93
+
94
+ add_action( 'cartflows_ca_update_order_status_action', array( $this, 'update_order_status' ) );
95
+
96
+ }
97
+
98
+ }
99
+
100
+ /**
101
+ * This function will send the email to the store admin when any abandoned cart email recovered.
102
+ *
103
+ * @param int | string $order_id Order id.
104
+ * @param string $wcar_old_status Old status of the order.
105
+ * @param string $wcar_new_status New status of the order.
106
+ */
107
+ public function wcar_send_successful_recovery_email_to_admin( $order_id, $wcar_old_status, $wcar_new_status ) {
108
+ global $woocommerce;
109
+
110
+ if ( in_array( $wcar_old_status, array( 'pending', 'failed', 'on-hold' ), true ) &&
111
+ in_array( $wcar_new_status, array( 'processing', 'completed' ), true )
112
+ ) {
113
+ $user_id = get_current_user_id();
114
+ $order = wc_get_order( $order_id );
115
+ if ( version_compare( $woocommerce->version, '3.0.0', '>=' ) ) {
116
+ $user_id = $order->get_user_id();
117
+ } else {
118
+ $user_id = $order->user_id;
119
+ }
120
+
121
+ $is_recoverd = $this->wcar_check_order_is_recovered( $order_id );
122
+
123
+ if ( $is_recoverd ) {
124
+ $order = wc_get_order( $order_id );
125
+ $email_heading = __( 'New Customer Order - Recovered Order ID: ' . $order_id . '', 'woo-cart-abandonment-recovery' ); //phpcs:ignore
126
+ $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
127
+ $email_subject = __( 'New Customer Order - Recovered Order ID: ' . $order_id . '', 'woo-cart-abandonment-recovery' ); //phpcs:ignore
128
+ $user_email = get_option( 'admin_email' );
129
+ $headers[] = 'From: Admin <' . $user_email . '>';
130
+ $headers[] = 'Content-Type: text/html';
131
+
132
+ ob_start();
133
+ wc_get_template(
134
+ 'emails/admin-new-order.php',
135
+ array(
136
+ 'order' => $order,
137
+ 'email_heading' => $email_heading,
138
+ 'sent_to_admin' => false,
139
+ 'plain_text' => false,
140
+ 'email' => true,
141
+ )
142
+ );
143
+
144
+ $email_body = ob_get_clean();
145
+ wc_mail( $user_email, $email_subject, $email_body, $headers );
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * This function will check if cart is recoverd from woocommerce and WCAR.
152
+ *
153
+ * @param int $order_id order id.
154
+ */
155
+ public function wcar_check_order_is_recovered( $order_id ) {
156
+
157
+ global $wpdb;
158
+ $order = wc_get_order( $order_id );
159
+ $email = $order->get_billing_email();
160
+ $cart_abandonment_table_name = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
161
+ $wcar_status = $wpdb->get_var($wpdb->prepare( "SELECT `order_status` FROM {$cart_abandonment_table_name} WHERE `email` = %s", $email )); // phpcs:ignore
162
+ $woo_status = $order->get_status();
163
+
164
+ if ( 'completed' === $wcar_status && in_array( $woo_status, array( 'completed', 'processing' ), true ) ) {
165
+ return true;
166
+ }
167
+ return false;
168
+ }
169
+
170
+ /**
171
+ * Update the Order status.
172
+ *
173
+ * @param integer $order_id order id.
174
+ * @param string $old_order_status old order status.
175
+ * @param string $new_order_status new order status.
176
+ */
177
+ public function wcf_ca_update_order_status( $order_id, $old_order_status, $new_order_status ) {
178
+
179
+ $acceptable_order_statuses = $this->get_acceptable_order_statuses();
180
+
181
+ $exclude_on_hold_order = apply_filters_deprecated( 'woo_ca_exclude_on_hold_order_from_tracking', array( false ), '1.2.8', 'New Option is introduced instead of this filter' );
182
+
183
+ if ( $exclude_on_hold_order & ! ( in_array( 'on-hold', $acceptable_order_statuses, true ) ) ) {
184
+ array_push( $acceptable_order_statuses, 'on-hold' );
185
+ }
186
+
187
+ if ( ( WCF_CART_FAILED_ORDER === $new_order_status ) ) {
188
+ return;
189
+ }
190
+
191
+ if ( $order_id && in_array( $new_order_status, $acceptable_order_statuses, true ) ) {
192
+
193
+ $order = wc_get_order( $order_id );
194
+
195
+ $order_email = $order->get_billing_email();
196
+ $captured_data = ( WCF_CART_FAILED_ORDER === $new_order_status ) ? $this->get_tracked_data_without_status( $order_email ) : $this->get_captured_data_by_email( $order_email );
197
+
198
+ if ( $captured_data && is_object( $captured_data ) ) {
199
+ $capture_status = $captured_data->order_status;
200
+ global $wpdb;
201
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
202
+
203
+ if ( ( WCF_CART_NORMAL_ORDER === $capture_status ) ) {
204
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $captured_data->session_id ) ) );
205
+ }
206
+
207
+ if ( ( WCF_CART_ABANDONED_ORDER === $capture_status || WCF_CART_LOST_ORDER === $capture_status ) ) {
208
+ $this->skip_future_emails_when_order_is_completed( sanitize_key( $captured_data->session_id ) );
209
+ $this->trigger_zapier_webhook( $captured_data->session_id, WCF_CART_COMPLETED_ORDER );
210
+ $note = __( 'This order was abandoned & subsequently recovered.', 'woo-cart-abandonment-recovery' );
211
+ $order->add_order_note( $note );
212
+ $order->save();
213
+ if ( WC()->session ) {
214
+ WC()->session->__unset( 'wcf_session_id' );
215
+ }
216
+ }
217
+ }
218
+ $wcar_email_admin_recovery = get_option( 'wcar_email_admin_on_recovery' );
219
+ if ( 'on' === $wcar_email_admin_recovery ) {
220
+ $this->wcar_send_successful_recovery_email_to_admin( $order_id, $old_order_status, $new_order_status );
221
+ }
222
+ }
223
+
224
+ }
225
+
226
+
227
+ /**
228
+ * Send preview emails.
229
+ */
230
+ public function send_preview_email() {
231
+
232
+ check_ajax_referer( WCF_EMAIL_TEMPLATES_NONCE, 'security' );
233
+ $mail_result = $this->send_email_templates( null, true );
234
+ if ( $mail_result ) {
235
+ wp_send_json_success( __( 'Mail has been sent successfully!', 'woo-cart-abandonment-recovery' ) );
236
+ } else {
237
+ wp_send_json_error( __( 'Mail sending failed!', 'woo-cart-abandonment-recovery' ) );
238
+ }
239
+ }
240
+
241
+
242
+ /**
243
+ * Delete tracked data and set cookie for the user.
244
+ */
245
+ public function skip_cart_tracking_by_gdpr() {
246
+ check_ajax_referer( 'cartflows_skip_cart_tracking_gdpr', 'security' );
247
+
248
+ global $wpdb;
249
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
250
+
251
+ $session_id = WC()->session->get( 'wcf_session_id' );
252
+ if ( $session_id ) {
253
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
254
+ }
255
+
256
+ setcookie( 'wcf_ca_skip_track_data', 'true', 0, '/' );
257
+ wp_send_json_success();
258
+ }
259
+
260
+
261
+ /**
262
+ * Create custom schedule.
263
+ *
264
+ * @param array $schedules schedules.
265
+ * @return mixed
266
+ */
267
+ public function cartflows_ca_update_order_status_action( $schedules ) {
268
+
269
+ /**
270
+ * Add filter to change the cron interval time to uodate order status.
271
+ */
272
+ $cron_time = apply_filters( 'woo_ca_update_order_cron_interval', 15 );
273
+
274
+ $schedules['every_fifteen_minutes'] = array(
275
+ 'interval' => $cron_time * MINUTE_IN_SECONDS,
276
+ 'display' => __( 'Every Fifteen Minutes', 'woo-cart-abandonment-recovery' ),
277
+ );
278
+
279
+ return $schedules;
280
+ }
281
+
282
+ /**
283
+ * Generate new coupon code for abandoned cart.
284
+ *
285
+ * @param string $discount_type discount type.
286
+ * @param float $amount amount.
287
+ * @param string $expiry expiry.
288
+ * @param string $free_shipping is free shipping.
289
+ * @param string $individual_use use coupon individual.
290
+ */
291
+ public function generate_coupon_code( $discount_type, $amount, $expiry = '', $free_shipping = 'no', $individual_use = 'no' ) {
292
+
293
+ $coupon_code = '';
294
+
295
+ $coupon_code = wp_generate_password( 8, false, false );
296
+
297
+ $new_coupon_id = wp_insert_post(
298
+ array(
299
+ 'post_title' => $coupon_code,
300
+ 'post_content' => '',
301
+ 'post_status' => 'publish',
302
+ 'post_author' => 1,
303
+ 'post_type' => 'shop_coupon',
304
+ )
305
+ );
306
+
307
+ $coupon_post_data = array(
308
+ 'discount_type' => $discount_type,
309
+ 'description' => WCF_CA_COUPON_DESCRIPTION,
310
+ 'coupon_amount' => $amount,
311
+ 'individual_use' => $individual_use,
312
+ 'product_ids' => '',
313
+ 'exclude_product_ids' => '',
314
+ 'usage_limit' => '1',
315
+ 'usage_count' => '0',
316
+ 'date_expires' => $expiry,
317
+ 'apply_before_tax' => 'yes',
318
+ 'free_shipping' => $free_shipping,
319
+ 'coupon_generated_by' => WCF_CA_COUPON_GENERATED_BY,
320
+ );
321
+
322
+ $coupon_post_data = apply_filters( 'woo_ca_generate_coupon', $coupon_post_data );
323
+
324
+ foreach ( $coupon_post_data as $key => $value ) {
325
+ update_post_meta( $new_coupon_id, $key, $value );
326
+ }
327
+
328
+ return $coupon_code;
329
+ }
330
+
331
+ /**
332
+ * Unsubscribe the user from the mailing list.
333
+ */
334
+ public function unsubscribe_cart_abandonment_emails() {
335
+
336
+ $unsubscribe = filter_input( INPUT_GET, 'unsubscribe', FILTER_VALIDATE_BOOLEAN );
337
+ $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
338
+ if ( $unsubscribe && $this->is_valid_token( $wcf_ac_token ) ) {
339
+ $token_data = $this->wcf_decode_token( $wcf_ac_token );
340
+ if ( isset( $token_data['wcf_session_id'] ) ) {
341
+ $session_id = $token_data['wcf_session_id'];
342
+
343
+ global $wpdb;
344
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
345
+ $wpdb->update(
346
+ $cart_abandonment_table,
347
+ array( 'unsubscribed' => true ),
348
+ array( 'session_id' => $session_id )
349
+ );
350
+ wp_die( esc_html__( 'You have successfully unsubscribed from our email list.', 'woo-cart-abandonment-recovery' ), esc_html__( 'Unsubscribed', 'woo-cart-abandonment-recovery' ) );
351
+
352
+ }
353
+ }
354
+
355
+ }
356
+
357
+
358
+ /**
359
+ * Link JS to mce button.
360
+ *
361
+ * @param array $plugins mce pluggins.
362
+ * @return mixed
363
+ */
364
+ public function wcf_filter_mce_plugin( $plugins ) {
365
+ $plugins['cartflows_ac'] = CARTFLOWS_CA_URL . 'admin/assets/js/admin-mce.js';
366
+ return $plugins;
367
+ }
368
+
369
+ /**
370
+ * Register button.
371
+ *
372
+ * @param array $buttons mce buttons.
373
+ * @return mixed
374
+ */
375
+ public function wcf_filter_mce_button( $buttons ) {
376
+ array_push( $buttons, 'cartflows_ac' );
377
+ return $buttons;
378
+ }
379
+
380
+ /**
381
+ * Initialise all the constants
382
+ */
383
+ public function define_cart_abandonment_constants() {
384
+ define( 'CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR', CARTFLOWS_CA_DIR . 'modules/cart-abandonment/' );
385
+ define( 'CARTFLOWS_CART_ABANDONMENT_TRACKING_URL', CARTFLOWS_CA_URL . 'modules/cart-abandonment/' );
386
+ define( 'WCF_CART_ABANDONED_ORDER', 'abandoned' );
387
+ define( 'WCF_CART_COMPLETED_ORDER', 'completed' );
388
+ define( 'WCF_CART_LOST_ORDER', 'lost' );
389
+ define( 'WCF_CART_NORMAL_ORDER', 'normal' );
390
+ define( 'WCF_CART_FAILED_ORDER', 'failed' );
391
+ define( 'CARTFLOWS_ZAPIER_ACTION_AFTER_TIME', 1800 );
392
+
393
+ define( 'WCF_ACTION_ABANDONED_CARTS', 'abandoned_carts' );
394
+ define( 'WCF_ACTION_RECOVERED_CARTS', 'recovered_carts' );
395
+ define( 'WCF_ACTION_LOST_CARTS', 'lost_carts' );
396
+ define( 'WCF_ACTION_SETTINGS', 'settings' );
397
+ define( 'WCF_ACTION_REPORTS', 'reports' );
398
+
399
+ define( 'WCF_SUB_ACTION_REPORTS_VIEW', 'view' );
400
+ define( 'WCF_SUB_ACTION_REPORTS_RESCHEDULE', 'reschedule' );
401
+
402
+ define( 'WCF_DEFAULT_CUT_OFF_TIME', 15 );
403
+ define( 'WCF_DEFAULT_COUPON_AMOUNT', 10 );
404
+
405
+ define( 'WCF_CA_DATETIME_FORMAT', 'Y-m-d H:i:s' );
406
+
407
+ define( 'WCF_CA_COUPON_DESCRIPTION', 'This coupon is for abandoned cart email templates.' );
408
+ define( 'WCF_CA_COUPON_GENERATED_BY', 'woo-cart-abandonment-recovery' );
409
+ }
410
+
411
+ /**
412
+ * Restore cart abandonemnt data on checkout page.
413
+ *
414
+ * @param array $fields checkout fields values.
415
+ * @return array field values
416
+ */
417
+ public function restore_cart_abandonment_data( $fields = array() ) {
418
+ global $woocommerce;
419
+ $result = array();
420
+ // Restore only of user is not logged in.
421
+ $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
422
+ if ( $this->is_valid_token( $wcf_ac_token ) ) {
423
+
424
+ // Check if `wcf_restore_token` exists to restore cart data.
425
+ $token_data = $this->wcf_decode_token( $wcf_ac_token );
426
+ if ( is_array( $token_data ) && isset( $token_data['wcf_session_id'] ) ) {
427
+ $result = $this->get_checkout_details( $token_data['wcf_session_id'] );
428
+ if ( isset( $result ) && WCF_CART_ABANDONED_ORDER === $result->order_status || WCF_CART_LOST_ORDER === $result->order_status ) {
429
+ WC()->session->set( 'wcf_session_id', $token_data['wcf_session_id'] );
430
+ }
431
+ }
432
+
433
+ if ( $result ) {
434
+ $cart_content = unserialize( $result->cart_contents );
435
+
436
+ if ( $cart_content ) {
437
+ $woocommerce->cart->empty_cart();
438
+ wc_clear_notices();
439
+ foreach ( $cart_content as $cart_item ) {
440
+
441
+ $cart_item_data = array();
442
+ $variation_data = array();
443
+ $id = $cart_item['product_id'];
444
+ $qty = $cart_item['quantity'];
445
+
446
+ // Skip bundled products when added main product.
447
+ if ( isset( $cart_item['bundled_by'] ) ) {
448
+ continue;
449
+ }
450
+
451
+ if ( isset( $cart_item['variation'] ) ) {
452
+ foreach ( $cart_item['variation'] as $key => $value ) {
453
+ $variation_data[ $key ] = $value;
454
+ }
455
+ }
456
+
457
+ $cart_item_data = $cart_item;
458
+
459
+ $woocommerce->cart->add_to_cart( $id, $qty, $cart_item['variation_id'], $variation_data, $cart_item_data );
460
+ }
461
+
462
+ if ( isset( $token_data['wcf_coupon_code'] ) && ! $woocommerce->cart->applied_coupons ) {
463
+ $woocommerce->cart->add_discount( $token_data['wcf_coupon_code'] );
464
+ }
465
+ }
466
+ $other_fields = unserialize( $result->other_fields );
467
+
468
+ $parts = explode( ',', $other_fields['wcf_location'] );
469
+ if ( count( $parts ) > 1 ) {
470
+ $country = $parts[0];
471
+ $city = trim( $parts[1] );
472
+ } else {
473
+ $country = $parts[0];
474
+ $city = '';
475
+ }
476
+
477
+ foreach ( $other_fields as $key => $value ) {
478
+ $key = str_replace( 'wcf_', '', $key );
479
+ $_POST[ $key ] = sanitize_text_field( $value );
480
+ }
481
+ $_POST['billing_first_name'] = sanitize_text_field( $other_fields['wcf_first_name'] );
482
+ $_POST['billing_last_name'] = sanitize_text_field( $other_fields['wcf_last_name'] );
483
+ $_POST['billing_phone'] = sanitize_text_field( $other_fields['wcf_phone_number'] );
484
+ $_POST['billing_email'] = sanitize_email( $result->email );
485
+ $_POST['billing_city'] = sanitize_text_field( $city );
486
+ $_POST['billing_country'] = sanitize_text_field( $country );
487
+
488
+ }
489
+ }
490
+ return $fields;
491
+ }
492
+
493
+ /**
494
+ * Add notice to inform user about test email checkout page.
495
+ */
496
+ public function test_email_checkout_page() {
497
+
498
+ $wcf_ac_token = filter_input( INPUT_GET, 'wcf_ac_token', FILTER_SANITIZE_STRING );
499
+ $token_data = $this->wcf_decode_token( $wcf_ac_token );
500
+ if ( is_checkout() && ! is_wc_endpoint_url() && isset( $token_data['wcf_preview_email'] ) && $token_data['wcf_preview_email'] ) {
501
+ wc_print_notice( __( 'This checkout page is generated by WooCommerce Cart Abandonment Recovery plugin from test mail.', 'woo-cart-abandonment-recovery' ), 'notice' );
502
+ }
503
+ }
504
+
505
+
506
+ /**
507
+ * Load cart abandonemnt tracking script.
508
+ *
509
+ * @return void
510
+ */
511
+ public function cart_abandonment_tracking_script() {
512
+
513
+ $wcf_ca_ignore_users = get_option( 'wcf_ca_ignore_users' );
514
+ $current_user = wp_get_current_user();
515
+ $roles = $current_user->roles;
516
+ $role = array_shift( $roles );
517
+ if ( ! empty( $wcf_ca_ignore_users ) ) {
518
+ foreach ( $wcf_ca_ignore_users as $user ) {
519
+ $user = strtolower( $user );
520
+ $role = preg_replace( '/_/', ' ', $role );
521
+ if ( $role === $user ) {
522
+ return;
523
+ }
524
+ }
525
+ }
526
+
527
+ global $post;
528
+ wp_enqueue_script(
529
+ 'cartflows-cart-abandonment-tracking',
530
+ CARTFLOWS_CART_ABANDONMENT_TRACKING_URL . 'assets/js/cart-abandonment-tracking.js',
531
+ array( 'jquery' ),
532
+ CARTFLOWS_CA_VER,
533
+ true
534
+ );
535
+
536
+ $vars = array(
537
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
538
+ '_nonce' => wp_create_nonce( 'cartflows_save_cart_abandonment_data' ),
539
+ '_gdpr_nonce' => wp_create_nonce( 'cartflows_skip_cart_tracking_gdpr' ),
540
+ '_post_id' => get_the_ID(),
541
+ '_show_gdpr_message' => ( wcf_ca()->utils->is_gdpr_enabled() && ! isset( $_COOKIE['wcf_ca_skip_track_data'] ) ),
542
+ '_gdpr_message' => get_option( 'wcf_ca_gdpr_message' ),
543
+ '_gdpr_nothanks_msg' => __( 'No Thanks', 'woo-cart-abandonment-recovery' ),
544
+ '_gdpr_after_no_thanks_msg' => __( 'You won\'t receive further emails from us, thank you!', 'woo-cart-abandonment-recovery' ),
545
+ 'enable_ca_tracking' => true,
546
+ );
547
+
548
+ wp_localize_script( 'cartflows-cart-abandonment-tracking', 'CartFlowsProCAVars', $vars );
549
+
550
+ }
551
+
552
+ /**
553
+ * Validate the token before use.
554
+ *
555
+ * @param string $token token form the url.
556
+ * @return bool
557
+ */
558
+ public function is_valid_token( $token ) {
559
+ $is_valid = false;
560
+ $token_data = $this->wcf_decode_token( $token );
561
+ if ( is_array( $token_data ) && array_key_exists( 'wcf_session_id', $token_data ) ) {
562
+ $result = $this->get_checkout_details( $token_data['wcf_session_id'] );
563
+ if ( isset( $result ) ) {
564
+ $is_valid = true;
565
+ }
566
+ }
567
+ return $is_valid;
568
+ }
569
+
570
+ /**
571
+ * Check before emails actually send to user.
572
+ *
573
+ * @param array $email_data email_data.
574
+ * @param array $current_cart_data cart data.
575
+ * @return bool
576
+ */
577
+ public function check_if_already_purchased_by_email_product_ids( $email_data, $current_cart_data ) {
578
+
579
+ global $wpdb;
580
+ $current_cart_data = unserialize( $current_cart_data );
581
+
582
+ // Fetch products & variations.
583
+ $products = array_values( wp_list_pluck( $current_cart_data, 'product_id' ) );
584
+ $variations = array_values( wp_list_pluck( $current_cart_data, 'variation_id' ) );
585
+ $current_products = array_unique( array_merge( $products, $variations ) );
586
+
587
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
588
+
589
+ $orders = wc_get_orders(
590
+ array(
591
+ 'billing_email' => $email_data->email,
592
+ 'status' => array( 'processing', 'completed' ),
593
+ 'date_after' => gmdate(
594
+ 'Y-m-d h:i:s',
595
+ strtotime( '-30 days' )
596
+ ),
597
+ )
598
+ );
599
+ $need_to_send_email = true;
600
+
601
+ foreach ( $orders as $order ) {
602
+ $order = wc_get_order( $order->get_id() );
603
+ $items = $order->get_items();
604
+ foreach ( $items as $item ) {
605
+ $product_id = $item->get_product_id();
606
+ if ( in_array( $product_id, $current_products, true ) ) {
607
+ /**
608
+ * Remove duplicate captured order for tracking.
609
+ */
610
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $email_data->session_id ) ) );
611
+ $need_to_send_email = false;
612
+ break;
613
+ }
614
+ }
615
+ }
616
+ return $need_to_send_email;
617
+ }
618
+
619
+ /**
620
+ * Execute Zapier webhook for further action inside Zapier.
621
+ *
622
+ * @since 1.0.0
623
+ */
624
+ public function update_order_status() {
625
+
626
+ global $wpdb;
627
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
628
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
629
+ $minutes = wcf_ca()->utils->get_cart_abandonment_tracking_cut_off_time();
630
+
631
+ /**
632
+ * Delete abandoned cart orders if empty.
633
+ */
634
+ $this->delete_empty_abandoned_order();
635
+
636
+ $wp_current_datetime = current_time( WCF_CA_DATETIME_FORMAT );
637
+ $abandoned_ids = $wpdb->get_results(
638
+ $wpdb->prepare('SELECT `session_id` FROM `' . $cart_abandonment_table . '` WHERE `order_status` = %s AND ADDDATE( `time`, INTERVAL %d MINUTE) <= %s', WCF_CART_NORMAL_ORDER, $minutes, $wp_current_datetime ), ARRAY_A // phpcs:ignore
639
+ );
640
+
641
+ foreach ( $abandoned_ids as $session_id ) {
642
+
643
+ if ( isset( $session_id['session_id'] ) ) {
644
+
645
+ $current_session_id = $session_id['session_id'];
646
+ $this->schedule_emails( $current_session_id );
647
+
648
+ $coupon_code = '';
649
+ $wcf_ca_coupon_code_status = get_option( 'wcf_ca_coupon_code_status' );
650
+
651
+ if ( 'on' === $wcf_ca_coupon_code_status ) {
652
+ $discount_type = get_option( 'wcf_ca_discount_type' );
653
+ $discount_type = $discount_type ? $discount_type : 'percent';
654
+ $amount = get_option( 'wcf_ca_coupon_amount' );
655
+ $amount = $amount ? $amount : WCF_DEFAULT_COUPON_AMOUNT;
656
+ $coupon_expiry_date = get_option( 'wcf_ca_coupon_expiry' );
657
+ $coupon_expiry_unit = get_option( 'wcf_ca_coupon_expiry_unit' );
658
+ $coupon_expiry_date = $coupon_expiry_date ? strtotime( $wp_current_datetime . ' +' . $coupon_expiry_date . ' ' . $coupon_expiry_unit ) : '';
659
+ $free_shipping_coupon = get_option( 'wcf_ca_free_shipping_coupon' );
660
+ $free_shipping = ( isset( $free_shipping_coupon ) && ( $free_shipping_coupon->meta_value ) ) ? 'yes' : 'no';
661
+
662
+ $individual_use_only = get_option( 'wcf_ca_individual_use_only' );
663
+ $individual_use = ( isset( $individual_use_only ) && ( $individual_use_only->meta_value ) ) ? 'yes' : 'no';
664
+
665
+ $coupon_code = $this->generate_coupon_code( $discount_type, $amount, $coupon_expiry_date, $free_shipping, $individual_use );
666
+ }
667
+
668
+ $wpdb->update(
669
+ $cart_abandonment_table,
670
+ array(
671
+ 'order_status' => WCF_CART_ABANDONED_ORDER,
672
+ 'coupon_code' => $coupon_code,
673
+ ),
674
+ array( 'session_id' => $current_session_id )
675
+ );
676
+
677
+ $this->trigger_zapier_webhook( $current_session_id, WCF_CART_ABANDONED_ORDER );
678
+ }
679
+ }
680
+
681
+ /**
682
+ * Send scheduled emails.
683
+ */
684
+ $this->send_emails_to_callback();
685
+
686
+ // Update order status to lost after campaign complete.
687
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
688
+ $wpdb->query(
689
+ $wpdb->prepare(
690
+ "UPDATE $cart_abandonment_table as ca SET order_status = 'lost' WHERE ca.order_status = %s AND DATE(ca.time) <= DATE_SUB( %s , INTERVAL 30 DAY)
691
+ AND ( (SELECT count(*) FROM $email_history_table WHERE ca_session_id = ca.session_id ) =
692
+ (SELECT count(*) FROM $email_history_table WHERE ca_session_id = ca.session_id AND email_sent = 1) )",
693
+ WCF_CART_ABANDONED_ORDER,
694
+ $wp_current_datetime
695
+ )
696
+ );
697
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
698
+
699
+ /**
700
+ * Delete garbage coupons.
701
+ */
702
+ $wcf_ca_auto_delete_coupons = get_option( 'wcf_ca_auto_delete_coupons' );
703
+
704
+ if ( isset( $wcf_ca_auto_delete_coupons ) && 'on' === $wcf_ca_auto_delete_coupons ) {
705
+ $this->delete_used_and_expired_coupons();
706
+ }
707
+
708
+ }
709
+
710
+ /**
711
+ * Send zapier webhook.
712
+ *
713
+ * @param string $session_id session id.
714
+ * @param string $order_status order status.
715
+ */
716
+ public function trigger_zapier_webhook( $session_id, $order_status ) {
717
+
718
+ $checkout_details = $this->get_checkout_details( $session_id );
719
+
720
+ if ( $checkout_details && wcf_ca()->utils->is_zapier_trigger_enabled() ) {
721
+ $trigger_details = array();
722
+ $url = get_option( 'wcf_ca_zapier_cart_abandoned_webhook' );
723
+
724
+ $other_details = unserialize( $checkout_details->other_fields );
725
+ $trigger_details['first_name'] = $other_details['wcf_first_name'];
726
+ $trigger_details['last_name'] = $other_details['wcf_last_name'];
727
+ $trigger_details['phone_number'] = $other_details['wcf_phone_number'];
728
+ $trigger_details['billing_address'] = $other_details['wcf_billing_company'] . ' ' . $other_details['wcf_billing_address_1'] . ', ' . $other_details['wcf_billing_state'] . ', ' . $other_details['wcf_location'] . ', ' . $other_details['wcf_billing_postcode'];
729
+ $trigger_details['billing_address'] = trim( $trigger_details['billing_address'], ', ' );
730
+ $trigger_details['shipping_address'] = $other_details['wcf_shipping_company'] . ' ' . $other_details['wcf_shipping_address_1'] . ', ' . $other_details['wcf_shipping_city'] . ', ' . $other_details['wcf_shipping_state'] . ', ' . $other_details['wcf_shipping_postcode'];
731
+ $trigger_details['shipping_address'] = trim( $trigger_details['shipping_address'], ', ' );
732
+ $trigger_details['email'] = $checkout_details->email;
733
+ $token_data = array( 'wcf_session_id' => $checkout_details->session_id );
734
+ $trigger_details['checkout_url'] = $this->get_checkout_url( $checkout_details->checkout_id, $token_data );
735
+ $trigger_details['product_names'] = $this->get_comma_separated_products( $checkout_details->cart_contents );
736
+ $trigger_details['coupon_code'] = $checkout_details->coupon_code;
737
+ $trigger_details['order_status'] = $order_status;
738
+ $trigger_details['cart_total'] = $checkout_details->cart_total;
739
+ $trigger_details['product_table'] = $this->get_email_product_block( $checkout_details->cart_contents, $checkout_details->cart_total );
740
+
741
+ $trigger_details = apply_filters( 'woo_ca_webhook_trigger_details', $trigger_details );
742
+
743
+ $parameters = http_build_query( $trigger_details );
744
+
745
+ wp_remote_post(
746
+ $url,
747
+ array(
748
+ 'body' => $parameters,
749
+ 'timeout' => '5',
750
+ 'redirection' => '5',
751
+ 'httpversion' => '1.0',
752
+ 'blocking' => true,
753
+ 'headers' => array(),
754
+ 'cookies' => array(),
755
+ )
756
+ );
757
+
758
+ }
759
+ }
760
+
761
+
762
+ /**
763
+ * Sanitize post array.
764
+ *
765
+ * @return array
766
+ */
767
+ public function sanitize_post_data() {
768
+
769
+ $input_post_values = array(
770
+ 'wcf_billing_company' => array(
771
+ 'default' => '',
772
+ 'sanitize' => FILTER_SANITIZE_STRING,
773
+ ),
774
+ 'wcf_email' => array(
775
+ 'default' => '',
776
+ 'sanitize' => FILTER_SANITIZE_EMAIL,
777
+ ),
778
+ 'wcf_billing_address_1' => array(
779
+ 'default' => '',
780
+ 'sanitize' => FILTER_SANITIZE_STRING,
781
+ ),
782
+ 'wcf_billing_address_2' => array(
783
+ 'default' => '',
784
+ 'sanitize' => FILTER_SANITIZE_STRING,
785
+ ),
786
+ 'wcf_billing_state' => array(
787
+ 'default' => '',
788
+ 'sanitize' => FILTER_SANITIZE_STRING,
789
+ ),
790
+ 'wcf_billing_postcode' => array(
791
+ 'default' => '',
792
+ 'sanitize' => FILTER_SANITIZE_STRING,
793
+ ),
794
+ 'wcf_shipping_first_name' => array(
795
+ 'default' => '',
796
+ 'sanitize' => FILTER_SANITIZE_STRING,
797
+ ),
798
+ 'wcf_shipping_last_name' => array(
799
+ 'default' => '',
800
+ 'sanitize' => FILTER_SANITIZE_STRING,
801
+ ),
802
+ 'wcf_shipping_company' => array(
803
+ 'default' => '',
804
+ 'sanitize' => FILTER_SANITIZE_STRING,
805
+ ),
806
+ 'wcf_shipping_country' => array(
807
+ 'default' => '',
808
+ 'sanitize' => FILTER_SANITIZE_STRING,
809
+ ),
810
+ 'wcf_shipping_address_1' => array(
811
+ 'default' => '',
812
+ 'sanitize' => FILTER_SANITIZE_STRING,
813
+ ),
814
+ 'wcf_shipping_address_2' => array(
815
+ 'default' => '',
816
+ 'sanitize' => FILTER_SANITIZE_STRING,
817
+ ),
818
+ 'wcf_shipping_city' => array(
819
+ 'default' => '',
820
+ 'sanitize' => FILTER_SANITIZE_STRING,
821
+ ),
822
+ 'wcf_shipping_state' => array(
823
+ 'default' => '',
824
+ 'sanitize' => FILTER_SANITIZE_STRING,
825
+ ),
826
+ 'wcf_shipping_postcode' => array(
827
+ 'default' => '',
828
+ 'sanitize' => FILTER_SANITIZE_STRING,
829
+ ),
830
+ 'wcf_order_comments' => array(
831
+ 'default' => '',
832
+ 'sanitize' => FILTER_SANITIZE_STRING,
833
+ ),
834
+ 'wcf_name' => array(
835
+ 'default' => '',
836
+ 'sanitize' => FILTER_SANITIZE_STRING,
837
+ ),
838
+ 'wcf_surname' => array(
839
+ 'default' => '',
840
+ 'sanitize' => FILTER_SANITIZE_STRING,
841
+ ),
842
+ 'wcf_phone' => array(
843
+ 'default' => '',
844
+ 'sanitize' => FILTER_SANITIZE_STRING,
845
+ ),
846
+ 'wcf_country' => array(
847
+ 'default' => '',
848
+ 'sanitize' => FILTER_SANITIZE_STRING,
849
+ ),
850
+ 'wcf_city' => array(
851
+ 'default' => '',
852
+ 'sanitize' => FILTER_SANITIZE_STRING,
853
+ ),
854
+ 'wcf_post_id' => array(
855
+ 'default' => 0,
856
+ 'sanitize' => FILTER_SANITIZE_NUMBER_INT,
857
+ ),
858
+ );
859
+
860
+ $sanitized_post = array();
861
+ foreach ( $input_post_values as $key => $input_post_value ) {
862
+
863
+ if ( isset( $_POST[ $key ] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
864
+ $sanitized_post[ $key ] = filter_input( INPUT_POST, $key, $input_post_value['sanitize'] );
865
+ } else {
866
+ $sanitized_post[ $key ] = $input_post_value['default'];
867
+ }
868
+ }
869
+ return $sanitized_post;
870
+
871
+ }
872
+
873
+
874
+ /**
875
+ * Save cart abandonment tracking and schedule new event.
876
+ *
877
+ * @since 1.0.0
878
+ */
879
+ public function save_cart_abandonment_data() {
880
+ check_ajax_referer( 'cartflows_save_cart_abandonment_data', 'security' );
881
+ $post_data = $this->sanitize_post_data();
882
+ if ( isset( $post_data['wcf_email'] ) ) {
883
+ $user_email = sanitize_email( $post_data['wcf_email'] );
884
+ global $wpdb;
885
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
886
+
887
+ // Verify if email is already exists.
888
+ $session_id = WC()->session->get( 'wcf_session_id' );
889
+ $session_checkout_details = null;
890
+ if ( isset( $session_id ) ) {
891
+ $session_checkout_details = $this->get_checkout_details( $session_id );
892
+ } else {
893
+ $session_checkout_details = $this->get_checkout_details_by_email( $user_email );
894
+ if ( $session_checkout_details ) {
895
+ $session_id = $session_checkout_details->session_id;
896
+ WC()->session->set( 'wcf_session_id', $session_id );
897
+ } else {
898
+ $session_id = md5( uniqid( wp_rand(), true ) );
899
+ }
900
+ }
901
+
902
+ $checkout_details = $this->prepare_abandonment_data( $post_data );
903
+
904
+ if ( isset( $session_checkout_details ) && WCF_CART_COMPLETED_ORDER === $session_checkout_details->order_status ) {
905
+ WC()->session->__unset( 'wcf_session_id' );
906
+ $session_id = md5( uniqid( wp_rand(), true ) );
907
+ }
908
+
909
+ if ( isset( $checkout_details['cart_total'] ) && $checkout_details['cart_total'] > 0 ) {
910
+
911
+ if ( ( ! is_null( $session_id ) ) && ! is_null( $session_checkout_details ) ) {
912
+
913
+ // Updating row in the Database where users Session id = same as prevously saved in Session.
914
+ $wpdb->update(
915
+ $cart_abandonment_table,
916
+ $checkout_details,
917
+ array( 'session_id' => $session_id )
918
+ );
919
+
920
+ } else {
921
+
922
+ $checkout_details['session_id'] = sanitize_text_field( $session_id );
923
+ // Inserting row into Database.
924
+ $wpdb->insert(
925
+ $cart_abandonment_table,
926
+ $checkout_details
927
+ );
928
+
929
+ // Storing session_id in WooCommerce session.
930
+ WC()->session->set( 'wcf_session_id', $session_id );
931
+
932
+ }
933
+ } else {
934
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
935
+ }
936
+
937
+ wp_send_json_success();
938
+ }
939
+ }
940
+
941
+
942
+ /**
943
+ * Prepare cart data to save for abandonment.
944
+ *
945
+ * @param array $post_data post data.
946
+ * @return array
947
+ */
948
+ public function prepare_abandonment_data( $post_data = array() ) {
949
+
950
+ if ( function_exists( 'WC' ) ) {
951
+
952
+ // Retrieving cart total value and currency.
953
+ $cart_total = WC()->cart->total;
954
+
955
+ // Retrieving cart products and their quantities.
956
+ $products = WC()->cart->get_cart();
957
+ $current_time = current_time( WCF_CA_DATETIME_FORMAT );
958
+ $other_fields = array(
959
+ 'wcf_billing_company' => $post_data['wcf_billing_company'],
960
+ 'wcf_billing_address_1' => $post_data['wcf_billing_address_1'],
961
+ 'wcf_billing_address_2' => $post_data['wcf_billing_address_2'],
962
+ 'wcf_billing_state' => $post_data['wcf_billing_state'],
963
+ 'wcf_billing_postcode' => $post_data['wcf_billing_postcode'],
964
+ 'wcf_shipping_first_name' => $post_data['wcf_shipping_first_name'],
965
+ 'wcf_shipping_last_name' => $post_data['wcf_shipping_last_name'],
966
+ 'wcf_shipping_company' => $post_data['wcf_shipping_company'],
967
+ 'wcf_shipping_country' => $post_data['wcf_shipping_country'],
968
+ 'wcf_shipping_address_1' => $post_data['wcf_shipping_address_1'],
969
+ 'wcf_shipping_address_2' => $post_data['wcf_shipping_address_2'],
970
+ 'wcf_shipping_city' => $post_data['wcf_shipping_city'],
971
+ 'wcf_shipping_state' => $post_data['wcf_shipping_state'],
972
+ 'wcf_shipping_postcode' => $post_data['wcf_shipping_postcode'],
973
+ 'wcf_order_comments' => $post_data['wcf_order_comments'],
974
+ 'wcf_first_name' => $post_data['wcf_name'],
975
+ 'wcf_last_name' => $post_data['wcf_surname'],
976
+ 'wcf_phone_number' => $post_data['wcf_phone'],
977
+ 'wcf_location' => $post_data['wcf_country'] . ', ' . $post_data['wcf_city'],
978
+ );
979
+
980
+ $checkout_details = array(
981
+ 'email' => $post_data['wcf_email'],
982
+ 'cart_contents' => serialize( $products ),
983
+ 'cart_total' => sanitize_text_field( $cart_total ),
984
+ 'time' => sanitize_text_field( $current_time ),
985
+ 'other_fields' => serialize( $other_fields ),
986
+ 'checkout_id' => $post_data['wcf_post_id'],
987
+ );
988
+ }
989
+ return $checkout_details;
990
+ }
991
+
992
+ /**
993
+ * Get the acceptable order statuses.
994
+ */
995
+ public function get_acceptable_order_statuses() {
996
+
997
+ $acceptable_order_statuses = get_option( 'wcf_ca_excludes_orders' );
998
+ $acceptable_order_statuses = array_map( 'strtolower', $acceptable_order_statuses );
999
+
1000
+ return $acceptable_order_statuses;
1001
+ }
1002
+
1003
+ /**
1004
+ * Deletes cart abandonment tracking and scheduled event.
1005
+ *
1006
+ * @param int $order_id Order ID.
1007
+ * @since 1.0.0
1008
+ */
1009
+ public function delete_cart_abandonment_data( $order_id ) {
1010
+
1011
+ $acceptable_order_statuses = $this->get_acceptable_order_statuses();
1012
+
1013
+ $order = wc_get_order( $order_id );
1014
+ $order_status = $order->get_status();
1015
+ if ( ! in_array( $order_status, $acceptable_order_statuses, true ) ) {
1016
+ // Proceed if order status in completed or processing.
1017
+ return;
1018
+ }
1019
+
1020
+ global $wpdb;
1021
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1022
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1023
+
1024
+ if ( isset( WC()->session ) ) {
1025
+ $session_id = WC()->session->get( 'wcf_session_id' );
1026
+
1027
+ if ( isset( $session_id ) ) {
1028
+ $checkout_details = $this->get_checkout_details( $session_id );
1029
+
1030
+ $has_mail_sent = count( $this->fetch_scheduled_emails( $session_id, true ) );
1031
+
1032
+ if ( ! $has_mail_sent ) {
1033
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
1034
+ } else {
1035
+ if ( $checkout_details && ( WCF_CART_ABANDONED_ORDER === $checkout_details->order_status || WCF_CART_LOST_ORDER === $checkout_details->order_status ) ) {
1036
+
1037
+ $this->skip_future_emails_when_order_is_completed( $session_id );
1038
+
1039
+ $this->trigger_zapier_webhook( $session_id, WCF_CART_COMPLETED_ORDER );
1040
+
1041
+ $order = wc_get_order( $order_id );
1042
+ $note = __( 'This order was abandoned & subsequently recovered.', 'woo-cart-abandonment-recovery' );
1043
+ $order->add_order_note( $note );
1044
+ $order->save();
1045
+
1046
+ } elseif ( WCF_CART_COMPLETED_ORDER !== $checkout_details->order_status ) {
1047
+ // Normal checkout.
1048
+
1049
+ $billing_email = filter_input( INPUT_POST, 'billing_email', FILTER_SANITIZE_EMAIL );
1050
+
1051
+ if ( $billing_email ) {
1052
+ $order_data = $this->get_captured_data_by_email( $billing_email );
1053
+
1054
+ if ( ! is_null( $order_data ) ) {
1055
+ $existing_cart_contents = unserialize( $order_data->cart_contents );
1056
+ $order_cart_contents = unserialize( $checkout_details->cart_contents );
1057
+ $existing_cart_products = array_keys( (array) $existing_cart_contents );
1058
+ $order_cart_products = array_keys( (array) $order_cart_contents );
1059
+ if ( $this->check_if_similar_cart( $existing_cart_products, $order_cart_products ) ) {
1060
+ $this->skip_future_emails_when_order_is_completed( $order_data->session_id );
1061
+ }
1062
+ }
1063
+ }
1064
+ $wpdb->delete( $cart_abandonment_table, array( 'session_id' => sanitize_key( $session_id ) ) );
1065
+ }
1066
+ }
1067
+ }
1068
+ if ( WC()->session ) {
1069
+ WC()->session->__unset( 'wcf_session_id' );
1070
+ }
1071
+ }
1072
+ }
1073
+
1074
+ /**
1075
+ * Unschedule future emails for completed orders.
1076
+ *
1077
+ * @param string $session_id session id.
1078
+ * @param bool $skip_complete skip update query.
1079
+ */
1080
+ public function skip_future_emails_when_order_is_completed( $session_id, $skip_complete = false ) {
1081
+
1082
+ global $wpdb;
1083
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1084
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1085
+
1086
+ if ( ! $skip_complete ) {
1087
+ $wpdb->update(
1088
+ $cart_abandonment_table,
1089
+ array(
1090
+ 'order_status' => WCF_CART_COMPLETED_ORDER,
1091
+ ),
1092
+ array(
1093
+ 'session_id' => sanitize_key( $session_id ),
1094
+ )
1095
+ );
1096
+ }
1097
+
1098
+ $wpdb->update(
1099
+ $email_history_table,
1100
+ array( 'email_sent' => -1 ),
1101
+ array(
1102
+ 'ca_session_id' => $session_id,
1103
+ 'email_sent' => 0,
1104
+ )
1105
+ );
1106
+ }
1107
+
1108
+ /**
1109
+ * Compare cart if similar products.
1110
+ *
1111
+ * @param array $cart_a cart_a.
1112
+ * @param array $cart_b cart_b.
1113
+ * @return bool
1114
+ */
1115
+ public function check_if_similar_cart( $cart_a, $cart_b ) {
1116
+ return (
1117
+ is_array( $cart_a )
1118
+ && is_array( $cart_b )
1119
+ && count( $cart_a ) === count( $cart_b )
1120
+ && array_diff( $cart_a, $cart_b ) === array_diff( $cart_b, $cart_a )
1121
+ );
1122
+ }
1123
+
1124
+
1125
+ /**
1126
+ * Get the checkout details for the user.
1127
+ *
1128
+ * @param string $wcf_session_id checkout page session id.
1129
+ * @since 1.0.0
1130
+ */
1131
+ public function get_checkout_details( $wcf_session_id ) {
1132
+ global $wpdb;
1133
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1134
+ $result = $wpdb->get_row(
1135
+ $wpdb->prepare('SELECT * FROM `' . $cart_abandonment_table . '` WHERE session_id = %s', $wcf_session_id ) // phpcs:ignore
1136
+ );
1137
+ return $result;
1138
+ }
1139
+
1140
+ /**
1141
+ * Get the checkout details for the user.
1142
+ *
1143
+ * @param string $email user email.
1144
+ * @since 1.0.0
1145
+ */
1146
+ public function get_checkout_details_by_email( $email ) {
1147
+ global $wpdb;
1148
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1149
+ $result = $wpdb->get_row(
1150
+ $wpdb->prepare('SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s AND `order_status` IN ( %s, %s )', $email, WCF_CART_ABANDONED_ORDER, WCF_CART_NORMAL_ORDER ) // phpcs:ignore
1151
+ );
1152
+ return $result;
1153
+ }
1154
+
1155
+
1156
+ /**
1157
+ * Get the checkout details for the user.
1158
+ *
1159
+ * @param string $value value.
1160
+ * @since 1.0.0
1161
+ */
1162
+ public function get_captured_data_by_email( $value ) {
1163
+ global $wpdb;
1164
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1165
+ $result = $wpdb->get_row(
1166
+ $wpdb->prepare(
1167
+ 'SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s AND `order_status` IN (%s, %s) ORDER BY `time` DESC LIMIT 1', $value, WCF_CART_ABANDONED_ORDER, WCF_CART_LOST_ORDER ) // phpcs:ignore
1168
+ );
1169
+ return $result;
1170
+ }
1171
+
1172
+
1173
+ /**
1174
+ * Get the checkout details for the user.
1175
+ *
1176
+ * @param string $value value.
1177
+ * @since 1.0.0
1178
+ */
1179
+ public function get_tracked_data_without_status( $value ) {
1180
+ global $wpdb;
1181
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1182
+ $result = $wpdb->get_row(
1183
+ $wpdb->prepare(
1184
+ 'SELECT * FROM `' . $cart_abandonment_table . '` WHERE email = %s LIMIT 1', $value ) // phpcs:ignore
1185
+ );
1186
+ return $result;
1187
+ }
1188
+
1189
+ /**
1190
+ * Add submenu to admin menu.
1191
+ *
1192
+ * @since 1.1.5
1193
+ */
1194
+ public function abandoned_cart_tracking_menu() {
1195
+
1196
+ $capability = current_user_can( 'manage_woocommerce' ) ? 'manage_woocommerce' : 'manage_options';
1197
+
1198
+ add_submenu_page(
1199
+ 'woocommerce',
1200
+ __( 'Cart Abandonment', 'woo-cart-abandonment-recovery' ),
1201
+ __( 'Cart Abandonment', 'woo-cart-abandonment-recovery' ),
1202
+ $capability,
1203
+ WCF_CA_PAGE_NAME,
1204
+ array( $this, 'render_abandoned_cart_tracking' )
1205
+ );
1206
+ }
1207
+
1208
+ /**
1209
+ * Render table view for cart abandonment tracking.
1210
+ *
1211
+ * @since 1.1.5
1212
+ */
1213
+ public function render_abandoned_cart_tracking() {
1214
+
1215
+ $wcf_list_table = Cartflows_Ca_Cart_Abandonment_Table::get_instance();
1216
+
1217
+ if ( 'delete' === $wcf_list_table->current_action() ) {
1218
+
1219
+ $ids = array();
1220
+ if ( isset( $_REQUEST['id'] ) && is_array( $_REQUEST['id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1221
+ $ids = array_map( 'intval', $_REQUEST['id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1222
+ }
1223
+ $deleted_row_count = empty( $ids ) ? 1 : count( $ids );
1224
+
1225
+ $wcf_list_table->process_bulk_action();
1226
+ $message = '<div class="notice notice-success is-dismissible" id="message"><p>' . sprintf( __( 'Items deleted: %d', 'woo-cart-abandonment-recovery' ), $deleted_row_count ) . '</p></div>'; // phpcs:ignore
1227
+ set_transient( 'wcf_ca_show_message', $message, 5 );
1228
+ if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
1229
+ wp_safe_redirect( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) );
1230
+ }
1231
+ } elseif ( 'unsubscribe' === $wcf_list_table->current_action() ) {
1232
+
1233
+ global $wpdb;
1234
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1235
+ $id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT );
1236
+
1237
+ $wpdb->update(
1238
+ $cart_abandonment_table,
1239
+ array( 'unsubscribed' => true ),
1240
+ array( 'id' => $id )
1241
+ );
1242
+ $wcf_list_table->process_bulk_action();
1243
+ $message = '<div class="notice notice-success is-dismissible" id="message"><p>' . sprintf( __( 'User(s) unsubscribed successfully!', 'woo-cart-abandonment-recovery' ) ) . '</p></div>'; // phpcs:ignore
1244
+ set_transient( 'wcf_ca_show_message', $message, 5 );
1245
+ if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
1246
+ wp_safe_redirect( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) );
1247
+ }
1248
+ }
1249
+ ?>
1250
+
1251
+ <?php
1252
+ include_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-cart-abandonment-tabs.php';
1253
+ ?>
1254
+ <?php
1255
+ }
1256
+
1257
+ /**
1258
+ * Count abandoned carts
1259
+ *
1260
+ * @since 1.1.5
1261
+ */
1262
+ public function abandoned_cart_count() {
1263
+ global $wpdb;
1264
+ $cart_abandonment_table_name = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1265
+
1266
+ $query = $wpdb->prepare( "SELECT COUNT(`id`) FROM {$cart_abandonment_table_name} WHERE `order_status` = %s", WCF_CART_ABANDONED_ORDER ); // phpcs:ignore
1267
+ $total_items = $wpdb->get_var( $query ); // phpcs:ignore
1268
+ return $total_items;
1269
+ }
1270
+
1271
+ /**
1272
+ * Load analytics scripts.
1273
+ */
1274
+ public function load_admin_cart_abandonment_script() {
1275
+
1276
+ $wcar_page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
1277
+
1278
+ if ( ! ( WCF_CA_PAGE_NAME === $wcar_page ) ) {
1279
+ return;
1280
+ }
1281
+
1282
+ // Styles.
1283
+ wp_enqueue_style( 'cartflows-cart-abandonment-admin', CARTFLOWS_CA_URL . 'admin/assets/css/admin-cart-abandonment.css', array(), CARTFLOWS_CA_VER );
1284
+
1285
+ wp_enqueue_script(
1286
+ 'cartflows-ca-email-tmpl-settings',
1287
+ CARTFLOWS_CA_URL . 'admin/assets/js/admin-email-templates.js',
1288
+ array( 'jquery' ),
1289
+ CARTFLOWS_CA_VER,
1290
+ false
1291
+ );
1292
+
1293
+ if ( WCF_CA_PAGE_NAME === $wcar_page ) {
1294
+ $filter_table = filter_input( INPUT_GET, 'filter_table', FILTER_SANITIZE_STRING );
1295
+ $from_date = filter_input( INPUT_GET, 'from_date', FILTER_SANITIZE_STRING );
1296
+ $to_date = filter_input( INPUT_GET, 'to_date', FILTER_SANITIZE_STRING );
1297
+ }
1298
+
1299
+ $vars = array(
1300
+ 'url' => 'admin-ajax.php',
1301
+
1302
+ // For delete coupons.
1303
+ '_delete_coupon_nonce' => wp_create_nonce( 'wcf_ca_delete_garbage_coupons' ),
1304
+ '_confirm_msg' => __( 'Do you really want to delete the used and expired coupons created by Cart Abandonment Plugin?', 'woo-cart-abandonment-recovery' ),
1305
+ '_confirm_msg_export' => __( 'Do you really want to export orders?', 'woo-cart-abandonment-recovery' ),
1306
+
1307
+ // For Search orders.
1308
+ '_search_button_nonce' => wp_create_nonce( 'wcf_ca_search_orders' ),
1309
+ '_result_msg' => __( 'No such order is found.', 'woo-cart-abandonment-recovery' ),
1310
+
1311
+ );
1312
+ wp_localize_script( 'cartflows-ca-email-tmpl-settings', 'wcf_ca_localized_vars', $vars );
1313
+ }
1314
+
1315
+
1316
+ /**
1317
+ * Render Cart abandonment display button beside title.
1318
+ */
1319
+ public function setup_cart_abandonment_button() {
1320
+
1321
+ if ( ! Cartflows_Admin::is_flow_edit_admin() ) {
1322
+ return;
1323
+ }
1324
+
1325
+ $reports_btn_markup = '<style>.wrap{ position:relative;}</style>';
1326
+ $reports_btn_markup .= "<div class='wcf-reports-button-wrap'>";
1327
+ $reports_btn_markup .= "<button class='wcf-cart-abandonment-reports-popup button button-secondary'>";
1328
+ $reports_btn_markup .= esc_html__( 'View Report', 'woo-cart-abandonment-recovery' );
1329
+ $reports_btn_markup .= '</button>';
1330
+ $reports_btn_markup .= '</div>';
1331
+
1332
+ echo wp_kses_post( $reports_btn_markup );
1333
+
1334
+ }
1335
+
1336
+ /**
1337
+ * Get start and end date for given interval.
1338
+ *
1339
+ * @param string $interval interval .
1340
+ * @return array
1341
+ */
1342
+ public function get_start_end_by_interval( $interval ) {
1343
+
1344
+ if ( 'today' === $interval ) {
1345
+ $start_date = gmdate( 'Y-m-d' );
1346
+ $end_date = gmdate( 'Y-m-d' );
1347
+ } else {
1348
+
1349
+ $days = $interval;
1350
+
1351
+ $start_date = gmdate( 'Y-m-d', strtotime( '-' . $days . ' days' ) );
1352
+ $end_date = gmdate( 'Y-m-d' );
1353
+ }
1354
+
1355
+ return array(
1356
+ 'start' => $start_date,
1357
+ 'end' => $end_date,
1358
+ );
1359
+ }
1360
+
1361
+
1362
+ /**
1363
+ * Get Attributable revenue.
1364
+ * Represents the revenue generated by this campaign.
1365
+ *
1366
+ * @param string $from_date from date.
1367
+ * @param string $to_date to date.
1368
+ * @param string $type abondened|completed.
1369
+ */
1370
+ public function get_report_by_type( $from_date, $to_date, $type = WCF_CART_ABANDONED_ORDER ) {
1371
+ global $wpdb;
1372
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1373
+ $minutes = wcf_ca()->utils->get_cart_abandonment_tracking_cut_off_time();
1374
+ $attributable_revenue = $wpdb->get_row(
1375
+ $wpdb->prepare( "SELECT SUM(`cart_total`) as revenue, count('*') as no_of_orders FROM {$cart_abandonment_table} WHERE `order_status` = %s AND DATE(`time`) >= %s AND DATE(`time`) <= %s ", $type, $from_date, $to_date ), // phpcs:ignore
1376
+ ARRAY_A
1377
+ );
1378
+ return $attributable_revenue;
1379
+ }
1380
+
1381
+
1382
+ /**
1383
+ * Get checkout url.
1384
+ *
1385
+ * @param integer $post_id post id.
1386
+ * @param string $token_data token data.
1387
+ * @return string
1388
+ */
1389
+ public function get_checkout_url( $post_id, $token_data ) {
1390
+
1391
+ $token = $this->wcf_generate_token( (array) $token_data );
1392
+ $checkout_url = get_permalink( $post_id ) . '?wcf_ac_token=' . $token;
1393
+ return esc_url( $checkout_url );
1394
+ }
1395
+
1396
+ /**
1397
+ * Geberate the token for the given data.
1398
+ *
1399
+ * @param array $data data.
1400
+ */
1401
+ public function wcf_generate_token( $data ) {
1402
+ return urlencode( base64_encode( http_build_query( $data ) ) );
1403
+ }
1404
+
1405
+ /**
1406
+ * Decode and get the original contents.
1407
+ *
1408
+ * @param string $token token.
1409
+ */
1410
+ public function wcf_decode_token( $token ) {
1411
+ $token = sanitize_text_field( $token );
1412
+ parse_str( base64_decode( urldecode( $token ) ), $token );
1413
+ return $token;
1414
+ }
1415
+
1416
+ /**
1417
+ * Render Cart abandonment tabs.
1418
+ *
1419
+ * @since 1.1.5
1420
+ */
1421
+ public function wcf_display_tabs() {
1422
+
1423
+ $wcar_action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
1424
+ $sub_action = filter_input( INPUT_GET, 'sub_action', FILTER_SANITIZE_STRING );
1425
+
1426
+ if ( ! $wcar_action ) {
1427
+ $wcar_action = WCF_ACTION_REPORTS;
1428
+ $active_settings = '';
1429
+ $active_reports = '';
1430
+ $active_email_templates = '';
1431
+ }
1432
+
1433
+ switch ( $wcar_action ) {
1434
+ case WCF_ACTION_SETTINGS:
1435
+ $active_settings = 'nav-tab-active';
1436
+ break;
1437
+ case WCF_ACTION_REPORTS:
1438
+ $active_reports = 'nav-tab-active';
1439
+ break;
1440
+ case WCF_ACTION_EMAIL_TEMPLATES:
1441
+ $active_email_templates = 'nav-tab-active';
1442
+ break;
1443
+ default:
1444
+ $active_reports = 'nav-tab-active';
1445
+ break;
1446
+ }
1447
+ // phpcs:disable
1448
+ ?>
1449
+
1450
+
1451
+ <div class="nav-tab-wrapper woo-nav-tab-wrapper">
1452
+
1453
+ <?php
1454
+ $url = add_query_arg( array(
1455
+ 'page' => WCF_CA_PAGE_NAME,
1456
+ 'action' => WCF_ACTION_REPORTS
1457
+ ), admin_url( '/admin.php' ) )
1458
+ ?>
1459
+ <a href="<?php echo $url; ?>"
1460
+ class="nav-tab
1461
+ <?php
1462
+ if ( isset( $active_reports ) ) {
1463
+ echo $active_reports;}
1464
+ ?>
1465
+ ">
1466
+ <?php _e( 'Report', 'woo-cart-abandonment-recovery' ); ?>
1467
+ </a>
1468
+
1469
+ <?php
1470
+ $url = add_query_arg( array(
1471
+ 'page' => WCF_CA_PAGE_NAME,
1472
+ 'action' => WCF_ACTION_EMAIL_TEMPLATES
1473
+ ), admin_url( '/admin.php' ) )
1474
+ ?>
1475
+ <a href="<?php echo $url; ?>"
1476
+ class="nav-tab
1477
+ <?php
1478
+ if ( isset( $active_email_templates ) ) {
1479
+ echo $active_email_templates;}
1480
+ ?>
1481
+ ">
1482
+ <?php _e( 'Follow-Up Emails', 'woo-cart-abandonment-recovery' ); ?>
1483
+ </a>
1484
+
1485
+ <?php
1486
+ $url = add_query_arg( array(
1487
+ 'page' => WCF_CA_PAGE_NAME,
1488
+ 'action' => WCF_ACTION_SETTINGS
1489
+ ), admin_url( '/admin.php' ) )
1490
+ ?>
1491
+ <a href="<?php echo $url; ?>"
1492
+ class="nav-tab
1493
+ <?php
1494
+ if ( isset( $active_settings ) ) {
1495
+ echo $active_settings;}
1496
+ ?>
1497
+ ">
1498
+ <?php _e( 'Settings', 'woo-cart-abandonment-recovery' ); ?>
1499
+ </a>
1500
+
1501
+ </div>
1502
+ <?php
1503
+ // phpcs:enable
1504
+ }
1505
+
1506
+ /**
1507
+ * Render Cart abandonment settings.
1508
+ *
1509
+ * @since 1.1.5
1510
+ */
1511
+ public function wcf_display_settings() {
1512
+ ?>
1513
+
1514
+ <form method="post" action="options.php">
1515
+ <?php settings_fields( WCF_CA_SETTINGS_OPTION_GROUP ); ?>
1516
+ <?php do_settings_sections( WCF_CA_PAGE_NAME ); ?>
1517
+ <?php submit_button(); ?>
1518
+ </form>
1519
+
1520
+ <?php
1521
+ }
1522
+
1523
+ /**
1524
+ * Render Cart abandonment reports.
1525
+ *
1526
+ * @since 1.1.5
1527
+ */
1528
+ public function wcf_display_reports() {
1529
+
1530
+ $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
1531
+ $filter_table = filter_input( INPUT_GET, 'filter_table', FILTER_SANITIZE_STRING );
1532
+
1533
+ if ( ! $filter ) {
1534
+ $filter = 'last_month';
1535
+ }
1536
+ if ( ! $filter_table ) {
1537
+ $filter_table = WCF_CART_ABANDONED_ORDER;
1538
+ }
1539
+
1540
+ $from_date = filter_input( INPUT_GET, 'from_date', FILTER_SANITIZE_STRING );
1541
+ $to_date = filter_input( INPUT_GET, 'to_date', FILTER_SANITIZE_STRING );
1542
+ $export_data = filter_input( INPUT_GET, 'export_data', FILTER_VALIDATE_BOOLEAN );
1543
+
1544
+ switch ( $filter ) {
1545
+
1546
+ case 'yesterday':
1547
+ $to_date = gmdate( 'Y-m-d', strtotime( '-1 days' ) );
1548
+ $from_date = $to_date;
1549
+ break;
1550
+ case 'today':
1551
+ $to_date = gmdate( 'Y-m-d' );
1552
+ $from_date = $to_date;
1553
+ break;
1554
+ case 'last_week':
1555
+ $from_date = gmdate( 'Y-m-d', strtotime( '-7 days' ) );
1556
+ $to_date = gmdate( 'Y-m-d' );
1557
+ break;
1558
+ case 'last_month':
1559
+ $from_date = gmdate( 'Y-m-d', strtotime( '-1 months' ) );
1560
+ $to_date = gmdate( 'Y-m-d' );
1561
+ break;
1562
+ case 'custom':
1563
+ $to_date = $to_date ? $to_date : gmdate( 'Y-m-d' );
1564
+ $from_date = $from_date ? $from_date : $to_date;
1565
+ break;
1566
+
1567
+ }
1568
+
1569
+ $abandoned_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_ABANDONED_ORDER );
1570
+ $recovered_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_COMPLETED_ORDER );
1571
+ $lost_report = $this->get_report_by_type( $from_date, $to_date, WCF_CART_LOST_ORDER );
1572
+
1573
+ $wcf_list_table = Cartflows_Ca_Cart_Abandonment_Table::get_instance();
1574
+ $wcf_list_table->prepare_items( $filter_table, $from_date, $to_date );
1575
+
1576
+ if ( $export_data ) {
1577
+
1578
+ $this->download_send_headers();
1579
+ echo $this->array2csv( $wcf_list_table->items ); //phpcs:ignore
1580
+ die;
1581
+
1582
+ }
1583
+
1584
+ $conversion_rate = 0;
1585
+ $total_orders = ( $recovered_report['no_of_orders'] + $abandoned_report['no_of_orders'] + $lost_report['no_of_orders'] );
1586
+ if ( $total_orders ) {
1587
+ $conversion_rate = ( $recovered_report['no_of_orders'] / $total_orders ) * 100;
1588
+ }
1589
+
1590
+ global $woocommerce;
1591
+ $conversion_rate = number_format_i18n( $conversion_rate, 2 );
1592
+ $currency_symbol = get_woocommerce_currency_symbol();
1593
+ require_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-cart-abandonment-reports.php';
1594
+
1595
+ }
1596
+
1597
+
1598
+ /**
1599
+ * Show report details for specific order.
1600
+ */
1601
+ public function wcf_display_report_details() {
1602
+
1603
+ $sesson_id = filter_input( INPUT_GET, 'session_id', FILTER_SANITIZE_STRING );
1604
+
1605
+ if ( $sesson_id ) {
1606
+ $details = $this->get_checkout_details( $sesson_id );
1607
+ $user_details = (object) unserialize( $details->other_fields );
1608
+ $scheduled_emails = $this->fetch_scheduled_emails( $sesson_id );
1609
+
1610
+ require_once CARTFLOWS_CART_ABANDONMENT_TRACKING_DIR . 'includes/admin/cartflows-ca-single-report-details.php';
1611
+ }
1612
+
1613
+ }
1614
+
1615
+ /**
1616
+ * Check and show warning message if cart abandonment is disabled.
1617
+ */
1618
+ public function wcf_show_warning_ca() {
1619
+ $settings_url = add_query_arg(
1620
+ array(
1621
+ 'page' => WCF_CA_PAGE_NAME,
1622
+ 'action' => WCF_ACTION_SETTINGS,
1623
+ ),
1624
+ admin_url( '/admin.php' )
1625
+ );
1626
+
1627
+ if ( ! wcf_ca()->utils->is_cart_abandonment_tracking_enabled() ) {
1628
+ ?>
1629
+ <div class="notice notice-warning is-dismissible">
1630
+ <p>
1631
+ <?php echo __('Looks like abandonment tracking is disabled! Please enable it from <a href=' . esc_url($settings_url) . '> <strong>settings</strong></a>.', 'woo-cart-abandonment-recovery'); // phpcs:ignore
1632
+ ?>
1633
+ </p>
1634
+ </div>
1635
+ <?php
1636
+ }
1637
+ }
1638
+
1639
+ /**
1640
+ * Callback trigger event to send the emails.
1641
+ */
1642
+ public function send_emails_to_callback() {
1643
+
1644
+ global $wpdb;
1645
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
1646
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
1647
+ $email_template_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE;
1648
+
1649
+ $current_time = current_time( WCF_CA_DATETIME_FORMAT );
1650
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
1651
+ $emails_send_to = $wpdb->get_results(
1652
+ $wpdb->prepare(
1653
+ 'SELECT *, EHT.id as email_history_id, ETT.id as email_template_id FROM ' . $email_history_table . ' as EHT
1654
+ INNER JOIN ' . $cart_abandonment_table . ' as CAT ON EHT.`ca_session_id` = CAT.`session_id`
1655
+ INNER JOIN ' . $email_template_table . ' as ETT ON ETT.`id` = EHT.`template_id`
1656
+ WHERE CAT.`order_status` = %s AND CAT.unsubscribed = 0 AND EHT.`email_sent` = 0 AND EHT.`scheduled_time` <= %s',
1657
+ WCF_CART_ABANDONED_ORDER,
1658
+ $current_time
1659
+ )
1660
+ );
1661
+ // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
1662
+ foreach ( $emails_send_to as $email_send_to ) {
1663
+ $email_result = $this->send_email_templates( $email_send_to );
1664
+ if ( $email_result ) {
1665
+ $wpdb->update(
1666
+ $email_history_table,
1667
+ array( 'email_sent' => true ),
1668
+ array( 'id' => $email_send_to->email_history_id )
1669
+ );
1670
+ }
1671
+ }
1672
+ }
1673
+
1674
+
1675
+ /**
1676
+ * Create a dummy object for the preview email.
1677
+ *
1678
+ * @return stdClass
1679
+ */
1680
+ public function create_dummy_session_for_preview_email() {
1681
+
1682
+ $email_data = new stdClass();
1683
+ $current_user = wp_get_current_user();
1684
+ $email_data->email_template_id = null;
1685
+ $email_data->checkout_id = wc_get_page_id( 'checkout' );
1686
+ $email_data->session_id = 'dummy-session-id';
1687
+ $email_send_to = filter_input( INPUT_POST, 'email_send_to', FILTER_SANITIZE_EMAIL );
1688
+ $email_data->email = $email_send_to ? $email_send_to : $current_user->user_email;
1689
+ $email_data->email_body = filter_input( INPUT_POST, 'email_body', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
1690
+ $email_data->email_subject = filter_input( INPUT_POST, 'email_subject', FILTER_SANITIZE_STRING );
1691
+ $email_data->email_body = html_entity_decode( $email_data->email_body, ENT_COMPAT, 'UTF-8' );
1692
+ $email_data->other_fields = serialize(
1693
+ array(
1694
+ 'wcf_first_name' => $current_user->user_firstname,
1695
+ 'wcf_last_name' => $current_user->user_lastname,
1696
+ )
1697
+ );
1698
+ if ( ! WC()->cart->get_cart_contents_count() ) {
1699
+ $args = array(
1700
+ 'posts_per_page' => 1,
1701
+ 'orderby' => 'rand',
1702
+ 'post_type' => 'product',
1703
+ 'meta_query' => array( //phpcs:ignore
1704
+ // Exclude out of stock products.
1705
+ array(
1706
+ 'key' => '_stock_status',
1707
+ 'value' => 'outofstock',
1708
+ 'compare' => 'NOT IN',
1709
+ ),
1710
+ ),
1711
+ 'tax_query' => array( //phpcs:ignore
1712
+ array(
1713
+ 'taxonomy' => 'product_type',
1714
+ 'field' => 'slug',
1715
+ 'terms' => 'simple',
1716
+ ),
1717
+ ),
1718
+ );
1719
+
1720
+ $random_products = get_posts( $args );
1721
+ if ( ! empty( $random_products ) ) {
1722
+ $random_product = reset( $random_products );
1723
+ WC()->cart->add_to_cart( $random_product->ID );
1724
+ }
1725
+ }
1726
+
1727
+ $email_data->cart_total = WC()->cart->total + floatval( WC()->cart->get_cart_shipping_total() );
1728
+ $email_data->cart_contents = serialize( WC()->cart->get_cart() );
1729
+ $email_data->time = current_time( WCF_CA_DATETIME_FORMAT );
1730
+ return $email_data;
1731
+ }
1732
+
1733
+ /**
1734
+ * Callback function to send email templates.
1735
+ *
1736
+ * @param array $email_data email data .
1737
+ * @param boolean $preview_email preview email.
1738
+ * @since 1.0.0
1739
+ */
1740
+ public function send_email_templates( $email_data, $preview_email = false ) {
1741
+
1742
+ if ( $preview_email ) {
1743
+ $email_data = $this->create_dummy_session_for_preview_email();
1744
+ }
1745
+
1746
+ if ( filter_var( $email_data->email, FILTER_VALIDATE_EMAIL ) ) {
1747
+ if ( ! $preview_email ) {
1748
+ if ( ! $this->check_if_already_purchased_by_email_product_ids( $email_data, $email_data->cart_contents ) ) {
1749
+ return false;
1750
+ }
1751
+ }
1752
+
1753
+ $other_fields = unserialize( $email_data->other_fields );
1754
+
1755
+ $from_email_name = get_option( 'wcf_ca_from_name' );
1756
+ $reply_name_preview = get_option( 'wcf_ca_reply_email' );
1757
+ $from_email_preview = get_option( 'wcf_ca_from_email' );
1758
+
1759
+ $user_first_name = ucfirst( $other_fields['wcf_first_name'] );
1760
+ $user_first_name = $user_first_name ? $user_first_name : __( 'there', 'woo-cart-abandonment-recovery' );
1761
+ $user_last_name = ucfirst( $other_fields['wcf_last_name'] );
1762
+ $user_full_name = trim( $user_first_name . ' ' . $user_last_name );
1763
+
1764
+ $subject_email_preview = stripslashes( html_entity_decode( $email_data->email_subject, ENT_QUOTES, 'UTF-8' ) );
1765
+ $subject_email_preview = convert_smilies( $subject_email_preview );
1766
+ $subject_email_preview = str_replace( '{{customer.firstname}}', $user_first_name, $subject_email_preview );
1767
+ $body_email_preview = convert_smilies( $email_data->email_body );
1768
+ $body_email_preview = str_replace( '{{customer.firstname}}', $user_first_name, $body_email_preview );
1769
+ $body_email_preview = str_replace( '{{customer.lastname}}', $user_last_name, $body_email_preview );
1770
+ $body_email_preview = str_replace( '{{customer.fullname}}', $user_full_name, $body_email_preview );
1771
+
1772
+ $email_instance = Cartflows_Ca_Email_Templates::get_instance();
1773
+ if ( $preview_email ) {
1774
+ $coupon_code = 'DUMMY-COUPON';
1775
+ } else {
1776
+ $override_global_coupon = $email_instance->get_email_template_meta_by_key( $email_data->email_template_id, 'override_global_coupon' );
1777
+ if ( $override_global_coupon->meta_value ) {
1778
+ $email_history = $email_instance->get_email_history_by_id( $email_data->email_history_id );
1779
+ $coupon_code = $email_history->coupon_code;
1780
+ } else {
1781
+ $coupon_code = $email_data->coupon_code;
1782
+ }
1783
+ }
1784
+
1785
+ $auto_apply_coupon = $email_instance->get_email_template_meta_by_key( $email_data->email_template_id, 'auto_coupon' );
1786
+
1787
+ $token_data = array(
1788
+ 'wcf_session_id' => $email_data->session_id,
1789
+ 'wcf_coupon_code' => isset( $auto_apply_coupon ) && $auto_apply_coupon->meta_value ? $coupon_code : null,
1790
+ 'wcf_preview_email' => $preview_email ? true : false,
1791
+ );
1792
+
1793
+ $checkout_url = $this->get_checkout_url( $email_data->checkout_id, $token_data );
1794
+
1795
+ $body_email_preview = str_replace( '{{cart.coupon_code}}', $coupon_code, $body_email_preview );
1796
+
1797
+ $current_time_stamp = $email_data->time;
1798
+ $body_email_preview = str_replace( '{{cart.abandoned_date}}', $current_time_stamp, $body_email_preview );
1799
+ $unsubscribe_element = '<a target="_blank" style="color: lightgray" href="' . $checkout_url . '&unsubscribe=true ">' . __( 'Unsubscribe', 'woo-cart-abandonment-recovery' ) . '</a>';
1800
+ $body_email_preview = str_replace( '{{cart.unsubscribe}}', $unsubscribe_element, $body_email_preview );
1801
+ $body_email_preview = str_replace( 'http://{{cart.checkout_url}}', '{{cart.checkout_url}}', $body_email_preview );
1802
+ $body_email_preview = str_replace( 'https://{{cart.checkout_url}}', '{{cart.checkout_url}}', $body_email_preview );
1803
+ $body_email_preview = str_replace( '{{cart.checkout_url}}', $checkout_url, $body_email_preview );
1804
+ $host = wp_parse_url( get_site_url() );
1805
+ $body_email_preview = str_replace( '{{site.url}}', $host['host'], $body_email_preview );
1806
+
1807
+ if ( false !== strpos( $body_email_preview, '{{cart.product.names}}' ) ) {
1808
+ $body_email_preview = str_replace( '{{cart.product.names}}', $this->get_comma_separated_products( $email_data->cart_contents ), $body_email_preview );
1809
+ }
1810
+
1811
+ $admin_user = get_users(
1812
+ array(
1813
+ 'role' => 'Administrator',
1814
+ 'number' => 1,
1815
+ )
1816
+ );
1817
+ $admin_user = reset( $admin_user );
1818
+ $admin_first_name = $admin_user->user_firstname ? $admin_user->user_firstname : 'Admin';
1819
+ $body_email_preview = str_replace( '{{admin.firstname}}', $admin_first_name, $body_email_preview );
1820
+ $body_email_preview = str_replace( '{{admin.company}}', get_bloginfo( 'name' ), $body_email_preview );
1821
+
1822
+ $headers = 'From: ' . $from_email_name . ' <' . $from_email_preview . '>' . "\r\n";
1823
+ $headers .= 'Content-Type: text/html' . "\r\n";
1824
+ $headers .= 'Reply-To: ' . $reply_name_preview . ' ' . "\r\n";
1825
+ $var = $this->get_email_product_block( $email_data->cart_contents, $email_data->cart_total );
1826
+
1827
+ $body_email_preview = str_replace( '{{cart.product.table}}', $var, $body_email_preview );
1828
+ $body_email_preview = wpautop( $body_email_preview );
1829
+ $mail_result = wp_mail( $email_data->email, $subject_email_preview, stripslashes( $body_email_preview ), $headers );
1830
+ if ( $mail_result ) {
1831
+ return true;
1832
+ } else {
1833
+ // Retry sending mail.
1834
+ $mail_result = wp_mail( $email_data->email, $subject_email_preview, stripslashes( $body_email_preview ), $headers );
1835
+ if ( ! $preview_email ) {
1836
+ return true;
1837
+ }
1838
+ return false;
1839
+ }
1840
+ } else {
1841
+ return false;
1842
+ }
1843
+
1844
+ }
1845
+
1846
+ /**
1847
+ * Generate comma separated products.
1848
+ *
1849
+ * @param object $cart_contents user cart details.
1850
+ */
1851
+ public function get_comma_separated_products( $cart_contents ) {
1852
+ $cart_comma_string = '';
1853
+ if ( ! $cart_contents ) {
1854
+ return $cart_comma_string;
1855
+ }
1856
+ $cart_data = unserialize( $cart_contents );
1857
+
1858
+ $cart_length = count( $cart_data );
1859
+ $index = 0;
1860
+ foreach ( $cart_data as $key => $product ) {
1861
+
1862
+ if ( ! isset( $product['product_id'] ) ) {
1863
+ continue;
1864
+ }
1865
+
1866
+ $cart_product = wc_get_product( $product['product_id'] );
1867
+
1868
+ if ( $cart_product ) {
1869
+ $cart_comma_string = $cart_comma_string . $cart_product->get_title();
1870
+ if ( ( $cart_length - 2 ) === $index ) {
1871
+ $cart_comma_string = $cart_comma_string . ' & ';
1872
+ } elseif ( ( $cart_length - 1 ) !== $index ) {
1873
+ $cart_comma_string = $cart_comma_string . ', ';
1874
+ }
1875
+ $index++;
1876
+ }
1877
+ }
1878
+ return $cart_comma_string;
1879
+
1880
+ }
1881
+
1882
+ /**
1883
+ * Generate the view for email product cart block.
1884
+ *
1885
+ * @param object $cart_contents user cart contents details.
1886
+ * @param float $cart_total user cart total.
1887
+ * @return string
1888
+ */
1889
+ public function get_email_product_block( $cart_contents, $cart_total ) {
1890
+
1891
+ $cart_items = unserialize( $cart_contents );
1892
+
1893
+ if ( ! is_array( $cart_items ) || ! count( $cart_items ) ) {
1894
+ return;
1895
+ }
1896
+
1897
+ $currency_symbol = get_woocommerce_currency_symbol();
1898
+ $tr = '';
1899
+ $style = array(
1900
+ 'product_image' => array(
1901
+ 'style' => 'height: 42px; width: 42px;',
1902
+ ),
1903
+ 'table' => array(
1904
+ 'style' => 'color: #636363; border: 1px solid #e5e5e5;',
1905
+ 'attribute' => 'align= left;',
1906
+ ),
1907
+ );
1908
+
1909
+ $style_filter = apply_filters( 'woo_ca_email_template_table_style', $style );
1910
+ $product_image_style = isset( $style_filter['product_image']['style'] ) ? $style_filter['product_image']['style'] : '';
1911
+ $style = isset( $style_filter['table']['style'] ) ? $style_filter['table']['style'] : '';
1912
+
1913
+ foreach ( $cart_items as $cart_item ) {
1914
+
1915
+ if ( isset( $cart_item['product_id'] ) && isset( $cart_item['quantity'] ) && isset( $cart_item['line_total'] ) ) {
1916
+ $id = 0 !== $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
1917
+ $image_url = get_the_post_thumbnail_url( $id );
1918
+ $image_url = ! empty( $image_url ) ? $image_url : get_the_post_thumbnail_url( $cart_item['product_id'] );
1919
+ $tr = $tr . '<tr style=' . $style . ' align="center">
1920
+ <td style="' . $style . '"><img class="demo_img" style="' . $product_image_style . '" src="' . esc_url( $image_url ) . '"></td>
1921
+ <td style="' . $style . '">' . get_the_title( $id ) . '</td>
1922
+ <td style="' . $style . '"> ' . $cart_item['quantity'] . ' </td>
1923
+ <td style="' . $style . '">' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1924
+ <td style="' . $style . '" >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1925
+ </tr> ';
1926
+ }
1927
+ }
1928
+
1929
+ /**
1930
+ * Add filter to toggle the Cart Total row.
1931
+ */
1932
+ $enable_cart_total = apply_filters( 'woo_ca_recovery_enable_cart_total', false );
1933
+ if ( $enable_cart_total ) {
1934
+ $tr = $tr . '<tr style="' . $style . '" align="center">
1935
+ <td colspan="4" style="' . $style . '"> ' . __( 'Cart Total ( Cart Total + Shipping + Tax )', 'woo-cart-abandonment-recovery' ) . ' </td>
1936
+ <td style="' . $style . '" >' . $currency_symbol . number_format_i18n( $cart_total, 2 ) . '</td>
1937
+ </tr> ';
1938
+ }
1939
+
1940
+ return '<table ' . $style_filter['table']['attribute'] . ' cellpadding="10" cellspacing="0" style="float: none; border: 1px solid #e5e5e5;">
1941
+ <tr align="center">
1942
+ <th style="' . $style . '">' . __( 'Item', 'woo-cart-abandonment-recovery' ) . '</th>
1943
+ <th style="' . $style . '">' . __( 'Name', 'woo-cart-abandonment-recovery' ) . '</th>
1944
+ <th style="' . $style . '">' . __( 'Quantity', 'woo-cart-abandonment-recovery' ) . '</th>
1945
+ <th style="' . $style . '">' . __( 'Price', 'woo-cart-abandonment-recovery' ) . '</th>
1946
+ <th style="' . $style . '">' . __( 'Line Subtotal', 'woo-cart-abandonment-recovery' ) . '</th>
1947
+ </tr> ' . $tr . '
1948
+ </table>';
1949
+ }
1950
+
1951
+ /**
1952
+ * Generate the view for admin product cart block.
1953
+ *
1954
+ * @param object $cart_contents user cart contents details.
1955
+ * @param float $cart_total user cart total.
1956
+ * @return string
1957
+ */
1958
+ public function get_admin_product_block( $cart_contents, $cart_total ) {
1959
+
1960
+ $cart_items = unserialize( $cart_contents );
1961
+
1962
+ if ( ! is_array( $cart_items ) || ! count( $cart_items ) ) {
1963
+ return;
1964
+ }
1965
+
1966
+ $currency_symbol = get_woocommerce_currency_symbol();
1967
+ $tr = '';
1968
+ $total = 0;
1969
+ $discount = 0;
1970
+ $tax = 0;
1971
+
1972
+ foreach ( $cart_items as $cart_item ) {
1973
+
1974
+ if ( isset( $cart_item['product_id'] ) && isset( $cart_item['quantity'] ) && isset( $cart_item['line_total'] ) && isset( $cart_item['line_subtotal'] ) ) {
1975
+ $id = 0 !== $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
1976
+ $discount = number_format_i18n( $discount + ( $cart_item['line_subtotal'] - $cart_item['line_total'] ), 2 );
1977
+ $total = number_format_i18n( $total + $cart_item['line_subtotal'], 2 );
1978
+ $tax = number_format_i18n( $tax + $cart_item['line_tax'], 2 );
1979
+ $image_url = get_the_post_thumbnail_url( $id );
1980
+ $image_url = ! empty( $image_url ) ? $image_url : get_the_post_thumbnail_url( $cart_item['product_id'] );
1981
+ $tr = $tr . '<tr align="center">
1982
+ <td ><img class="demo_img" width="42" height="42" src=" ' . esc_url( $image_url ) . ' "/></td>
1983
+ <td >' . get_the_title( $id ) . '</td>
1984
+ <td > ' . $cart_item['quantity'] . ' </td>
1985
+ <td >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1986
+ <td >' . $currency_symbol . number_format_i18n( $cart_item['line_total'], 2 ) . '</td>
1987
+ </tr> ';
1988
+ }
1989
+ }
1990
+
1991
+ return '<table align="left" cellspacing="0" class="widefat fixed striped posts">
1992
+ <thead>
1993
+ <tr align="center">
1994
+ <th >' . __( 'Item', 'woo-cart-abandonment-recovery' ) . '</th>
1995
+ <th >' . __( 'Name', 'woo-cart-abandonment-recovery' ) . '</th>
1996
+ <th >' . __( 'Quantity', 'woo-cart-abandonment-recovery' ) . '</th>
1997
+ <th >' . __( 'Price', 'woo-cart-abandonment-recovery' ) . '</th>
1998
+ <th >' . __( 'Line Subtotal', 'woo-cart-abandonment-recovery' ) . '</th>
1999
+ </tr>
2000
+ </thead>
2001
+ <tbody>
2002
+ ' . $tr . '
2003
+ <tr align="center" id="wcf-ca-discount">
2004
+ <td colspan="4" >' . __( 'Discount', 'woo-cart-abandonment-recovery' ) . '</td>
2005
+ <td>' . $currency_symbol . ( $discount ) . '</td>
2006
+ </tr>
2007
+ <tr align="center" id="wcf-ca-other">
2008
+ <td colspan="4" >' . __( 'Other', 'woo-cart-abandonment-recovery' ) . '</td>
2009
+ <td>' . $currency_symbol . ( $tax ) . '</td>
2010
+ </tr>
2011
+
2012
+ <tr align="center" id="wcf-ca-shipping">
2013
+ <td colspan="4" >' . __( 'Shipping', 'woo-cart-abandonment-recovery' ) . '</td>
2014
+ <td>' . $currency_symbol . number_format_i18n( $discount + ( $cart_total - $total ) - $tax, 2 ) . '</td>
2015
+ </tr>
2016
+ <tr align="center" id="wcf-ca-cart-total">
2017
+ <td colspan="4" >' . __( 'Cart Total', 'woo-cart-abandonment-recovery' ) . '</td>
2018
+ <td>' . $currency_symbol . $cart_total . '</td>
2019
+ </tr>
2020
+ </tbody>
2021
+ </table>';
2022
+ }
2023
+
2024
+ /**
2025
+ * Schedule events for the abadoned carts to send emails.
2026
+ *
2027
+ * @param integer $session_id user session id.
2028
+ * @param boolean $force_reschedule force reschedule.
2029
+ */
2030
+ public function schedule_emails( $session_id, $force_reschedule = false ) {
2031
+
2032
+ $checkout_details = $this->get_checkout_details( $session_id );
2033
+
2034
+ if ( ( $checkout_details->unsubscribed ) || ( WCF_CART_COMPLETED_ORDER === $checkout_details->order_status ) ) {
2035
+ return;
2036
+ }
2037
+
2038
+ $scheduled_emails = $this->fetch_scheduled_emails( $session_id );
2039
+ $scheduled_templates = array_column( $scheduled_emails, 'template_id' ); //phpcs:ignore
2040
+ $scheduled_time_from = $checkout_details->time;
2041
+
2042
+ if ( $force_reschedule ) {
2043
+ $scheduled_time_from = current_time( WCF_CA_DATETIME_FORMAT );
2044
+ }
2045
+
2046
+ $email_tmpl = Cartflows_Ca_Email_Templates::get_instance();
2047
+ $templates = $email_tmpl->fetch_all_active_templates();
2048
+
2049
+ global $wpdb;
2050
+
2051
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
2052
+
2053
+ foreach ( $templates as $template ) {
2054
+
2055
+ if ( false !== array_search( $template->id, $scheduled_templates, true ) && false === $force_reschedule ) {
2056
+ continue;
2057
+ }
2058
+
2059
+ $timestamp_str = '+' . $template->frequency . ' ' . $template->frequency_unit . 'S';
2060
+ $scheduled_time = gmdate( WCF_CA_DATETIME_FORMAT, strtotime( $scheduled_time_from . $timestamp_str ) );
2061
+ $discount_type = $email_tmpl->get_email_template_meta_by_key( $template->id, 'discount_type' );
2062
+ $discount_type = isset( $discount_type->meta_value ) ? $discount_type->meta_value : '';
2063
+ $amount = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_amount' );
2064
+ $amount = isset( $amount->meta_value ) ? $amount->meta_value : '';
2065
+
2066
+ $coupon_expiry_date = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_expiry_date' );
2067
+ $coupon_expiry_unit = $email_tmpl->get_email_template_meta_by_key( $template->id, 'coupon_expiry_unit' );
2068
+ $coupon_expiry_date = isset( $coupon_expiry_date->meta_value ) ? $coupon_expiry_date->meta_value : '';
2069
+ $coupon_expiry_unit = isset( $coupon_expiry_unit->meta_value ) ? $coupon_expiry_unit->meta_value : 'hours';
2070
+
2071
+ $coupon_expiry_date = $coupon_expiry_date ? strtotime( $scheduled_time . ' +' . $coupon_expiry_date . ' ' . $coupon_expiry_unit ) : '';
2072
+
2073
+ $free_shipping_coupon = $email_tmpl->get_email_template_meta_by_key( $template->id, 'free_shipping_coupon' );
2074
+ $free_shipping = ( isset( $free_shipping_coupon ) && ( $free_shipping_coupon->meta_value ) ) ? 'yes' : 'no';
2075
+
2076
+ $individual_use_only = $email_tmpl->get_email_template_meta_by_key( $template->id, 'individual_use_only' );
2077
+ $individual_use = ( isset( $individual_use_only ) && ( $individual_use_only->meta_value ) ) ? 'yes' : 'no';
2078
+
2079
+ $override_global_coupon = $email_tmpl->get_email_template_meta_by_key( $template->id, 'override_global_coupon' );
2080
+
2081
+ $new_coupon_code = '';
2082
+ if ( $override_global_coupon->meta_value ) {
2083
+ $new_coupon_code = $this->generate_coupon_code( $discount_type, $amount, $coupon_expiry_date, $free_shipping, $individual_use );
2084
+ }
2085
+
2086
+ $wpdb->replace(
2087
+ $email_history_table,
2088
+ array(
2089
+ 'template_id' => $template->id,
2090
+ 'ca_session_id' => $checkout_details->session_id,
2091
+ 'coupon_code' => $new_coupon_code,
2092
+ 'scheduled_time' => $scheduled_time,
2093
+ )
2094
+ );
2095
+ }
2096
+ }
2097
+
2098
+ /**
2099
+ * Fetch all the scheduled emails with templates for the specific session.
2100
+ *
2101
+ * @param string $session_id session id.
2102
+ * @param boolean $fetch_sent sfetch sent emails.
2103
+ * @return array|object|null
2104
+ */
2105
+ public function fetch_scheduled_emails( $session_id, $fetch_sent = false ) {
2106
+ global $wpdb;
2107
+ $email_history_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_HISTORY_TABLE;
2108
+ $email_template_table = $wpdb->prefix . CARTFLOWS_CA_EMAIL_TEMPLATE_TABLE;
2109
+
2110
+ $query = $wpdb->prepare("SELECT * FROM $email_history_table as eht INNER JOIN $email_template_table as ett ON eht.template_id = ett.id WHERE ca_session_id = %s", sanitize_text_field($session_id)); // phpcs:ignore
2111
+
2112
+ if ( $fetch_sent ) {
2113
+ $query .= ' AND email_sent = 1';
2114
+ }
2115
+
2116
+ $result = $wpdb->get_results( $query ); // phpcs:ignore
2117
+ return $result;
2118
+ }
2119
+
2120
+ /**
2121
+ * Delete orders from cart abandonment table whose cart total is zero and order status is abandoned.
2122
+ */
2123
+ public function delete_empty_abandoned_order() {
2124
+ global $wpdb;
2125
+
2126
+ $cart_abandonment_table = $wpdb->prefix . CARTFLOWS_CA_CART_ABANDONMENT_TABLE;
2127
+
2128
+ $where = array(
2129
+ 'cart_total' => 0,
2130
+ );
2131
+
2132
+ $wpdb->delete( $cart_abandonment_table, $where );
2133
+ }
2134
+
2135
+
2136
+ /**
2137
+ * Check if transient is set for delete garbage coupons.
2138
+ */
2139
+ public function delete_used_and_expired_coupons() {
2140
+ $is_ajax_request = wp_doing_ajax();
2141
+ $is_transient_set = false;
2142
+ global $wpdb;
2143
+ if ( $is_ajax_request ) {
2144
+ check_ajax_referer( 'wcf_ca_delete_garbage_coupons', 'security' );
2145
+ } else {
2146
+ $is_transient_set = get_transient( 'woocommerce_ca_delete_garbage_coupons' );
2147
+ }
2148
+
2149
+ if ( false === $is_transient_set || $is_ajax_request ) {
2150
+ $coupons = $this->delete_garbage_coupons();
2151
+ $coupon_count = count( $coupons );
2152
+
2153
+ if ( $coupon_count ) {
2154
+ $coupons_post_ids = implode( ',', wp_list_pluck( $coupons, 'ID' ) );
2155
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE post_id IN(" . $coupons_post_ids . ')' );//phpcs:ignore
2156
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}posts WHERE ID IN(" . $coupons_post_ids . ')' );//phpcs:ignore
2157
+ }
2158
+
2159
+ if ( ! $is_ajax_request ) {
2160
+ set_transient( 'woocommerce_ca_delete_garbage_coupons', $coupons, WEEK_IN_SECONDS );
2161
+ return;
2162
+ }
2163
+
2164
+ // translators: %1$s: Coupons Deleted, %2$s: Deleted coupons count'.
2165
+ wp_send_json_success( sprintf( __( '%1$s: %2$d', 'woo-cart-abandonment-recovery' ), 'Coupons Deleted', $coupon_count ) );
2166
+
2167
+ }
2168
+ }
2169
+
2170
+
2171
+ /**
2172
+ * Set transient and delete garbage coupons.
2173
+ */
2174
+ public function delete_garbage_coupons() {
2175
+
2176
+ global $wpdb;
2177
+
2178
+ $coupon_generated_by = WCF_CA_COUPON_GENERATED_BY;
2179
+ $timestamp = time();
2180
+ $post_type = 'shop_coupon';
2181
+ $coupons = $wpdb->get_results(
2182
+ $wpdb->prepare(
2183
+ "SELECT ID, coupon_code, usage_limit, total_usaged, expiry_date FROM (
2184
+ SELECT p.ID,
2185
+ p.post_title AS coupon_code,
2186
+ Max(CASE WHEN pm.meta_key = 'date_expires' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS expiry_date,
2187
+ Max(CASE WHEN pm.meta_key = 'usage_limit' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS usage_limit,
2188
+ Max(CASE WHEN pm.meta_key = 'usage_count' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS total_usaged,
2189
+
2190
+ Max(CASE WHEN pm.meta_key = 'coupon_generated_by' AND p.`ID` = pm.`post_id` THEN pm.meta_value END) AS coupon_generated_by
2191
+ FROM {$wpdb->prefix}posts AS p
2192
+ INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
2193
+ WHERE p.`post_type` = %s
2194
+
2195
+ GROUP BY p.ID
2196
+ ) AS final_res WHERE coupon_generated_by IS NOT NULL AND coupon_generated_by = %s AND ( ( usage_limit = total_usaged ) OR ( expiry_date <= %d AND expiry_date != '') )",
2197
+ $post_type,
2198
+ $coupon_generated_by,
2199
+ $timestamp
2200
+ )
2201
+ );
2202
+ return $coupons;
2203
+ }
2204
+
2205
+ /**
2206
+ * Send headers to export orders to csv format.
2207
+ */
2208
+ private function download_send_headers() {
2209
+ $now = gmdate( 'Y-m-d-H-i-s' );
2210
+ $filename = 'woo-cart-abandonment-recovery-export-' . $now . '.csv';
2211
+
2212
+ header( 'Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate' );
2213
+ header( "Last-Modified: {$now} GMT" );
2214
+
2215
+ // force download.
2216
+ header( 'Content-Type: application/force-download' );
2217
+ header( 'Content-Type: application/octet-stream' );
2218
+ header( 'Content-Type: application/download' );
2219
+
2220
+ // disposition / encoding on response body.
2221
+ header( "Content-Disposition: attachment;filename={$filename}" );
2222
+ header( 'Content-Transfer-Encoding: binary' );
2223
+ }
2224
+
2225
+ /**
2226
+ * Convert users data to csv format.
2227
+ *
2228
+ * @param array $user_data users data.
2229
+ */
2230
+ private function array2csv( array $user_data ) {
2231
+ if ( empty( $user_data ) ) {
2232
+ return;
2233
+ }
2234
+ ob_clean();
2235
+ ob_start();
2236
+ $data_file = fopen( 'php://output', 'w' );
2237
+ fputcsv(
2238
+ $data_file,
2239
+ array(
2240
+ 'First-Name',
2241
+ 'Last-Name',
2242
+ 'Email',
2243
+ 'Phone',
2244
+ 'Products',
2245
+ 'Cart-Total in ' . get_woocommerce_currency(),
2246
+ 'Order-Status',
2247
+ 'Unsubscribed',
2248
+ 'Coupon-Code',
2249
+ )
2250
+ );
2251
+ foreach ( $user_data as $data ) {
2252
+ $name = unserialize( $data['other_fields'] );
2253
+ $checkout_details = $this->get_checkout_details( $data['session_id'] );
2254
+ $cart_data = $this->get_comma_separated_products( $checkout_details->cart_contents );
2255
+ fputcsv(
2256
+ $data_file,
2257
+ array(
2258
+ $name['wcf_first_name'],
2259
+ $name['wcf_last_name'],
2260
+ $data['email'],
2261
+ $name['wcf_phone_number'],
2262
+ $cart_data,
2263
+ $data['cart_total'],
2264
+ $data['order_status'],
2265
+ $data['unsubscribed'] ? 'Yes' : 'No',
2266
+ $data['coupon_code'],
2267
+ )
2268
+ );
2269
+
2270
+ }
2271
+ fclose( $data_file ); //phpcs:ignore
2272
+ return ob_get_clean();
2273
+ }
2274
+ }
2275
+
2276
+ Cartflows_Ca_Cart_Abandonment::get_instance();
modules/cart-abandonment/class-cartflows-ca-email-templates-table.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  if ( ! class_exists( 'WP_List_Table' ) ) {
9
  include_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
10
  }
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  if ( ! class_exists( 'WP_List_Table' ) ) {
13
  include_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
14
  }
modules/cart-abandonment/class-cartflows-ca-email-templates.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  define( 'CARTFLOWS_EMAIL_TEMPLATE_DIR', CARTFLOWS_CA_DIR . 'modules/cart-abandonment/' );
9
  define( 'CARTFLOWS_EMAIL_TEMPLATE_URL', CARTFLOWS_CA_URL . 'modules/cart-abandonment/' );
10
 
@@ -186,6 +190,7 @@ class Cartflows_Ca_Email_Templates {
186
  define( 'WCF_CA_COUPON_CODE_SECTION', 'cartflows_coupon_code_settings_section' );
187
  define( 'WCF_CA_ZAPIER_SETTINGS_SECTION', 'cartflows_zapier_settings_section' );
188
  define( 'WCF_CA_GDPR_SETTINGS_SECTION', 'cartflows_gdpr_settings_section' );
 
189
 
190
  define( 'WCF_CA_SETTINGS_OPTION_GROUP', 'cartflows-cart-abandonment-settings' );
191
  define( 'WCF_CA_EMAIL_SETTINGS_OPTION_GROUP', 'cartflows-cart-abandonment-email-settings' );
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  define( 'CARTFLOWS_EMAIL_TEMPLATE_DIR', CARTFLOWS_CA_DIR . 'modules/cart-abandonment/' );
13
  define( 'CARTFLOWS_EMAIL_TEMPLATE_URL', CARTFLOWS_CA_URL . 'modules/cart-abandonment/' );
14
 
190
  define( 'WCF_CA_COUPON_CODE_SECTION', 'cartflows_coupon_code_settings_section' );
191
  define( 'WCF_CA_ZAPIER_SETTINGS_SECTION', 'cartflows_zapier_settings_section' );
192
  define( 'WCF_CA_GDPR_SETTINGS_SECTION', 'cartflows_gdpr_settings_section' );
193
+ define( 'WCF_CA_PLUGIN_SETTINGS_SECTION', 'cartflows_cart_abandonment_plugin_settings_section' );
194
 
195
  define( 'WCF_CA_SETTINGS_OPTION_GROUP', 'cartflows-cart-abandonment-settings' );
196
  define( 'WCF_CA_EMAIL_SETTINGS_OPTION_GROUP', 'cartflows-cart-abandonment-email-settings' );
modules/cart-abandonment/class-cartflows-ca-module-loader.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  /**
9
  * Cart Abandonment DB class.
10
  */
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  /**
13
  * Cart Abandonment DB class.
14
  */
modules/cart-abandonment/includes/admin/cartflows-ca-single-report-details.php CHANGED
@@ -1,247 +1,252 @@
1
- <?php
2
- /**
3
- * Cartflows view for single cart abandonment report details.
4
- *
5
- * @package Woocommerce-Cart-Abandonment-Recovery
6
- */
7
-
8
- ?>
9
-
10
-
11
- <div class="wcf-ca-report-btn">
12
- <div class="wcf-ca-left-report-field-group">
13
- <?php
14
- if ( wp_get_referer() ) {
15
- $back_link = wp_get_referer();
16
- } else {
17
- $back_link = add_query_arg(
18
- array(
19
- 'page' => WCF_CA_PAGE_NAME,
20
- 'action' => WCF_ACTION_REPORTS,
21
- ),
22
- admin_url( '/admin.php' )
23
- );
24
- }
25
- ?>
26
- <a href="<?php echo esc_attr( $back_link ); ?>" class="button button-secondary back-button"><span
27
- class="dashicons dashicons-arrow-left"></span> <?php esc_html_e( 'Back to Reports', 'woo-cart-abandonment-recovery' ); ?> </a>
28
- </div>
29
- </div>
30
-
31
- <!-- First panel Start -->
32
- <div class="wcf-ca-panel">
33
- <div class="wcf-ca-column wcf-ca-column-two wcf-ca-margin-right">
34
- <div class="wcf-ca-email-data">
35
-
36
- <div class="wcf-ca-report-btn" style="padding: 0px">
37
- <div class="wcf-ca-left-report-field-group">
38
- <h2> <?php esc_html_e( 'Email Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
39
- </div>
40
- <div class="wcf-ca-right-report-field-group">
41
-
42
- <?php if ( WCF_CART_ABANDONED_ORDER === $details->order_status && ! $details->unsubscribed ) : ?>
43
- <?php add_thickbox(); ?>
44
- <div id="wcf-ca-confirm-email-reschedule" style="display:none;">
45
- <div style="text-align:center;">
46
- <p>
47
- <?php
48
- esc_html_e(
49
- 'All new activated emails will be reschedule for this abandoned order. New emails will be sent to user according to schedule time.', 'woo-cart-abandonment-recovery'
50
- );
51
- ?>
52
- </p>
53
- <p>
54
- <strong><?php esc_html_e( 'Are your sure?', 'woo-cart-abandonment-recovery' ); ?></strong>
55
- </p>
56
- <p>
57
- <button onclick="window.location.search += '&sub_action=<?php echo esc_attr( WCF_SUB_ACTION_REPORTS_RESCHEDULE ); ?>';"
58
- class="button button-secondary"> <?php esc_html_e( 'Reschedule', 'woo-cart-abandonment-recovery' ); ?>
59
- </button>
60
- <button type="button"
61
- onclick='document.getElementById("TB_closeWindowButton").click()'
62
- class="button button-secondary"> <?php esc_html_e( 'Close', 'woo-cart-abandonment-recovery' ); ?>
63
- </button>
64
- </p>
65
- </div>
66
- </div>
67
- <a name="<?php esc_html_e( 'Do you really want to reschedule emails?', 'woo-cart-abandonment-recovery' ); ?>" href="#TB_inline?&width=500&height=200&inlineId=wcf-ca-confirm-email-reschedule" class="thickbox button button-secondary"> <?php esc_html_e( 'Reschedule Emails', 'woo-cart-abandonment-recovery' ); ?> </a>
68
- <?php endif; ?>
69
- </div>
70
- </div>
71
-
72
- <?php if ( empty( $scheduled_emails ) ) : ?>
73
- <div style="text-align: center;"><strong> <?php esc_html_e( ' No Email Scheduled.', 'woo-cart-abandonment-recovery' ); ?></strong>
74
- </div>
75
- <?php else : ?>
76
- <table cellpadding="15" cellspacing="0" class="wcf-table wcf-table-striped fixed posts">
77
- <thead>
78
- <tr>
79
-
80
- <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Scheduled Template', 'woo-cart-abandonment-recovery' ); ?></th>
81
- <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Subject', 'woo-cart-abandonment-recovery' ); ?></th>
82
- <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Coupon', 'woo-cart-abandonment-recovery' ); ?></th>
83
- <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Sent', 'woo-cart-abandonment-recovery' ); ?></th>
84
- <th class="wcf-ca-report-table-row"><span class="dashicons dashicons-clock"></span> <?php esc_html_e( 'Scheduled At', 'woo-cart-abandonment-recovery' ); ?>
85
- </th>
86
-
87
- </tr>
88
- </thead>
89
-
90
- <tbody>
91
- <?php foreach ( $scheduled_emails as $scheduled_email ) : ?>
92
-
93
- <?php
94
- $email_tmpl_url = wp_nonce_url(
95
- add_query_arg(
96
- array(
97
- 'page' => WCF_CA_PAGE_NAME,
98
- 'action' => WCF_ACTION_EMAIL_TEMPLATES,
99
- 'sub_action' => WCF_SUB_ACTION_EDIT_EMAIL_TEMPLATES,
100
- 'id' => $scheduled_email->template_id,
101
- ),
102
- admin_url( '/admin.php' )
103
- ),
104
- WCF_EMAIL_TEMPLATES_NONCE
105
- );
106
-
107
-
108
-
109
- switch ( $scheduled_email->email_sent ) {
110
- case 0:
111
- if ( $details->unsubscribed ) {
112
- $icon = '<span class="dashicons dashicons-minus"></span>';
113
- $title_text = esc_html__( 'The email has been unsubscribed and won\'t be sent further.', 'woo-cart-abandonment-recovery' );
114
- } else {
115
- $icon = '<span class="dashicons dashicons-no"></span>';
116
- $title_text = esc_html__( 'Email is in the queue and will be sent at the scheduled time.', 'woo-cart-abandonment-recovery' );
117
- }
118
- break;
119
- case 1:
120
- $icon = '<span class="dashicons dashicons-yes wp-ui-text-highlight" ></span>';
121
- $title_text = esc_html__( 'The email has been sent.', 'woo-cart-abandonment-recovery' );
122
- break;
123
- case -1:
124
- $icon = '<span class="dashicons dashicons-dismiss wp-ui-text-highlight" ></span>';
125
- $title_text = esc_html__( 'The email has been unscheduled due to the complete order and won\'t be sent further.', 'woo-cart-abandonment-recovery' );
126
- break;
127
- }
128
-
129
-
130
- $scheduled_time = gmdate( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $scheduled_email->scheduled_time ) );
131
- ?>
132
-
133
- <tr class="wcf-ca-report-table-row">
134
- <td class="wcf-ca-report-table-row"><a
135
- href="<?php echo esc_url( $email_tmpl_url ); ?>"
136
- class="wp-ui-text-highlight"> <?php echo esc_attr( $scheduled_email->template_name ); ?> </a>
137
- </td>
138
- <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_email->email_subject ); ?> </td>
139
- <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_email->coupon_code ? $scheduled_email->coupon_code : '--' ); ?> </td>
140
- <td class="wcf-ca-report-table-row wcf-ca-icon-row"> <?php echo( $icon ); //phpcs:ignore ?>
141
- <span class="wcf-ca-tooltip-text"><?php echo esc_attr( $title_text ); ?></span>
142
- </td>
143
- <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_time ); ?> </td>
144
- </tr>
145
- <?php endforeach; ?>
146
- </tbody>
147
- </table>
148
- <?php endif; ?>
149
-
150
- </div>
151
- </div>
152
-
153
- <div class="wcf-ca-column wcf-ca-column-two wcf-ca-margin-left">
154
- <div class="wcf-ca-user-detail ">
155
-
156
- <div class="wcf-ca-report-btn" style="padding: 0px">
157
- <div class="wcf-ca-left-report-field-group">
158
- <h2> <?php esc_html_e( 'User Address Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
159
- </div>
160
- <div class="wcf-ca-right-report-field-group">
161
- <?php if ( $details->unsubscribed ) : ?>
162
- <span class="wcf-ca-tag"> <?php esc_html_e( 'Unsubscribed', 'woo-cart-abandonment-recovery' ); ?> </span>
163
- <?php endif; ?>
164
-
165
- <span class="wcf-ca-tag"> <?php echo esc_attr( ucfirst( $details->order_status ) ); ?> </span>
166
- </div>
167
- </div>
168
-
169
- <div class="wcf-ca-user-address wcf-pull-left">
170
- <h3> <?php esc_html_e( 'Billing Address', 'woo-cart-abandonment-recovery' ); ?> </h3>
171
- <p><strong> <?php esc_html_e( 'Name', 'woo-cart-abandonment-recovery' ); ?> </strong>
172
- <?php echo esc_attr( $user_details->wcf_first_name . ' ' . $user_details->wcf_last_name ); ?> </p>
173
- <p>
174
- <strong> <?php esc_html_e( 'Email address', 'woo-cart-abandonment-recovery' ); ?> </strong>
175
- <a href="mailto:<?php echo esc_attr( $details->email ); ?>"><?php echo esc_attr( $details->email ); ?></a>
176
- </p>
177
-
178
- <p>
179
- <strong> <?php esc_html_e( 'Phone', 'woo-cart-abandonment-recovery' ); ?> </strong>
180
- <a href="tel:<?php echo esc_attr( $user_details->wcf_phone_number ); ?>"><?php echo esc_attr( $user_details->wcf_phone_number ); ?></a>
181
- </p>
182
-
183
- <p>
184
- <strong> <?php esc_html_e( 'Address 1:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_address_1 ); ?>
185
- </p>
186
- <p>
187
- <strong> <?php esc_html_e( 'Address 2:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_address_2 ); ?>
188
- </p>
189
- <p>
190
- <strong> <?php esc_html_e( 'Country, City:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_location ); ?>
191
- </p>
192
- <p>
193
- <strong> <?php esc_html_e( 'State:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_state ); ?>
194
- </p>
195
-
196
- <p>
197
- <strong> <?php esc_html_e( 'Postcode:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_postcode ); ?>
198
- </p>
199
- </div>
200
-
201
- <div class="wcf-ca-user-address wcf-pull-left">
202
- <h3> <?php esc_html_e( 'Shipping Address', 'woo-cart-abandonment-recovery' ); ?> </h3>
203
- <p>
204
- <strong> <?php esc_html_e( 'Address 1:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_address_1 ); ?>
205
- </p>
206
- <p>
207
- <strong> <?php esc_html_e( 'Address 2:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_address_2 ); ?>
208
- </p>
209
- <p>
210
- <strong> <?php esc_html_e( 'City:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_city ); ?>
211
- </p>
212
- <p>
213
- <strong> <?php esc_html_e( 'State:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_state ); ?>
214
- </p>
215
- <p>
216
- <strong> <?php esc_html_e( 'Country:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_country ); ?>
217
- </p>
218
- <p>
219
- <strong> <?php esc_html_e( 'Postcode:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_postcode ); ?>
220
- </p>
221
- <p>
222
- <?php
223
- $cart_abandonment = Cartflows_Ca_Cart_Abandonment::get_instance();
224
- $token_data = array( 'wcf_session_id' => $details->session_id );
225
- ?>
226
- <strong> <a target="_blank" href=" <?php echo $cart_abandonment->get_checkout_url( $details->checkout_id, $token_data ); //phpcs:ignore?> ">
227
- <?php esc_html_e( 'Checkout Link', 'woo-cart-abandonment-recovery' ); ?>
228
- </a>
229
- </strong>
230
- </p>
231
- </div>
232
-
233
- </div>
234
- </div>
235
- </div>
236
- <!-- First panel closed -->
237
-
238
- <!-- Second panel Start -->
239
- <div class="wcf-ca-panel">
240
- <div class="wcf-ca-column wcf-ca-column-one">
241
- <div class="wcf-ca-user-order">
242
- <h2> <?php esc_html_e( 'User Order Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
243
- <?php echo( $this->get_admin_product_block( $details->cart_contents, $details->cart_total ) ); //phpcs:ignore ?>
244
- </div>
245
- </div>
246
- </div>
247
- <!-- Second panel closed -->
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cartflows view for single cart abandonment report details.
4
+ *
5
+ * @package Woocommerce-Cart-Abandonment-Recovery
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ ?>
13
+
14
+
15
+ <div class="wcf-ca-report-btn">
16
+ <div class="wcf-ca-left-report-field-group">
17
+ <?php
18
+ if ( wp_get_referer() ) {
19
+ $back_link = wp_get_referer();
20
+ } else {
21
+ $back_link = add_query_arg(
22
+ array(
23
+ 'page' => WCF_CA_PAGE_NAME,
24
+ 'action' => WCF_ACTION_REPORTS,
25
+ ),
26
+ admin_url( '/admin.php' )
27
+ );
28
+ }
29
+ ?>
30
+ <a href="<?php echo esc_attr( $back_link ); ?>" class="button button-secondary back-button"><span
31
+ class="dashicons dashicons-arrow-left"></span> <?php esc_html_e( 'Back to Reports', 'woo-cart-abandonment-recovery' ); ?> </a>
32
+ </div>
33
+ </div>
34
+
35
+ <!-- First panel Start -->
36
+ <div class="wcf-ca-panel">
37
+ <div class="wcf-ca-column wcf-ca-column-two wcf-ca-margin-right">
38
+ <div class="wcf-ca-email-data">
39
+
40
+ <div class="wcf-ca-report-btn" style="padding: 0px">
41
+ <div class="wcf-ca-left-report-field-group">
42
+ <h2> <?php esc_html_e( 'Email Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
43
+ </div>
44
+ <div class="wcf-ca-right-report-field-group">
45
+
46
+ <?php if ( WCF_CART_ABANDONED_ORDER === $details->order_status && ! $details->unsubscribed ) : ?>
47
+ <?php add_thickbox(); ?>
48
+ <div id="wcf-ca-confirm-email-reschedule" style="display:none;">
49
+ <div style="text-align:center;">
50
+ <p>
51
+ <?php
52
+ esc_html_e(
53
+ 'All new activated emails will be reschedule for this abandoned order. New emails will be sent to user according to schedule time.',
54
+ 'woo-cart-abandonment-recovery'
55
+ );
56
+ ?>
57
+ </p>
58
+ <p>
59
+ <strong><?php esc_html_e( 'Are your sure?', 'woo-cart-abandonment-recovery' ); ?></strong>
60
+ </p>
61
+ <p>
62
+ <button onclick="window.location.search += '&sub_action=<?php echo esc_attr( WCF_SUB_ACTION_REPORTS_RESCHEDULE ); ?>';"
63
+ class="button button-secondary"> <?php esc_html_e( 'Reschedule', 'woo-cart-abandonment-recovery' ); ?>
64
+ </button>
65
+ <button type="button"
66
+ onclick='document.getElementById("TB_closeWindowButton").click()'
67
+ class="button button-secondary"> <?php esc_html_e( 'Close', 'woo-cart-abandonment-recovery' ); ?>
68
+ </button>
69
+ </p>
70
+ </div>
71
+ </div>
72
+ <a name="<?php esc_html_e( 'Do you really want to reschedule emails?', 'woo-cart-abandonment-recovery' ); ?>" href="#TB_inline?&width=500&height=200&inlineId=wcf-ca-confirm-email-reschedule" class="thickbox button button-secondary"> <?php esc_html_e( 'Reschedule Emails', 'woo-cart-abandonment-recovery' ); ?> </a>
73
+ <?php endif; ?>
74
+ </div>
75
+ </div>
76
+
77
+ <?php if ( empty( $scheduled_emails ) ) : ?>
78
+ <div style="text-align: center;"><strong> <?php esc_html_e( ' No Email Scheduled.', 'woo-cart-abandonment-recovery' ); ?></strong>
79
+ </div>
80
+ <?php else : ?>
81
+ <table cellpadding="15" cellspacing="0" class="wcf-table wcf-table-striped fixed posts">
82
+ <thead>
83
+ <tr>
84
+
85
+ <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Scheduled Template', 'woo-cart-abandonment-recovery' ); ?></th>
86
+ <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Subject', 'woo-cart-abandonment-recovery' ); ?></th>
87
+ <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Coupon', 'woo-cart-abandonment-recovery' ); ?></th>
88
+ <th class="wcf-ca-report-table-row"> <?php esc_html_e( 'Email Sent', 'woo-cart-abandonment-recovery' ); ?></th>
89
+ <th class="wcf-ca-report-table-row"><span class="dashicons dashicons-clock"></span> <?php esc_html_e( 'Scheduled At', 'woo-cart-abandonment-recovery' ); ?>
90
+ </th>
91
+
92
+ </tr>
93
+ </thead>
94
+
95
+ <tbody>
96
+ <?php foreach ( $scheduled_emails as $scheduled_email ) : ?>
97
+
98
+ <?php
99
+ $email_tmpl_url = wp_nonce_url(
100
+ add_query_arg(
101
+ array(
102
+ 'page' => WCF_CA_PAGE_NAME,
103
+ 'action' => WCF_ACTION_EMAIL_TEMPLATES,
104
+ 'sub_action' => WCF_SUB_ACTION_EDIT_EMAIL_TEMPLATES,
105
+ 'id' => $scheduled_email->template_id,
106
+ ),
107
+ admin_url( '/admin.php' )
108
+ ),
109
+ WCF_EMAIL_TEMPLATES_NONCE
110
+ );
111
+
112
+
113
+
114
+ switch ( $scheduled_email->email_sent ) {
115
+ case 0:
116
+ if ( $details->unsubscribed ) {
117
+ $icon = '<span class="dashicons dashicons-minus"></span>';
118
+ $title_text = esc_html__( 'The email has been unsubscribed and won\'t be sent further.', 'woo-cart-abandonment-recovery' );
119
+ } else {
120
+ $icon = '<span class="dashicons dashicons-no"></span>';
121
+ $title_text = esc_html__( 'Email is in the queue and will be sent at the scheduled time.', 'woo-cart-abandonment-recovery' );
122
+ }
123
+ break;
124
+ case 1:
125
+ $icon = '<span class="dashicons dashicons-yes wp-ui-text-highlight" ></span>';
126
+ $title_text = esc_html__( 'The email has been sent.', 'woo-cart-abandonment-recovery' );
127
+ break;
128
+ case -1:
129
+ $icon = '<span class="dashicons dashicons-dismiss wp-ui-text-highlight" ></span>';
130
+ $title_text = esc_html__( 'The email has been unscheduled due to the complete order and won\'t be sent further.', 'woo-cart-abandonment-recovery' );
131
+ break;
132
+ }
133
+
134
+
135
+ $scheduled_time = gmdate( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $scheduled_email->scheduled_time ) );
136
+ ?>
137
+
138
+ <tr class="wcf-ca-report-table-row">
139
+ <td class="wcf-ca-report-table-row"><a
140
+ href="<?php echo esc_url( $email_tmpl_url ); ?>"
141
+ class="wp-ui-text-highlight"> <?php echo esc_attr( $scheduled_email->template_name ); ?> </a>
142
+ </td>
143
+ <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_email->email_subject ); ?> </td>
144
+ <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_email->coupon_code ? $scheduled_email->coupon_code : '--' ); ?> </td>
145
+ <td class="wcf-ca-report-table-row wcf-ca-icon-row"> <?php echo( $icon ); //phpcs:ignore ?>
146
+ <span class="wcf-ca-tooltip-text"><?php echo esc_attr( $title_text ); ?></span>
147
+ </td>
148
+ <td class="wcf-ca-report-table-row"> <?php echo esc_attr( $scheduled_time ); ?> </td>
149
+ </tr>
150
+ <?php endforeach; ?>
151
+ </tbody>
152
+ </table>
153
+ <?php endif; ?>
154
+
155
+ </div>
156
+ </div>
157
+
158
+ <div class="wcf-ca-column wcf-ca-column-two wcf-ca-margin-left">
159
+ <div class="wcf-ca-user-detail ">
160
+
161
+ <div class="wcf-ca-report-btn" style="padding: 0px">
162
+ <div class="wcf-ca-left-report-field-group">
163
+ <h2> <?php esc_html_e( 'User Address Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
164
+ </div>
165
+ <div class="wcf-ca-right-report-field-group">
166
+ <?php if ( $details->unsubscribed ) : ?>
167
+ <span class="wcf-ca-tag"> <?php esc_html_e( 'Unsubscribed', 'woo-cart-abandonment-recovery' ); ?> </span>
168
+ <?php endif; ?>
169
+
170
+ <span class="wcf-ca-tag"> <?php echo esc_attr( ucfirst( $details->order_status ) ); ?> </span>
171
+ </div>
172
+ </div>
173
+
174
+ <div class="wcf-ca-user-address wcf-pull-left">
175
+ <h3> <?php esc_html_e( 'Billing Address', 'woo-cart-abandonment-recovery' ); ?> </h3>
176
+ <p><strong> <?php esc_html_e( 'Name', 'woo-cart-abandonment-recovery' ); ?> </strong>
177
+ <?php echo esc_attr( $user_details->wcf_first_name . ' ' . $user_details->wcf_last_name ); ?> </p>
178
+ <p>
179
+ <strong> <?php esc_html_e( 'Email address', 'woo-cart-abandonment-recovery' ); ?> </strong>
180
+ <a href="mailto:<?php echo esc_attr( $details->email ); ?>"><?php echo esc_attr( $details->email ); ?></a>
181
+ </p>
182
+
183
+ <p>
184
+ <strong> <?php esc_html_e( 'Phone', 'woo-cart-abandonment-recovery' ); ?> </strong>
185
+ <a href="tel:<?php echo esc_attr( $user_details->wcf_phone_number ); ?>"><?php echo esc_attr( $user_details->wcf_phone_number ); ?></a>
186
+ </p>
187
+
188
+ <p>
189
+ <strong> <?php esc_html_e( 'Address 1:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_address_1 ); ?>
190
+ </p>
191
+ <p>
192
+ <strong> <?php esc_html_e( 'Address 2:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_address_2 ); ?>
193
+ </p>
194
+ <p>
195
+ <strong> <?php esc_html_e( 'Country, City:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_location ); ?>
196
+ </p>
197
+ <p>
198
+ <strong> <?php esc_html_e( 'State:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_state ); ?>
199
+ </p>
200
+
201
+ <p>
202
+ <strong> <?php esc_html_e( 'Postcode:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_billing_postcode ); ?>
203
+ </p>
204
+ </div>
205
+
206
+ <div class="wcf-ca-user-address wcf-pull-left">
207
+ <h3> <?php esc_html_e( 'Shipping Address', 'woo-cart-abandonment-recovery' ); ?> </h3>
208
+ <p>
209
+ <strong> <?php esc_html_e( 'Address 1:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_address_1 ); ?>
210
+ </p>
211
+ <p>
212
+ <strong> <?php esc_html_e( 'Address 2:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_address_2 ); ?>
213
+ </p>
214
+ <p>
215
+ <strong> <?php esc_html_e( 'City:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_city ); ?>
216
+ </p>
217
+ <p>
218
+ <strong> <?php esc_html_e( 'State:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_state ); ?>
219
+ </p>
220
+ <p>
221
+ <strong> <?php esc_html_e( 'Country:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_country ); ?>
222
+ </p>
223
+ <p>
224
+ <strong> <?php esc_html_e( 'Postcode:', 'woo-cart-abandonment-recovery' ); ?> </strong> <?php echo esc_attr( $user_details->wcf_shipping_postcode ); ?>
225
+ </p>
226
+ <p>
227
+ <?php
228
+ $cart_abandonment = Cartflows_Ca_Cart_Abandonment::get_instance();
229
+ $token_data = array( 'wcf_session_id' => $details->session_id );
230
+ ?>
231
+ <strong> <a target="_blank" href=" <?php echo $cart_abandonment->get_checkout_url( $details->checkout_id, $token_data ); //phpcs:ignore?> ">
232
+ <?php esc_html_e( 'Checkout Link', 'woo-cart-abandonment-recovery' ); ?>
233
+ </a>
234
+ </strong>
235
+ </p>
236
+ </div>
237
+
238
+ </div>
239
+ </div>
240
+ </div>
241
+ <!-- First panel closed -->
242
+
243
+ <!-- Second panel Start -->
244
+ <div class="wcf-ca-panel">
245
+ <div class="wcf-ca-column wcf-ca-column-one">
246
+ <div class="wcf-ca-user-order">
247
+ <h2> <?php esc_html_e( 'User Order Details:', 'woo-cart-abandonment-recovery' ); ?> </h2>
248
+ <?php echo( $this->get_admin_product_block( $details->cart_contents, $details->cart_total ) ); //phpcs:ignore ?>
249
+ </div>
250
+ </div>
251
+ </div>
252
+ <!-- Second panel closed -->
modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-reports.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  ?>
9
 
10
  <div class="wcf-ca-report-btn">
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  ?>
13
 
14
  <div class="wcf-ca-report-btn">
modules/cart-abandonment/includes/admin/cartflows-cart-abandonment-tabs.php CHANGED
@@ -5,6 +5,10 @@
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
 
 
 
 
8
  ?>
9
  <div class="wrap">
10
  <h1 id="wcf_cart_abandonment_tracking_table"><?php echo esc_html__( 'WooCommerce Cart Abandonment Recovery ', 'woo-cart-abandonment-recovery' ); ?></h1>
5
  * @package Woocommerce-Cart-Abandonment-Recovery
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
  ?>
13
  <div class="wrap">
14
  <h1 id="wcf_cart_abandonment_tracking_table"><?php echo esc_html__( 'WooCommerce Cart Abandonment Recovery ', 'woo-cart-abandonment-recovery' ); ?></h1>
readme.txt CHANGED
@@ -1,241 +1,246 @@
1
- === WooCommerce Cart Abandonment Recovery ===
2
- Contributors: brainstormforce, wpcrafter
3
- Donate link: https://www.paypal.me/BrainstormForce
4
- Tags: woocommerce, cart abandonment, cart recovery
5
- Requires at least: 4.4
6
- Tested up to: 5.6
7
- Stable tag: 1.2.9
8
- Requires PHP: 5.6
9
- License: GPLv2 or later
10
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
-
12
- Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
13
-
14
- == Description ==
15
-
16
- **It’s Time to Stop Cart Abandonments and Recover Your Lost Revenue!**
17
-
18
- Research shows about 60% to 80% of the users who go to the checkout page, do not complete their purchase. Even the best <a href="https://wpastra.com/best-checkout-experience/" target="_blank">optimized checkout process</a> has an abandonment rate of 20%
19
-
20
- Cart gets abandoned for many reasons.
21
-
22
- But we have good news for you. Install our plugin and automatically recover your lost revenue **absolutely free**.
23
-
24
- **How Does This Plugin Work?**
25
-
26
- The plugin captures the email address of users on the checkout page.
27
-
28
- If the purchase is not completed within 15 minutes, it starts sending an automated series of follow up emails that you can customize to match your brand.
29
-
30
- Through the email series, you can: remind them to complete the purchase, ask for feedback or offer a custom discount that will entice potential buyers to complete the purchase. You can send as many emails as you would like.
31
-
32
- Below is just an example of email sequence you can have:
33
-
34
- * **1st email after 1 hour**: Ask if there was any technical issue.
35
-
36
- * **2nd email after 24 hours**: Remind to complete purchase
37
-
38
- * **3rd email after 72 hours**: Offer a unique, limited time 5% discount
39
-
40
- **Some Amazing Features**
41
-
42
-
43
- WooCommerce Cart Abandonment Recovery offers everything you need to recover your abandoning carts.
44
-
45
- * **Unique Checkout Links**: Email a unique checkout link to each shopper that takes them exactly where they left off. So if a shopper had filled the checkout form, click on the unique link takes him to a prefilled checkout page. Less the friction for the shopper, and more conversions for you!
46
-
47
- * **GDPR Compliant**: You can optionally show a GDPR notice on the checkout page.
48
-
49
- * **Ready templates for follow up emails**: Writing emails from scratch can be a pain. Not everyone can frame effective follow-up emails. We provide ready conversion tested email templates.
50
-
51
- * **Webhooks**: Use a marketing automation tool like Active Campaign, Campaign Monitor, etc? This plugin integrates with all of them through webhook easily.
52
-
53
- * **Coupon Code**: This plugin can generate limited time unique discount coupons to entice your shoppers and send them automatically via email.
54
-
55
- * **Reports**: You get a full report of how the plugin is working behind the scenes, and recovering your lost revenue on autopilot.
56
-
57
- [youtube https://www.youtube.com/watch?v=VWGxjjuP2_k]
58
-
59
- **Is This Plugin Really Free? What’s the Catch?**
60
-
61
- Absolutely. There is no catch at all. This is a 100% free gift for all WooCommerce users from our team at CartFlows.
62
-
63
- <a href="https://cartflows.com" target="_blank">CartFlows</a> is a premium WooCommerce plugin that is used to create marketing and sales funnels. With CartFlows, you can design better checkout pages, add <a href="https://cartflows.com" target="_blank">order bump</a> or even <a href="https://cartflows.com" target="_blank">one-click upsell</a> functionality.
64
-
65
- **Will It Affects the Performance of My WooCommerce Shop?**
66
-
67
- No. Just as WooCommerce, this plugin stores everything locally in the database of WordPress. Emails are sent via WordPress native 'wpmail' function or SMTP. We have optimized the plugin so it leaves no performance impact.
68
-
69
-
70
- **How Can I Get Started?**
71
-
72
- Getting started is very easy.
73
-
74
- * **Step 1**: Install and activate the WooCommerce Cart Abandonment Recovery plugin.
75
-
76
- * **Step 2**: Review the default email templates. Make necessary changes if required.
77
-
78
- * **Step 3**: You’re done!
79
-
80
- The plugin will start recovering your lost sales in as quickly as 15 minutes.
81
-
82
- == Installation ==
83
-
84
- 1. Upload `woocommerce-cart-abandonment-recovery.zip` to the `/wp-content/plugins/` directory
85
- 2. Activate the plugin through the 'Plugins' menu in WordPress
86
-
87
- == Frequently Asked Questions ==
88
-
89
- = What Is Meant by Cart Abandonment? =
90
-
91
- While shopping, a user adds products to the cart. But if this user doesn't complete the purchase on the checkout page, the cart is considered abandoned.
92
-
93
- = How Often Cart Gets Abandoned? Is It Happening on My Shop? =
94
-
95
- According to research, 6 out of 10 visitors, leave the checkout page and do not complete the purchase. Even if you have motivated enough, 20% to 25% of the shoppers leave the purchase process.
96
-
97
- = Do I Need Any Technical Experience to Use This Plugin? =
98
-
99
- Any WooCommerce shop owner selling anything online can use this plugin.You will not need any technical experience to start recovering your lost revenue. Once initiated the plugin will work automatically for you.
100
-
101
- = Can I Send Coupon with Follow up Email? =
102
-
103
- Yes. You can generate and send unique, limited-time discount coupon. Though there is no compulsion of sending coupon. It's completely optional.
104
-
105
- = Can I Directly Take Users to Checkout Page, Exactly Where They Left Off? =
106
-
107
- Absolutely. A unique checkout link can be sent with follow up email. This will take the shopper to their prefilled checkout page. This will make the purchase process easier for the shopper.
108
-
109
- = Can Marketing Automation Tools Be Integrated with This? =
110
-
111
- Plugin offers Webhooks. This allows integrating Active Campaign, Mautic, Campaign Monitor, etc with the plugin.
112
-
113
- = Will This Plugin Add Any Extra Time in Website Loading? =
114
-
115
- Not at all. All the plugin data is stored in its own database table. It is completely self-hosted plugin. It works smoothly and does not leave any impact on the performance of the website. So you don't have to worry about the speed.
116
-
117
- = Why This Awesome Plugin Is Free? =
118
-
119
- Here are few thoughts behind making it available for free:
120
- - It is our way of saying thank you to the community and helping shop owners to boost their profits.
121
- - And quite honestly, we want you to try one of our products for free. And when you see how helpful it is, it should get you excited to buy other products from us in the future
122
-
123
- == Screenshots ==
124
-
125
- 1. Track recovery report for abandonment sales from the dashboard
126
- 2. Schedule the follow-up emails from one place
127
- 3. Prebuilt and easy-to-edit Email templates
128
- 4. General settings for Email, Webhook (Coupon Code), GDPR
129
-
130
- == Changelog ==
131
-
132
- = Version 1.2.9 - Thursday, 14th January 2021 =
133
- * New: Added the filter before coupon generation to modify the coupon arguments.
134
- * Improvement: Added the Phone number field in export data.
135
- * Fix: Fixed the get_title on boolean error and PHP 8 notices.
136
- * Fix: Showing wrong product images for variation.
137
-
138
- = Version 1.2.8 - Friday, 14th August 2020 =
139
- * New: Added new option to prevent recovery emails for specific order status.
140
- * Fix: Deprecated the 'woo_ca_exclude_on_hold_order_from_tracking' filter.
141
-
142
- = Version 1.2.7 - Tuesday, 16th June 2020 =
143
- * New: Users can now share [non-personal usage data](https://my.cartflows.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking) to help us test and develop better products.
144
-
145
- = Version 1.2.6 - Thursday, 21st May 2020 =
146
- * New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
147
- * Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
148
- * Fix: Coupons generated by plugin were not deleting.
149
- * Fix: Variation/Custom product attributes were excluded from the recovered cart.
150
-
151
- = Version 1.2.5 - Wednesday, 11th March 2020 =
152
- * Improvement: Allowed plugin access to the shop manager.
153
- * Fix: Variable product name not showing in the product table.
154
- * Fix: All orders are not exporting due to the wrong pagination.
155
- * Fix: Not showing the next page's orders.
156
-
157
- = Version 1.2.4 - Thursday, 06th February 2020 =
158
- * New: Added option to export abandoned orders.
159
- * New: Added option to search abandoned orders.
160
- * Improvement: Compatibility with the latest WordPress PHP_CodeSniffer rules.
161
- * Fix: Get id error while sending emails.
162
-
163
- = Version 1.2.3 - Thursday, 12th December 2019 =
164
- * New: Added option to unsubscribe users in bulk.
165
- * New: Added filter 'woo_ca_exclude_on_hold_order_from_tracking' to exclude on hold orders from the tracking.
166
- * New: Added product table shortcode for webhook.
167
- * Improvement: Updated filter 'woo_ca_email_template_table_style' for product table alignment.
168
- * Fix: Sometimes test emails are not sending.
169
-
170
- = Version 1.2.2 - Tuesday, 12th November 2019 =
171
- * Fix: Duplicate order issue for variation products.
172
-
173
- = Version 1.2.1 - Tuesday, 5th November 2019 =
174
- * New: Added delete option for used & expired coupons which will be created now onwards.
175
- * Fix: Sometimes order status remains "abandoned" for initially failed orders.
176
- * Fix: Strings updated for translation.
177
-
178
- = Version 1.2.0 - Monday, 14th October 2019 =
179
- * New: Added support for PPOM products.
180
- * Improvement: Added email activate toggle button on grid.
181
- * Improvement: Added notice on the checkout page for test emails.
182
- * Fix: Zero-value orders getting tracked.
183
- * Fix: Disable tracking for the custom user roles.
184
-
185
- = Version 1.1.9 - Thursday, 19th September 2019 =
186
- * New: Option added to ignore users from cart abandonment process.
187
- * New: Filter added to customize the styling of email template table.
188
- * Improvement: Added compatibility with Razorpay plugin.
189
- * Fix: Email template markup was breaking after save.
190
- * Fix: Failed orders were getting marked as completed.
191
- * Fix: Empty order was getting tracked and email sending for it.
192
- * Fix: Email settings options were swapping value of from and reply-to.
193
-
194
- = Version 1.1.8 - Tuesday, 3rd September 2019 =
195
- * New: Option added to auto-apply coupon on the checkout.
196
- * New: Option added to apply coupon individually.
197
- * New: Option added to create free shipping coupons.
198
-
199
- = Version 1.1.7 - Monday, 12th August 2019 =
200
- * New: Filter added to show the cart total inside the email template.
201
- * New: Filter added to change the cart abandoned time.
202
- * Improvement: Order tracking logic updated for automated payments.
203
- * Improvement: Update report dashboard DateTime format to WordPress format.
204
- * Fix: Broken image in the email template.
205
-
206
- = Version 1.1.6 - Friday, 19th July 2019 =
207
- * New: Bundled product support for email checkout URL.
208
- * Improvement: Added phone number and address while triggering the to webhook.
209
- * Fix: Creating tables and default settings on activation.
210
-
211
- = Version 1.1.5 - Tuesday, 9th July 2019 =
212
- * Fix: Other crons disappearing issue.
213
-
214
- = Version 1.1.4 - Tuesday, 9th July 2019 =
215
- * Fix: Follow up emails were getting sent even after the completion of the order.
216
- * Fix: Email template variable 'Abandoned Product Names' warning issue.
217
-
218
- = Version 1.1.3 - Thursday, 27th June 2019 =
219
- * Improvement: Added checkout link for abandoned cart inside the admin section.
220
- * Fix: Added pagination for reports.
221
- * Fix: Recover report calculations before campaign triggers.
222
- * Fix: Empty cart notice when CartFlows checkout is set global.
223
-
224
- = Version 1.1.2 - Wednesday, 12th June 2019 =
225
- * Fix: Issue of timezone while sending mail through cron.
226
- * Fix: Delete single cart abandonment order.
227
- * Fix: MySql 5.5 support for CURRENT_TIMESTAMP.
228
-
229
- = Version 1.1.1 - Thursday, 06th June 2019 =
230
- * New: Added feature to reschedule emails for Admin.
231
- * Fix: Coupon expiry time issue.
232
- * Fix: Email issue for a user who has an already purchased order.
233
- * Fix: Translatable strings updated.
234
-
235
- = Version 1.1.0 - Thursday, 30th May 2019 =
236
- * Added a view for admin to check email status specific to the particular abandoned user.
237
-
238
- = Version 1.0.0 - Monday, 27th May 2019 =
239
- * Initial Release
240
-
 
 
 
 
 
241
  == Upgrade Notice ==
1
+ === WooCommerce Cart Abandonment Recovery ===
2
+ Contributors: brainstormforce, wpcrafter
3
+ Donate link: https://www.paypal.me/BrainstormForce
4
+ Tags: woocommerce, cart abandonment, cart recovery
5
+ Requires at least: 4.4
6
+ Tested up to: 5.6
7
+ Stable tag: 1.2.10
8
+ Requires PHP: 5.6
9
+ License: GPLv2 or later
10
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
13
+
14
+ == Description ==
15
+
16
+ **It’s Time to Stop Cart Abandonments and Recover Your Lost Revenue!**
17
+
18
+ Research shows about 60% to 80% of the users who go to the checkout page, do not complete their purchase. Even the best <a href="https://wpastra.com/best-checkout-experience/" target="_blank">optimized checkout process</a> has an abandonment rate of 20%
19
+
20
+ Cart gets abandoned for many reasons.
21
+
22
+ But we have good news for you. Install our plugin and automatically recover your lost revenue **absolutely free**.
23
+
24
+ **How Does This Plugin Work?**
25
+
26
+ The plugin captures the email address of users on the checkout page.
27
+
28
+ If the purchase is not completed within 15 minutes, it starts sending an automated series of follow up emails that you can customize to match your brand.
29
+
30
+ Through the email series, you can: remind them to complete the purchase, ask for feedback or offer a custom discount that will entice potential buyers to complete the purchase. You can send as many emails as you would like.
31
+
32
+ Below is just an example of email sequence you can have:
33
+
34
+ * **1st email after 1 hour**: Ask if there was any technical issue.
35
+
36
+ * **2nd email after 24 hours**: Remind to complete purchase
37
+
38
+ * **3rd email after 72 hours**: Offer a unique, limited time 5% discount
39
+
40
+ **Some Amazing Features**
41
+
42
+
43
+ WooCommerce Cart Abandonment Recovery offers everything you need to recover your abandoning carts.
44
+
45
+ * **Unique Checkout Links**: Email a unique checkout link to each shopper that takes them exactly where they left off. So if a shopper had filled the checkout form, click on the unique link takes him to a prefilled checkout page. Less the friction for the shopper, and more conversions for you!
46
+
47
+ * **GDPR Compliant**: You can optionally show a GDPR notice on the checkout page.
48
+
49
+ * **Ready templates for follow up emails**: Writing emails from scratch can be a pain. Not everyone can frame effective follow-up emails. We provide ready conversion tested email templates.
50
+
51
+ * **Webhooks**: Use a marketing automation tool like Active Campaign, Campaign Monitor, etc? This plugin integrates with all of them through webhook easily.
52
+
53
+ * **Coupon Code**: This plugin can generate limited time unique discount coupons to entice your shoppers and send them automatically via email.
54
+
55
+ * **Reports**: You get a full report of how the plugin is working behind the scenes, and recovering your lost revenue on autopilot.
56
+
57
+ [youtube https://www.youtube.com/watch?v=VWGxjjuP2_k]
58
+
59
+ **Is This Plugin Really Free? What’s the Catch?**
60
+
61
+ Absolutely. There is no catch at all. This is a 100% free gift for all WooCommerce users from our team at CartFlows.
62
+
63
+ <a href="https://cartflows.com" target="_blank">CartFlows</a> is a premium WooCommerce plugin that is used to create marketing and sales funnels. With CartFlows, you can design better checkout pages, add <a href="https://cartflows.com" target="_blank">order bump</a> or even <a href="https://cartflows.com" target="_blank">one-click upsell</a> functionality.
64
+
65
+ **Will It Affects the Performance of My WooCommerce Shop?**
66
+
67
+ No. Just as WooCommerce, this plugin stores everything locally in the database of WordPress. Emails are sent via WordPress native 'wpmail' function or SMTP. We have optimized the plugin so it leaves no performance impact.
68
+
69
+
70
+ **How Can I Get Started?**
71
+
72
+ Getting started is very easy.
73
+
74
+ * **Step 1**: Install and activate the WooCommerce Cart Abandonment Recovery plugin.
75
+
76
+ * **Step 2**: Review the default email templates. Make necessary changes if required.
77
+
78
+ * **Step 3**: You’re done!
79
+
80
+ The plugin will start recovering your lost sales in as quickly as 15 minutes.
81
+
82
+ == Installation ==
83
+
84
+ 1. Upload `woocommerce-cart-abandonment-recovery.zip` to the `/wp-content/plugins/` directory
85
+ 2. Activate the plugin through the 'Plugins' menu in WordPress
86
+
87
+ == Frequently Asked Questions ==
88
+
89
+ = What Is Meant by Cart Abandonment? =
90
+
91
+ While shopping, a user adds products to the cart. But if this user doesn't complete the purchase on the checkout page, the cart is considered abandoned.
92
+
93
+ = How Often Cart Gets Abandoned? Is It Happening on My Shop? =
94
+
95
+ According to research, 6 out of 10 visitors, leave the checkout page and do not complete the purchase. Even if you have motivated enough, 20% to 25% of the shoppers leave the purchase process.
96
+
97
+ = Do I Need Any Technical Experience to Use This Plugin? =
98
+
99
+ Any WooCommerce shop owner selling anything online can use this plugin.You will not need any technical experience to start recovering your lost revenue. Once initiated the plugin will work automatically for you.
100
+
101
+ = Can I Send Coupon with Follow up Email? =
102
+
103
+ Yes. You can generate and send unique, limited-time discount coupon. Though there is no compulsion of sending coupon. It's completely optional.
104
+
105
+ = Can I Directly Take Users to Checkout Page, Exactly Where They Left Off? =
106
+
107
+ Absolutely. A unique checkout link can be sent with follow up email. This will take the shopper to their prefilled checkout page. This will make the purchase process easier for the shopper.
108
+
109
+ = Can Marketing Automation Tools Be Integrated with This? =
110
+
111
+ Plugin offers Webhooks. This allows integrating Active Campaign, Mautic, Campaign Monitor, etc with the plugin.
112
+
113
+ = Will This Plugin Add Any Extra Time in Website Loading? =
114
+
115
+ Not at all. All the plugin data is stored in its own database table. It is completely self-hosted plugin. It works smoothly and does not leave any impact on the performance of the website. So you don't have to worry about the speed.
116
+
117
+ = Why This Awesome Plugin Is Free? =
118
+
119
+ Here are few thoughts behind making it available for free:
120
+ - It is our way of saying thank you to the community and helping shop owners to boost their profits.
121
+ - And quite honestly, we want you to try one of our products for free. And when you see how helpful it is, it should get you excited to buy other products from us in the future
122
+
123
+ == Screenshots ==
124
+
125
+ 1. Track recovery report for abandonment sales from the dashboard
126
+ 2. Schedule the follow-up emails from one place
127
+ 3. Prebuilt and easy-to-edit Email templates
128
+ 4. General settings for Email, Webhook (Coupon Code), GDPR
129
+
130
+ == Changelog ==
131
+
132
+ = Version 1.2.10 - Tuesday, 16th February 2021 =
133
+ * New: Added the option to delete the plugin data on plugin deletion.
134
+ * New: Added the filter before triggering the webhook.
135
+ * Improvement: Showing Parent product image if variation image is not set.
136
+
137
+ = Version 1.2.9 - Thursday, 14th January 2021 =
138
+ * New: Added the filter before coupon generation to modify the coupon arguments.
139
+ * Improvement: Added the Phone number field in export data.
140
+ * Fix: Fixed the get_title on boolean error and PHP 8 notices.
141
+ * Fix: Showing wrong product images for variation.
142
+
143
+ = Version 1.2.8 - Friday, 14th August 2020 =
144
+ * New: Added new option to prevent recovery emails for specific order status.
145
+ * Fix: Deprecated the 'woo_ca_exclude_on_hold_order_from_tracking' filter.
146
+
147
+ = Version 1.2.7 - Tuesday, 16th June 2020 =
148
+ * New: Users can now share [non-personal usage data](https://my.cartflows.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking) to help us test and develop better products.
149
+
150
+ = Version 1.2.6 - Thursday, 21st May 2020 =
151
+ * New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
152
+ * Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
153
+ * Fix: Coupons generated by plugin were not deleting.
154
+ * Fix: Variation/Custom product attributes were excluded from the recovered cart.
155
+
156
+ = Version 1.2.5 - Wednesday, 11th March 2020 =
157
+ * Improvement: Allowed plugin access to the shop manager.
158
+ * Fix: Variable product name not showing in the product table.
159
+ * Fix: All orders are not exporting due to the wrong pagination.
160
+ * Fix: Not showing the next page's orders.
161
+
162
+ = Version 1.2.4 - Thursday, 06th February 2020 =
163
+ * New: Added option to export abandoned orders.
164
+ * New: Added option to search abandoned orders.
165
+ * Improvement: Compatibility with the latest WordPress PHP_CodeSniffer rules.
166
+ * Fix: Get id error while sending emails.
167
+
168
+ = Version 1.2.3 - Thursday, 12th December 2019 =
169
+ * New: Added option to unsubscribe users in bulk.
170
+ * New: Added filter 'woo_ca_exclude_on_hold_order_from_tracking' to exclude on hold orders from the tracking.
171
+ * New: Added product table shortcode for webhook.
172
+ * Improvement: Updated filter 'woo_ca_email_template_table_style' for product table alignment.
173
+ * Fix: Sometimes test emails are not sending.
174
+
175
+ = Version 1.2.2 - Tuesday, 12th November 2019 =
176
+ * Fix: Duplicate order issue for variation products.
177
+
178
+ = Version 1.2.1 - Tuesday, 5th November 2019 =
179
+ * New: Added delete option for used & expired coupons which will be created now onwards.
180
+ * Fix: Sometimes order status remains "abandoned" for initially failed orders.
181
+ * Fix: Strings updated for translation.
182
+
183
+ = Version 1.2.0 - Monday, 14th October 2019 =
184
+ * New: Added support for PPOM products.
185
+ * Improvement: Added email activate toggle button on grid.
186
+ * Improvement: Added notice on the checkout page for test emails.
187
+ * Fix: Zero-value orders getting tracked.
188
+ * Fix: Disable tracking for the custom user roles.
189
+
190
+ = Version 1.1.9 - Thursday, 19th September 2019 =
191
+ * New: Option added to ignore users from cart abandonment process.
192
+ * New: Filter added to customize the styling of email template table.
193
+ * Improvement: Added compatibility with Razorpay plugin.
194
+ * Fix: Email template markup was breaking after save.
195
+ * Fix: Failed orders were getting marked as completed.
196
+ * Fix: Empty order was getting tracked and email sending for it.
197
+ * Fix: Email settings options were swapping value of from and reply-to.
198
+
199
+ = Version 1.1.8 - Tuesday, 3rd September 2019 =
200
+ * New: Option added to auto-apply coupon on the checkout.
201
+ * New: Option added to apply coupon individually.
202
+ * New: Option added to create free shipping coupons.
203
+
204
+ = Version 1.1.7 - Monday, 12th August 2019 =
205
+ * New: Filter added to show the cart total inside the email template.
206
+ * New: Filter added to change the cart abandoned time.
207
+ * Improvement: Order tracking logic updated for automated payments.
208
+ * Improvement: Update report dashboard DateTime format to WordPress format.
209
+ * Fix: Broken image in the email template.
210
+
211
+ = Version 1.1.6 - Friday, 19th July 2019 =
212
+ * New: Bundled product support for email checkout URL.
213
+ * Improvement: Added phone number and address while triggering the to webhook.
214
+ * Fix: Creating tables and default settings on activation.
215
+
216
+ = Version 1.1.5 - Tuesday, 9th July 2019 =
217
+ * Fix: Other crons disappearing issue.
218
+
219
+ = Version 1.1.4 - Tuesday, 9th July 2019 =
220
+ * Fix: Follow up emails were getting sent even after the completion of the order.
221
+ * Fix: Email template variable 'Abandoned Product Names' warning issue.
222
+
223
+ = Version 1.1.3 - Thursday, 27th June 2019 =
224
+ * Improvement: Added checkout link for abandoned cart inside the admin section.
225
+ * Fix: Added pagination for reports.
226
+ * Fix: Recover report calculations before campaign triggers.
227
+ * Fix: Empty cart notice when CartFlows checkout is set global.
228
+
229
+ = Version 1.1.2 - Wednesday, 12th June 2019 =
230
+ * Fix: Issue of timezone while sending mail through cron.
231
+ * Fix: Delete single cart abandonment order.
232
+ * Fix: MySql 5.5 support for CURRENT_TIMESTAMP.
233
+
234
+ = Version 1.1.1 - Thursday, 06th June 2019 =
235
+ * New: Added feature to reschedule emails for Admin.
236
+ * Fix: Coupon expiry time issue.
237
+ * Fix: Email issue for a user who has an already purchased order.
238
+ * Fix: Translatable strings updated.
239
+
240
+ = Version 1.1.0 - Thursday, 30th May 2019 =
241
+ * Added a view for admin to check email status specific to the particular abandoned user.
242
+
243
+ = Version 1.0.0 - Monday, 27th May 2019 =
244
+ * Initial Release
245
+
246
  == Upgrade Notice ==
uninstall.php CHANGED
@@ -10,5 +10,47 @@ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
10
  exit;
11
  }
12
 
 
13
 
14
  wp_clear_scheduled_hook( 'cartflows_ca_update_order_status_action' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  exit;
11
  }
12
 
13
+ global $wpdb;
14
 
15
  wp_clear_scheduled_hook( 'cartflows_ca_update_order_status_action' );
16
+
17
+ $delete_data = get_option( 'wcf_ca_delete_plugin_data' );
18
+
19
+ if ( 'on' === $delete_data ) {
20
+
21
+ $options = array(
22
+ 'wcf_ca_status',
23
+ 'wcf_ca_gdpr_status',
24
+ 'wcf_ca_coupon_code_status',
25
+ 'wcf_ca_zapier_tracking_status',
26
+ 'wcf_ca_cut_off_time',
27
+ 'wcf_ca_from_name',
28
+ 'wcf_ca_from_email',
29
+ 'wcf_ca_reply_email',
30
+ 'wcf_ca_discount_type',
31
+ 'wcf_ca_coupon_amount',
32
+ 'wcf_ca_zapier_cart_abandoned_webhook',
33
+ 'wcf_ca_gdpr_message',
34
+ 'wcf_ca_coupon_expiry',
35
+ 'wcf_ca_coupon_expiry_unit',
36
+ 'wcf_ca_excludes_orders',
37
+ 'wcf_ca_delete_plugin_data',
38
+ 'wcf_ca_version',
39
+ );
40
+
41
+ // Delete all options data.
42
+ foreach ( $options as $index => $key ) {
43
+ delete_option( $key );
44
+ }
45
+
46
+ // Drop the tables.
47
+ $wpdb->get_results( "DROP TABLE IF EXISTS {$wpdb->prefix}cartflows_ca_email_templates_meta" );
48
+
49
+ $wpdb->get_results( "DROP TABLE IF EXISTS {$wpdb->prefix}cartflows_ca_email_history" );
50
+
51
+ $wpdb->get_results( "DROP TABLE IF EXISTS {$wpdb->prefix}cartflows_ca_email_templates" );
52
+
53
+ $wpdb->get_results( "DROP TABLE IF EXISTS {$wpdb->prefix}cartflows_ca_cart_abandonment" );
54
+
55
+ }
56
+
woo-cart-abandonment-recovery.php CHANGED
@@ -1,24 +1,24 @@
1
- <?php
2
- /**
3
- * Plugin Name: WooCommerce Cart Abandonment Recovery
4
- * Plugin URI: https://cartflows.com/
5
- * Description: Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
6
- * Version: 1.2.9
7
- * Author: CartFlows Inc
8
- * Author URI: https://cartflows.com/
9
- * Text Domain: woo-cart-abandonment-recovery
10
- * WC requires at least: 3.0
11
- * WC tested up to: 4.9
12
- *
13
- * @package Woocommerce-Cart-Abandonment-Recovery
14
- */
15
-
16
- /**
17
- * Set constants.
18
- */
19
- define( 'CARTFLOWS_CA_FILE', __FILE__ );
20
-
21
- /**
22
- * Loader
23
- */
24
- require_once 'classes/class-cartflows-ca-loader.php';
1
+ <?php
2
+ /**
3
+ * Plugin Name: WooCommerce Cart Abandonment Recovery
4
+ * Plugin URI: https://cartflows.com/
5
+ * Description: Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
6
+ * Version: 1.2.10
7
+ * Author: CartFlows Inc
8
+ * Author URI: https://cartflows.com/
9
+ * Text Domain: woo-cart-abandonment-recovery
10
+ * WC requires at least: 3.0
11
+ * WC tested up to: 5.0.0
12
+ *
13
+ * @package Woocommerce-Cart-Abandonment-Recovery
14
+ */
15
+
16
+ /**
17
+ * Set constants.
18
+ */
19
+ define( 'CARTFLOWS_CA_FILE', __FILE__ );
20
+
21
+ /**
22
+ * Loader
23
+ */
24
+ require_once 'classes/class-cartflows-ca-loader.php';