Astra Starter Sites - Version 1.2.0

Version Description

Download this release

Release Info

Developer Nikschavan
Plugin Icon Astra Starter Sites
Version 1.2.0
Comparing to
See all releases

Code changes from version 1.1.9 to 1.2.0

astra-sites.php CHANGED
@@ -1,54 +1,54 @@
1
- <?php
2
- /**
3
- * Plugin Name: Astra Starter Sites
4
- * Plugin URI: http://www.wpastra.com/pro/
5
- * Description: Import free sites build with Astra theme.
6
- * Version: 1.1.9
7
- * Author: Brainstorm Force
8
- * Author URI: http://www.brainstormforce.com
9
- * Text Domain: astra-sites
10
- *
11
- * @package Astra Sites
12
- */
13
-
14
- /**
15
- * Set constants.
16
- */
17
- if ( ! defined( 'ASTRA_SITES_NAME' ) ) {
18
- define( 'ASTRA_SITES_NAME', __( 'Astra Sites', 'astra-sites' ) );
19
- }
20
-
21
- if ( ! defined( 'ASTRA_SITES_VER' ) ) {
22
- define( 'ASTRA_SITES_VER', '1.1.9' );
23
- }
24
-
25
- if ( ! defined( 'ASTRA_SITES_FILE' ) ) {
26
- define( 'ASTRA_SITES_FILE', __FILE__ );
27
- }
28
-
29
- if ( ! defined( 'ASTRA_SITES_BASE' ) ) {
30
- define( 'ASTRA_SITES_BASE', plugin_basename( ASTRA_SITES_FILE ) );
31
- }
32
-
33
- if ( ! defined( 'ASTRA_SITES_DIR' ) ) {
34
- define( 'ASTRA_SITES_DIR', plugin_dir_path( ASTRA_SITES_FILE ) );
35
- }
36
-
37
- if ( ! defined( 'ASTRA_SITES_URI' ) ) {
38
- define( 'ASTRA_SITES_URI', plugins_url( '/', ASTRA_SITES_FILE ) );
39
- }
40
-
41
- if ( ! function_exists( 'astra_sites_setup' ) ) :
42
-
43
- /**
44
- * Astra Sites Setup
45
- *
46
- * @since 1.0.5
47
- */
48
- function astra_sites_setup() {
49
- require_once ASTRA_SITES_DIR . 'inc/classes/class-astra-sites.php';
50
- }
51
-
52
- add_action( 'plugins_loaded', 'astra_sites_setup' );
53
-
54
- endif;
1
+ <?php
2
+ /**
3
+ * Plugin Name: Astra Starter Sites
4
+ * Plugin URI: http://www.wpastra.com/pro/
5
+ * Description: Import free sites build with Astra theme.
6
+ * Version: 1.2.0
7
+ * Author: Brainstorm Force
8
+ * Author URI: http://www.brainstormforce.com
9
+ * Text Domain: astra-sites
10
+ *
11
+ * @package Astra Sites
12
+ */
13
+
14
+ /**
15
+ * Set constants.
16
+ */
17
+ if ( ! defined( 'ASTRA_SITES_NAME' ) ) {
18
+ define( 'ASTRA_SITES_NAME', __( 'Astra Sites', 'astra-sites' ) );
19
+ }
20
+
21
+ if ( ! defined( 'ASTRA_SITES_VER' ) ) {
22
+ define( 'ASTRA_SITES_VER', '1.2.0' );
23
+ }
24
+
25
+ if ( ! defined( 'ASTRA_SITES_FILE' ) ) {
26
+ define( 'ASTRA_SITES_FILE', __FILE__ );
27
+ }
28
+
29
+ if ( ! defined( 'ASTRA_SITES_BASE' ) ) {
30
+ define( 'ASTRA_SITES_BASE', plugin_basename( ASTRA_SITES_FILE ) );
31
+ }
32
+
33
+ if ( ! defined( 'ASTRA_SITES_DIR' ) ) {
34
+ define( 'ASTRA_SITES_DIR', plugin_dir_path( ASTRA_SITES_FILE ) );
35
+ }
36
+
37
+ if ( ! defined( 'ASTRA_SITES_URI' ) ) {
38
+ define( 'ASTRA_SITES_URI', plugins_url( '/', ASTRA_SITES_FILE ) );
39
+ }
40
+
41
+ if ( ! function_exists( 'astra_sites_setup' ) ) :
42
+
43
+ /**
44
+ * Astra Sites Setup
45
+ *
46
+ * @since 1.0.5
47
+ */
48
+ function astra_sites_setup() {
49
+ require_once ASTRA_SITES_DIR . 'inc/classes/class-astra-sites.php';
50
+ }
51
+
52
+ add_action( 'plugins_loaded', 'astra_sites_setup' );
53
+
54
+ endif;
inc/assets/css/admin.css CHANGED
@@ -1,463 +1,463 @@
1
- .wrap .status,
2
- .wrap .site-type {
3
- position: absolute;
4
- z-index: 1;
5
- color: #fff;
6
- padding: 0.5em 1em;
7
- top: -0.5em;
8
- text-transform: uppercase;
9
- }
10
- .wrap .status,
11
- .wrap .site-type.premium {
12
- background: #0073aa;
13
- }
14
- .wrap .status {
15
- left: -0.5em;
16
- }
17
- .wrap .site-type.premium {
18
- right: -0.5em;
19
- }
20
-
21
- .wrap .status.publish,
22
- .wrap .site-type.free {
23
- display: none;
24
- }
25
-
26
- .install-theme-info .site-type {
27
- display: none;
28
- }
29
-
30
- .theme {
31
- position: relative;
32
- }
33
- .wrap .astra-sites-preview .site-type.premium {
34
- display: block;
35
- display: none;
36
- position: relative;
37
- margin: 0.5em 0em 1em 0em;
38
- top: 0;
39
- left: 0;
40
- text-align: center;
41
- }
42
-
43
- .theme-details-read-more.open {
44
- margin: 0.5em 0 0 0;
45
- }
46
-
47
- .astra-sites-preview .theme-screenshot {
48
- width: 100%;
49
- }
50
-
51
- .install-theme-info .site-type.premium {
52
- display: none;
53
- }
54
-
55
- /**
56
- * Required Plugins
57
- */
58
- .required-plugins.loading {
59
- text-align: center;
60
- }
61
- .required-plugins button {
62
- float: right;
63
- }
64
- .required-plugins .plugin-card {
65
- float: none;
66
- width: 100%;
67
- border: none;
68
- margin: 0 0 0.8em 0;
69
- display: flex;
70
- justify-content: space-between;
71
- align-items: center;
72
- transition: background ease 0.8s;
73
- }
74
- .required-plugins .plugin-card.plugin-card-update-failed {
75
- flex-wrap: wrap;
76
- }
77
- .required-plugins .spinner {
78
- float: none;
79
- margin: 0;
80
- }
81
-
82
- .expanded .wp-full-overlay-footer {
83
- height: 111px;
84
- }
85
-
86
- .wp-full-overlay-footer .view-site,
87
- .wp-full-overlay-footer .go-pro,
88
- .wp-full-overlay-footer .astra-demo-import {
89
- width: 100%;
90
- text-align: center;
91
- }
92
-
93
- .wp-core-ui .wp-full-overlay-footer .button.button-hero,
94
- .wp-core-ui .wp-full-overlay-footer .button-group.button-hero .button {
95
- padding: 0 10px 1px;
96
- }
97
-
98
- .wp-full-overlay-footer .installing:before {
99
- vertical-align: text-bottom;
100
- }
101
-
102
- .required-plugins-wrap h4 {
103
- margin: 1em 0 0.5em 0;
104
- padding: 0.5em 0;
105
- transition: all ease 0.3s;
106
- }
107
-
108
- /**
109
- * Read more link
110
- */
111
- .wp-core-ui .theme-details-read-more:focus,
112
- .wp-core-ui .theme-details-read-more:hover {
113
- outline: none;
114
- box-shadow: none;
115
- }
116
- .wp-core-ui .theme-details-read-more {
117
- margin: 10px 0;
118
- display: none;
119
- text-decoration: none;
120
- }
121
-
122
- /**
123
- * Go pro.
124
- */
125
- .wp-core-ui .go-pro.button[disabled] {
126
- background-color: #fcb92c !important;
127
- color: white !important;
128
- box-shadow: 1px 0 #eab23a !important;
129
- text-shadow: 1px 0 #6b4e13 !important;
130
- border-color: #e2a932 !important;
131
- cursor: pointer;
132
- }
133
- .wp-core-ui .view-site .dashicons,
134
- .wp-core-ui .go-pro .dashicons {
135
- font-size: 1rem;
136
- vertical-align: middle;
137
- }
138
-
139
- /**
140
- * Errors
141
- */
142
- .plugin-card-update-failed .notice {
143
- margin-top: 1.5em;
144
- }
145
-
146
- .no-themes {
147
- margin-top: 40px;
148
- }
149
-
150
- .no-themes p {
151
- font-size: 15px;
152
- }
153
-
154
- .no-themes .left-margin {
155
- margin-left: 30px;
156
- }
157
-
158
- /**
159
- *
160
- */
161
- .astra-sites-preview .wp-full-overlay-sidebar-content {
162
- bottom: 100px;
163
- }
164
-
165
- .footer-import-button-wrap {
166
- padding: 10px 20px;
167
- }
168
-
169
- .footer-import-button-wrap .button {
170
- margin: 0;
171
- }
172
-
173
- .astra-sites-preview.expanded .wp-full-overlay-footer {
174
- left: initial;
175
- }
176
-
177
- /**
178
- * Menu Page
179
- */
180
- .astra-sites-title {
181
- float: left;
182
- font-size: 23px;
183
- font-weight: 400;
184
- margin: 0 0 6px 0px;
185
- padding: 0;
186
- line-height: 29px;
187
- }
188
-
189
- #astra-sites-menu-page {
190
- margin-top: 10px;
191
- }
192
-
193
- /**
194
- * API Error
195
- */
196
- .astra-api-error {
197
- margin: 0 0 0.5em 0;
198
- }
199
-
200
- /**
201
- * Grid
202
- */
203
- .wp-filter .search-form {
204
- margin-left: 1em;
205
- }
206
- .wp-filter .search-form input[type="search"] {
207
- width: 200px;
208
- font-size: 13px;
209
- padding: 5px 10px;
210
- }
211
- .section-left {
212
- display: inline-block;
213
- }
214
- .section-right {
215
- float: right;
216
- }
217
- .filter-count {
218
- min-width: 3em;
219
- }
220
- .astra-site-preview-on {
221
- overflow: hidden;
222
- }
223
-
224
- #astra-sites-menu-page .notice {
225
- margin-left: 0;
226
- width: auto;
227
- float: none;
228
- }
229
- .filters-wrap {
230
- display: inline-block;
231
- }
232
- .spinner-wrap {
233
- text-align: center;
234
- }
235
- .spinner-wrap .spinner {
236
- float: none;
237
- }
238
- .hide-me {
239
- display: none !important;
240
- }
241
- #astra-sites-admin {
242
- height: 100vh;
243
- }
244
- .install-theme-info > .notice {
245
- margin: 5px 0 10px 0;
246
- }
247
-
248
- .astra-sites-suggestions:before {
249
- border: 5px dashed #ccc;
250
- position: absolute;
251
- left: 0;
252
- right: 0;
253
- top: 0;
254
- bottom: 0px;
255
- }
256
-
257
- .astra-sites-suggestions {
258
- min-height: 280px;
259
- border: none !important;
260
- }
261
-
262
- .astra-sites-suggestions a {
263
- border: none;
264
- outline: none;
265
- }
266
-
267
- .astra-sites-suggestions .inner {
268
- border: 6px solid #24282e !important;
269
- padding: 27% 10% 50% 10%;
270
- text-align: center;
271
- position: absolute;
272
- left: 0;
273
- right: 0;
274
- top: 0;
275
- background: #33383d;
276
- bottom: 0;
277
- color: #eee;
278
- cursor: auto;
279
- }
280
-
281
- .astra-sites-suggestions .inner a {
282
- color: #00b9eb;
283
- }
284
-
285
- .astra-sites-suggestions p {
286
- font-size: 1rem;
287
- margin: 0;
288
- }
289
-
290
- .astra-notice {
291
- margin: 2em 2em 0em 0em;
292
- }
293
-
294
- .no-themes .description {
295
- display: block;
296
- }
297
-
298
- /**
299
- * Responsive Button UI
300
- */
301
- .astra-sites-preview .wp-full-overlay-footer .devices button.active:before,
302
- .astra-sites-preview .wp-full-overlay-footer .devices button:hover:before {
303
- color: #0073aa;
304
- }
305
- .astra-sites-preview .wp-full-overlay-footer .devices button:before {
306
- color: #c1c1c1;
307
- }
308
- .astra-sites-preview .wp-full-overlay-footer .devices button:hover {
309
- background-color: transparent;
310
- }
311
- .astra-sites-preview .wp-full-overlay-footer .devices button {
312
- border: none;
313
- }
314
- .astra-sites-preview .wp-full-overlay-footer .devices button:focus,
315
- .astra-sites-preview .wp-full-overlay-footer .devices button.active:hover {
316
- border-bottom-color: transparent;
317
- background-color: transparent;
318
- }
319
- .not-click-able {
320
- pointer-events: none !important;
321
- }
322
- body.page-builder-selected .select-page-builder,
323
- body.loading-content .select-page-builder {
324
- display: none;
325
- }
326
- .select-page-builder .up-arrow {
327
- -webkit-transform: rotate(90deg);
328
- -moz-transform: rotate(90deg);
329
- -ms-transform: rotate(90deg);
330
- transform: rotate(90deg);
331
- display: inline-block;
332
- font-size: 1.5em;
333
- color: #797979;
334
- vertical-align: middle;
335
- margin-right: 10px;
336
- -webkit-transition: all linear 0.6s;
337
- -moz-transition: all linear 0.6s;
338
- -ms-transition: all linear 0.6s;
339
- transition: all linear 0.6s;
340
- -webkit-animation-duration: 1s;
341
- animation-duration: 1s;
342
- -webkit-animation-fill-mode: both;
343
- animation-fill-mode: both;
344
- -webkit-animation-timing-function: ease-in-out;
345
- animation-timing-function: ease-in-out;
346
- animation-iteration-count: infinite;
347
- -webkit-animation-iteration-count: infinite;
348
- animation-name: bounce;
349
- -moz-animation-name: bounce;
350
- }
351
- .select-page-builder {
352
- margin-left: 6em;
353
- }
354
-
355
- .select-page-builder img {
356
- max-width: 100%;
357
- }
358
-
359
- .select-page-builder .note-wrap {
360
- position: absolute;
361
- padding: 40px 0;
362
- margin-left: 170px;
363
- right: 0;
364
- left: 0;
365
- }
366
-
367
- .select-page-builder h3 {
368
- margin: 0;
369
- font-size: 2em;
370
- }
371
- .select-page-builder {
372
- margin: -20px 0 0 -5px;
373
- position: absolute;
374
- }
375
- .select-page-builder .note {
376
- margin-left: 1.5em;
377
- }
378
-
379
- @keyframes bounce {
380
- 0%,
381
- 100%,
382
- 20%,
383
- 50%,
384
- 80% {
385
- -webkit-transform: translateY(-0px) rotate(90deg);
386
- -moz-transform: translateY(-0px) rotate(90deg);
387
- -ms-transform: translateY(-0px) rotate(90deg);
388
- transform: translateY(-0px) rotate(90deg);
389
- }
390
- 40% {
391
- -webkit-transform: translateY(-2px) rotate(90deg);
392
- -moz-transform: translateY(-2px) rotate(90deg);
393
- -ms-transform: translateY(-2px) rotate(90deg);
394
- transform: translateY(-2px) rotate(90deg);
395
- }
396
- 60% {
397
- -webkit-transform: translateY(-1px) rotate(90deg);
398
- -moz-transform: translateY(-1px) rotate(90deg);
399
- -ms-transform: translateY(-1px) rotate(90deg);
400
- transform: translateY(-1px) rotate(90deg);
401
- }
402
- }
403
-
404
- /**
405
- * Processing Animation
406
- */
407
- .astra-demo-import.button.updating-message:before,
408
- .astra-demo-import.button.installing:before {
409
- -webkit-animation: cssAnimation .72s ease infinite;
410
- -moz-animation: cssAnimation .72s ease infinite;
411
- -o-animation: cssAnimation .72s ease infinite;
412
- -ms-animation: cssAnimation .72s ease infinite;
413
- animation: cssAnimation .72s ease infinite;
414
- }
415
-
416
- @-webkit-keyframes cssAnimation {
417
- from {
418
- -webkit-transform: rotate(0);
419
- -moz-transform: rotate(0);
420
- -o-transform: rotate(0);
421
- -ms-transform: rotate(0);
422
- transform: rotate(0);
423
- }
424
- to {
425
- -webkit-transform: rotate(360deg);
426
- -moz-transform: rotate(360deg);
427
- -o-transform: rotate(360deg);
428
- -ms-transform: rotate(360deg);
429
- transform: rotate(360deg);
430
- }
431
- }
432
- @-moz-keyframes cssAnimation {
433
- from {
434
- -webkit-transform: rotate(0);
435
- -moz-transform: rotate(0);
436
- -o-transform: rotate(0);
437
- -ms-transform: rotate(0);
438
- transform: rotate(0);
439
- }
440
- to {
441
- -webkit-transform: rotate(360deg);
442
- -moz-transform: rotate(360deg);
443
- -o-transform: rotate(360deg);
444
- -ms-transform: rotate(360deg);
445
- transform: rotate(360deg);
446
- }
447
- }
448
- @-o-keyframes cssAnimation {
449
- from {
450
- -webkit-transform: rotate(0);
451
- -moz-transform: rotate(0);
452
- -o-transform: rotate(0);
453
- -ms-transform: rotate(0);
454
- transform: rotate(0);
455
- }
456
- to {
457
- -webkit-transform: rotate(360deg);
458
- -moz-transform: rotate(360deg);
459
- -o-transform: rotate(360deg);
460
- -ms-transform: rotate(360deg);
461
- transform: rotate(360deg);
462
- }
463
ย  }
1
+ .wrap .status,
2
+ .wrap .site-type {
3
+ position: absolute;
4
+ z-index: 1;
5
+ color: #fff;
6
+ padding: 0.5em 1em;
7
+ top: -0.5em;
8
+ text-transform: uppercase;
9
+ }
10
+ .wrap .status,
11
+ .wrap .site-type.premium {
12
+ background: #0073aa;
13
+ }
14
+ .wrap .status {
15
+ left: -0.5em;
16
+ }
17
+ .wrap .site-type.premium {
18
+ right: -0.5em;
19
+ }
20
+
21
+ .wrap .status.publish,
22
+ .wrap .site-type.free {
23
+ display: none;
24
+ }
25
+
26
+ .install-theme-info .site-type {
27
+ display: none;
28
+ }
29
+
30
+ .theme {
31
+ position: relative;
32
+ }
33
+ .wrap .astra-sites-preview .site-type.premium {
34
+ display: block;
35
+ display: none;
36
+ position: relative;
37
+ margin: 0.5em 0em 1em 0em;
38
+ top: 0;
39
+ left: 0;
40
+ text-align: center;
41
+ }
42
+
43
+ .theme-details-read-more.open {
44
+ margin: 0.5em 0 0 0;
45
+ }
46
+
47
+ .astra-sites-preview .theme-screenshot {
48
+ width: 100%;
49
+ }
50
+
51
+ .install-theme-info .site-type.premium {
52
+ display: none;
53
+ }
54
+
55
+ /**
56
+ * Required Plugins
57
+ */
58
+ .required-plugins.loading {
59
+ text-align: center;
60
+ }
61
+ .required-plugins button {
62
+ float: right;
63
+ }
64
+ .required-plugins .plugin-card {
65
+ float: none;
66
+ width: 100%;
67
+ border: none;
68
+ margin: 0 0 0.8em 0;
69
+ display: flex;
70
+ justify-content: space-between;
71
+ align-items: center;
72
+ transition: background ease 0.8s;
73
+ }
74
+ .required-plugins .plugin-card.plugin-card-update-failed {
75
+ flex-wrap: wrap;
76
+ }
77
+ .required-plugins .spinner {
78
+ float: none;
79
+ margin: 0;
80
+ }
81
+
82
+ .expanded .wp-full-overlay-footer {
83
+ height: 111px;
84
+ }
85
+
86
+ .wp-full-overlay-footer .view-site,
87
+ .wp-full-overlay-footer .go-pro,
88
+ .wp-full-overlay-footer .astra-demo-import {
89
+ width: 100%;
90
+ text-align: center;
91
+ }
92
+
93
+ .wp-core-ui .wp-full-overlay-footer .button.button-hero,
94
+ .wp-core-ui .wp-full-overlay-footer .button-group.button-hero .button {
95
+ padding: 0 10px 1px;
96
+ }
97
+
98
+ .wp-full-overlay-footer .installing:before {
99
+ vertical-align: text-bottom;
100
+ }
101
+
102
+ .required-plugins-wrap h4 {
103
+ margin: 1em 0 0.5em 0;
104
+ padding: 0.5em 0;
105
+ transition: all ease 0.3s;
106
+ }
107
+
108
+ /**
109
+ * Read more link
110
+ */
111
+ .wp-core-ui .theme-details-read-more:focus,
112
+ .wp-core-ui .theme-details-read-more:hover {
113
+ outline: none;
114
+ box-shadow: none;
115
+ }
116
+ .wp-core-ui .theme-details-read-more {
117
+ margin: 10px 0;
118
+ display: none;
119
+ text-decoration: none;
120
+ }
121
+
122
+ /**
123
+ * Go pro.
124
+ */
125
+ .wp-core-ui .go-pro.button[disabled] {
126
+ background-color: #fcb92c !important;
127
+ color: white !important;
128
+ box-shadow: 1px 0 #eab23a !important;
129
+ text-shadow: 1px 0 #6b4e13 !important;
130
+ border-color: #e2a932 !important;
131
+ cursor: pointer;
132
+ }
133
+ .wp-core-ui .view-site .dashicons,
134
+ .wp-core-ui .go-pro .dashicons {
135
+ font-size: 1rem;
136
+ vertical-align: middle;
137
+ }
138
+
139
+ /**
140
+ * Errors
141
+ */
142
+ .plugin-card-update-failed .notice {
143
+ margin-top: 1.5em;
144
+ }
145
+
146
+ .no-themes {
147
+ margin-top: 40px;
148
+ }
149
+
150
+ .no-themes p {
151
+ font-size: 15px;
152
+ }
153
+
154
+ .no-themes .left-margin {
155
+ margin-left: 30px;
156
+ }
157
+
158
+ /**
159
+ *
160
+ */
161
+ .astra-sites-preview .wp-full-overlay-sidebar-content {
162
+ bottom: 100px;
163
+ }
164
+
165
+ .footer-import-button-wrap {
166
+ padding: 10px 20px;
167
+ }
168
+
169
+ .footer-import-button-wrap .button {
170
+ margin: 0;
171
+ }
172
+
173
+ .astra-sites-preview.expanded .wp-full-overlay-footer {
174
+ left: initial;
175
+ }
176
+
177
+ /**
178
+ * Menu Page
179
+ */
180
+ .astra-sites-title {
181
+ float: left;
182
+ font-size: 23px;
183
+ font-weight: 400;
184
+ margin: 0 0 6px 0px;
185
+ padding: 0;
186
+ line-height: 29px;
187
+ }
188
+
189
+ #astra-sites-menu-page {
190
+ margin-top: 10px;
191
+ }
192
+
193
+ /**
194
+ * API Error
195
+ */
196
+ .astra-api-error {
197
+ margin: 0 0 0.5em 0;
198
+ }
199
+
200
+ /**
201
+ * Grid
202
+ */
203
+ .wp-filter .search-form {
204
+ margin-left: 1em;
205
+ }
206
+ .wp-filter .search-form input[type="search"] {
207
+ width: 200px;
208
+ font-size: 13px;
209
+ padding: 5px 10px;
210
+ }
211
+ .section-left {
212
+ display: inline-block;
213
+ }
214
+ .section-right {
215
+ float: right;
216
+ }
217
+ .filter-count {
218
+ min-width: 3em;
219
+ }
220
+ .astra-site-preview-on {
221
+ overflow: hidden;
222
+ }
223
+
224
+ #astra-sites-menu-page .notice {
225
+ margin-left: 0;
226
+ width: auto;
227
+ float: none;
228
+ }
229
+ .filters-wrap {
230
+ display: inline-block;
231
+ }
232
+ .spinner-wrap {
233
+ text-align: center;
234
+ }
235
+ .spinner-wrap .spinner {
236
+ float: none;
237
+ }
238
+ .hide-me {
239
+ display: none !important;
240
+ }
241
+ #astra-sites-admin {
242
+ height: 100vh;
243
+ }
244
+ .install-theme-info > .notice {
245
+ margin: 5px 0 10px 0;
246
+ }
247
+
248
+ .astra-sites-suggestions:before {
249
+ border: 5px dashed #ccc;
250
+ position: absolute;
251
+ left: 0;
252
+ right: 0;
253
+ top: 0;
254
+ bottom: 0px;
255
+ }
256
+
257
+ .astra-sites-suggestions {
258
+ min-height: 280px;
259
+ border: none !important;
260
+ }
261
+
262
+ .astra-sites-suggestions a {
263
+ border: none;
264
+ outline: none;
265
+ }
266
+
267
+ .astra-sites-suggestions .inner {
268
+ border: 6px solid #24282e !important;
269
+ padding: 27% 10% 50% 10%;
270
+ text-align: center;
271
+ position: absolute;
272
+ left: 0;
273
+ right: 0;
274
+ top: 0;
275
+ background: #33383d;
276
+ bottom: 0;
277
+ color: #eee;
278
+ cursor: auto;
279
+ }
280
+
281
+ .astra-sites-suggestions .inner a {
282
+ color: #00b9eb;
283
+ }
284
+
285
+ .astra-sites-suggestions p {
286
+ font-size: 1rem;
287
+ margin: 0;
288
+ }
289
+
290
+ .astra-notice {
291
+ margin: 2em 2em 0em 0em;
292
+ }
293
+
294
+ .no-themes .description {
295
+ display: block;
296
+ }
297
+
298
+ /**
299
+ * Responsive Button UI
300
+ */
301
+ .astra-sites-preview .wp-full-overlay-footer .devices button.active:before,
302
+ .astra-sites-preview .wp-full-overlay-footer .devices button:hover:before {
303
+ color: #0073aa;
304
+ }
305
+ .astra-sites-preview .wp-full-overlay-footer .devices button:before {
306
+ color: #c1c1c1;
307
+ }
308
+ .astra-sites-preview .wp-full-overlay-footer .devices button:hover {
309
+ background-color: transparent;
310
+ }
311
+ .astra-sites-preview .wp-full-overlay-footer .devices button {
312
+ border: none;
313
+ }
314
+ .astra-sites-preview .wp-full-overlay-footer .devices button:focus,
315
+ .astra-sites-preview .wp-full-overlay-footer .devices button.active:hover {
316
+ border-bottom-color: transparent;
317
+ background-color: transparent;
318
+ }
319
+ .not-click-able {
320
+ pointer-events: none !important;
321
+ }
322
+ body.page-builder-selected .select-page-builder,
323
+ body.loading-content .select-page-builder {
324
+ display: none;
325
+ }
326
+ .select-page-builder .up-arrow {
327
+ -webkit-transform: rotate(90deg);
328
+ -moz-transform: rotate(90deg);
329
+ -ms-transform: rotate(90deg);
330
+ transform: rotate(90deg);
331
+ display: inline-block;
332
+ font-size: 1.5em;
333
+ color: #797979;
334
+ vertical-align: middle;
335
+ margin-right: 10px;
336
+ -webkit-transition: all linear 0.6s;
337
+ -moz-transition: all linear 0.6s;
338
+ -ms-transition: all linear 0.6s;
339
+ transition: all linear 0.6s;
340
+ -webkit-animation-duration: 1s;
341
+ animation-duration: 1s;
342
+ -webkit-animation-fill-mode: both;
343
+ animation-fill-mode: both;
344
+ -webkit-animation-timing-function: ease-in-out;
345
+ animation-timing-function: ease-in-out;
346
+ animation-iteration-count: infinite;
347
+ -webkit-animation-iteration-count: infinite;
348
+ animation-name: bounce;
349
+ -moz-animation-name: bounce;
350
+ }
351
+ .select-page-builder {
352
+ margin-left: 6em;
353
+ }
354
+
355
+ .select-page-builder img {
356
+ max-width: 100%;
357
+ }
358
+
359
+ .select-page-builder .note-wrap {
360
+ position: absolute;
361
+ padding: 40px 0;
362
+ margin-left: 170px;
363
+ right: 0;
364
+ left: 0;
365
+ }
366
+
367
+ .select-page-builder h3 {
368
+ margin: 0;
369
+ font-size: 2em;
370
+ }
371
+ .select-page-builder {
372
+ margin: -20px 0 0 -5px;
373
+ position: absolute;
374
+ }
375
+ .select-page-builder .note {
376
+ margin-left: 1.5em;
377
+ }
378
+
379
+ @keyframes bounce {
380
+ 0%,
381
+ 100%,
382
+ 20%,
383
+ 50%,
384
+ 80% {
385
+ -webkit-transform: translateY(-0px) rotate(90deg);
386
+ -moz-transform: translateY(-0px) rotate(90deg);
387
+ -ms-transform: translateY(-0px) rotate(90deg);
388
+ transform: translateY(-0px) rotate(90deg);
389
+ }
390
+ 40% {
391
+ -webkit-transform: translateY(-2px) rotate(90deg);
392
+ -moz-transform: translateY(-2px) rotate(90deg);
393
+ -ms-transform: translateY(-2px) rotate(90deg);
394
+ transform: translateY(-2px) rotate(90deg);
395
+ }
396
+ 60% {
397
+ -webkit-transform: translateY(-1px) rotate(90deg);
398
+ -moz-transform: translateY(-1px) rotate(90deg);
399
+ -ms-transform: translateY(-1px) rotate(90deg);
400
+ transform: translateY(-1px) rotate(90deg);
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Processing Animation
406
+ */
407
+ .astra-demo-import.button.updating-message:before,
408
+ .astra-demo-import.button.installing:before {
409
+ -webkit-animation: cssAnimation .72s ease infinite;
410
+ -moz-animation: cssAnimation .72s ease infinite;
411
+ -o-animation: cssAnimation .72s ease infinite;
412
+ -ms-animation: cssAnimation .72s ease infinite;
413
+ animation: cssAnimation .72s ease infinite;
414
+ }
415
+
416
+ @-webkit-keyframes cssAnimation {
417
+ from {
418
+ -webkit-transform: rotate(0);
419
+ -moz-transform: rotate(0);
420
+ -o-transform: rotate(0);
421
+ -ms-transform: rotate(0);
422
+ transform: rotate(0);
423
+ }
424
+ to {
425
+ -webkit-transform: rotate(360deg);
426
+ -moz-transform: rotate(360deg);
427
+ -o-transform: rotate(360deg);
428
+ -ms-transform: rotate(360deg);
429
+ transform: rotate(360deg);
430
+ }
431
+ }
432
+ @-moz-keyframes cssAnimation {
433
+ from {
434
+ -webkit-transform: rotate(0);
435
+ -moz-transform: rotate(0);
436
+ -o-transform: rotate(0);
437
+ -ms-transform: rotate(0);
438
+ transform: rotate(0);
439
+ }
440
+ to {
441
+ -webkit-transform: rotate(360deg);
442
+ -moz-transform: rotate(360deg);
443
+ -o-transform: rotate(360deg);
444
+ -ms-transform: rotate(360deg);
445
+ transform: rotate(360deg);
446
+ }
447
+ }
448
+ @-o-keyframes cssAnimation {
449
+ from {
450
+ -webkit-transform: rotate(0);
451
+ -moz-transform: rotate(0);
452
+ -o-transform: rotate(0);
453
+ -ms-transform: rotate(0);
454
+ transform: rotate(0);
455
+ }
456
+ to {
457
+ -webkit-transform: rotate(360deg);
458
+ -moz-transform: rotate(360deg);
459
+ -o-transform: rotate(360deg);
460
+ -ms-transform: rotate(360deg);
461
+ transform: rotate(360deg);
462
+ }
463
ย  }
inc/assets/js/admin-page.js CHANGED
@@ -1,1360 +1,1360 @@
1
- /**
2
- * AJAX Request Queue
3
- *
4
- * - add()
5
- * - remove()
6
- * - run()
7
- * - stop()
8
- *
9
- * @since 1.0.0
10
- */
11
- var AstraSitesAjaxQueue = (function() {
12
-
13
- var requests = [];
14
-
15
- return {
16
-
17
- /**
18
- * Add AJAX request
19
- *
20
- * @since 1.0.0
21
- */
22
- add: function(opt) {
23
- requests.push(opt);
24
- },
25
-
26
- /**
27
- * Remove AJAX request
28
- *
29
- * @since 1.0.0
30
- */
31
- remove: function(opt) {
32
- if( jQuery.inArray(opt, requests) > -1 )
33
- requests.splice($.inArray(opt, requests), 1);
34
- },
35
-
36
- /**
37
- * Run / Process AJAX request
38
- *
39
- * @since 1.0.0
40
- */
41
- run: function() {
42
- var self = this,
43
- oriSuc;
44
-
45
- if( requests.length ) {
46
- oriSuc = requests[0].complete;
47
-
48
- requests[0].complete = function() {
49
- if( typeof(oriSuc) === 'function' ) oriSuc();
50
- requests.shift();
51
- self.run.apply(self, []);
52
- };
53
-
54
- jQuery.ajax(requests[0]);
55
-
56
- } else {
57
-
58
- self.tid = setTimeout(function() {
59
- self.run.apply(self, []);
60
- }, 1000);
61
- }
62
- },
63
-
64
- /**
65
- * Stop AJAX request
66
- *
67
- * @since 1.0.0
68
- */
69
- stop: function() {
70
-
71
- requests = [];
72
- clearTimeout(this.tid);
73
- }
74
- };
75
-
76
- }());
77
-
78
- (function($){
79
-
80
- var AstraSSEImport = {
81
- complete: {
82
- posts: 0,
83
- media: 0,
84
- users: 0,
85
- comments: 0,
86
- terms: 0,
87
- },
88
-
89
- updateDelta: function (type, delta) {
90
- this.complete[ type ] += delta;
91
-
92
- var self = this;
93
- requestAnimationFrame(function () {
94
- self.render();
95
- });
96
- },
97
- updateProgress: function ( type, complete, total ) {
98
- var text = complete + '/' + total;
99
-
100
- if( 'undefined' !== type && 'undefined' !== text ) {
101
- total = parseInt( total, 10 );
102
- if ( 0 === total || isNaN( total ) ) {
103
- total = 1;
104
- }
105
- var percent = parseInt( complete, 10 ) / total;
106
- var progress = Math.round( percent * 100 ) + '%';
107
- var progress_bar = percent * 100;
108
- }
109
- },
110
- render: function () {
111
- var types = Object.keys( this.complete );
112
- var complete = 0;
113
- var total = 0;
114
-
115
- for (var i = types.length - 1; i >= 0; i--) {
116
- var type = types[i];
117
- this.updateProgress( type, this.complete[ type ], this.data.count[ type ] );
118
-
119
- complete += this.complete[ type ];
120
- total += this.data.count[ type ];
121
- }
122
-
123
- this.updateProgress( 'total', complete, total );
124
- }
125
- };
126
-
127
- AstraSitesAdmin = {
128
-
129
- log_file : '',
130
- customizer_data : '',
131
- wxr_url : '',
132
- options_data : '',
133
- widgets_data : '',
134
-
135
- init: function()
136
- {
137
- this._resetPagedCount();
138
- this._bind();
139
- },
140
-
141
- /**
142
- * Debugging.
143
- *
144
- * @param {mixed} data Mixed data.
145
- */
146
- _log: function( data ) {
147
-
148
- if( astraSitesAdmin.debug ) {
149
-
150
- var date = new Date();
151
- var time = date.toLocaleTimeString();
152
-
153
- if (typeof data == 'object') {
154
- console.log('%c ' + JSON.stringify( data ) + ' ' + time, 'background: #ededed; color: #444');
155
- } else {
156
- console.log('%c ' + data + ' ' + time, 'background: #ededed; color: #444');
157
- }
158
-
159
-
160
- }
161
- },
162
-
163
- /**
164
- * Binds events for the Astra Sites.
165
- *
166
- * @since 1.0.0
167
- * @access private
168
- * @method _bind
169
- */
170
- _bind: function()
171
- {
172
- $( document ).on('click' , '.devices button', AstraSitesAdmin._previewDevice);
173
- $( document ).on('click' , '.theme-browser .theme-screenshot, .theme-browser .more-details, .theme-browser .install-theme-preview', AstraSitesAdmin._preview);
174
- $( document ).on('click' , '.next-theme', AstraSitesAdmin._nextTheme);
175
- $( document ).on('click' , '.previous-theme', AstraSitesAdmin._previousTheme);
176
- $( document ).on('click' , '.collapse-sidebar', AstraSitesAdmin._collapse);
177
- $( document ).on('click' , '.astra-demo-import', AstraSitesAdmin._importDemo);
178
- $( document ).on('click' , '.install-now', AstraSitesAdmin._installNow);
179
- $( document ).on('click' , '.close-full-overlay', AstraSitesAdmin._fullOverlay);
180
- $( document ).on('click' , '.activate-now', AstraSitesAdmin._activateNow);
181
- $( document ).on('wp-plugin-installing' , AstraSitesAdmin._pluginInstalling);
182
- $( document ).on('wp-plugin-install-error' , AstraSitesAdmin._installError);
183
- $( document ).on('wp-plugin-install-success' , AstraSitesAdmin._installSuccess);
184
-
185
- $( document ).on('astra-sites-import-set-site-data-done' , AstraSitesAdmin._importCustomizerSettings );
186
- $( document ).on('astra-sites-import-customizer-settings-done' , AstraSitesAdmin._importPrepareXML );
187
- $( document ).on('astra-sites-import-xml-done' , AstraSitesAdmin._importSiteOptions );
188
- $( document ).on('astra-sites-import-options-done' , AstraSitesAdmin._importWidgets );
189
- $( document ).on('astra-sites-import-widgets-done' , AstraSitesAdmin._importEnd );
190
- },
191
-
192
- /**
193
- * 5. Import Complete.
194
- */
195
- _importEnd: function( event ) {
196
-
197
- $.ajax({
198
- url : astraSitesAdmin.ajaxurl,
199
- type : 'POST',
200
- dataType: 'json',
201
- data : {
202
- action : 'astra-sites-import-end',
203
- },
204
- beforeSend: function() {
205
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importComplete );
206
- }
207
- })
208
- .fail(function( jqXHR ){
209
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
210
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
211
- })
212
- .done(function ( data ) {
213
-
214
- // 5. Fail - Import Complete.
215
- if( false === data.success ) {
216
- AstraSitesAdmin._importFailMessage( data.data );
217
- AstraSitesAdmin._log( data.data );
218
- } else {
219
-
220
- // 5. Pass - Import Complete.
221
- AstraSitesAdmin._importSuccessMessage();
222
- AstraSitesAdmin._log( astraSitesAdmin.log.success + ' ' + astraSitesAdmin.siteURL );
223
- }
224
- });
225
- },
226
-
227
- /**
228
- * 4. Import Widgets.
229
- */
230
- _importWidgets: function( event ) {
231
-
232
- $.ajax({
233
- url : astraSitesAdmin.ajaxurl,
234
- type : 'POST',
235
- dataType: 'json',
236
- data : {
237
- action : 'astra-sites-import-widgets',
238
- widgets_data : AstraSitesAdmin.widgets_data,
239
- },
240
- beforeSend: function() {
241
- AstraSitesAdmin._log( astraSitesAdmin.log.importWidgets );
242
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingWidgets );
243
- },
244
- })
245
- .fail(function( jqXHR ){
246
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
247
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
248
- })
249
- .done(function ( widgets_data ) {
250
-
251
- // 4. Fail - Import Widgets.
252
- if( false === widgets_data.success ) {
253
- AstraSitesAdmin._importFailMessage( widgets_data.data );
254
- AstraSitesAdmin._log( widgets_data.data );
255
-
256
- } else {
257
-
258
- // 4. Pass - Import Widgets.
259
- AstraSitesAdmin._log( astraSitesAdmin.log.importWidgetsSuccess );
260
- $(document).trigger( 'astra-sites-import-widgets-done' );
261
- }
262
- });
263
- },
264
-
265
- /**
266
- * 3. Import Site Options.
267
- */
268
- _importSiteOptions: function( event ) {
269
-
270
- $.ajax({
271
- url : astraSitesAdmin.ajaxurl,
272
- type : 'POST',
273
- dataType: 'json',
274
- data : {
275
- action : 'astra-sites-import-options',
276
- options_data : AstraSitesAdmin.options_data,
277
- },
278
- beforeSend: function() {
279
- AstraSitesAdmin._log( astraSitesAdmin.log.importOptions );
280
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingOptions );
281
- },
282
- })
283
- .fail(function( jqXHR ){
284
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
285
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
286
- })
287
- .done(function ( options_data ) {
288
-
289
- // 3. Fail - Import Site Options.
290
- if( false === options_data.success ) {
291
- AstraSitesAdmin._log( options_data );
292
- AstraSitesAdmin._importFailMessage( options_data.data );
293
- AstraSitesAdmin._log( options_data.data );
294
-
295
- } else {
296
-
297
- // 3. Pass - Import Site Options.
298
- AstraSitesAdmin._log( astraSitesAdmin.log.importOptionsSuccess );
299
- $(document).trigger( 'astra-sites-import-options-done' );
300
- }
301
- });
302
- },
303
-
304
- /**
305
- * 2. Prepare XML Data.
306
- */
307
- _importPrepareXML: function( event ) {
308
-
309
- $.ajax({
310
- url : astraSitesAdmin.ajaxurl,
311
- type : 'POST',
312
- dataType: 'json',
313
- data : {
314
- action : 'astra-sites-import-prepare-xml',
315
- wxr_url : AstraSitesAdmin.wxr_url,
316
- },
317
- beforeSend: function() {
318
- AstraSitesAdmin._log( astraSitesAdmin.log.importXMLPrepare );
319
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importXMLPreparing );
320
- },
321
- })
322
- .fail(function( jqXHR ){
323
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
324
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
325
- })
326
- .done(function ( xml_data ) {
327
-
328
- // 2. Fail - Prepare XML Data.
329
- if( false === xml_data.success ) {
330
- AstraSitesAdmin._log( xml_data );
331
- AstraSitesAdmin._importFailMessage( xml_data.data );
332
- AstraSitesAdmin._log( xml_data.data );
333
-
334
- } else {
335
-
336
- // 2. Pass - Prepare XML Data.
337
- AstraSitesAdmin._log( astraSitesAdmin.log.importXMLPrepareSuccess );
338
-
339
- // Import XML though Event Source.
340
- AstraSSEImport.data = xml_data.data;
341
- AstraSSEImport.render();
342
-
343
- AstraSitesAdmin._log( astraSitesAdmin.log.importXML );
344
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingXML );
345
-
346
- var evtSource = new EventSource( AstraSSEImport.data.url );
347
- evtSource.onmessage = function ( message ) {
348
- var data = JSON.parse( message.data );
349
- switch ( data.action ) {
350
- case 'updateDelta':
351
- AstraSSEImport.updateDelta( data.type, data.delta );
352
- break;
353
-
354
- case 'complete':
355
- evtSource.close();
356
-
357
- // 2. Pass - Import XML though "Source Event".
358
- AstraSitesAdmin._log( astraSitesAdmin.log.importXMLSuccess );
359
- AstraSitesAdmin._log( '----- SSE - XML import Complete -----' );
360
-
361
- $(document).trigger( 'astra-sites-import-xml-done' );
362
-
363
- break;
364
- }
365
- };
366
- evtSource.addEventListener( 'log', function ( message ) {
367
- var data = JSON.parse( message.data );
368
- AstraSitesAdmin._log( data.level + ' ' + data.message );
369
- });
370
- }
371
- });
372
- },
373
-
374
- /**
375
- * 1. Import Customizer Options.
376
- */
377
- _importCustomizerSettings: function( event ) {
378
-
379
- $.ajax({
380
- url : astraSitesAdmin.ajaxurl,
381
- type : 'POST',
382
- dataType: 'json',
383
- data : {
384
- action : 'astra-sites-import-customizer-settings',
385
- customizer_data : AstraSitesAdmin.customizer_data,
386
- },
387
- beforeSend: function() {
388
- AstraSitesAdmin._log( astraSitesAdmin.log.importCustomizer );
389
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingCustomizer );
390
- },
391
- })
392
- .fail(function( jqXHR ){
393
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
394
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
395
- })
396
- .done(function ( customizer_data ) {
397
-
398
- // 1. Fail - Import Customizer Options.
399
- if( false === customizer_data.success ) {
400
- AstraSitesAdmin._importFailMessage( customizer_data.data );
401
- AstraSitesAdmin._log( customizer_data.data );
402
- } else {
403
-
404
- // 1. Pass - Import Customizer Options.
405
- AstraSitesAdmin._log( astraSitesAdmin.log.importCustomizerSuccess );
406
-
407
- $(document).trigger( 'astra-sites-import-customizer-settings-done' );
408
- }
409
- });
410
- },
411
-
412
- /**
413
- * Import Success Button.
414
- *
415
- * @param {string} data Error message.
416
- */
417
- _importSuccessMessage: function() {
418
-
419
- $('.astra-demo-import').removeClass('updating-message installing')
420
- .removeAttr('data-import')
421
- .addClass('view-site')
422
- .removeClass('astra-demo-import')
423
- .text( astraSitesAdmin.strings.viewSite )
424
- .attr('target', '_blank')
425
- .append('<i class="dashicons dashicons-external"></i>')
426
- .attr('href', astraSitesAdmin.siteURL );
427
- },
428
-
429
- /**
430
- * Preview Device
431
- */
432
- _previewDevice: function( event ) {
433
- var device = $( event.currentTarget ).data( 'device' );
434
-
435
- $('.theme-install-overlay')
436
- .removeClass( 'preview-desktop preview-tablet preview-mobile' )
437
- .addClass( 'preview-' + device )
438
- .data( 'current-preview-device', device );
439
-
440
- AstraSitesAdmin._tooglePreviewDeviceButtons( device );
441
- },
442
-
443
- /**
444
- * Toggle Preview Buttons
445
- */
446
- _tooglePreviewDeviceButtons: function( newDevice ) {
447
- var $devices = $( '.wp-full-overlay-footer .devices' );
448
-
449
- $devices.find( 'button' )
450
- .removeClass( 'active' )
451
- .attr( 'aria-pressed', false );
452
-
453
- $devices.find( 'button.preview-' + newDevice )
454
- .addClass( 'active' )
455
- .attr( 'aria-pressed', true );
456
- },
457
-
458
- /**
459
- * Import Error Button.
460
- *
461
- * @param {string} data Error message.
462
- */
463
- _importFailMessage: function( message, from ) {
464
-
465
- $('.astra-demo-import')
466
- .addClass('go-pro button-primary')
467
- .removeClass('updating-message installing')
468
- .removeAttr('data-import')
469
- .attr('target', '_blank')
470
- .append('<i class="dashicons dashicons-external"></i>')
471
- .removeClass('astra-demo-import');
472
-
473
- // Add the doc link due to import log file not generated.
474
- if( 'undefined' === from ) {
475
-
476
- $('.wp-full-overlay-header .go-pro').text( astraSitesAdmin.strings.importFailedBtnSmall );
477
- $('.wp-full-overlay-footer .go-pro').text( astraSitesAdmin.strings.importFailedBtnLarge );
478
- $('.go-pro').attr('href', astraSitesAdmin.log.serverConfiguration );
479
-
480
- // Add the import log file link.
481
- } else {
482
-
483
- $('.wp-full-overlay-header .go-pro').text( astraSitesAdmin.strings.importFailBtn );
484
- $('.wp-full-overlay-footer .go-pro').text( astraSitesAdmin.strings.importFailBtnLarge )
485
-
486
- // Add the import log file link.
487
- if( 'undefined' !== AstraSitesAdmin.log_file_url ) {
488
- $('.go-pro').attr('href', AstraSitesAdmin.log_file_url );
489
- } else {
490
- $('.go-pro').attr('href', astraSitesAdmin.log.serverConfiguration );
491
- }
492
- }
493
-
494
- var output = '<div class="astra-api-error notice notice-error notice-alt is-dismissible">';
495
- output += ' <p>'+message+'</p>';
496
- output += ' <button type="button" class="notice-dismiss">';
497
- output += ' <span class="screen-reader-text">'+commonL10n.dismiss+'</span>';
498
- output += ' </button>';
499
- output += '</div>';
500
-
501
- // Fail Notice.
502
- $('.install-theme-info').append( output );
503
-
504
-
505
- // !important to add trigger.
506
- // Which reinitialize the dismiss error message events.
507
- $(document).trigger('wp-updates-notice-added');
508
- },
509
-
510
-
511
- /**
512
- * Install Now
513
- */
514
- _installNow: function(event)
515
- {
516
- event.preventDefault();
517
-
518
- var $button = jQuery( event.target ),
519
- $document = jQuery(document);
520
-
521
- if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
522
- return;
523
- }
524
-
525
- if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
526
- wp.updates.requestFilesystemCredentials( event );
527
-
528
- $document.on( 'credential-modal-cancel', function() {
529
- var $message = $( '.install-now.updating-message' );
530
-
531
- $message
532
- .removeClass( 'updating-message' )
533
- .text( wp.updates.l10n.installNow );
534
-
535
- wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
536
- } );
537
- }
538
-
539
- AstraSitesAdmin._log( astraSitesAdmin.log.installingPlugin + ' ' + $button.data( 'slug' ) );
540
-
541
- wp.updates.installPlugin( {
542
- slug: $button.data( 'slug' )
543
- } );
544
- },
545
-
546
- /**
547
- * Install Success
548
- */
549
- _installSuccess: function( event, response ) {
550
-
551
- event.preventDefault();
552
-
553
- AstraSitesAdmin._log( astraSitesAdmin.log.installed + ' ' + response.slug );
554
-
555
- var $message = jQuery( '.plugin-card-' + response.slug ).find( '.button' );
556
- var $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val();
557
- var $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
558
-
559
- // Transform the 'Install' button into an 'Activate' button.
560
- var $init = $message.data('init');
561
-
562
- $message.removeClass( 'install-now installed button-disabled updated-message' )
563
- .addClass('updating-message')
564
- .html( astraSitesAdmin.strings.btnActivating );
565
-
566
- // Reset not installed plugins list.
567
- var pluginsList = astraSitesAdmin.requiredPlugins.notinstalled;
568
- astraSitesAdmin.requiredPlugins.notinstalled = AstraSitesAdmin._removePluginFromQueue( response.slug, pluginsList );
569
-
570
- // WordPress adds "Activate" button after waiting for 1000ms. So we will run our activation after that.
571
- setTimeout( function() {
572
-
573
- $.ajax({
574
- url: astraSitesAdmin.ajaxurl,
575
- type: 'POST',
576
- data: {
577
- 'action' : 'astra-required-plugin-activate',
578
- 'init' : $init,
579
- 'options' : $siteOptions,
580
- 'enabledExtensions' : $enabledExtensions,
581
- },
582
- })
583
- .done(function (result) {
584
-
585
- if( result.success ) {
586
-
587
- var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
588
-
589
- // Reset not installed plugins list.
590
- astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( response.slug, pluginsList );
591
-
592
- $message.removeClass( 'button-primary install-now activate-now updating-message' )
593
- .attr('disabled', 'disabled')
594
- .addClass('disabled')
595
- .text( astraSitesAdmin.strings.btnActive );
596
-
597
- // Enable Demo Import Button
598
- AstraSitesAdmin._enable_demo_import_button();
599
-
600
- } else {
601
-
602
- $message.removeClass( 'updating-message' );
603
-
604
- }
605
-
606
- });
607
-
608
- }, 1200 );
609
-
610
- },
611
-
612
- /**
613
- * Plugin Installation Error.
614
- */
615
- _installError: function( event, response ) {
616
-
617
- var $card = jQuery( '.plugin-card-' + response.slug );
618
-
619
- AstraSitesAdmin._log( response.errorMessage + ' ' + response.slug );
620
-
621
- $card
622
- .removeClass( 'button-primary' )
623
- .addClass( 'disabled' )
624
- .html( wp.updates.l10n.installFailedShort );
625
-
626
- AstraSitesAdmin._importFailMessage( response.errorMessage );
627
- },
628
-
629
- /**
630
- * Installing Plugin
631
- */
632
- _pluginInstalling: function(event, args) {
633
- event.preventDefault();
634
-
635
- var $card = jQuery( '.plugin-card-' + args.slug );
636
- var $button = $card.find( '.button' );
637
-
638
- AstraSitesAdmin._log( astraSitesAdmin.log.installingPlugin + ' ' + args.slug );
639
-
640
- $card.addClass('updating-message');
641
- $button.addClass('already-started');
642
-
643
- },
644
-
645
- /**
646
- * Render Demo Preview
647
- */
648
- _activateNow: function( eventn ) {
649
-
650
- event.preventDefault();
651
-
652
- var $button = jQuery( event.target ),
653
- $init = $button.data( 'init' ),
654
- $slug = $button.data( 'slug' );
655
-
656
- if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
657
- return;
658
- }
659
-
660
- AstraSitesAdmin._log( astraSitesAdmin.log.activating + ' ' + $slug );
661
-
662
- $button.addClass('updating-message button-primary')
663
- .html( astraSitesAdmin.strings.btnActivating );
664
-
665
- var $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val();
666
- var $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
667
-
668
- $.ajax({
669
- url: astraSitesAdmin.ajaxurl,
670
- type: 'POST',
671
- data: {
672
- 'action' : 'astra-required-plugin-activate',
673
- 'init' : $init,
674
- 'options' : $siteOptions,
675
- 'enabledExtensions' : $enabledExtensions,
676
- },
677
- })
678
- .done(function (result) {
679
-
680
- if( result.success ) {
681
-
682
- AstraSitesAdmin._log( astraSitesAdmin.log.activated + ' ' + $slug );
683
-
684
- var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
685
-
686
- // Reset not installed plugins list.
687
- astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( $slug, pluginsList );
688
-
689
- $button.removeClass( 'button-primary install-now activate-now updating-message' )
690
- .attr('disabled', 'disabled')
691
- .addClass('disabled')
692
- .text( astraSitesAdmin.strings.btnActive );
693
-
694
- // Enable Demo Import Button
695
- AstraSitesAdmin._enable_demo_import_button();
696
-
697
- }
698
-
699
- })
700
- .fail(function () {
701
- });
702
-
703
- },
704
-
705
- /**
706
- * Full Overlay
707
- */
708
- _fullOverlay: function (event) {
709
- event.preventDefault();
710
-
711
- jQuery('.theme-install-overlay').css('display', 'none');
712
- jQuery('.theme-install-overlay').remove();
713
- jQuery('.theme-preview-on').removeClass('theme-preview-on');
714
- jQuery('html').removeClass('astra-site-preview-on');
715
- },
716
-
717
- /**
718
- * Bulk Plugin Active & Install
719
- */
720
- _bulkPluginInstallActivate: function()
721
- {
722
- if( 0 === astraSitesAdmin.requiredPlugins.length ) {
723
- return;
724
- }
725
-
726
- jQuery('.required-plugins')
727
- .find('.install-now')
728
- .addClass( 'updating-message' )
729
- .removeClass( 'install-now' )
730
- .text( wp.updates.l10n.installing );
731
-
732
- jQuery('.required-plugins')
733
- .find('.activate-now')
734
- .addClass('updating-message')
735
- .removeClass( 'activate-now' )
736
- .html( astraSitesAdmin.strings.btnActivating );
737
-
738
- var not_installed = astraSitesAdmin.requiredPlugins.notinstalled || '';
739
- var activate_plugins = astraSitesAdmin.requiredPlugins.inactive || '';
740
-
741
- // First Install Bulk.
742
- if( not_installed.length > 0 ) {
743
- AstraSitesAdmin._installAllPlugins( not_installed );
744
- }
745
-
746
- // Second Activate Bulk.
747
- if( activate_plugins.length > 0 ) {
748
- AstraSitesAdmin._activateAllPlugins( activate_plugins );
749
- }
750
-
751
- },
752
-
753
- /**
754
- * Activate All Plugins.
755
- */
756
- _activateAllPlugins: function( activate_plugins ) {
757
-
758
- // Activate ALl Plugins.
759
- AstraSitesAjaxQueue.stop();
760
- AstraSitesAjaxQueue.run();
761
-
762
- AstraSitesAdmin._log( astraSitesAdmin.log.bulkActivation );
763
-
764
- $.each( activate_plugins, function(index, single_plugin) {
765
-
766
- var $card = jQuery( '.plugin-card-' + single_plugin.slug ),
767
- $button = $card.find('.button'),
768
- $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val(),
769
- $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
770
-
771
- $button.addClass('updating-message');
772
-
773
- AstraSitesAjaxQueue.add({
774
- url: astraSitesAdmin.ajaxurl,
775
- type: 'POST',
776
- data: {
777
- 'action' : 'astra-required-plugin-activate',
778
- 'init' : single_plugin.init,
779
- 'options' : $siteOptions,
780
- 'enabledExtensions' : $enabledExtensions,
781
- },
782
- success: function( result ){
783
-
784
- if( result.success ) {
785
-
786
- AstraSitesAdmin._log( astraSitesAdmin.log.activate + ' ' + single_plugin.slug );
787
-
788
- var $card = jQuery( '.plugin-card-' + single_plugin.slug );
789
- var $button = $card.find( '.button' );
790
- if( ! $button.hasClass('already-started') ) {
791
- var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
792
-
793
- // Reset not installed plugins list.
794
- astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( single_plugin.slug, pluginsList );
795
- }
796
-
797
- $button.removeClass( 'button-primary install-now activate-now updating-message' )
798
- .attr('disabled', 'disabled')
799
- .addClass('disabled')
800
- .text( astraSitesAdmin.strings.btnActive );
801
-
802
- // Enable Demo Import Button
803
- AstraSitesAdmin._enable_demo_import_button();
804
- } else {
805
- AstraSitesAdmin._log( astraSitesAdmin.log.activationError + ' - ' + single_plugin.slug );
806
- }
807
- }
808
- });
809
- });
810
- },
811
-
812
- /**
813
- * Install All Plugins.
814
- */
815
- _installAllPlugins: function( not_installed ) {
816
-
817
- AstraSitesAdmin._log( astraSitesAdmin.log.bulkInstall );
818
-
819
- $.each( not_installed, function(index, single_plugin) {
820
-
821
- var $card = jQuery( '.plugin-card-' + single_plugin.slug ),
822
- $button = $card.find('.button');
823
-
824
- if( ! $button.hasClass('already-started') ) {
825
-
826
- // Add each plugin activate request in Ajax queue.
827
- // @see wp-admin/js/updates.js
828
- wp.updates.queue.push( {
829
- action: 'install-plugin', // Required action.
830
- data: {
831
- slug: single_plugin.slug
832
- }
833
- } );
834
- }
835
- });
836
-
837
- // Required to set queue.
838
- wp.updates.queueChecker();
839
- },
840
-
841
- /**
842
- * Fires when a nav item is clicked.
843
- *
844
- * @since 1.0
845
- * @access private
846
- * @method _importDemo
847
- */
848
- _importDemo: function()
849
- {
850
- var $this = jQuery(this),
851
- $theme = $this.closest('.astra-sites-preview').find('.wp-full-overlay-header'),
852
- apiURL = $theme.data('demo-api') || '',
853
- plugins = $theme.data('required-plugins');
854
-
855
- var disabled = $this.attr('data-import');
856
-
857
- if ( typeof disabled !== 'undefined' && disabled === 'disabled' || $this.hasClass('disabled') ) {
858
-
859
- $('.astra-demo-import').addClass('updating-message installing')
860
- .text( wp.updates.l10n.installing );
861
-
862
- /**
863
- * Process Bulk Plugin Install & Activate
864
- */
865
- AstraSitesAdmin._bulkPluginInstallActivate();
866
-
867
- return;
868
- }
869
-
870
- // Proceed?
871
- if( ! confirm( astraSitesAdmin.strings.importWarning ) ) {
872
- return;
873
- }
874
-
875
- // Remove all notices before import start.
876
- $('.install-theme-info > .notice').remove();
877
-
878
- $('.astra-demo-import').attr('data-import', 'disabled')
879
- .addClass('updating-message installing')
880
- .text( astraSitesAdmin.strings.importingDemo );
881
-
882
- $this.closest('.theme').focus();
883
-
884
- var $theme = $this.closest('.astra-sites-preview').find('.wp-full-overlay-header');
885
-
886
- var apiURL = $theme.data('demo-api') || '';
887
-
888
- // Site Import by API URL.
889
- if( apiURL ) {
890
- AstraSitesAdmin._importSite( apiURL );
891
- }
892
-
893
- },
894
-
895
- /**
896
- * Start Import Process by API URL.
897
- *
898
- * @param {string} apiURL Site API URL.
899
- */
900
- _importSite: function( apiURL ) {
901
-
902
- AstraSitesAdmin._log( astraSitesAdmin.log.api + ' : ' + apiURL );
903
- AstraSitesAdmin._log( astraSitesAdmin.log.importing );
904
-
905
- $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.gettingData );
906
-
907
- // 1. Request Site Import
908
- $.ajax({
909
- url : astraSitesAdmin.ajaxurl,
910
- type : 'POST',
911
- dataType: 'json',
912
- data : {
913
- 'action' : 'astra-sites-import-set-site-data',
914
- 'api_url' : apiURL,
915
- },
916
- })
917
- .fail(function( jqXHR ){
918
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
919
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
920
- })
921
- .done(function ( demo_data ) {
922
-
923
- // 1. Fail - Request Site Import
924
- if( false === demo_data.success ) {
925
-
926
- AstraSitesAdmin._importFailMessage( demo_data.data );
927
-
928
- } else {
929
-
930
- // Set log file URL.
931
- if( 'log_file' in demo_data.data ){
932
- AstraSitesAdmin.log_file_url = decodeURIComponent( demo_data.data.log_file ) || '';
933
- }
934
-
935
- // 1. Pass - Request Site Import
936
- AstraSitesAdmin._log( astraSitesAdmin.log.processingRequest );
937
-
938
- AstraSitesAdmin.customizer_data = JSON.stringify( demo_data.data['astra-site-customizer-data'] ) || '';
939
- AstraSitesAdmin.wxr_url = encodeURI( demo_data.data['astra-site-wxr-path'] ) || '';
940
- AstraSitesAdmin.options_data = JSON.stringify( demo_data.data['astra-site-options-data'] ) || '';
941
- AstraSitesAdmin.widgets_data = JSON.stringify( demo_data.data['astra-site-widgets-data'] ) || '';
942
-
943
- $(document).trigger( 'astra-sites-import-set-site-data-done' );
944
- }
945
-
946
- });
947
-
948
- },
949
-
950
- /**
951
- * Collapse Sidebar.
952
- */
953
- _collapse: function() {
954
- event.preventDefault();
955
-
956
- overlay = jQuery('.wp-full-overlay');
957
-
958
- if (overlay.hasClass('expanded')) {
959
- overlay.removeClass('expanded');
960
- overlay.addClass('collapsed');
961
- return;
962
- }
963
-
964
- if (overlay.hasClass('collapsed')) {
965
- overlay.removeClass('collapsed');
966
- overlay.addClass('expanded');
967
- return;
968
- }
969
- },
970
-
971
- /**
972
- * Previous Theme.
973
- */
974
- _previousTheme: function (event) {
975
- event.preventDefault();
976
-
977
- currentDemo = jQuery('.theme-preview-on');
978
- currentDemo.removeClass('theme-preview-on');
979
- prevDemo = currentDemo.prev('.theme');
980
- prevDemo.addClass('theme-preview-on');
981
-
982
- AstraSitesAdmin._renderDemoPreview(prevDemo);
983
- },
984
-
985
- /**
986
- * Next Theme.
987
- */
988
- _nextTheme: function (event) {
989
- event.preventDefault();
990
- currentDemo = jQuery('.theme-preview-on')
991
- currentDemo.removeClass('theme-preview-on');
992
- nextDemo = currentDemo.next('.theme');
993
- nextDemo.addClass('theme-preview-on');
994
-
995
- AstraSitesAdmin._renderDemoPreview( nextDemo );
996
- },
997
-
998
- /**
999
- * Individual Site Preview
1000
- *
1001
- * On click on image, more link & preview button.
1002
- */
1003
- _preview: function( event ) {
1004
-
1005
- event.preventDefault();
1006
-
1007
- var self = jQuery(this).parents('.theme');
1008
- self.addClass('theme-preview-on');
1009
-
1010
- jQuery('html').addClass('astra-site-preview-on');
1011
-
1012
- AstraSitesAdmin._renderDemoPreview( self );
1013
- },
1014
-
1015
- /**
1016
- * Check Next Previous Buttons.
1017
- */
1018
- _checkNextPrevButtons: function() {
1019
- currentDemo = jQuery('.theme-preview-on');
1020
- nextDemo = currentDemo.nextAll('.theme').length;
1021
- prevDemo = currentDemo.prevAll('.theme').length;
1022
-
1023
- if (nextDemo == 0) {
1024
- jQuery('.next-theme').addClass('disabled');
1025
- } else if (nextDemo != 0) {
1026
- jQuery('.next-theme').removeClass('disabled');
1027
- }
1028
-
1029
- if (prevDemo == 0) {
1030
- jQuery('.previous-theme').addClass('disabled');
1031
- } else if (prevDemo != 0) {
1032
- jQuery('.previous-theme').removeClass('disabled');
1033
- }
1034
-
1035
- return;
1036
- },
1037
-
1038
- /**
1039
- * Render Demo Preview
1040
- */
1041
- _renderDemoPreview: function(anchor) {
1042
-
1043
- var demoId = anchor.data('id') || '',
1044
- apiURL = anchor.data('demo-api') || '',
1045
- demoType = anchor.data('demo-type') || '',
1046
- demoURL = anchor.data('demo-url') || '',
1047
- screenshot = anchor.data('screenshot') || '',
1048
- demo_name = anchor.data('demo-name') || '',
1049
- demo_slug = anchor.data('demo-slug') || '',
1050
- content = anchor.data('content') || '',
1051
- requiredPlugins = anchor.data('required-plugins') || '',
1052
- astraSiteOptions = anchor.find('.astra-site-options').val() || '';
1053
- astraEnabledExtensions = anchor.find('.astra-enabled-extensions').val() || '';
1054
-
1055
- AstraSitesAdmin._log( astraSitesAdmin.log.preview + ' "' + demo_name + '" URL : ' + demoURL );
1056
-
1057
- var template = wp.template('astra-site-preview');
1058
-
1059
- templateData = [{
1060
- id : demoId,
1061
- astra_demo_type : demoType,
1062
- astra_demo_url : demoURL,
1063
- demo_api : apiURL,
1064
- screenshot : screenshot,
1065
- demo_name : demo_name,
1066
- slug : demo_slug,
1067
- content : content,
1068
- required_plugins : JSON.stringify(requiredPlugins),
1069
- astra_site_options : astraSiteOptions,
1070
- astra_enabled_extensions : astraEnabledExtensions,
1071
- }];
1072
-
1073
- // delete any earlier fullscreen preview before we render new one.
1074
- jQuery('.theme-install-overlay').remove();
1075
-
1076
- jQuery('#astra-sites-menu-page').append(template(templateData[0]));
1077
- jQuery('.theme-install-overlay').css('display', 'block');
1078
- AstraSitesAdmin._checkNextPrevButtons();
1079
-
1080
- var desc = jQuery('.theme-details');
1081
- var descHeight = parseInt( desc.outerHeight() );
1082
- var descBtn = jQuery('.theme-details-read-more');
1083
-
1084
- if( $.isArray( requiredPlugins ) ) {
1085
-
1086
- if( descHeight >= 55 ) {
1087
-
1088
- // Show button.
1089
- descBtn.css( 'display', 'inline-block' );
1090
-
1091
- // Set height upto 3 line.
1092
- desc.css( 'height', 57 );
1093
-
1094
- // Button Click.
1095
- descBtn.click(function(event) {
1096
-
1097
- if( descBtn.hasClass('open') ) {
1098
- desc.animate({ height: 57 },
1099
- 300, function() {
1100
- descBtn.removeClass('open');
1101
- descBtn.html( astraSitesAdmin.strings.DescExpand );
1102
- });
1103
- } else {
1104
- desc.animate({ height: descHeight },
1105
- 300, function() {
1106
- descBtn.addClass('open');
1107
- descBtn.html( astraSitesAdmin.strings.DescCollapse );
1108
- });
1109
- }
1110
-
1111
- });
1112
- }
1113
-
1114
- // or
1115
- var $pluginsFilter = jQuery( '#plugin-filter' ),
1116
- data = {
1117
- action : 'astra-required-plugins',
1118
- _ajax_nonce : astraSitesAdmin._ajax_nonce,
1119
- required_plugins : requiredPlugins
1120
- };
1121
-
1122
- // Add disabled class from import button.
1123
- $('.astra-demo-import')
1124
- .addClass('disabled not-click-able')
1125
- .removeAttr('data-import');
1126
-
1127
- jQuery('.required-plugins').addClass('loading').html('<span class="spinner is-active"></span>');
1128
-
1129
- // Required Required.
1130
- $.ajax({
1131
- url : astraSitesAdmin.ajaxurl,
1132
- type : 'POST',
1133
- data : data,
1134
- })
1135
- .fail(function( jqXHR ){
1136
-
1137
- // Remove loader.
1138
- jQuery('.required-plugins').removeClass('loading').html('');
1139
-
1140
- AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText, 'plugins' );
1141
- AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
1142
- })
1143
- .done(function ( response ) {
1144
-
1145
- // Release disabled class from import button.
1146
- $('.astra-demo-import')
1147
- .removeClass('disabled not-click-able')
1148
- .attr('data-import', 'disabled');
1149
-
1150
- // Remove loader.
1151
- jQuery('.required-plugins').removeClass('loading').html('');
1152
-
1153
- /**
1154
- * Count remaining plugins.
1155
- * @type number
1156
- */
1157
- var remaining_plugins = 0;
1158
-
1159
- /**
1160
- * Not Installed
1161
- *
1162
- * List of not installed required plugins.
1163
- */
1164
- if ( typeof response.data.notinstalled !== 'undefined' ) {
1165
-
1166
- // Add not have installed plugins count.
1167
- remaining_plugins += parseInt( response.data.notinstalled.length );
1168
-
1169
- jQuery( response.data.notinstalled ).each(function( index, plugin ) {
1170
-
1171
- var output = '<div class="plugin-card ';
1172
- output += ' plugin-card-'+plugin.slug+'"';
1173
- output += ' data-slug="'+plugin.slug+'"';
1174
- output += ' data-init="'+plugin.init+'">';
1175
- output += ' <span class="title">'+plugin.name+'</span>';
1176
- output += ' <button class="button install-now"';
1177
- output += ' data-init="' + plugin.init + '"';
1178
- output += ' data-slug="' + plugin.slug + '"';
1179
- output += ' data-name="' + plugin.name + '">';
1180
- output += wp.updates.l10n.installNow;
1181
- output += ' </button>';
1182
- // output += ' <span class="dashicons-no dashicons"></span>';
1183
- output += '</div>';
1184
-
1185
- jQuery('.required-plugins').append(output);
1186
-
1187
- });
1188
- }
1189
-
1190
- /**
1191
- * Inactive
1192
- *
1193
- * List of not inactive required plugins.
1194
- */
1195
- if ( typeof response.data.inactive !== 'undefined' ) {
1196
-
1197
- // Add inactive plugins count.
1198
- remaining_plugins += parseInt( response.data.inactive.length );
1199
-
1200
- jQuery( response.data.inactive ).each(function( index, plugin ) {
1201
-
1202
- var output = '<div class="plugin-card ';
1203
- output += ' plugin-card-'+plugin.slug+'"';
1204
- output += ' data-slug="'+plugin.slug+'"';
1205
- output += ' data-init="'+plugin.init+'">';
1206
- output += ' <span class="title">'+plugin.name+'</span>';
1207
- output += ' <button class="button activate-now button-primary"';
1208
- output += ' data-init="' + plugin.init + '"';
1209
- output += ' data-slug="' + plugin.slug + '"';
1210
- output += ' data-name="' + plugin.name + '">';
1211
- output += wp.updates.l10n.activatePlugin;
1212
- output += ' </button>';
1213
- // output += ' <span class="dashicons-no dashicons"></span>';
1214
- output += '</div>';
1215
-
1216
- jQuery('.required-plugins').append(output);
1217
-
1218
- });
1219
- }
1220
-
1221
- /**
1222
- * Active
1223
- *
1224
- * List of not active required plugins.
1225
- */
1226
- if ( typeof response.data.active !== 'undefined' ) {
1227
-
1228
- jQuery( response.data.active ).each(function( index, plugin ) {
1229
-
1230
- var output = '<div class="plugin-card ';
1231
- output += ' plugin-card-'+plugin.slug+'"';
1232
- output += ' data-slug="'+plugin.slug+'"';
1233
- output += ' data-init="'+plugin.init+'">';
1234
- output += ' <span class="title">'+plugin.name+'</span>';
1235
- output += ' <button class="button disabled"';
1236
- output += ' data-slug="' + plugin.slug + '"';
1237
- output += ' data-name="' + plugin.name + '">';
1238
- output += astraSitesAdmin.strings.btnActive;
1239
- output += ' </button>';
1240
- // output += ' <span class="dashicons-yes dashicons"></span>';
1241
- output += '</div>';
1242
-
1243
- jQuery('.required-plugins').append(output);
1244
-
1245
- });
1246
- }
1247
-
1248
- /**
1249
- * Enable Demo Import Button
1250
- * @type number
1251
- */
1252
- astraSitesAdmin.requiredPlugins = response.data;
1253
- AstraSitesAdmin._enable_demo_import_button();
1254
-
1255
- });
1256
-
1257
- } else {
1258
-
1259
- // Enable Demo Import Button
1260
- AstraSitesAdmin._enable_demo_import_button( demoType );
1261
- jQuery('.required-plugins-wrap').remove();
1262
- }
1263
-
1264
- return;
1265
- },
1266
-
1267
- /**
1268
- * Enable Demo Import Button.
1269
- */
1270
- _enable_demo_import_button: function( type ) {
1271
-
1272
- type = ( undefined !== type ) ? type : 'free';
1273
-
1274
- switch( type ) {
1275
-
1276
- case 'free':
1277
- var all_buttons = parseInt( jQuery( '.plugin-card .button' ).length ) || 0,
1278
- disabled_buttons = parseInt( jQuery( '.plugin-card .button.disabled' ).length ) || 0;
1279
-
1280
- if( all_buttons === disabled_buttons ) {
1281
-
1282
- jQuery('.astra-demo-import')
1283
- .removeAttr('data-import')
1284
- .removeClass('installing updating-message')
1285
- .addClass('button-primary')
1286
- .text( astraSitesAdmin.strings.importDemo );
1287
- }
1288
-
1289
- break;
1290
-
1291
- case 'upgrade':
1292
- var demo_slug = jQuery('.wp-full-overlay-header').attr('data-demo-slug');
1293
-
1294
- jQuery('.astra-demo-import')
1295
- .addClass('go-pro button-primary')
1296
- .removeClass('astra-demo-import')
1297
- .attr('target', '_blank')
1298
- .attr('href', astraSitesAdmin.getUpgradeURL + demo_slug )
1299
- .text( astraSitesAdmin.getUpgradeText )
1300
- .append('<i class="dashicons dashicons-external"></i>');
1301
- break;
1302
-
1303
- default:
1304
- var demo_slug = jQuery('.wp-full-overlay-header').attr('data-demo-slug');
1305
-
1306
- jQuery('.astra-demo-import')
1307
- .addClass('go-pro button-primary')
1308
- .removeClass('astra-demo-import')
1309
- .attr('target', '_blank')
1310
- .attr('href', astraSitesAdmin.getProURL )
1311
- .text( astraSitesAdmin.getProText )
1312
- .append('<i class="dashicons dashicons-external"></i>');
1313
- break;
1314
- }
1315
-
1316
- },
1317
-
1318
- /**
1319
- * Update Page Count.
1320
- */
1321
- _updatedPagedCount: function() {
1322
- paged = parseInt(jQuery('body').attr('data-astra-demo-paged'));
1323
- jQuery('body').attr('data-astra-demo-paged', paged + 1);
1324
- window.setTimeout(function () {
1325
- jQuery('body').data('scrolling', false);
1326
- }, 800);
1327
- },
1328
-
1329
- /**
1330
- * Reset Page Count.
1331
- */
1332
- _resetPagedCount: function() {
1333
-
1334
- $('body').addClass('loading-content');
1335
- $('body').attr('data-astra-demo-last-request', '1');
1336
- $('body').attr('data-astra-demo-paged', '1');
1337
- $('body').attr('data-astra-demo-search', '');
1338
- $('body').attr('data-scrolling', false);
1339
-
1340
- },
1341
-
1342
- /**
1343
- * Remove plugin from the queue.
1344
- */
1345
- _removePluginFromQueue: function( removeItem, pluginsList ) {
1346
- return jQuery.grep(pluginsList, function( value ) {
1347
- return value.slug != removeItem;
1348
- });
1349
- }
1350
-
1351
- };
1352
-
1353
- /**
1354
- * Initialize AstraSitesAdmin
1355
- */
1356
- $(function(){
1357
- AstraSitesAdmin.init();
1358
- });
1359
-
1360
ย  })(jQuery);
1
+ /**
2
+ * AJAX Request Queue
3
+ *
4
+ * - add()
5
+ * - remove()
6
+ * - run()
7
+ * - stop()
8
+ *
9
+ * @since 1.0.0
10
+ */
11
+ var AstraSitesAjaxQueue = (function() {
12
+
13
+ var requests = [];
14
+
15
+ return {
16
+
17
+ /**
18
+ * Add AJAX request
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ add: function(opt) {
23
+ requests.push(opt);
24
+ },
25
+
26
+ /**
27
+ * Remove AJAX request
28
+ *
29
+ * @since 1.0.0
30
+ */
31
+ remove: function(opt) {
32
+ if( jQuery.inArray(opt, requests) > -1 )
33
+ requests.splice($.inArray(opt, requests), 1);
34
+ },
35
+
36
+ /**
37
+ * Run / Process AJAX request
38
+ *
39
+ * @since 1.0.0
40
+ */
41
+ run: function() {
42
+ var self = this,
43
+ oriSuc;
44
+
45
+ if( requests.length ) {
46
+ oriSuc = requests[0].complete;
47
+
48
+ requests[0].complete = function() {
49
+ if( typeof(oriSuc) === 'function' ) oriSuc();
50
+ requests.shift();
51
+ self.run.apply(self, []);
52
+ };
53
+
54
+ jQuery.ajax(requests[0]);
55
+
56
+ } else {
57
+
58
+ self.tid = setTimeout(function() {
59
+ self.run.apply(self, []);
60
+ }, 1000);
61
+ }
62
+ },
63
+
64
+ /**
65
+ * Stop AJAX request
66
+ *
67
+ * @since 1.0.0
68
+ */
69
+ stop: function() {
70
+
71
+ requests = [];
72
+ clearTimeout(this.tid);
73
+ }
74
+ };
75
+
76
+ }());
77
+
78
+ (function($){
79
+
80
+ var AstraSSEImport = {
81
+ complete: {
82
+ posts: 0,
83
+ media: 0,
84
+ users: 0,
85
+ comments: 0,
86
+ terms: 0,
87
+ },
88
+
89
+ updateDelta: function (type, delta) {
90
+ this.complete[ type ] += delta;
91
+
92
+ var self = this;
93
+ requestAnimationFrame(function () {
94
+ self.render();
95
+ });
96
+ },
97
+ updateProgress: function ( type, complete, total ) {
98
+ var text = complete + '/' + total;
99
+
100
+ if( 'undefined' !== type && 'undefined' !== text ) {
101
+ total = parseInt( total, 10 );
102
+ if ( 0 === total || isNaN( total ) ) {
103
+ total = 1;
104
+ }
105
+ var percent = parseInt( complete, 10 ) / total;
106
+ var progress = Math.round( percent * 100 ) + '%';
107
+ var progress_bar = percent * 100;
108
+ }
109
+ },
110
+ render: function () {
111
+ var types = Object.keys( this.complete );
112
+ var complete = 0;
113
+ var total = 0;
114
+
115
+ for (var i = types.length - 1; i >= 0; i--) {
116
+ var type = types[i];
117
+ this.updateProgress( type, this.complete[ type ], this.data.count[ type ] );
118
+
119
+ complete += this.complete[ type ];
120
+ total += this.data.count[ type ];
121
+ }
122
+
123
+ this.updateProgress( 'total', complete, total );
124
+ }
125
+ };
126
+
127
+ AstraSitesAdmin = {
128
+
129
+ log_file : '',
130
+ customizer_data : '',
131
+ wxr_url : '',
132
+ options_data : '',
133
+ widgets_data : '',
134
+
135
+ init: function()
136
+ {
137
+ this._resetPagedCount();
138
+ this._bind();
139
+ },
140
+
141
+ /**
142
+ * Debugging.
143
+ *
144
+ * @param {mixed} data Mixed data.
145
+ */
146
+ _log: function( data ) {
147
+
148
+ if( astraSitesAdmin.debug ) {
149
+
150
+ var date = new Date();
151
+ var time = date.toLocaleTimeString();
152
+
153
+ if (typeof data == 'object') {
154
+ console.log('%c ' + JSON.stringify( data ) + ' ' + time, 'background: #ededed; color: #444');
155
+ } else {
156
+ console.log('%c ' + data + ' ' + time, 'background: #ededed; color: #444');
157
+ }
158
+
159
+
160
+ }
161
+ },
162
+
163
+ /**
164
+ * Binds events for the Astra Sites.
165
+ *
166
+ * @since 1.0.0
167
+ * @access private
168
+ * @method _bind
169
+ */
170
+ _bind: function()
171
+ {
172
+ $( document ).on('click' , '.devices button', AstraSitesAdmin._previewDevice);
173
+ $( document ).on('click' , '.theme-browser .theme-screenshot, .theme-browser .more-details, .theme-browser .install-theme-preview', AstraSitesAdmin._preview);
174
+ $( document ).on('click' , '.next-theme', AstraSitesAdmin._nextTheme);
175
+ $( document ).on('click' , '.previous-theme', AstraSitesAdmin._previousTheme);
176
+ $( document ).on('click' , '.collapse-sidebar', AstraSitesAdmin._collapse);
177
+ $( document ).on('click' , '.astra-demo-import', AstraSitesAdmin._importDemo);
178
+ $( document ).on('click' , '.install-now', AstraSitesAdmin._installNow);
179
+ $( document ).on('click' , '.close-full-overlay', AstraSitesAdmin._fullOverlay);
180
+ $( document ).on('click' , '.activate-now', AstraSitesAdmin._activateNow);
181
+ $( document ).on('wp-plugin-installing' , AstraSitesAdmin._pluginInstalling);
182
+ $( document ).on('wp-plugin-install-error' , AstraSitesAdmin._installError);
183
+ $( document ).on('wp-plugin-install-success' , AstraSitesAdmin._installSuccess);
184
+
185
+ $( document ).on('astra-sites-import-set-site-data-done' , AstraSitesAdmin._importCustomizerSettings );
186
+ $( document ).on('astra-sites-import-customizer-settings-done' , AstraSitesAdmin._importPrepareXML );
187
+ $( document ).on('astra-sites-import-xml-done' , AstraSitesAdmin._importSiteOptions );
188
+ $( document ).on('astra-sites-import-options-done' , AstraSitesAdmin._importWidgets );
189
+ $( document ).on('astra-sites-import-widgets-done' , AstraSitesAdmin._importEnd );
190
+ },
191
+
192
+ /**
193
+ * 5. Import Complete.
194
+ */
195
+ _importEnd: function( event ) {
196
+
197
+ $.ajax({
198
+ url : astraSitesAdmin.ajaxurl,
199
+ type : 'POST',
200
+ dataType: 'json',
201
+ data : {
202
+ action : 'astra-sites-import-end',
203
+ },
204
+ beforeSend: function() {
205
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importComplete );
206
+ }
207
+ })
208
+ .fail(function( jqXHR ){
209
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
210
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
211
+ })
212
+ .done(function ( data ) {
213
+
214
+ // 5. Fail - Import Complete.
215
+ if( false === data.success ) {
216
+ AstraSitesAdmin._importFailMessage( data.data );
217
+ AstraSitesAdmin._log( data.data );
218
+ } else {
219
+
220
+ // 5. Pass - Import Complete.
221
+ AstraSitesAdmin._importSuccessMessage();
222
+ AstraSitesAdmin._log( astraSitesAdmin.log.success + ' ' + astraSitesAdmin.siteURL );
223
+ }
224
+ });
225
+ },
226
+
227
+ /**
228
+ * 4. Import Widgets.
229
+ */
230
+ _importWidgets: function( event ) {
231
+
232
+ $.ajax({
233
+ url : astraSitesAdmin.ajaxurl,
234
+ type : 'POST',
235
+ dataType: 'json',
236
+ data : {
237
+ action : 'astra-sites-import-widgets',
238
+ widgets_data : AstraSitesAdmin.widgets_data,
239
+ },
240
+ beforeSend: function() {
241
+ AstraSitesAdmin._log( astraSitesAdmin.log.importWidgets );
242
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingWidgets );
243
+ },
244
+ })
245
+ .fail(function( jqXHR ){
246
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
247
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
248
+ })
249
+ .done(function ( widgets_data ) {
250
+
251
+ // 4. Fail - Import Widgets.
252
+ if( false === widgets_data.success ) {
253
+ AstraSitesAdmin._importFailMessage( widgets_data.data );
254
+ AstraSitesAdmin._log( widgets_data.data );
255
+
256
+ } else {
257
+
258
+ // 4. Pass - Import Widgets.
259
+ AstraSitesAdmin._log( astraSitesAdmin.log.importWidgetsSuccess );
260
+ $(document).trigger( 'astra-sites-import-widgets-done' );
261
+ }
262
+ });
263
+ },
264
+
265
+ /**
266
+ * 3. Import Site Options.
267
+ */
268
+ _importSiteOptions: function( event ) {
269
+
270
+ $.ajax({
271
+ url : astraSitesAdmin.ajaxurl,
272
+ type : 'POST',
273
+ dataType: 'json',
274
+ data : {
275
+ action : 'astra-sites-import-options',
276
+ options_data : AstraSitesAdmin.options_data,
277
+ },
278
+ beforeSend: function() {
279
+ AstraSitesAdmin._log( astraSitesAdmin.log.importOptions );
280
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingOptions );
281
+ },
282
+ })
283
+ .fail(function( jqXHR ){
284
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
285
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
286
+ })
287
+ .done(function ( options_data ) {
288
+
289
+ // 3. Fail - Import Site Options.
290
+ if( false === options_data.success ) {
291
+ AstraSitesAdmin._log( options_data );
292
+ AstraSitesAdmin._importFailMessage( options_data.data );
293
+ AstraSitesAdmin._log( options_data.data );
294
+
295
+ } else {
296
+
297
+ // 3. Pass - Import Site Options.
298
+ AstraSitesAdmin._log( astraSitesAdmin.log.importOptionsSuccess );
299
+ $(document).trigger( 'astra-sites-import-options-done' );
300
+ }
301
+ });
302
+ },
303
+
304
+ /**
305
+ * 2. Prepare XML Data.
306
+ */
307
+ _importPrepareXML: function( event ) {
308
+
309
+ $.ajax({
310
+ url : astraSitesAdmin.ajaxurl,
311
+ type : 'POST',
312
+ dataType: 'json',
313
+ data : {
314
+ action : 'astra-sites-import-prepare-xml',
315
+ wxr_url : AstraSitesAdmin.wxr_url,
316
+ },
317
+ beforeSend: function() {
318
+ AstraSitesAdmin._log( astraSitesAdmin.log.importXMLPrepare );
319
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importXMLPreparing );
320
+ },
321
+ })
322
+ .fail(function( jqXHR ){
323
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
324
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
325
+ })
326
+ .done(function ( xml_data ) {
327
+
328
+ // 2. Fail - Prepare XML Data.
329
+ if( false === xml_data.success ) {
330
+ AstraSitesAdmin._log( xml_data );
331
+ AstraSitesAdmin._importFailMessage( xml_data.data );
332
+ AstraSitesAdmin._log( xml_data.data );
333
+
334
+ } else {
335
+
336
+ // 2. Pass - Prepare XML Data.
337
+ AstraSitesAdmin._log( astraSitesAdmin.log.importXMLPrepareSuccess );
338
+
339
+ // Import XML though Event Source.
340
+ AstraSSEImport.data = xml_data.data;
341
+ AstraSSEImport.render();
342
+
343
+ AstraSitesAdmin._log( astraSitesAdmin.log.importXML );
344
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingXML );
345
+
346
+ var evtSource = new EventSource( AstraSSEImport.data.url );
347
+ evtSource.onmessage = function ( message ) {
348
+ var data = JSON.parse( message.data );
349
+ switch ( data.action ) {
350
+ case 'updateDelta':
351
+ AstraSSEImport.updateDelta( data.type, data.delta );
352
+ break;
353
+
354
+ case 'complete':
355
+ evtSource.close();
356
+
357
+ // 2. Pass - Import XML though "Source Event".
358
+ AstraSitesAdmin._log( astraSitesAdmin.log.importXMLSuccess );
359
+ AstraSitesAdmin._log( '----- SSE - XML import Complete -----' );
360
+
361
+ $(document).trigger( 'astra-sites-import-xml-done' );
362
+
363
+ break;
364
+ }
365
+ };
366
+ evtSource.addEventListener( 'log', function ( message ) {
367
+ var data = JSON.parse( message.data );
368
+ AstraSitesAdmin._log( data.level + ' ' + data.message );
369
+ });
370
+ }
371
+ });
372
+ },
373
+
374
+ /**
375
+ * 1. Import Customizer Options.
376
+ */
377
+ _importCustomizerSettings: function( event ) {
378
+
379
+ $.ajax({
380
+ url : astraSitesAdmin.ajaxurl,
381
+ type : 'POST',
382
+ dataType: 'json',
383
+ data : {
384
+ action : 'astra-sites-import-customizer-settings',
385
+ customizer_data : AstraSitesAdmin.customizer_data,
386
+ },
387
+ beforeSend: function() {
388
+ AstraSitesAdmin._log( astraSitesAdmin.log.importCustomizer );
389
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.importingCustomizer );
390
+ },
391
+ })
392
+ .fail(function( jqXHR ){
393
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
394
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
395
+ })
396
+ .done(function ( customizer_data ) {
397
+
398
+ // 1. Fail - Import Customizer Options.
399
+ if( false === customizer_data.success ) {
400
+ AstraSitesAdmin._importFailMessage( customizer_data.data );
401
+ AstraSitesAdmin._log( customizer_data.data );
402
+ } else {
403
+
404
+ // 1. Pass - Import Customizer Options.
405
+ AstraSitesAdmin._log( astraSitesAdmin.log.importCustomizerSuccess );
406
+
407
+ $(document).trigger( 'astra-sites-import-customizer-settings-done' );
408
+ }
409
+ });
410
+ },
411
+
412
+ /**
413
+ * Import Success Button.
414
+ *
415
+ * @param {string} data Error message.
416
+ */
417
+ _importSuccessMessage: function() {
418
+
419
+ $('.astra-demo-import').removeClass('updating-message installing')
420
+ .removeAttr('data-import')
421
+ .addClass('view-site')
422
+ .removeClass('astra-demo-import')
423
+ .text( astraSitesAdmin.strings.viewSite )
424
+ .attr('target', '_blank')
425
+ .append('<i class="dashicons dashicons-external"></i>')
426
+ .attr('href', astraSitesAdmin.siteURL );
427
+ },
428
+
429
+ /**
430
+ * Preview Device
431
+ */
432
+ _previewDevice: function( event ) {
433
+ var device = $( event.currentTarget ).data( 'device' );
434
+
435
+ $('.theme-install-overlay')
436
+ .removeClass( 'preview-desktop preview-tablet preview-mobile' )
437
+ .addClass( 'preview-' + device )
438
+ .data( 'current-preview-device', device );
439
+
440
+ AstraSitesAdmin._tooglePreviewDeviceButtons( device );
441
+ },
442
+
443
+ /**
444
+ * Toggle Preview Buttons
445
+ */
446
+ _tooglePreviewDeviceButtons: function( newDevice ) {
447
+ var $devices = $( '.wp-full-overlay-footer .devices' );
448
+
449
+ $devices.find( 'button' )
450
+ .removeClass( 'active' )
451
+ .attr( 'aria-pressed', false );
452
+
453
+ $devices.find( 'button.preview-' + newDevice )
454
+ .addClass( 'active' )
455
+ .attr( 'aria-pressed', true );
456
+ },
457
+
458
+ /**
459
+ * Import Error Button.
460
+ *
461
+ * @param {string} data Error message.
462
+ */
463
+ _importFailMessage: function( message, from ) {
464
+
465
+ $('.astra-demo-import')
466
+ .addClass('go-pro button-primary')
467
+ .removeClass('updating-message installing')
468
+ .removeAttr('data-import')
469
+ .attr('target', '_blank')
470
+ .append('<i class="dashicons dashicons-external"></i>')
471
+ .removeClass('astra-demo-import');
472
+
473
+ // Add the doc link due to import log file not generated.
474
+ if( 'undefined' === from ) {
475
+
476
+ $('.wp-full-overlay-header .go-pro').text( astraSitesAdmin.strings.importFailedBtnSmall );
477
+ $('.wp-full-overlay-footer .go-pro').text( astraSitesAdmin.strings.importFailedBtnLarge );
478
+ $('.go-pro').attr('href', astraSitesAdmin.log.serverConfiguration );
479
+
480
+ // Add the import log file link.
481
+ } else {
482
+
483
+ $('.wp-full-overlay-header .go-pro').text( astraSitesAdmin.strings.importFailBtn );
484
+ $('.wp-full-overlay-footer .go-pro').text( astraSitesAdmin.strings.importFailBtnLarge )
485
+
486
+ // Add the import log file link.
487
+ if( 'undefined' !== AstraSitesAdmin.log_file_url ) {
488
+ $('.go-pro').attr('href', AstraSitesAdmin.log_file_url );
489
+ } else {
490
+ $('.go-pro').attr('href', astraSitesAdmin.log.serverConfiguration );
491
+ }
492
+ }
493
+
494
+ var output = '<div class="astra-api-error notice notice-error notice-alt is-dismissible">';
495
+ output += ' <p>'+message+'</p>';
496
+ output += ' <button type="button" class="notice-dismiss">';
497
+ output += ' <span class="screen-reader-text">'+commonL10n.dismiss+'</span>';
498
+ output += ' </button>';
499
+ output += '</div>';
500
+
501
+ // Fail Notice.
502
+ $('.install-theme-info').append( output );
503
+
504
+
505
+ // !important to add trigger.
506
+ // Which reinitialize the dismiss error message events.
507
+ $(document).trigger('wp-updates-notice-added');
508
+ },
509
+
510
+
511
+ /**
512
+ * Install Now
513
+ */
514
+ _installNow: function(event)
515
+ {
516
+ event.preventDefault();
517
+
518
+ var $button = jQuery( event.target ),
519
+ $document = jQuery(document);
520
+
521
+ if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
522
+ return;
523
+ }
524
+
525
+ if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
526
+ wp.updates.requestFilesystemCredentials( event );
527
+
528
+ $document.on( 'credential-modal-cancel', function() {
529
+ var $message = $( '.install-now.updating-message' );
530
+
531
+ $message
532
+ .removeClass( 'updating-message' )
533
+ .text( wp.updates.l10n.installNow );
534
+
535
+ wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
536
+ } );
537
+ }
538
+
539
+ AstraSitesAdmin._log( astraSitesAdmin.log.installingPlugin + ' ' + $button.data( 'slug' ) );
540
+
541
+ wp.updates.installPlugin( {
542
+ slug: $button.data( 'slug' )
543
+ } );
544
+ },
545
+
546
+ /**
547
+ * Install Success
548
+ */
549
+ _installSuccess: function( event, response ) {
550
+
551
+ event.preventDefault();
552
+
553
+ AstraSitesAdmin._log( astraSitesAdmin.log.installed + ' ' + response.slug );
554
+
555
+ var $message = jQuery( '.plugin-card-' + response.slug ).find( '.button' );
556
+ var $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val();
557
+ var $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
558
+
559
+ // Transform the 'Install' button into an 'Activate' button.
560
+ var $init = $message.data('init');
561
+
562
+ $message.removeClass( 'install-now installed button-disabled updated-message' )
563
+ .addClass('updating-message')
564
+ .html( astraSitesAdmin.strings.btnActivating );
565
+
566
+ // Reset not installed plugins list.
567
+ var pluginsList = astraSitesAdmin.requiredPlugins.notinstalled;
568
+ astraSitesAdmin.requiredPlugins.notinstalled = AstraSitesAdmin._removePluginFromQueue( response.slug, pluginsList );
569
+
570
+ // WordPress adds "Activate" button after waiting for 1000ms. So we will run our activation after that.
571
+ setTimeout( function() {
572
+
573
+ $.ajax({
574
+ url: astraSitesAdmin.ajaxurl,
575
+ type: 'POST',
576
+ data: {
577
+ 'action' : 'astra-required-plugin-activate',
578
+ 'init' : $init,
579
+ 'options' : $siteOptions,
580
+ 'enabledExtensions' : $enabledExtensions,
581
+ },
582
+ })
583
+ .done(function (result) {
584
+
585
+ if( result.success ) {
586
+
587
+ var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
588
+
589
+ // Reset not installed plugins list.
590
+ astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( response.slug, pluginsList );
591
+
592
+ $message.removeClass( 'button-primary install-now activate-now updating-message' )
593
+ .attr('disabled', 'disabled')
594
+ .addClass('disabled')
595
+ .text( astraSitesAdmin.strings.btnActive );
596
+
597
+ // Enable Demo Import Button
598
+ AstraSitesAdmin._enable_demo_import_button();
599
+
600
+ } else {
601
+
602
+ $message.removeClass( 'updating-message' );
603
+
604
+ }
605
+
606
+ });
607
+
608
+ }, 1200 );
609
+
610
+ },
611
+
612
+ /**
613
+ * Plugin Installation Error.
614
+ */
615
+ _installError: function( event, response ) {
616
+
617
+ var $card = jQuery( '.plugin-card-' + response.slug );
618
+
619
+ AstraSitesAdmin._log( response.errorMessage + ' ' + response.slug );
620
+
621
+ $card
622
+ .removeClass( 'button-primary' )
623
+ .addClass( 'disabled' )
624
+ .html( wp.updates.l10n.installFailedShort );
625
+
626
+ AstraSitesAdmin._importFailMessage( response.errorMessage );
627
+ },
628
+
629
+ /**
630
+ * Installing Plugin
631
+ */
632
+ _pluginInstalling: function(event, args) {
633
+ event.preventDefault();
634
+
635
+ var $card = jQuery( '.plugin-card-' + args.slug );
636
+ var $button = $card.find( '.button' );
637
+
638
+ AstraSitesAdmin._log( astraSitesAdmin.log.installingPlugin + ' ' + args.slug );
639
+
640
+ $card.addClass('updating-message');
641
+ $button.addClass('already-started');
642
+
643
+ },
644
+
645
+ /**
646
+ * Render Demo Preview
647
+ */
648
+ _activateNow: function( eventn ) {
649
+
650
+ event.preventDefault();
651
+
652
+ var $button = jQuery( event.target ),
653
+ $init = $button.data( 'init' ),
654
+ $slug = $button.data( 'slug' );
655
+
656
+ if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
657
+ return;
658
+ }
659
+
660
+ AstraSitesAdmin._log( astraSitesAdmin.log.activating + ' ' + $slug );
661
+
662
+ $button.addClass('updating-message button-primary')
663
+ .html( astraSitesAdmin.strings.btnActivating );
664
+
665
+ var $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val();
666
+ var $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
667
+
668
+ $.ajax({
669
+ url: astraSitesAdmin.ajaxurl,
670
+ type: 'POST',
671
+ data: {
672
+ 'action' : 'astra-required-plugin-activate',
673
+ 'init' : $init,
674
+ 'options' : $siteOptions,
675
+ 'enabledExtensions' : $enabledExtensions,
676
+ },
677
+ })
678
+ .done(function (result) {
679
+
680
+ if( result.success ) {
681
+
682
+ AstraSitesAdmin._log( astraSitesAdmin.log.activated + ' ' + $slug );
683
+
684
+ var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
685
+
686
+ // Reset not installed plugins list.
687
+ astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( $slug, pluginsList );
688
+
689
+ $button.removeClass( 'button-primary install-now activate-now updating-message' )
690
+ .attr('disabled', 'disabled')
691
+ .addClass('disabled')
692
+ .text( astraSitesAdmin.strings.btnActive );
693
+
694
+ // Enable Demo Import Button
695
+ AstraSitesAdmin._enable_demo_import_button();
696
+
697
+ }
698
+
699
+ })
700
+ .fail(function () {
701
+ });
702
+
703
+ },
704
+
705
+ /**
706
+ * Full Overlay
707
+ */
708
+ _fullOverlay: function (event) {
709
+ event.preventDefault();
710
+
711
+ jQuery('.theme-install-overlay').css('display', 'none');
712
+ jQuery('.theme-install-overlay').remove();
713
+ jQuery('.theme-preview-on').removeClass('theme-preview-on');
714
+ jQuery('html').removeClass('astra-site-preview-on');
715
+ },
716
+
717
+ /**
718
+ * Bulk Plugin Active & Install
719
+ */
720
+ _bulkPluginInstallActivate: function()
721
+ {
722
+ if( 0 === astraSitesAdmin.requiredPlugins.length ) {
723
+ return;
724
+ }
725
+
726
+ jQuery('.required-plugins')
727
+ .find('.install-now')
728
+ .addClass( 'updating-message' )
729
+ .removeClass( 'install-now' )
730
+ .text( wp.updates.l10n.installing );
731
+
732
+ jQuery('.required-plugins')
733
+ .find('.activate-now')
734
+ .addClass('updating-message')
735
+ .removeClass( 'activate-now' )
736
+ .html( astraSitesAdmin.strings.btnActivating );
737
+
738
+ var not_installed = astraSitesAdmin.requiredPlugins.notinstalled || '';
739
+ var activate_plugins = astraSitesAdmin.requiredPlugins.inactive || '';
740
+
741
+ // First Install Bulk.
742
+ if( not_installed.length > 0 ) {
743
+ AstraSitesAdmin._installAllPlugins( not_installed );
744
+ }
745
+
746
+ // Second Activate Bulk.
747
+ if( activate_plugins.length > 0 ) {
748
+ AstraSitesAdmin._activateAllPlugins( activate_plugins );
749
+ }
750
+
751
+ },
752
+
753
+ /**
754
+ * Activate All Plugins.
755
+ */
756
+ _activateAllPlugins: function( activate_plugins ) {
757
+
758
+ // Activate ALl Plugins.
759
+ AstraSitesAjaxQueue.stop();
760
+ AstraSitesAjaxQueue.run();
761
+
762
+ AstraSitesAdmin._log( astraSitesAdmin.log.bulkActivation );
763
+
764
+ $.each( activate_plugins, function(index, single_plugin) {
765
+
766
+ var $card = jQuery( '.plugin-card-' + single_plugin.slug ),
767
+ $button = $card.find('.button'),
768
+ $siteOptions = jQuery( '.wp-full-overlay-header').find('.astra-site-options').val(),
769
+ $enabledExtensions = jQuery( '.wp-full-overlay-header').find('.astra-enabled-extensions').val();
770
+
771
+ $button.addClass('updating-message');
772
+
773
+ AstraSitesAjaxQueue.add({
774
+ url: astraSitesAdmin.ajaxurl,
775
+ type: 'POST',
776
+ data: {
777
+ 'action' : 'astra-required-plugin-activate',
778
+ 'init' : single_plugin.init,
779
+ 'options' : $siteOptions,
780
+ 'enabledExtensions' : $enabledExtensions,
781
+ },
782
+ success: function( result ){
783
+
784
+ if( result.success ) {
785
+
786
+ AstraSitesAdmin._log( astraSitesAdmin.log.activate + ' ' + single_plugin.slug );
787
+
788
+ var $card = jQuery( '.plugin-card-' + single_plugin.slug );
789
+ var $button = $card.find( '.button' );
790
+ if( ! $button.hasClass('already-started') ) {
791
+ var pluginsList = astraSitesAdmin.requiredPlugins.inactive;
792
+
793
+ // Reset not installed plugins list.
794
+ astraSitesAdmin.requiredPlugins.inactive = AstraSitesAdmin._removePluginFromQueue( single_plugin.slug, pluginsList );
795
+ }
796
+
797
+ $button.removeClass( 'button-primary install-now activate-now updating-message' )
798
+ .attr('disabled', 'disabled')
799
+ .addClass('disabled')
800
+ .text( astraSitesAdmin.strings.btnActive );
801
+
802
+ // Enable Demo Import Button
803
+ AstraSitesAdmin._enable_demo_import_button();
804
+ } else {
805
+ AstraSitesAdmin._log( astraSitesAdmin.log.activationError + ' - ' + single_plugin.slug );
806
+ }
807
+ }
808
+ });
809
+ });
810
+ },
811
+
812
+ /**
813
+ * Install All Plugins.
814
+ */
815
+ _installAllPlugins: function( not_installed ) {
816
+
817
+ AstraSitesAdmin._log( astraSitesAdmin.log.bulkInstall );
818
+
819
+ $.each( not_installed, function(index, single_plugin) {
820
+
821
+ var $card = jQuery( '.plugin-card-' + single_plugin.slug ),
822
+ $button = $card.find('.button');
823
+
824
+ if( ! $button.hasClass('already-started') ) {
825
+
826
+ // Add each plugin activate request in Ajax queue.
827
+ // @see wp-admin/js/updates.js
828
+ wp.updates.queue.push( {
829
+ action: 'install-plugin', // Required action.
830
+ data: {
831
+ slug: single_plugin.slug
832
+ }
833
+ } );
834
+ }
835
+ });
836
+
837
+ // Required to set queue.
838
+ wp.updates.queueChecker();
839
+ },
840
+
841
+ /**
842
+ * Fires when a nav item is clicked.
843
+ *
844
+ * @since 1.0
845
+ * @access private
846
+ * @method _importDemo
847
+ */
848
+ _importDemo: function()
849
+ {
850
+ var $this = jQuery(this),
851
+ $theme = $this.closest('.astra-sites-preview').find('.wp-full-overlay-header'),
852
+ apiURL = $theme.data('demo-api') || '',
853
+ plugins = $theme.data('required-plugins');
854
+
855
+ var disabled = $this.attr('data-import');
856
+
857
+ if ( typeof disabled !== 'undefined' && disabled === 'disabled' || $this.hasClass('disabled') ) {
858
+
859
+ $('.astra-demo-import').addClass('updating-message installing')
860
+ .text( wp.updates.l10n.installing );
861
+
862
+ /**
863
+ * Process Bulk Plugin Install & Activate
864
+ */
865
+ AstraSitesAdmin._bulkPluginInstallActivate();
866
+
867
+ return;
868
+ }
869
+
870
+ // Proceed?
871
+ if( ! confirm( astraSitesAdmin.strings.importWarning ) ) {
872
+ return;
873
+ }
874
+
875
+ // Remove all notices before import start.
876
+ $('.install-theme-info > .notice').remove();
877
+
878
+ $('.astra-demo-import').attr('data-import', 'disabled')
879
+ .addClass('updating-message installing')
880
+ .text( astraSitesAdmin.strings.importingDemo );
881
+
882
+ $this.closest('.theme').focus();
883
+
884
+ var $theme = $this.closest('.astra-sites-preview').find('.wp-full-overlay-header');
885
+
886
+ var apiURL = $theme.data('demo-api') || '';
887
+
888
+ // Site Import by API URL.
889
+ if( apiURL ) {
890
+ AstraSitesAdmin._importSite( apiURL );
891
+ }
892
+
893
+ },
894
+
895
+ /**
896
+ * Start Import Process by API URL.
897
+ *
898
+ * @param {string} apiURL Site API URL.
899
+ */
900
+ _importSite: function( apiURL ) {
901
+
902
+ AstraSitesAdmin._log( astraSitesAdmin.log.api + ' : ' + apiURL );
903
+ AstraSitesAdmin._log( astraSitesAdmin.log.importing );
904
+
905
+ $('.button-hero.astra-demo-import').text( astraSitesAdmin.log.gettingData );
906
+
907
+ // 1. Request Site Import
908
+ $.ajax({
909
+ url : astraSitesAdmin.ajaxurl,
910
+ type : 'POST',
911
+ dataType: 'json',
912
+ data : {
913
+ 'action' : 'astra-sites-import-set-site-data',
914
+ 'api_url' : apiURL,
915
+ },
916
+ })
917
+ .fail(function( jqXHR ){
918
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText );
919
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
920
+ })
921
+ .done(function ( demo_data ) {
922
+
923
+ // 1. Fail - Request Site Import
924
+ if( false === demo_data.success ) {
925
+
926
+ AstraSitesAdmin._importFailMessage( demo_data.data );
927
+
928
+ } else {
929
+
930
+ // Set log file URL.
931
+ if( 'log_file' in demo_data.data ){
932
+ AstraSitesAdmin.log_file_url = decodeURIComponent( demo_data.data.log_file ) || '';
933
+ }
934
+
935
+ // 1. Pass - Request Site Import
936
+ AstraSitesAdmin._log( astraSitesAdmin.log.processingRequest );
937
+
938
+ AstraSitesAdmin.customizer_data = JSON.stringify( demo_data.data['astra-site-customizer-data'] ) || '';
939
+ AstraSitesAdmin.wxr_url = encodeURI( demo_data.data['astra-site-wxr-path'] ) || '';
940
+ AstraSitesAdmin.options_data = JSON.stringify( demo_data.data['astra-site-options-data'] ) || '';
941
+ AstraSitesAdmin.widgets_data = JSON.stringify( demo_data.data['astra-site-widgets-data'] ) || '';
942
+
943
+ $(document).trigger( 'astra-sites-import-set-site-data-done' );
944
+ }
945
+
946
+ });
947
+
948
+ },
949
+
950
+ /**
951
+ * Collapse Sidebar.
952
+ */
953
+ _collapse: function() {
954
+ event.preventDefault();
955
+
956
+ overlay = jQuery('.wp-full-overlay');
957
+
958
+ if (overlay.hasClass('expanded')) {
959
+ overlay.removeClass('expanded');
960
+ overlay.addClass('collapsed');
961
+ return;
962
+ }
963
+
964
+ if (overlay.hasClass('collapsed')) {
965
+ overlay.removeClass('collapsed');
966
+ overlay.addClass('expanded');
967
+ return;
968
+ }
969
+ },
970
+
971
+ /**
972
+ * Previous Theme.
973
+ */
974
+ _previousTheme: function (event) {
975
+ event.preventDefault();
976
+
977
+ currentDemo = jQuery('.theme-preview-on');
978
+ currentDemo.removeClass('theme-preview-on');
979
+ prevDemo = currentDemo.prev('.theme');
980
+ prevDemo.addClass('theme-preview-on');
981
+
982
+ AstraSitesAdmin._renderDemoPreview(prevDemo);
983
+ },
984
+
985
+ /**
986
+ * Next Theme.
987
+ */
988
+ _nextTheme: function (event) {
989
+ event.preventDefault();
990
+ currentDemo = jQuery('.theme-preview-on')
991
+ currentDemo.removeClass('theme-preview-on');
992
+ nextDemo = currentDemo.next('.theme');
993
+ nextDemo.addClass('theme-preview-on');
994
+
995
+ AstraSitesAdmin._renderDemoPreview( nextDemo );
996
+ },
997
+
998
+ /**
999
+ * Individual Site Preview
1000
+ *
1001
+ * On click on image, more link & preview button.
1002
+ */
1003
+ _preview: function( event ) {
1004
+
1005
+ event.preventDefault();
1006
+
1007
+ var self = jQuery(this).parents('.theme');
1008
+ self.addClass('theme-preview-on');
1009
+
1010
+ jQuery('html').addClass('astra-site-preview-on');
1011
+
1012
+ AstraSitesAdmin._renderDemoPreview( self );
1013
+ },
1014
+
1015
+ /**
1016
+ * Check Next Previous Buttons.
1017
+ */
1018
+ _checkNextPrevButtons: function() {
1019
+ currentDemo = jQuery('.theme-preview-on');
1020
+ nextDemo = currentDemo.nextAll('.theme').length;
1021
+ prevDemo = currentDemo.prevAll('.theme').length;
1022
+
1023
+ if (nextDemo == 0) {
1024
+ jQuery('.next-theme').addClass('disabled');
1025
+ } else if (nextDemo != 0) {
1026
+ jQuery('.next-theme').removeClass('disabled');
1027
+ }
1028
+
1029
+ if (prevDemo == 0) {
1030
+ jQuery('.previous-theme').addClass('disabled');
1031
+ } else if (prevDemo != 0) {
1032
+ jQuery('.previous-theme').removeClass('disabled');
1033
+ }
1034
+
1035
+ return;
1036
+ },
1037
+
1038
+ /**
1039
+ * Render Demo Preview
1040
+ */
1041
+ _renderDemoPreview: function(anchor) {
1042
+
1043
+ var demoId = anchor.data('id') || '',
1044
+ apiURL = anchor.data('demo-api') || '',
1045
+ demoType = anchor.data('demo-type') || '',
1046
+ demoURL = anchor.data('demo-url') || '',
1047
+ screenshot = anchor.data('screenshot') || '',
1048
+ demo_name = anchor.data('demo-name') || '',
1049
+ demo_slug = anchor.data('demo-slug') || '',
1050
+ content = anchor.data('content') || '',
1051
+ requiredPlugins = anchor.data('required-plugins') || '',
1052
+ astraSiteOptions = anchor.find('.astra-site-options').val() || '';
1053
+ astraEnabledExtensions = anchor.find('.astra-enabled-extensions').val() || '';
1054
+
1055
+ AstraSitesAdmin._log( astraSitesAdmin.log.preview + ' "' + demo_name + '" URL : ' + demoURL );
1056
+
1057
+ var template = wp.template('astra-site-preview');
1058
+
1059
+ templateData = [{
1060
+ id : demoId,
1061
+ astra_demo_type : demoType,
1062
+ astra_demo_url : demoURL,
1063
+ demo_api : apiURL,
1064
+ screenshot : screenshot,
1065
+ demo_name : demo_name,
1066
+ slug : demo_slug,
1067
+ content : content,
1068
+ required_plugins : JSON.stringify(requiredPlugins),
1069
+ astra_site_options : astraSiteOptions,
1070
+ astra_enabled_extensions : astraEnabledExtensions,
1071
+ }];
1072
+
1073
+ // delete any earlier fullscreen preview before we render new one.
1074
+ jQuery('.theme-install-overlay').remove();
1075
+
1076
+ jQuery('#astra-sites-menu-page').append(template(templateData[0]));
1077
+ jQuery('.theme-install-overlay').css('display', 'block');
1078
+ AstraSitesAdmin._checkNextPrevButtons();
1079
+
1080
+ var desc = jQuery('.theme-details');
1081
+ var descHeight = parseInt( desc.outerHeight() );
1082
+ var descBtn = jQuery('.theme-details-read-more');
1083
+
1084
+ if( $.isArray( requiredPlugins ) ) {
1085
+
1086
+ if( descHeight >= 55 ) {
1087
+
1088
+ // Show button.
1089
+ descBtn.css( 'display', 'inline-block' );
1090
+
1091
+ // Set height upto 3 line.
1092
+ desc.css( 'height', 57 );
1093
+
1094
+ // Button Click.
1095
+ descBtn.click(function(event) {
1096
+
1097
+ if( descBtn.hasClass('open') ) {
1098
+ desc.animate({ height: 57 },
1099
+ 300, function() {
1100
+ descBtn.removeClass('open');
1101
+ descBtn.html( astraSitesAdmin.strings.DescExpand );
1102
+ });
1103
+ } else {
1104
+ desc.animate({ height: descHeight },
1105
+ 300, function() {
1106
+ descBtn.addClass('open');
1107
+ descBtn.html( astraSitesAdmin.strings.DescCollapse );
1108
+ });
1109
+ }
1110
+
1111
+ });
1112
+ }
1113
+
1114
+ // or
1115
+ var $pluginsFilter = jQuery( '#plugin-filter' ),
1116
+ data = {
1117
+ action : 'astra-required-plugins',
1118
+ _ajax_nonce : astraSitesAdmin._ajax_nonce,
1119
+ required_plugins : requiredPlugins
1120
+ };
1121
+
1122
+ // Add disabled class from import button.
1123
+ $('.astra-demo-import')
1124
+ .addClass('disabled not-click-able')
1125
+ .removeAttr('data-import');
1126
+
1127
+ jQuery('.required-plugins').addClass('loading').html('<span class="spinner is-active"></span>');
1128
+
1129
+ // Required Required.
1130
+ $.ajax({
1131
+ url : astraSitesAdmin.ajaxurl,
1132
+ type : 'POST',
1133
+ data : data,
1134
+ })
1135
+ .fail(function( jqXHR ){
1136
+
1137
+ // Remove loader.
1138
+ jQuery('.required-plugins').removeClass('loading').html('');
1139
+
1140
+ AstraSitesAdmin._importFailMessage( jqXHR.status + ' ' + jqXHR.responseText, 'plugins' );
1141
+ AstraSitesAdmin._log( jqXHR.status + ' ' + jqXHR.responseText );
1142
+ })
1143
+ .done(function ( response ) {
1144
+
1145
+ // Release disabled class from import button.
1146
+ $('.astra-demo-import')
1147
+ .removeClass('disabled not-click-able')
1148
+ .attr('data-import', 'disabled');
1149
+
1150
+ // Remove loader.
1151
+ jQuery('.required-plugins').removeClass('loading').html('');
1152
+
1153
+ /**
1154
+ * Count remaining plugins.
1155
+ * @type number
1156
+ */
1157
+ var remaining_plugins = 0;
1158
+
1159
+ /**
1160
+ * Not Installed
1161
+ *
1162
+ * List of not installed required plugins.
1163
+ */
1164
+ if ( typeof response.data.notinstalled !== 'undefined' ) {
1165
+
1166
+ // Add not have installed plugins count.
1167
+ remaining_plugins += parseInt( response.data.notinstalled.length );
1168
+
1169
+ jQuery( response.data.notinstalled ).each(function( index, plugin ) {
1170
+
1171
+ var output = '<div class="plugin-card ';
1172
+ output += ' plugin-card-'+plugin.slug+'"';
1173
+ output += ' data-slug="'+plugin.slug+'"';
1174
+ output += ' data-init="'+plugin.init+'">';
1175
+ output += ' <span class="title">'+plugin.name+'</span>';
1176
+ output += ' <button class="button install-now"';
1177
+ output += ' data-init="' + plugin.init + '"';
1178
+ output += ' data-slug="' + plugin.slug + '"';
1179
+ output += ' data-name="' + plugin.name + '">';
1180
+ output += wp.updates.l10n.installNow;
1181
+ output += ' </button>';
1182
+ // output += ' <span class="dashicons-no dashicons"></span>';
1183
+ output += '</div>';
1184
+
1185
+ jQuery('.required-plugins').append(output);
1186
+
1187
+ });
1188
+ }
1189
+
1190
+ /**
1191
+ * Inactive
1192
+ *
1193
+ * List of not inactive required plugins.
1194
+ */
1195
+ if ( typeof response.data.inactive !== 'undefined' ) {
1196
+
1197
+ // Add inactive plugins count.
1198
+ remaining_plugins += parseInt( response.data.inactive.length );
1199
+
1200
+ jQuery( response.data.inactive ).each(function( index, plugin ) {
1201
+
1202
+ var output = '<div class="plugin-card ';
1203
+ output += ' plugin-card-'+plugin.slug+'"';
1204
+ output += ' data-slug="'+plugin.slug+'"';
1205
+ output += ' data-init="'+plugin.init+'">';
1206
+ output += ' <span class="title">'+plugin.name+'</span>';
1207
+ output += ' <button class="button activate-now button-primary"';
1208
+ output += ' data-init="' + plugin.init + '"';
1209
+ output += ' data-slug="' + plugin.slug + '"';
1210
+ output += ' data-name="' + plugin.name + '">';
1211
+ output += wp.updates.l10n.activatePlugin;
1212
+ output += ' </button>';
1213
+ // output += ' <span class="dashicons-no dashicons"></span>';
1214
+ output += '</div>';
1215
+
1216
+ jQuery('.required-plugins').append(output);
1217
+
1218
+ });
1219
+ }
1220
+
1221
+ /**
1222
+ * Active
1223
+ *
1224
+ * List of not active required plugins.
1225
+ */
1226
+ if ( typeof response.data.active !== 'undefined' ) {
1227
+
1228
+ jQuery( response.data.active ).each(function( index, plugin ) {
1229
+
1230
+ var output = '<div class="plugin-card ';
1231
+ output += ' plugin-card-'+plugin.slug+'"';
1232
+ output += ' data-slug="'+plugin.slug+'"';
1233
+ output += ' data-init="'+plugin.init+'">';
1234
+ output += ' <span class="title">'+plugin.name+'</span>';
1235
+ output += ' <button class="button disabled"';
1236
+ output += ' data-slug="' + plugin.slug + '"';
1237
+ output += ' data-name="' + plugin.name + '">';
1238
+ output += astraSitesAdmin.strings.btnActive;
1239
+ output += ' </button>';
1240
+ // output += ' <span class="dashicons-yes dashicons"></span>';
1241
+ output += '</div>';
1242
+
1243
+ jQuery('.required-plugins').append(output);
1244
+
1245
+ });
1246
+ }
1247
+
1248
+ /**
1249
+ * Enable Demo Import Button
1250
+ * @type number
1251
+ */
1252
+ astraSitesAdmin.requiredPlugins = response.data;
1253
+ AstraSitesAdmin._enable_demo_import_button();
1254
+
1255
+ });
1256
+
1257
+ } else {
1258
+
1259
+ // Enable Demo Import Button
1260
+ AstraSitesAdmin._enable_demo_import_button( demoType );
1261
+ jQuery('.required-plugins-wrap').remove();
1262
+ }
1263
+
1264
+ return;
1265
+ },
1266
+
1267
+ /**
1268
+ * Enable Demo Import Button.
1269
+ */
1270
+ _enable_demo_import_button: function( type ) {
1271
+
1272
+ type = ( undefined !== type ) ? type : 'free';
1273
+
1274
+ switch( type ) {
1275
+
1276
+ case 'free':
1277
+ var all_buttons = parseInt( jQuery( '.plugin-card .button' ).length ) || 0,
1278
+ disabled_buttons = parseInt( jQuery( '.plugin-card .button.disabled' ).length ) || 0;
1279
+
1280
+ if( all_buttons === disabled_buttons ) {
1281
+
1282
+ jQuery('.astra-demo-import')
1283
+ .removeAttr('data-import')
1284
+ .removeClass('installing updating-message')
1285
+ .addClass('button-primary')
1286
+ .text( astraSitesAdmin.strings.importDemo );
1287
+ }
1288
+
1289
+ break;
1290
+
1291
+ case 'upgrade':
1292
+ var demo_slug = jQuery('.wp-full-overlay-header').attr('data-demo-slug');
1293
+
1294
+ jQuery('.astra-demo-import')
1295
+ .addClass('go-pro button-primary')
1296
+ .removeClass('astra-demo-import')
1297
+ .attr('target', '_blank')
1298
+ .attr('href', astraSitesAdmin.getUpgradeURL + demo_slug )
1299
+ .text( astraSitesAdmin.getUpgradeText )
1300
+ .append('<i class="dashicons dashicons-external"></i>');
1301
+ break;
1302
+
1303
+ default:
1304
+ var demo_slug = jQuery('.wp-full-overlay-header').attr('data-demo-slug');
1305
+
1306
+ jQuery('.astra-demo-import')
1307
+ .addClass('go-pro button-primary')
1308
+ .removeClass('astra-demo-import')
1309
+ .attr('target', '_blank')
1310
+ .attr('href', astraSitesAdmin.getProURL )
1311
+ .text( astraSitesAdmin.getProText )
1312
+ .append('<i class="dashicons dashicons-external"></i>');
1313
+ break;
1314
+ }
1315
+
1316
+ },
1317
+
1318
+ /**
1319
+ * Update Page Count.
1320
+ */
1321
+ _updatedPagedCount: function() {
1322
+ paged = parseInt(jQuery('body').attr('data-astra-demo-paged'));
1323
+ jQuery('body').attr('data-astra-demo-paged', paged + 1);
1324
+ window.setTimeout(function () {
1325
+ jQuery('body').data('scrolling', false);
1326
+ }, 800);
1327
+ },
1328
+
1329
+ /**
1330
+ * Reset Page Count.
1331
+ */
1332
+ _resetPagedCount: function() {
1333
+
1334
+ $('body').addClass('loading-content');
1335
+ $('body').attr('data-astra-demo-last-request', '1');
1336
+ $('body').attr('data-astra-demo-paged', '1');
1337
+ $('body').attr('data-astra-demo-search', '');
1338
+ $('body').attr('data-scrolling', false);
1339
+
1340
+ },
1341
+
1342
+ /**
1343
+ * Remove plugin from the queue.
1344
+ */
1345
+ _removePluginFromQueue: function( removeItem, pluginsList ) {
1346
+ return jQuery.grep(pluginsList, function( value ) {
1347
+ return value.slug != removeItem;
1348
+ });
1349
+ }
1350
+
1351
+ };
1352
+
1353
+ /**
1354
+ * Initialize AstraSitesAdmin
1355
+ */
1356
+ $(function(){
1357
+ AstraSitesAdmin.init();
1358
+ });
1359
+
1360
ย  })(jQuery);
inc/assets/js/astra-sites-api.js CHANGED
@@ -1,58 +1,58 @@
1
-
2
- (function($){
3
-
4
- AstraSitesAPI = {
5
-
6
- _api_url : astraSitesApi.ApiURL,
7
-
8
- /**
9
- * API Request
10
- */
11
- _api_request: function( args ) {
12
-
13
- // Set API Request Data.
14
- var data = {
15
- url: AstraSitesAPI._api_url + args.slug,
16
- cache: false,
17
- };
18
-
19
- if( astraRenderGrid.headers ) {
20
- data.headers = astraRenderGrid.headers;
21
- }
22
-
23
- $.ajax( data )
24
- .done(function( items, status, XHR ) {
25
-
26
- if( 'success' === status && XHR.getResponseHeader('x-wp-total') ) {
27
-
28
- var data = {
29
- args : args,
30
- items : items,
31
- items_count : XHR.getResponseHeader('x-wp-total') || 0,
32
- };
33
-
34
- if( 'undefined' !== args.trigger && '' !== args.trigger ) {
35
- $(document).trigger( args.trigger, [data] );
36
- }
37
-
38
- } else {
39
- $(document).trigger( 'astra-sites-api-request-error' );
40
- }
41
-
42
- })
43
- .fail(function( jqXHR, textStatus ) {
44
-
45
- $(document).trigger( 'astra-sites-api-request-fail' );
46
-
47
- })
48
- .always(function() {
49
-
50
- $(document).trigger( 'astra-sites-api-request-always' );
51
-
52
- });
53
-
54
- },
55
-
56
- };
57
-
58
ย  })(jQuery);
1
+
2
+ (function($){
3
+
4
+ AstraSitesAPI = {
5
+
6
+ _api_url : astraSitesApi.ApiURL,
7
+
8
+ /**
9
+ * API Request
10
+ */
11
+ _api_request: function( args ) {
12
+
13
+ // Set API Request Data.
14
+ var data = {
15
+ url: AstraSitesAPI._api_url + args.slug,
16
+ cache: false,
17
+ };
18
+
19
+ if( astraRenderGrid.headers ) {
20
+ data.headers = astraRenderGrid.headers;
21
+ }
22
+
23
+ $.ajax( data )
24
+ .done(function( items, status, XHR ) {
25
+
26
+ if( 'success' === status && XHR.getResponseHeader('x-wp-total') ) {
27
+
28
+ var data = {
29
+ args : args,
30
+ items : items,
31
+ items_count : XHR.getResponseHeader('x-wp-total') || 0,
32
+ };
33
+
34
+ if( 'undefined' !== args.trigger && '' !== args.trigger ) {
35
+ $(document).trigger( args.trigger, [data] );
36
+ }
37
+
38
+ } else {
39
+ $(document).trigger( 'astra-sites-api-request-error' );
40
+ }
41
+
42
+ })
43
+ .fail(function( jqXHR, textStatus ) {
44
+
45
+ $(document).trigger( 'astra-sites-api-request-fail' );
46
+
47
+ })
48
+ .always(function() {
49
+
50
+ $(document).trigger( 'astra-sites-api-request-always' );
51
+
52
+ });
53
+
54
+ },
55
+
56
+ };
57
+
58
ย  })(jQuery);
inc/assets/js/astra-sites-notices.js CHANGED
@@ -1,21 +1,21 @@
1
- jQuery(document).ready(function ($) {
2
-
3
- jQuery( '.astra-notice.is-dismissible .notice-dismiss' ).on( 'click', function() {
4
- var $id = jQuery( this ).attr( 'id' ) || '';
5
- var $time = jQuery( this ).attr( 'dismissible-time' ) || '';
6
- var $meta = jQuery( this ).attr( 'dismissible-meta' ) || '';
7
-
8
- jQuery.ajax({
9
- url: ajaxurl,
10
- type: 'POST',
11
- data: {
12
- action : 'astra-notices',
13
- id : $id,
14
- meta : $meta,
15
- time : $time,
16
- },
17
- });
18
-
19
- });
20
-
21
ย  });
1
+ jQuery(document).ready(function ($) {
2
+
3
+ jQuery( '.astra-notice.is-dismissible .notice-dismiss' ).on( 'click', function() {
4
+ var $id = jQuery( this ).attr( 'id' ) || '';
5
+ var $time = jQuery( this ).attr( 'dismissible-time' ) || '';
6
+ var $meta = jQuery( this ).attr( 'dismissible-meta' ) || '';
7
+
8
+ jQuery.ajax({
9
+ url: ajaxurl,
10
+ type: 'POST',
11
+ data: {
12
+ action : 'astra-notices',
13
+ id : $id,
14
+ meta : $meta,
15
+ time : $time,
16
+ },
17
+ });
18
+
19
+ });
20
+
21
ย  });
inc/assets/js/eventsource.js CHANGED
@@ -1,673 +1,673 @@
1
- /** @license
2
- * eventsource.js
3
- * Available under MIT License (MIT)
4
- * https://github.com/Yaffle/EventSource/
5
- */
6
-
7
- /*jslint indent: 2, vars: true, plusplus: true */
8
- /*global setTimeout, clearTimeout */
9
-
10
- (function (global) {
11
- "use strict";
12
-
13
- var setTimeout = global.setTimeout;
14
- var clearTimeout = global.clearTimeout;
15
- var XMLHttpRequest = global.XMLHttpRequest;
16
- var XDomainRequest = global.XDomainRequest;
17
- var NativeEventSource = global.EventSource;
18
- var document = global.document;
19
-
20
- if (Object.create == null) {
21
- Object.create = function (C) {
22
- function F(){}
23
- F.prototype = C;
24
- return new F();
25
- };
26
- }
27
-
28
- var k = function () {
29
- };
30
-
31
- function XHRWrapper(xhr) {
32
- this.withCredentials = false;
33
- this.responseType = "";
34
- this.readyState = 0;
35
- this.status = 0;
36
- this.statusText = "";
37
- this.responseText = "";
38
- this.onprogress = k;
39
- this.onreadystatechange = k;
40
- this._contentType = "";
41
- this._xhr = xhr;
42
- this._sendTimeout = 0;
43
- this._abort = k;
44
- }
45
-
46
- XHRWrapper.prototype.open = function (method, url) {
47
- this._abort(true);
48
-
49
- var that = this;
50
- var xhr = this._xhr;
51
- var state = 1;
52
- var timeout = 0;
53
-
54
- this._abort = function (silent) {
55
- if (that._sendTimeout !== 0) {
56
- clearTimeout(that._sendTimeout);
57
- that._sendTimeout = 0;
58
- }
59
- if (state === 1 || state === 2 || state === 3) {
60
- state = 4;
61
- xhr.onload = k;
62
- xhr.onerror = k;
63
- xhr.onabort = k;
64
- xhr.onprogress = k;
65
- xhr.onreadystatechange = k;
66
- // IE 8 - 9: XDomainRequest#abort() does not fire any event
67
- // Opera < 10: XMLHttpRequest#abort() does not fire any event
68
- xhr.abort();
69
- if (timeout !== 0) {
70
- clearTimeout(timeout);
71
- timeout = 0;
72
- }
73
- if (!silent) {
74
- that.readyState = 4;
75
- that.onreadystatechange();
76
- }
77
- }
78
- state = 0;
79
- };
80
-
81
- var onStart = function () {
82
- if (state === 1) {
83
- //state = 2;
84
- var status = 0;
85
- var statusText = "";
86
- var contentType = undefined;
87
- if (!("contentType" in xhr)) {
88
- try {
89
- status = xhr.status;
90
- statusText = xhr.statusText;
91
- contentType = xhr.getResponseHeader("Content-Type");
92
- } catch (error) {
93
- // IE < 10 throws exception for `xhr.status` when xhr.readyState === 2 || xhr.readyState === 3
94
- // Opera < 11 throws exception for `xhr.status` when xhr.readyState === 2
95
- // https://bugs.webkit.org/show_bug.cgi?id=29121
96
- status = 0;
97
- statusText = "";
98
- contentType = undefined;
99
- // Firefox < 14, Chrome ?, Safari ?
100
- // https://bugs.webkit.org/show_bug.cgi?id=29658
101
- // https://bugs.webkit.org/show_bug.cgi?id=77854
102
- }
103
- } else {
104
- status = 200;
105
- statusText = "OK";
106
- contentType = xhr.contentType;
107
- }
108
- if (status !== 0) {
109
- state = 2;
110
- that.readyState = 2;
111
- that.status = status;
112
- that.statusText = statusText;
113
- that._contentType = contentType;
114
- that.onreadystatechange();
115
- }
116
- }
117
- };
118
- var onProgress = function () {
119
- onStart();
120
- if (state === 2 || state === 3) {
121
- state = 3;
122
- var responseText = "";
123
- try {
124
- responseText = xhr.responseText;
125
- } catch (error) {
126
- // IE 8 - 9 with XMLHttpRequest
127
- }
128
- that.readyState = 3;
129
- that.responseText = responseText;
130
- that.onprogress();
131
- }
132
- };
133
- var onFinish = function () {
134
- // Firefox 52 fires "readystatechange" (xhr.readyState === 4) without final "readystatechange" (xhr.readyState === 3)
135
- // IE 8 fires "onload" without "onprogress"
136
- onProgress();
137
- if (state === 1 || state === 2 || state === 3) {
138
- state = 4;
139
- if (timeout !== 0) {
140
- clearTimeout(timeout);
141
- timeout = 0;
142
- }
143
- that.readyState = 4;
144
- that.onreadystatechange();
145
- }
146
- };
147
- var onReadyStateChange = function () {
148
- if (xhr != undefined) { // Opera 12
149
- if (xhr.readyState === 4) {
150
- onFinish();
151
- } else if (xhr.readyState === 3) {
152
- onProgress();
153
- } else if (xhr.readyState === 2) {
154
- onStart();
155
- }
156
- }
157
- };
158
- var onTimeout = function () {
159
- timeout = setTimeout(function () {
160
- onTimeout();
161
- }, 500);
162
- if (xhr.readyState === 3) {
163
- onProgress();
164
- }
165
- };
166
-
167
- // XDomainRequest#abort removes onprogress, onerror, onload
168
- xhr.onload = onFinish;
169
- xhr.onerror = onFinish;
170
- // improper fix to match Firefox behaviour, but it is better than just ignore abort
171
- // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596
172
- // https://bugzilla.mozilla.org/show_bug.cgi?id=880200
173
- // https://code.google.com/p/chromium/issues/detail?id=153570
174
- // IE 8 fires "onload" without "onprogress
175
- xhr.onabort = onFinish;
176
-
177
- // https://bugzilla.mozilla.org/show_bug.cgi?id=736723
178
- if (!("sendAsBinary" in XMLHttpRequest.prototype) && !("mozAnon" in XMLHttpRequest.prototype)) {
179
- xhr.onprogress = onProgress;
180
- }
181
-
182
- // IE 8 - 9 (XMLHTTPRequest)
183
- // Opera < 12
184
- // Firefox < 3.5
185
- // Firefox 3.5 - 3.6 - ? < 9.0
186
- // onprogress is not fired sometimes or delayed
187
- // see also #64
188
- xhr.onreadystatechange = onReadyStateChange;
189
-
190
- if ("contentType" in xhr) {
191
- url += (url.indexOf("?", 0) === -1 ? "?" : "&") + "padding=true";
192
- }
193
- xhr.open(method, url, true);
194
-
195
- if ("readyState" in xhr) {
196
- // workaround for Opera 12 issue with "progress" events
197
- // #91
198
- timeout = setTimeout(function () {
199
- onTimeout();
200
- }, 0);
201
- }
202
- };
203
- XHRWrapper.prototype.abort = function () {
204
- this._abort(false);
205
- };
206
- XHRWrapper.prototype.getResponseHeader = function (name) {
207
- return this._contentType;
208
- };
209
- XHRWrapper.prototype.setRequestHeader = function (name, value) {
210
- var xhr = this._xhr;
211
- if ("setRequestHeader" in xhr) {
212
- xhr.setRequestHeader(name, value);
213
- }
214
- };
215
- XHRWrapper.prototype.send = function () {
216
- // loading indicator in Safari < ? (6), Chrome < 14, Firefox
217
- if (!("ontimeout" in XMLHttpRequest.prototype) &&
218
- document != undefined &&
219
- document.readyState != undefined &&
220
- document.readyState !== "complete") {
221
- var that = this;
222
- that._sendTimeout = setTimeout(function () {
223
- that._sendTimeout = 0;
224
- that.send();
225
- }, 4);
226
- return;
227
- }
228
-
229
- var xhr = this._xhr;
230
- // withCredentials should be set after "open" for Safari and Chrome (< 19 ?)
231
- xhr.withCredentials = this.withCredentials;
232
- xhr.responseType = this.responseType;
233
- try {
234
- // xhr.send(); throws "Not enough arguments" in Firefox 3.0
235
- xhr.send(undefined);
236
- } catch (error1) {
237
- // Safari 5.1.7, Opera 12
238
- throw error1;
239
- }
240
- };
241
-
242
- function XHRTransport(xhr) {
243
- this._xhr = new XHRWrapper(xhr);
244
- }
245
-
246
- XHRTransport.prototype.open = function (onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) {
247
- var xhr = this._xhr;
248
- xhr.open("GET", url);
249
- var offset = 0;
250
- xhr.onprogress = function () {
251
- var responseText = xhr.responseText;
252
- var chunk = responseText.slice(offset);
253
- offset += chunk.length;
254
- onProgressCallback(chunk);
255
- };
256
- xhr.onreadystatechange = function () {
257
- if (xhr.readyState === 2) {
258
- var status = xhr.status;
259
- var statusText = xhr.statusText;
260
- var contentType = xhr.getResponseHeader("Content-Type");
261
- onStartCallback(status, statusText, contentType);
262
- } else if (xhr.readyState === 4) {
263
- onFinishCallback();
264
- }
265
- };
266
- xhr.withCredentials = withCredentials;
267
- xhr.responseType = "text";
268
- for (var name in headers) {
269
- if (Object.prototype.hasOwnProperty.call(headers, name)) {
270
- xhr.setRequestHeader(name, headers[name]);
271
- }
272
- }
273
- xhr.send();
274
- };
275
-
276
- XHRTransport.prototype.cancel = function () {
277
- var xhr = this._xhr;
278
- xhr.abort();
279
- };
280
-
281
- function EventTarget() {
282
- this._listeners = Object.create(null);
283
- }
284
-
285
- function throwError(e) {
286
- setTimeout(function () {
287
- throw e;
288
- }, 0);
289
- }
290
-
291
- EventTarget.prototype.dispatchEvent = function (event) {
292
- event.target = this;
293
- var typeListeners = this._listeners[event.type];
294
- if (typeListeners != undefined) {
295
- var length = typeListeners.length;
296
- for (var i = 0; i < length; i += 1) {
297
- var listener = typeListeners[i];
298
- try {
299
- if (typeof listener.handleEvent === "function") {
300
- listener.handleEvent(event);
301
- } else {
302
- listener.call(this, event);
303
- }
304
- } catch (e) {
305
- throwError(e);
306
- }
307
- }
308
- }
309
- };
310
- EventTarget.prototype.addEventListener = function (type, listener) {
311
- type = String(type);
312
- var listeners = this._listeners;
313
- var typeListeners = listeners[type];
314
- if (typeListeners == undefined) {
315
- typeListeners = [];
316
- listeners[type] = typeListeners;
317
- }
318
- var found = false;
319
- for (var i = 0; i < typeListeners.length; i += 1) {
320
- if (typeListeners[i] === listener) {
321
- found = true;
322
- }
323
- }
324
- if (!found) {
325
- typeListeners.push(listener);
326
- }
327
- };
328
- EventTarget.prototype.removeEventListener = function (type, listener) {
329
- type = String(type);
330
- var listeners = this._listeners;
331
- var typeListeners = listeners[type];
332
- if (typeListeners != undefined) {
333
- var filtered = [];
334
- for (var i = 0; i < typeListeners.length; i += 1) {
335
- if (typeListeners[i] !== listener) {
336
- filtered.push(typeListeners[i]);
337
- }
338
- }
339
- if (filtered.length === 0) {
340
- delete listeners[type];
341
- } else {
342
- listeners[type] = filtered;
343
- }
344
- }
345
- };
346
-
347
- function Event(type) {
348
- this.type = type;
349
- this.target = undefined;
350
- }
351
-
352
- function MessageEvent(type, options) {
353
- Event.call(this, type);
354
- this.data = options.data;
355
- this.lastEventId = options.lastEventId;
356
- }
357
-
358
- MessageEvent.prototype = Object.create(Event.prototype);
359
-
360
- var WAITING = -1;
361
- var CONNECTING = 0;
362
- var OPEN = 1;
363
- var CLOSED = 2;
364
-
365
- var AFTER_CR = -1;
366
- var FIELD_START = 0;
367
- var FIELD = 1;
368
- var VALUE_START = 2;
369
- var VALUE = 3;
370
-
371
- var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i;
372
-
373
- var MINIMUM_DURATION = 1000;
374
- var MAXIMUM_DURATION = 18000000;
375
-
376
- var parseDuration = function (value, def) {
377
- var n = parseInt(value, 10);
378
- if (n !== n) {
379
- n = def;
380
- }
381
- return clampDuration(n);
382
- };
383
- var clampDuration = function (n) {
384
- return Math.min(Math.max(n, MINIMUM_DURATION), MAXIMUM_DURATION);
385
- };
386
-
387
- var fire = function (that, f, event) {
388
- try {
389
- if (typeof f === "function") {
390
- f.call(that, event);
391
- }
392
- } catch (e) {
393
- throwError(e);
394
- }
395
- };
396
-
397
- function EventSourcePolyfill(url, options) {
398
- EventTarget.call(this);
399
-
400
- this.onopen = undefined;
401
- this.onmessage = undefined;
402
- this.onerror = undefined;
403
-
404
- this.url = undefined;
405
- this.readyState = undefined;
406
- this.withCredentials = undefined;
407
-
408
- this._close = undefined;
409
-
410
- start(this, url, options);
411
- }
412
-
413
- function start(es, url, options) {
414
- url = String(url);
415
- var withCredentials = options != undefined && Boolean(options.withCredentials);
416
-
417
- var initialRetry = clampDuration(1000);
418
- var heartbeatTimeout = clampDuration(45000);
419
-
420
- var lastEventId = "";
421
- var retry = initialRetry;
422
- var wasActivity = false;
423
- var headers = options != undefined && options.headers != undefined ? JSON.parse(JSON.stringify(options.headers)) : undefined;
424
- var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : (XDomainRequest != undefined ? XDomainRequest : XMLHttpRequest);
425
- var transport = new XHRTransport(new CurrentTransport());
426
- var timeout = 0;
427
- var currentState = WAITING;
428
- var dataBuffer = "";
429
- var lastEventIdBuffer = "";
430
- var eventTypeBuffer = "";
431
-
432
- var textBuffer = "";
433
- var state = FIELD_START;
434
- var fieldStart = 0;
435
- var valueStart = 0;
436
-
437
- var onStart = function (status, statusText, contentType) {
438
- if (currentState === CONNECTING) {
439
- if (status === 200 && contentType != undefined && contentTypeRegExp.test(contentType)) {
440
- currentState = OPEN;
441
- wasActivity = true;
442
- retry = initialRetry;
443
- es.readyState = OPEN;
444
- var event = new Event("open");
445
- es.dispatchEvent(event);
446
- fire(es, es.onopen, event);
447
- } else {
448
- var message = "";
449
- if (status !== 200) {
450
- if (statusText) {
451
- statusText = statusText.replace(/\s+/g, " ");
452
- }
453
- message = "EventSource's response has a status " + status + " " + statusText + " that is not 200. Aborting the connection.";
454
- } else {
455
- message = "EventSource's response has a Content-Type specifying an unsupported type: " + (contentType == undefined ? "-" : contentType.replace(/\s+/g, " ")) + ". Aborting the connection.";
456
- }
457
- throwError(new Error(message));
458
- close();
459
- var event = new Event("error");
460
- es.dispatchEvent(event);
461
- fire(es, es.onerror, event);
462
- }
463
- }
464
- };
465
-
466
- var onProgress = function (textChunk) {
467
- if (currentState === OPEN) {
468
- var n = -1;
469
- for (var i = 0; i < textChunk.length; i += 1) {
470
- var c = textChunk.charCodeAt(i);
471
- if (c === "\n".charCodeAt(0) || c === "\r".charCodeAt(0)) {
472
- n = i;
473
- }
474
- }
475
- var chunk = (n !== -1 ? textBuffer : "") + textChunk.slice(0, n + 1);
476
- textBuffer = (n === -1 ? textBuffer : "") + textChunk.slice(n + 1);
477
- if (chunk !== "") {
478
- wasActivity = true;
479
- }
480
- for (var position = 0; position < chunk.length; position += 1) {
481
- var c = chunk.charCodeAt(position);
482
- if (state === AFTER_CR && c === "\n".charCodeAt(0)) {
483
- state = FIELD_START;
484
- } else {
485
- if (state === AFTER_CR) {
486
- state = FIELD_START;
487
- }
488
- if (c === "\r".charCodeAt(0) || c === "\n".charCodeAt(0)) {
489
- if (state !== FIELD_START) {
490
- if (state === FIELD) {
491
- valueStart = position + 1;
492
- }
493
- var field = chunk.slice(fieldStart, valueStart - 1);
494
- var value = chunk.slice(valueStart + (valueStart < position && chunk.charCodeAt(valueStart) === " ".charCodeAt(0) ? 1 : 0), position);
495
- if (field === "data") {
496
- dataBuffer += "\n";
497
- dataBuffer += value;
498
- } else if (field === "id") {
499
- lastEventIdBuffer = value;
500
- } else if (field === "event") {
501
- eventTypeBuffer = value;
502
- } else if (field === "retry") {
503
- initialRetry = parseDuration(value, initialRetry);
504
- retry = initialRetry;
505
- } else if (field === "heartbeatTimeout") {
506
- heartbeatTimeout = parseDuration(value, heartbeatTimeout);
507
- if (timeout !== 0) {
508
- clearTimeout(timeout);
509
- timeout = setTimeout(function () {
510
- onTimeout();
511
- }, heartbeatTimeout);
512
- }
513
- }
514
- }
515
- if (state === FIELD_START) {
516
- if (dataBuffer !== "") {
517
- lastEventId = lastEventIdBuffer;
518
- if (eventTypeBuffer === "") {
519
- eventTypeBuffer = "message";
520
- }
521
- var event = new MessageEvent(eventTypeBuffer, {
522
- data: dataBuffer.slice(1),
523
- lastEventId: lastEventIdBuffer
524
- });
525
- es.dispatchEvent(event);
526
- if (eventTypeBuffer === "message") {
527
- fire(es, es.onmessage, event);
528
- }
529
- if (currentState === CLOSED) {
530
- return;
531
- }
532
- }
533
- dataBuffer = "";
534
- eventTypeBuffer = "";
535
- }
536
- state = c === "\r".charCodeAt(0) ? AFTER_CR : FIELD_START;
537
- } else {
538
- if (state === FIELD_START) {
539
- fieldStart = position;
540
- state = FIELD;
541
- }
542
- if (state === FIELD) {
543
- if (c === ":".charCodeAt(0)) {
544
- valueStart = position + 1;
545
- state = VALUE_START;
546
- }
547
- } else if (state === VALUE_START) {
548
- state = VALUE;
549
- }
550
- }
551
- }
552
- }
553
- }
554
- };
555
-
556
- var onFinish = function () {
557
- if (currentState === OPEN || currentState === CONNECTING) {
558
- currentState = WAITING;
559
- if (timeout !== 0) {
560
- clearTimeout(timeout);
561
- timeout = 0;
562
- }
563
- timeout = setTimeout(function () {
564
- onTimeout();
565
- }, retry);
566
- retry = clampDuration(Math.min(initialRetry * 16, retry * 2));
567
-
568
- es.readyState = CONNECTING;
569
- var event = new Event("error");
570
- es.dispatchEvent(event);
571
- fire(es, es.onerror, event);
572
- }
573
- };
574
-
575
- var close = function () {
576
- currentState = CLOSED;
577
- transport.cancel();
578
- if (timeout !== 0) {
579
- clearTimeout(timeout);
580
- timeout = 0;
581
- }
582
- es.readyState = CLOSED;
583
- };
584
-
585
- var onTimeout = function () {
586
- timeout = 0;
587
-
588
- if (currentState !== WAITING) {
589
- if (!wasActivity) {
590
- throwError(new Error("No activity within " + heartbeatTimeout + " milliseconds. Reconnecting."));
591
- transport.cancel();
592
- } else {
593
- wasActivity = false;
594
- timeout = setTimeout(function () {
595
- onTimeout();
596
- }, heartbeatTimeout);
597
- }
598
- return;
599
- }
600
-
601
- wasActivity = false;
602
- timeout = setTimeout(function () {
603
- onTimeout();
604
- }, heartbeatTimeout);
605
-
606
- currentState = CONNECTING;
607
- dataBuffer = "";
608
- eventTypeBuffer = "";
609
- lastEventIdBuffer = lastEventId;
610
- textBuffer = "";
611
- fieldStart = 0;
612
- valueStart = 0;
613
- state = FIELD_START;
614
-
615
- // https://bugzilla.mozilla.org/show_bug.cgi?id=428916
616
- // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers.
617
- var requestURL = url;
618
- if (url.slice(0, 5) !== "data:" &&
619
- url.slice(0, 5) !== "blob:") {
620
- requestURL = url + (url.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(lastEventId);
621
- }
622
- var requestHeaders = {};
623
- requestHeaders["Accept"] = "text/event-stream";
624
- if (headers != undefined) {
625
- for (var name in headers) {
626
- if (Object.prototype.hasOwnProperty.call(headers, name)) {
627
- requestHeaders[name] = headers[name];
628
- }
629
- }
630
- }
631
- try {
632
- transport.open(onStart, onProgress, onFinish, requestURL, withCredentials, requestHeaders);
633
- } catch (error) {
634
- close();
635
- throw error;
636
- }
637
- };
638
-
639
- es.url = url;
640
- es.readyState = CONNECTING;
641
- es.withCredentials = withCredentials;
642
- es._close = close;
643
-
644
- onTimeout();
645
- }
646
-
647
- EventSourcePolyfill.prototype = Object.create(EventTarget.prototype);
648
- EventSourcePolyfill.prototype.CONNECTING = CONNECTING;
649
- EventSourcePolyfill.prototype.OPEN = OPEN;
650
- EventSourcePolyfill.prototype.CLOSED = CLOSED;
651
- EventSourcePolyfill.prototype.close = function () {
652
- this._close();
653
- };
654
-
655
- EventSourcePolyfill.CONNECTING = CONNECTING;
656
- EventSourcePolyfill.OPEN = OPEN;
657
- EventSourcePolyfill.CLOSED = CLOSED;
658
- EventSourcePolyfill.prototype.withCredentials = undefined;
659
-
660
- global.EventSourcePolyfill = EventSourcePolyfill;
661
- global.NativeEventSource = NativeEventSource;
662
-
663
- if (XMLHttpRequest != undefined && (NativeEventSource == undefined || !("withCredentials" in NativeEventSource.prototype))) {
664
- // Why replace a native EventSource ?
665
- // https://bugzilla.mozilla.org/show_bug.cgi?id=444328
666
- // https://bugzilla.mozilla.org/show_bug.cgi?id=831392
667
- // https://code.google.com/p/chromium/issues/detail?id=260144
668
- // https://code.google.com/p/chromium/issues/detail?id=225654
669
- // ...
670
- global.EventSource = EventSourcePolyfill;
671
- }
672
-
673
- }(typeof window !== 'undefined' ? window : this));
1
+ /** @license
2
+ * eventsource.js
3
+ * Available under MIT License (MIT)
4
+ * https://github.com/Yaffle/EventSource/
5
+ */
6
+
7
+ /*jslint indent: 2, vars: true, plusplus: true */
8
+ /*global setTimeout, clearTimeout */
9
+
10
+ (function (global) {
11
+ "use strict";
12
+
13
+ var setTimeout = global.setTimeout;
14
+ var clearTimeout = global.clearTimeout;
15
+ var XMLHttpRequest = global.XMLHttpRequest;
16
+ var XDomainRequest = global.XDomainRequest;
17
+ var NativeEventSource = global.EventSource;
18
+ var document = global.document;
19
+
20
+ if (Object.create == null) {
21
+ Object.create = function (C) {
22
+ function F(){}
23
+ F.prototype = C;
24
+ return new F();
25
+ };
26
+ }
27
+
28
+ var k = function () {
29
+ };
30
+
31
+ function XHRWrapper(xhr) {
32
+ this.withCredentials = false;
33
+ this.responseType = "";
34
+ this.readyState = 0;
35
+ this.status = 0;
36
+ this.statusText = "";
37
+ this.responseText = "";
38
+ this.onprogress = k;
39
+ this.onreadystatechange = k;
40
+ this._contentType = "";
41
+ this._xhr = xhr;
42
+ this._sendTimeout = 0;
43
+ this._abort = k;
44
+ }
45
+
46
+ XHRWrapper.prototype.open = function (method, url) {
47
+ this._abort(true);
48
+
49
+ var that = this;
50
+ var xhr = this._xhr;
51
+ var state = 1;
52
+ var timeout = 0;
53
+
54
+ this._abort = function (silent) {
55
+ if (that._sendTimeout !== 0) {
56
+ clearTimeout(that._sendTimeout);
57
+ that._sendTimeout = 0;
58
+ }
59
+ if (state === 1 || state === 2 || state === 3) {
60
+ state = 4;
61
+ xhr.onload = k;
62
+ xhr.onerror = k;
63
+ xhr.onabort = k;
64
+ xhr.onprogress = k;
65
+ xhr.onreadystatechange = k;
66
+ // IE 8 - 9: XDomainRequest#abort() does not fire any event
67
+ // Opera < 10: XMLHttpRequest#abort() does not fire any event
68
+ xhr.abort();
69
+ if (timeout !== 0) {
70
+ clearTimeout(timeout);
71
+ timeout = 0;
72
+ }
73
+ if (!silent) {
74
+ that.readyState = 4;
75
+ that.onreadystatechange();
76
+ }
77
+ }
78
+ state = 0;
79
+ };
80
+
81
+ var onStart = function () {
82
+ if (state === 1) {
83
+ //state = 2;
84
+ var status = 0;
85
+ var statusText = "";
86
+ var contentType = undefined;
87
+ if (!("contentType" in xhr)) {
88
+ try {
89
+ status = xhr.status;
90
+ statusText = xhr.statusText;
91
+ contentType = xhr.getResponseHeader("Content-Type");
92
+ } catch (error) {
93
+ // IE < 10 throws exception for `xhr.status` when xhr.readyState === 2 || xhr.readyState === 3
94
+ // Opera < 11 throws exception for `xhr.status` when xhr.readyState === 2
95
+ // https://bugs.webkit.org/show_bug.cgi?id=29121
96
+ status = 0;
97
+ statusText = "";
98
+ contentType = undefined;
99
+ // Firefox < 14, Chrome ?, Safari ?
100
+ // https://bugs.webkit.org/show_bug.cgi?id=29658
101
+ // https://bugs.webkit.org/show_bug.cgi?id=77854
102
+ }
103
+ } else {
104
+ status = 200;
105
+ statusText = "OK";
106
+ contentType = xhr.contentType;
107
+ }
108
+ if (status !== 0) {
109
+ state = 2;
110
+ that.readyState = 2;
111
+ that.status = status;
112
+ that.statusText = statusText;
113
+ that._contentType = contentType;
114
+ that.onreadystatechange();
115
+ }
116
+ }
117
+ };
118
+ var onProgress = function () {
119
+ onStart();
120
+ if (state === 2 || state === 3) {
121
+ state = 3;
122
+ var responseText = "";
123
+ try {
124
+ responseText = xhr.responseText;
125
+ } catch (error) {
126
+ // IE 8 - 9 with XMLHttpRequest
127
+ }
128
+ that.readyState = 3;
129
+ that.responseText = responseText;
130
+ that.onprogress();
131
+ }
132
+ };
133
+ var onFinish = function () {
134
+ // Firefox 52 fires "readystatechange" (xhr.readyState === 4) without final "readystatechange" (xhr.readyState === 3)
135
+ // IE 8 fires "onload" without "onprogress"
136
+ onProgress();
137
+ if (state === 1 || state === 2 || state === 3) {
138
+ state = 4;
139
+ if (timeout !== 0) {
140
+ clearTimeout(timeout);
141
+ timeout = 0;
142
+ }
143
+ that.readyState = 4;
144
+ that.onreadystatechange();
145
+ }
146
+ };
147
+ var onReadyStateChange = function () {
148
+ if (xhr != undefined) { // Opera 12
149
+ if (xhr.readyState === 4) {
150
+ onFinish();
151
+ } else if (xhr.readyState === 3) {
152
+ onProgress();
153
+ } else if (xhr.readyState === 2) {
154
+ onStart();
155
+ }
156
+ }
157
+ };
158
+ var onTimeout = function () {
159
+ timeout = setTimeout(function () {
160
+ onTimeout();
161
+ }, 500);
162
+ if (xhr.readyState === 3) {
163
+ onProgress();
164
+ }
165
+ };
166
+
167
+ // XDomainRequest#abort removes onprogress, onerror, onload
168
+ xhr.onload = onFinish;
169
+ xhr.onerror = onFinish;
170
+ // improper fix to match Firefox behaviour, but it is better than just ignore abort
171
+ // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596
172
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=880200
173
+ // https://code.google.com/p/chromium/issues/detail?id=153570
174
+ // IE 8 fires "onload" without "onprogress
175
+ xhr.onabort = onFinish;
176
+
177
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=736723
178
+ if (!("sendAsBinary" in XMLHttpRequest.prototype) && !("mozAnon" in XMLHttpRequest.prototype)) {
179
+ xhr.onprogress = onProgress;
180
+ }
181
+
182
+ // IE 8 - 9 (XMLHTTPRequest)
183
+ // Opera < 12
184
+ // Firefox < 3.5
185
+ // Firefox 3.5 - 3.6 - ? < 9.0
186
+ // onprogress is not fired sometimes or delayed
187
+ // see also #64
188
+ xhr.onreadystatechange = onReadyStateChange;
189
+
190
+ if ("contentType" in xhr) {
191
+ url += (url.indexOf("?", 0) === -1 ? "?" : "&") + "padding=true";
192
+ }
193
+ xhr.open(method, url, true);
194
+
195
+ if ("readyState" in xhr) {
196
+ // workaround for Opera 12 issue with "progress" events
197
+ // #91
198
+ timeout = setTimeout(function () {
199
+ onTimeout();
200
+ }, 0);
201
+ }
202
+ };
203
+ XHRWrapper.prototype.abort = function () {
204
+ this._abort(false);
205
+ };
206
+ XHRWrapper.prototype.getResponseHeader = function (name) {
207
+ return this._contentType;
208
+ };
209
+ XHRWrapper.prototype.setRequestHeader = function (name, value) {
210
+ var xhr = this._xhr;
211
+ if ("setRequestHeader" in xhr) {
212
+ xhr.setRequestHeader(name, value);
213
+ }
214
+ };
215
+ XHRWrapper.prototype.send = function () {
216
+ // loading indicator in Safari < ? (6), Chrome < 14, Firefox
217
+ if (!("ontimeout" in XMLHttpRequest.prototype) &&
218
+ document != undefined &&
219
+ document.readyState != undefined &&
220
+ document.readyState !== "complete") {
221
+ var that = this;
222
+ that._sendTimeout = setTimeout(function () {
223
+ that._sendTimeout = 0;
224
+ that.send();
225
+ }, 4);
226
+ return;
227
+ }
228
+
229
+ var xhr = this._xhr;
230
+ // withCredentials should be set after "open" for Safari and Chrome (< 19 ?)
231
+ xhr.withCredentials = this.withCredentials;
232
+ xhr.responseType = this.responseType;
233
+ try {
234
+ // xhr.send(); throws "Not enough arguments" in Firefox 3.0
235
+ xhr.send(undefined);
236
+ } catch (error1) {
237
+ // Safari 5.1.7, Opera 12
238
+ throw error1;
239
+ }
240
+ };
241
+
242
+ function XHRTransport(xhr) {
243
+ this._xhr = new XHRWrapper(xhr);
244
+ }
245
+
246
+ XHRTransport.prototype.open = function (onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) {
247
+ var xhr = this._xhr;
248
+ xhr.open("GET", url);
249
+ var offset = 0;
250
+ xhr.onprogress = function () {
251
+ var responseText = xhr.responseText;
252
+ var chunk = responseText.slice(offset);
253
+ offset += chunk.length;
254
+ onProgressCallback(chunk);
255
+ };
256
+ xhr.onreadystatechange = function () {
257
+ if (xhr.readyState === 2) {
258
+ var status = xhr.status;
259
+ var statusText = xhr.statusText;
260
+ var contentType = xhr.getResponseHeader("Content-Type");
261
+ onStartCallback(status, statusText, contentType);
262
+ } else if (xhr.readyState === 4) {
263
+ onFinishCallback();
264
+ }
265
+ };
266
+ xhr.withCredentials = withCredentials;
267
+ xhr.responseType = "text";
268
+ for (var name in headers) {
269
+ if (Object.prototype.hasOwnProperty.call(headers, name)) {
270
+ xhr.setRequestHeader(name, headers[name]);
271
+ }
272
+ }
273
+ xhr.send();
274
+ };
275
+
276
+ XHRTransport.prototype.cancel = function () {
277
+ var xhr = this._xhr;
278
+ xhr.abort();
279
+ };
280
+
281
+ function EventTarget() {
282
+ this._listeners = Object.create(null);
283
+ }
284
+
285
+ function throwError(e) {
286
+ setTimeout(function () {
287
+ throw e;
288
+ }, 0);
289
+ }
290
+
291
+ EventTarget.prototype.dispatchEvent = function (event) {
292
+ event.target = this;
293
+ var typeListeners = this._listeners[event.type];
294
+ if (typeListeners != undefined) {
295
+ var length = typeListeners.length;
296
+ for (var i = 0; i < length; i += 1) {
297
+ var listener = typeListeners[i];
298
+ try {
299
+ if (typeof listener.handleEvent === "function") {
300
+ listener.handleEvent(event);
301
+ } else {
302
+ listener.call(this, event);
303
+ }
304
+ } catch (e) {
305
+ throwError(e);
306
+ }
307
+ }
308
+ }
309
+ };
310
+ EventTarget.prototype.addEventListener = function (type, listener) {
311
+ type = String(type);
312
+ var listeners = this._listeners;
313
+ var typeListeners = listeners[type];
314
+ if (typeListeners == undefined) {
315
+ typeListeners = [];
316
+ listeners[type] = typeListeners;
317
+ }
318
+ var found = false;
319
+ for (var i = 0; i < typeListeners.length; i += 1) {
320
+ if (typeListeners[i] === listener) {
321
+ found = true;
322
+ }
323
+ }
324
+ if (!found) {
325
+ typeListeners.push(listener);
326
+ }
327
+ };
328
+ EventTarget.prototype.removeEventListener = function (type, listener) {
329
+ type = String(type);
330
+ var listeners = this._listeners;
331
+ var typeListeners = listeners[type];
332
+ if (typeListeners != undefined) {
333
+ var filtered = [];
334
+ for (var i = 0; i < typeListeners.length; i += 1) {
335
+ if (typeListeners[i] !== listener) {
336
+ filtered.push(typeListeners[i]);
337
+ }
338
+ }
339
+ if (filtered.length === 0) {
340
+ delete listeners[type];
341
+ } else {
342
+ listeners[type] = filtered;
343
+ }
344
+ }
345
+ };
346
+
347
+ function Event(type) {
348
+ this.type = type;
349
+ this.target = undefined;
350
+ }
351
+
352
+ function MessageEvent(type, options) {
353
+ Event.call(this, type);
354
+ this.data = options.data;
355
+ this.lastEventId = options.lastEventId;
356
+ }
357
+
358
+ MessageEvent.prototype = Object.create(Event.prototype);
359
+
360
+ var WAITING = -1;
361
+ var CONNECTING = 0;
362
+ var OPEN = 1;
363
+ var CLOSED = 2;
364
+
365
+ var AFTER_CR = -1;
366
+ var FIELD_START = 0;
367
+ var FIELD = 1;
368
+ var VALUE_START = 2;
369
+ var VALUE = 3;
370
+
371
+ var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i;
372
+
373
+ var MINIMUM_DURATION = 1000;
374
+ var MAXIMUM_DURATION = 18000000;
375
+
376
+ var parseDuration = function (value, def) {
377
+ var n = parseInt(value, 10);
378
+ if (n !== n) {
379
+ n = def;
380
+ }
381
+ return clampDuration(n);
382
+ };
383
+ var clampDuration = function (n) {
384
+ return Math.min(Math.max(n, MINIMUM_DURATION), MAXIMUM_DURATION);
385
+ };
386
+
387
+ var fire = function (that, f, event) {
388
+ try {
389
+ if (typeof f === "function") {
390
+ f.call(that, event);
391
+ }
392
+ } catch (e) {
393
+ throwError(e);
394
+ }
395
+ };
396
+
397
+ function EventSourcePolyfill(url, options) {
398
+ EventTarget.call(this);
399
+
400
+ this.onopen = undefined;
401
+ this.onmessage = undefined;
402
+ this.onerror = undefined;
403
+
404
+ this.url = undefined;
405
+ this.readyState = undefined;
406
+ this.withCredentials = undefined;
407
+
408
+ this._close = undefined;
409
+
410
+ start(this, url, options);
411
+ }
412
+
413
+ function start(es, url, options) {
414
+ url = String(url);
415
+ var withCredentials = options != undefined && Boolean(options.withCredentials);
416
+
417
+ var initialRetry = clampDuration(1000);
418
+ var heartbeatTimeout = clampDuration(45000);
419
+
420
+ var lastEventId = "";
421
+ var retry = initialRetry;
422
+ var wasActivity = false;
423
+ var headers = options != undefined && options.headers != undefined ? JSON.parse(JSON.stringify(options.headers)) : undefined;
424
+ var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : (XDomainRequest != undefined ? XDomainRequest : XMLHttpRequest);
425
+ var transport = new XHRTransport(new CurrentTransport());
426
+ var timeout = 0;
427
+ var currentState = WAITING;
428
+ var dataBuffer = "";
429
+ var lastEventIdBuffer = "";
430
+ var eventTypeBuffer = "";
431
+
432
+ var textBuffer = "";
433
+ var state = FIELD_START;
434
+ var fieldStart = 0;
435
+ var valueStart = 0;
436
+
437
+ var onStart = function (status, statusText, contentType) {
438
+ if (currentState === CONNECTING) {
439
+ if (status === 200 && contentType != undefined && contentTypeRegExp.test(contentType)) {
440
+ currentState = OPEN;
441
+ wasActivity = true;
442
+ retry = initialRetry;
443
+ es.readyState = OPEN;
444
+ var event = new Event("open");
445
+ es.dispatchEvent(event);
446
+ fire(es, es.onopen, event);
447
+ } else {
448
+ var message = "";
449
+ if (status !== 200) {
450
+ if (statusText) {
451
+ statusText = statusText.replace(/\s+/g, " ");
452
+ }
453
+ message = "EventSource's response has a status " + status + " " + statusText + " that is not 200. Aborting the connection.";
454
+ } else {
455
+ message = "EventSource's response has a Content-Type specifying an unsupported type: " + (contentType == undefined ? "-" : contentType.replace(/\s+/g, " ")) + ". Aborting the connection.";
456
+ }
457
+ throwError(new Error(message));
458
+ close();
459
+ var event = new Event("error");
460
+ es.dispatchEvent(event);
461
+ fire(es, es.onerror, event);
462
+ }
463
+ }
464
+ };
465
+
466
+ var onProgress = function (textChunk) {
467
+ if (currentState === OPEN) {
468
+ var n = -1;
469
+ for (var i = 0; i < textChunk.length; i += 1) {
470
+ var c = textChunk.charCodeAt(i);
471
+ if (c === "\n".charCodeAt(0) || c === "\r".charCodeAt(0)) {
472
+ n = i;
473
+ }
474
+ }
475
+ var chunk = (n !== -1 ? textBuffer : "") + textChunk.slice(0, n + 1);
476
+ textBuffer = (n === -1 ? textBuffer : "") + textChunk.slice(n + 1);
477
+ if (chunk !== "") {
478
+ wasActivity = true;
479
+ }
480
+ for (var position = 0; position < chunk.length; position += 1) {
481
+ var c = chunk.charCodeAt(position);
482
+ if (state === AFTER_CR && c === "\n".charCodeAt(0)) {
483
+ state = FIELD_START;
484
+ } else {
485
+ if (state === AFTER_CR) {
486
+ state = FIELD_START;
487
+ }
488
+ if (c === "\r".charCodeAt(0) || c === "\n".charCodeAt(0)) {
489
+ if (state !== FIELD_START) {
490
+ if (state === FIELD) {
491
+ valueStart = position + 1;
492
+ }
493
+ var field = chunk.slice(fieldStart, valueStart - 1);
494
+ var value = chunk.slice(valueStart + (valueStart < position && chunk.charCodeAt(valueStart) === " ".charCodeAt(0) ? 1 : 0), position);
495
+ if (field === "data") {
496
+ dataBuffer += "\n";
497
+ dataBuffer += value;
498
+ } else if (field === "id") {
499
+ lastEventIdBuffer = value;
500
+ } else if (field === "event") {
501
+ eventTypeBuffer = value;
502
+ } else if (field === "retry") {
503
+ initialRetry = parseDuration(value, initialRetry);
504
+ retry = initialRetry;
505
+ } else if (field === "heartbeatTimeout") {
506
+ heartbeatTimeout = parseDuration(value, heartbeatTimeout);
507
+ if (timeout !== 0) {
508
+ clearTimeout(timeout);
509
+ timeout = setTimeout(function () {
510
+ onTimeout();
511
+ }, heartbeatTimeout);
512
+ }
513
+ }
514
+ }
515
+ if (state === FIELD_START) {
516
+ if (dataBuffer !== "") {
517
+ lastEventId = lastEventIdBuffer;
518
+ if (eventTypeBuffer === "") {
519
+ eventTypeBuffer = "message";
520
+ }
521
+ var event = new MessageEvent(eventTypeBuffer, {
522
+ data: dataBuffer.slice(1),
523
+ lastEventId: lastEventIdBuffer
524
+ });
525
+ es.dispatchEvent(event);
526
+ if (eventTypeBuffer === "message") {
527
+ fire(es, es.onmessage, event);
528
+ }
529
+ if (currentState === CLOSED) {
530
+ return;
531
+ }
532
+ }
533
+ dataBuffer = "";
534
+ eventTypeBuffer = "";
535
+ }
536
+ state = c === "\r".charCodeAt(0) ? AFTER_CR : FIELD_START;
537
+ } else {
538
+ if (state === FIELD_START) {
539
+ fieldStart = position;
540
+ state = FIELD;
541
+ }
542
+ if (state === FIELD) {
543
+ if (c === ":".charCodeAt(0)) {
544
+ valueStart = position + 1;
545
+ state = VALUE_START;
546
+ }
547
+ } else if (state === VALUE_START) {
548
+ state = VALUE;
549
+ }
550
+ }
551
+ }
552
+ }
553
+ }
554
+ };
555
+
556
+ var onFinish = function () {
557
+ if (currentState === OPEN || currentState === CONNECTING) {
558
+ currentState = WAITING;
559
+ if (timeout !== 0) {
560
+ clearTimeout(timeout);
561
+ timeout = 0;
562
+ }
563
+ timeout = setTimeout(function () {
564
+ onTimeout();
565
+ }, retry);
566
+ retry = clampDuration(Math.min(initialRetry * 16, retry * 2));
567
+
568
+ es.readyState = CONNECTING;
569
+ var event = new Event("error");
570
+ es.dispatchEvent(event);
571
+ fire(es, es.onerror, event);
572
+ }
573
+ };
574
+
575
+ var close = function () {
576
+ currentState = CLOSED;
577
+ transport.cancel();
578
+ if (timeout !== 0) {
579
+ clearTimeout(timeout);
580
+ timeout = 0;
581
+ }
582
+ es.readyState = CLOSED;
583
+ };
584
+
585
+ var onTimeout = function () {
586
+ timeout = 0;
587
+
588
+ if (currentState !== WAITING) {
589
+ if (!wasActivity) {
590
+ throwError(new Error("No activity within " + heartbeatTimeout + " milliseconds. Reconnecting."));
591
+ transport.cancel();
592
+ } else {
593
+ wasActivity = false;
594
+ timeout = setTimeout(function () {
595
+ onTimeout();
596
+ }, heartbeatTimeout);
597
+ }
598
+ return;
599
+ }
600
+
601
+ wasActivity = false;
602
+ timeout = setTimeout(function () {
603
+ onTimeout();
604
+ }, heartbeatTimeout);
605
+
606
+ currentState = CONNECTING;
607
+ dataBuffer = "";
608
+ eventTypeBuffer = "";
609
+ lastEventIdBuffer = lastEventId;
610
+ textBuffer = "";
611
+ fieldStart = 0;
612
+ valueStart = 0;
613
+ state = FIELD_START;
614
+
615
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=428916
616
+ // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers.
617
+ var requestURL = url;
618
+ if (url.slice(0, 5) !== "data:" &&
619
+ url.slice(0, 5) !== "blob:") {
620
+ requestURL = url + (url.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(lastEventId);
621
+ }
622
+ var requestHeaders = {};
623
+ requestHeaders["Accept"] = "text/event-stream";
624
+ if (headers != undefined) {
625
+ for (var name in headers) {
626
+ if (Object.prototype.hasOwnProperty.call(headers, name)) {
627
+ requestHeaders[name] = headers[name];
628
+ }
629
+ }
630
+ }
631
+ try {
632
+ transport.open(onStart, onProgress, onFinish, requestURL, withCredentials, requestHeaders);
633
+ } catch (error) {
634
+ close();
635
+ throw error;
636
+ }
637
+ };
638
+
639
+ es.url = url;
640
+ es.readyState = CONNECTING;
641
+ es.withCredentials = withCredentials;
642
+ es._close = close;
643
+
644
+ onTimeout();
645
+ }
646
+
647
+ EventSourcePolyfill.prototype = Object.create(EventTarget.prototype);
648
+ EventSourcePolyfill.prototype.CONNECTING = CONNECTING;
649
+ EventSourcePolyfill.prototype.OPEN = OPEN;
650
+ EventSourcePolyfill.prototype.CLOSED = CLOSED;
651
+ EventSourcePolyfill.prototype.close = function () {
652
+ this._close();
653
+ };
654
+
655
+ EventSourcePolyfill.CONNECTING = CONNECTING;
656
+ EventSourcePolyfill.OPEN = OPEN;
657
+ EventSourcePolyfill.CLOSED = CLOSED;
658
+ EventSourcePolyfill.prototype.withCredentials = undefined;
659
+
660
+ global.EventSourcePolyfill = EventSourcePolyfill;
661
+ global.NativeEventSource = NativeEventSource;
662
+
663
+ if (XMLHttpRequest != undefined && (NativeEventSource == undefined || !("withCredentials" in NativeEventSource.prototype))) {
664
+ // Why replace a native EventSource ?
665
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=444328
666
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=831392
667
+ // https://code.google.com/p/chromium/issues/detail?id=260144
668
+ // https://code.google.com/p/chromium/issues/detail?id=225654
669
+ // ...
670
+ global.EventSource = EventSourcePolyfill;
671
+ }
672
+
673
+ }(typeof window !== 'undefined' ? window : this));
inc/assets/js/eventsource.min.js CHANGED
@@ -1,6 +1,6 @@
1
- /** @license
2
- * eventsource.js
3
- * Available under MIT License (MIT)
4
- * https://github.com/Yaffle/EventSource/
5
- */
6
ย  !function(a){"use strict";function b(a){this.withCredentials=!1,this.responseType="",this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=p,this.onreadystatechange=p,this._contentType="",this._xhr=a,this._sendTimeout=0,this._abort=p}function c(a){this._xhr=new b(a)}function d(){this._listeners=Object.create(null)}function e(a){j(function(){throw a},0)}function f(a){this.type=a,this.target=void 0}function g(a,b){f.call(this,a),this.data=b.data,this.lastEventId=b.lastEventId}function h(a,b){d.call(this),this.onopen=void 0,this.onmessage=void 0,this.onerror=void 0,this.url=void 0,this.readyState=void 0,this.withCredentials=void 0,this._close=void 0,i(this,a,b)}function i(a,b,d){b=String(b);var h=void 0!=d&&Boolean(d.withCredentials),i=D(1e3),n=D(45e3),o="",p=i,A=!1,B=void 0!=d&&void 0!=d.headers?JSON.parse(JSON.stringify(d.headers)):void 0,F=void 0!=d&&void 0!=d.Transport?d.Transport:void 0!=m?m:l,G=new c(new F),H=0,I=q,J="",K="",L="",M="",N=v,O=0,P=0,Q=function(b,c,d){if(I===r)if(200===b&&void 0!=d&&z.test(d)){I=s,A=!0,p=i,a.readyState=s;var g=new f("open");a.dispatchEvent(g),E(a,a.onopen,g)}else{var h="";200!==b?(c&&(c=c.replace(/\s+/g," ")),h="EventSource's response has a status "+b+" "+c+" that is not 200. Aborting the connection."):h="EventSource's response has a Content-Type specifying an unsupported type: "+(void 0==d?"-":d.replace(/\s+/g," "))+". Aborting the connection.",e(new Error(h)),T();var g=new f("error");a.dispatchEvent(g),E(a,a.onerror,g)}},R=function(b){if(I===s){for(var c=-1,d=0;d<b.length;d+=1){var e=b.charCodeAt(d);(e==="\n".charCodeAt(0)||e==="\r".charCodeAt(0))&&(c=d)}var f=(-1!==c?M:"")+b.slice(0,c+1);M=(-1===c?M:"")+b.slice(c+1),""!==f&&(A=!0);for(var h=0;h<f.length;h+=1){var e=f.charCodeAt(h);if(N===u&&e==="\n".charCodeAt(0))N=v;else if(N===u&&(N=v),e==="\r".charCodeAt(0)||e==="\n".charCodeAt(0)){if(N!==v){N===w&&(P=h+1);var l=f.slice(O,P-1),m=f.slice(P+(h>P&&f.charCodeAt(P)===" ".charCodeAt(0)?1:0),h);"data"===l?(J+="\n",J+=m):"id"===l?K=m:"event"===l?L=m:"retry"===l?(i=C(m,i),p=i):"heartbeatTimeout"===l&&(n=C(m,n),0!==H&&(k(H),H=j(function(){U()},n)))}if(N===v){if(""!==J){o=K,""===L&&(L="message");var q=new g(L,{data:J.slice(1),lastEventId:K});if(a.dispatchEvent(q),"message"===L&&E(a,a.onmessage,q),I===t)return}J="",L=""}N=e==="\r".charCodeAt(0)?u:v}else N===v&&(O=h,N=w),N===w?e===":".charCodeAt(0)&&(P=h+1,N=x):N===x&&(N=y)}}},S=function(){if(I===s||I===r){I=q,0!==H&&(k(H),H=0),H=j(function(){U()},p),p=D(Math.min(16*i,2*p)),a.readyState=r;var b=new f("error");a.dispatchEvent(b),E(a,a.onerror,b)}},T=function(){I=t,G.cancel(),0!==H&&(k(H),H=0),a.readyState=t},U=function(){if(H=0,I!==q)return void(A?(A=!1,H=j(function(){U()},n)):(e(new Error("No activity within "+n+" milliseconds. Reconnecting.")),G.cancel()));A=!1,H=j(function(){U()},n),I=r,J="",L="",K=o,M="",O=0,P=0,N=v;var a=b;"data:"!==b.slice(0,5)&&"blob:"!==b.slice(0,5)&&(a=b+(-1===b.indexOf("?",0)?"?":"&")+"lastEventId="+encodeURIComponent(o));var c={};if(c.Accept="text/event-stream",void 0!=B)for(var d in B)Object.prototype.hasOwnProperty.call(B,d)&&(c[d]=B[d]);try{G.open(Q,R,S,a,h,c)}catch(f){throw T(),f}};a.url=b,a.readyState=r,a.withCredentials=h,a._close=T,U()}var j=a.setTimeout,k=a.clearTimeout,l=a.XMLHttpRequest,m=a.XDomainRequest,n=a.EventSource,o=a.document;null==Object.create&&(Object.create=function(a){function b(){}return b.prototype=a,new b});var p=function(){};b.prototype.open=function(a,b){this._abort(!0);var c=this,d=this._xhr,e=1,f=0;this._abort=function(a){0!==c._sendTimeout&&(k(c._sendTimeout),c._sendTimeout=0),(1===e||2===e||3===e)&&(e=4,d.onload=p,d.onerror=p,d.onabort=p,d.onprogress=p,d.onreadystatechange=p,d.abort(),0!==f&&(k(f),f=0),a||(c.readyState=4,c.onreadystatechange())),e=0};var g=function(){if(1===e){var a=0,b="",f=void 0;if("contentType"in d)a=200,b="OK",f=d.contentType;else try{a=d.status,b=d.statusText,f=d.getResponseHeader("Content-Type")}catch(g){a=0,b="",f=void 0}0!==a&&(e=2,c.readyState=2,c.status=a,c.statusText=b,c._contentType=f,c.onreadystatechange())}},h=function(){if(g(),2===e||3===e){e=3;var a="";try{a=d.responseText}catch(b){}c.readyState=3,c.responseText=a,c.onprogress()}},i=function(){h(),(1===e||2===e||3===e)&&(e=4,0!==f&&(k(f),f=0),c.readyState=4,c.onreadystatechange())},m=function(){void 0!=d&&(4===d.readyState?i():3===d.readyState?h():2===d.readyState&&g())},n=function(){f=j(function(){n()},500),3===d.readyState&&h()};d.onload=i,d.onerror=i,d.onabort=i,"sendAsBinary"in l.prototype||"mozAnon"in l.prototype||(d.onprogress=h),d.onreadystatechange=m,"contentType"in d&&(b+=(-1===b.indexOf("?",0)?"?":"&")+"padding=true"),d.open(a,b,!0),"readyState"in d&&(f=j(function(){n()},0))},b.prototype.abort=function(){this._abort(!1)},b.prototype.getResponseHeader=function(a){return this._contentType},b.prototype.setRequestHeader=function(a,b){var c=this._xhr;"setRequestHeader"in c&&c.setRequestHeader(a,b)},b.prototype.send=function(){if(!("ontimeout"in l.prototype)&&void 0!=o&&void 0!=o.readyState&&"complete"!==o.readyState){var a=this;return void(a._sendTimeout=j(function(){a._sendTimeout=0,a.send()},4))}var b=this._xhr;b.withCredentials=this.withCredentials,b.responseType=this.responseType;try{b.send(void 0)}catch(c){throw c}},c.prototype.open=function(a,b,c,d,e,f){var g=this._xhr;g.open("GET",d);var h=0;g.onprogress=function(){var a=g.responseText,c=a.slice(h);h+=c.length,b(c)},g.onreadystatechange=function(){if(2===g.readyState){var b=g.status,d=g.statusText,e=g.getResponseHeader("Content-Type");a(b,d,e)}else 4===g.readyState&&c()},g.withCredentials=e,g.responseType="text";for(var i in f)Object.prototype.hasOwnProperty.call(f,i)&&g.setRequestHeader(i,f[i]);g.send()},c.prototype.cancel=function(){var a=this._xhr;a.abort()},d.prototype.dispatchEvent=function(a){a.target=this;var b=this._listeners[a.type];if(void 0!=b)for(var c=b.length,d=0;c>d;d+=1){var f=b[d];try{"function"==typeof f.handleEvent?f.handleEvent(a):f.call(this,a)}catch(g){e(g)}}},d.prototype.addEventListener=function(a,b){a=String(a);var c=this._listeners,d=c[a];void 0==d&&(d=[],c[a]=d);for(var e=!1,f=0;f<d.length;f+=1)d[f]===b&&(e=!0);e||d.push(b)},d.prototype.removeEventListener=function(a,b){a=String(a);var c=this._listeners,d=c[a];if(void 0!=d){for(var e=[],f=0;f<d.length;f+=1)d[f]!==b&&e.push(d[f]);0===e.length?delete c[a]:c[a]=e}},g.prototype=Object.create(f.prototype);var q=-1,r=0,s=1,t=2,u=-1,v=0,w=1,x=2,y=3,z=/^text\/event\-stream;?(\s*charset\=utf\-8)?$/i,A=1e3,B=18e6,C=function(a,b){var c=parseInt(a,10);return c!==c&&(c=b),D(c)},D=function(a){return Math.min(Math.max(a,A),B)},E=function(a,b,c){try{"function"==typeof b&&b.call(a,c)}catch(d){e(d)}};h.prototype=Object.create(d.prototype),h.prototype.CONNECTING=r,h.prototype.OPEN=s,h.prototype.CLOSED=t,h.prototype.close=function(){this._close()},h.CONNECTING=r,h.OPEN=s,h.CLOSED=t,h.prototype.withCredentials=void 0,a.EventSourcePolyfill=h,a.NativeEventSource=n,void 0==l||void 0!=n&&"withCredentials"in n.prototype||(a.EventSource=h)}("undefined"!=typeof window?window:this);
1
+ /** @license
2
+ * eventsource.js
3
+ * Available under MIT License (MIT)
4
+ * https://github.com/Yaffle/EventSource/
5
+ */
6
ย  !function(a){"use strict";function b(a){this.withCredentials=!1,this.responseType="",this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=p,this.onreadystatechange=p,this._contentType="",this._xhr=a,this._sendTimeout=0,this._abort=p}function c(a){this._xhr=new b(a)}function d(){this._listeners=Object.create(null)}function e(a){j(function(){throw a},0)}function f(a){this.type=a,this.target=void 0}function g(a,b){f.call(this,a),this.data=b.data,this.lastEventId=b.lastEventId}function h(a,b){d.call(this),this.onopen=void 0,this.onmessage=void 0,this.onerror=void 0,this.url=void 0,this.readyState=void 0,this.withCredentials=void 0,this._close=void 0,i(this,a,b)}function i(a,b,d){b=String(b);var h=void 0!=d&&Boolean(d.withCredentials),i=D(1e3),n=D(45e3),o="",p=i,A=!1,B=void 0!=d&&void 0!=d.headers?JSON.parse(JSON.stringify(d.headers)):void 0,F=void 0!=d&&void 0!=d.Transport?d.Transport:void 0!=m?m:l,G=new c(new F),H=0,I=q,J="",K="",L="",M="",N=v,O=0,P=0,Q=function(b,c,d){if(I===r)if(200===b&&void 0!=d&&z.test(d)){I=s,A=!0,p=i,a.readyState=s;var g=new f("open");a.dispatchEvent(g),E(a,a.onopen,g)}else{var h="";200!==b?(c&&(c=c.replace(/\s+/g," ")),h="EventSource's response has a status "+b+" "+c+" that is not 200. Aborting the connection."):h="EventSource's response has a Content-Type specifying an unsupported type: "+(void 0==d?"-":d.replace(/\s+/g," "))+". Aborting the connection.",e(new Error(h)),T();var g=new f("error");a.dispatchEvent(g),E(a,a.onerror,g)}},R=function(b){if(I===s){for(var c=-1,d=0;d<b.length;d+=1){var e=b.charCodeAt(d);(e==="\n".charCodeAt(0)||e==="\r".charCodeAt(0))&&(c=d)}var f=(-1!==c?M:"")+b.slice(0,c+1);M=(-1===c?M:"")+b.slice(c+1),""!==f&&(A=!0);for(var h=0;h<f.length;h+=1){var e=f.charCodeAt(h);if(N===u&&e==="\n".charCodeAt(0))N=v;else if(N===u&&(N=v),e==="\r".charCodeAt(0)||e==="\n".charCodeAt(0)){if(N!==v){N===w&&(P=h+1);var l=f.slice(O,P-1),m=f.slice(P+(h>P&&f.charCodeAt(P)===" ".charCodeAt(0)?1:0),h);"data"===l?(J+="\n",J+=m):"id"===l?K=m:"event"===l?L=m:"retry"===l?(i=C(m,i),p=i):"heartbeatTimeout"===l&&(n=C(m,n),0!==H&&(k(H),H=j(function(){U()},n)))}if(N===v){if(""!==J){o=K,""===L&&(L="message");var q=new g(L,{data:J.slice(1),lastEventId:K});if(a.dispatchEvent(q),"message"===L&&E(a,a.onmessage,q),I===t)return}J="",L=""}N=e==="\r".charCodeAt(0)?u:v}else N===v&&(O=h,N=w),N===w?e===":".charCodeAt(0)&&(P=h+1,N=x):N===x&&(N=y)}}},S=function(){if(I===s||I===r){I=q,0!==H&&(k(H),H=0),H=j(function(){U()},p),p=D(Math.min(16*i,2*p)),a.readyState=r;var b=new f("error");a.dispatchEvent(b),E(a,a.onerror,b)}},T=function(){I=t,G.cancel(),0!==H&&(k(H),H=0),a.readyState=t},U=function(){if(H=0,I!==q)return void(A?(A=!1,H=j(function(){U()},n)):(e(new Error("No activity within "+n+" milliseconds. Reconnecting.")),G.cancel()));A=!1,H=j(function(){U()},n),I=r,J="",L="",K=o,M="",O=0,P=0,N=v;var a=b;"data:"!==b.slice(0,5)&&"blob:"!==b.slice(0,5)&&(a=b+(-1===b.indexOf("?",0)?"?":"&")+"lastEventId="+encodeURIComponent(o));var c={};if(c.Accept="text/event-stream",void 0!=B)for(var d in B)Object.prototype.hasOwnProperty.call(B,d)&&(c[d]=B[d]);try{G.open(Q,R,S,a,h,c)}catch(f){throw T(),f}};a.url=b,a.readyState=r,a.withCredentials=h,a._close=T,U()}var j=a.setTimeout,k=a.clearTimeout,l=a.XMLHttpRequest,m=a.XDomainRequest,n=a.EventSource,o=a.document;null==Object.create&&(Object.create=function(a){function b(){}return b.prototype=a,new b});var p=function(){};b.prototype.open=function(a,b){this._abort(!0);var c=this,d=this._xhr,e=1,f=0;this._abort=function(a){0!==c._sendTimeout&&(k(c._sendTimeout),c._sendTimeout=0),(1===e||2===e||3===e)&&(e=4,d.onload=p,d.onerror=p,d.onabort=p,d.onprogress=p,d.onreadystatechange=p,d.abort(),0!==f&&(k(f),f=0),a||(c.readyState=4,c.onreadystatechange())),e=0};var g=function(){if(1===e){var a=0,b="",f=void 0;if("contentType"in d)a=200,b="OK",f=d.contentType;else try{a=d.status,b=d.statusText,f=d.getResponseHeader("Content-Type")}catch(g){a=0,b="",f=void 0}0!==a&&(e=2,c.readyState=2,c.status=a,c.statusText=b,c._contentType=f,c.onreadystatechange())}},h=function(){if(g(),2===e||3===e){e=3;var a="";try{a=d.responseText}catch(b){}c.readyState=3,c.responseText=a,c.onprogress()}},i=function(){h(),(1===e||2===e||3===e)&&(e=4,0!==f&&(k(f),f=0),c.readyState=4,c.onreadystatechange())},m=function(){void 0!=d&&(4===d.readyState?i():3===d.readyState?h():2===d.readyState&&g())},n=function(){f=j(function(){n()},500),3===d.readyState&&h()};d.onload=i,d.onerror=i,d.onabort=i,"sendAsBinary"in l.prototype||"mozAnon"in l.prototype||(d.onprogress=h),d.onreadystatechange=m,"contentType"in d&&(b+=(-1===b.indexOf("?",0)?"?":"&")+"padding=true"),d.open(a,b,!0),"readyState"in d&&(f=j(function(){n()},0))},b.prototype.abort=function(){this._abort(!1)},b.prototype.getResponseHeader=function(a){return this._contentType},b.prototype.setRequestHeader=function(a,b){var c=this._xhr;"setRequestHeader"in c&&c.setRequestHeader(a,b)},b.prototype.send=function(){if(!("ontimeout"in l.prototype)&&void 0!=o&&void 0!=o.readyState&&"complete"!==o.readyState){var a=this;return void(a._sendTimeout=j(function(){a._sendTimeout=0,a.send()},4))}var b=this._xhr;b.withCredentials=this.withCredentials,b.responseType=this.responseType;try{b.send(void 0)}catch(c){throw c}},c.prototype.open=function(a,b,c,d,e,f){var g=this._xhr;g.open("GET",d);var h=0;g.onprogress=function(){var a=g.responseText,c=a.slice(h);h+=c.length,b(c)},g.onreadystatechange=function(){if(2===g.readyState){var b=g.status,d=g.statusText,e=g.getResponseHeader("Content-Type");a(b,d,e)}else 4===g.readyState&&c()},g.withCredentials=e,g.responseType="text";for(var i in f)Object.prototype.hasOwnProperty.call(f,i)&&g.setRequestHeader(i,f[i]);g.send()},c.prototype.cancel=function(){var a=this._xhr;a.abort()},d.prototype.dispatchEvent=function(a){a.target=this;var b=this._listeners[a.type];if(void 0!=b)for(var c=b.length,d=0;c>d;d+=1){var f=b[d];try{"function"==typeof f.handleEvent?f.handleEvent(a):f.call(this,a)}catch(g){e(g)}}},d.prototype.addEventListener=function(a,b){a=String(a);var c=this._listeners,d=c[a];void 0==d&&(d=[],c[a]=d);for(var e=!1,f=0;f<d.length;f+=1)d[f]===b&&(e=!0);e||d.push(b)},d.prototype.removeEventListener=function(a,b){a=String(a);var c=this._listeners,d=c[a];if(void 0!=d){for(var e=[],f=0;f<d.length;f+=1)d[f]!==b&&e.push(d[f]);0===e.length?delete c[a]:c[a]=e}},g.prototype=Object.create(f.prototype);var q=-1,r=0,s=1,t=2,u=-1,v=0,w=1,x=2,y=3,z=/^text\/event\-stream;?(\s*charset\=utf\-8)?$/i,A=1e3,B=18e6,C=function(a,b){var c=parseInt(a,10);return c!==c&&(c=b),D(c)},D=function(a){return Math.min(Math.max(a,A),B)},E=function(a,b,c){try{"function"==typeof b&&b.call(a,c)}catch(d){e(d)}};h.prototype=Object.create(d.prototype),h.prototype.CONNECTING=r,h.prototype.OPEN=s,h.prototype.CLOSED=t,h.prototype.close=function(){this._close()},h.CONNECTING=r,h.OPEN=s,h.CLOSED=t,h.prototype.withCredentials=void 0,a.EventSourcePolyfill=h,a.NativeEventSource=n,void 0==l||void 0!=n&&"withCredentials"in n.prototype||(a.EventSource=h)}("undefined"!=typeof window?window:this);
inc/assets/js/render-grid.js CHANGED
@@ -1,519 +1,519 @@
1
- (function($){
2
- AstraRender = {
3
-
4
- _ref : null,
5
-
6
- /**
7
- * _api_params = {
8
- * 'search' : '',
9
- * 'per_page' : '',
10
- * 'astra-site-category' : '',
11
- * 'astra-site-page-builder' : '',
12
- * 'page' : '',
13
- * };
14
- *
15
- * E.g. per_page=<page-id>&astra-site-category=<category-ids>&astra-site-page-builder=<page-builder-ids>&page=<page>
16
- */
17
- _api_params : {},
18
- _breakpoint : 768,
19
-
20
- init: function()
21
- {
22
- this._resetPagedCount();
23
- this._bind();
24
- this._showFilters();
25
- },
26
-
27
- /**
28
- * Binds events for the Astra Sites.
29
- *
30
- * @since 1.0.0
31
- * @access private
32
- * @method _bind
33
- */
34
- _bind: function()
35
- {
36
- $( document ).on('astra-sites-api-request-error' , AstraRender._addSuggestionBox );
37
- $( document ).on('astra-sites-api-request-fail' , AstraRender._addSuggestionBox );
38
- $( document ).on('astra-api-post-loaded-on-scroll' , AstraRender._reinitGridScrolled );
39
- $( document ).on('astra-api-post-loaded' , AstraRender._reinitGrid );
40
- $( document ).on('astra-api-category-loaded' , AstraRender._addFilters );
41
- $( document ).on('astra-api-all-category-loaded' , AstraRender._loadFirstGrid );
42
-
43
- // Event's for API request.
44
- $( document ).on('click' , '.filter-links a', AstraRender._filterClick );
45
- $( document ).on('keyup input' , '#wp-filter-search-input', AstraRender._search );
46
- $( document ).on('scroll' , AstraRender._scroll );
47
-
48
- },
49
-
50
- /**
51
- * On Filter Clicked
52
- *
53
- * Prepare Before API Request:
54
- * - Empty search input field to avoid search term on filter click.
55
- * - Remove Inline Height
56
- * - Added 'hide-me' class to hide the 'No more sites!' string.
57
- * - Added 'loading-content' for body.
58
- * - Show spinner.
59
- */
60
- _filterClick: function( event ) {
61
-
62
- event.preventDefault();
63
-
64
- if( $( this ).parents('.astra-site-category').length && ! $('body').hasClass('page-builder-selected') ) {
65
- return;
66
- }
67
-
68
- $(this).parents('.filter-links').find('a').removeClass('current');
69
- $(this).addClass('current');
70
-
71
- // Prepare Before Search.
72
- $('.no-more-demos').addClass('hide-me');
73
- $('.astra-sites-suggestions').remove();
74
-
75
- // Empty the search input only click on category filter not on page builder filter.
76
- if( $(this).parents('.filter-links').hasClass('astra-site-category') ) {
77
- $('#wp-filter-search-input').val('');
78
- }
79
- $('#astra-sites').hide().css('height', '');
80
-
81
- $('body').addClass('loading-content');
82
- $('#astra-sites-admin').find('.spinner').removeClass('hide-me');
83
-
84
- // Show sites.
85
- AstraRender._showSites();
86
- },
87
-
88
- /**
89
- * Search Site.
90
- *
91
- * Prepare Before API Request:
92
- * - Remove Inline Height
93
- * - Added 'hide-me' class to hide the 'No more sites!' string.
94
- * - Added 'loading-content' for body.
95
- * - Show spinner.
96
- */
97
- _search: function() {
98
-
99
- if( ! $('body').hasClass('page-builder-selected') ) {
100
- return;
101
- }
102
-
103
- $this = jQuery('#wp-filter-search-input').val();
104
-
105
- // Prepare Before Search.
106
- $('#astra-sites').hide().css('height', '');
107
- $('.no-more-demos').addClass('hide-me');
108
- $('.astra-sites-suggestions').remove();
109
-
110
- $('body').addClass('loading-content');
111
- $('#astra-sites-admin').find('.spinner').removeClass('hide-me');
112
-
113
- window.clearTimeout(AstraRender._ref);
114
- AstraRender._ref = window.setTimeout(function () {
115
- AstraRender._ref = null;
116
-
117
- AstraRender._resetPagedCount();
118
- jQuery('body').addClass('loading-content');
119
- jQuery('body').attr('data-astra-demo-search', $this);
120
-
121
- AstraRender._showSites();
122
-
123
- }, 500);
124
-
125
- },
126
-
127
- /**
128
- * On Scroll
129
- */
130
- _scroll: function(event) {
131
-
132
- if( ! $('body').hasClass('page-builder-selected') ) {
133
- return;
134
- }
135
-
136
- if( ! $('body').hasClass('listed-all-sites') ) {
137
-
138
- var scrollDistance = jQuery(window).scrollTop();
139
-
140
- var themesBottom = Math.abs(jQuery(window).height() - jQuery('#astra-sites').offset().top - jQuery('#astra-sites').height());
141
- themesBottom = themesBottom - 100;
142
-
143
- ajaxLoading = jQuery('body').data('scrolling');
144
-
145
- if (scrollDistance > themesBottom && ajaxLoading == false) {
146
- AstraRender._updatedPagedCount();
147
-
148
- if( ! $('#astra-sites .no-themes').length ) {
149
- $('#astra-sites-admin').find('.spinner').addClass('is-active');
150
- }
151
-
152
- jQuery('body').data('scrolling', true);
153
-
154
- /**
155
- * @see _reinitGridScrolled() which called in trigger 'astra-api-post-loaded-on-scroll'
156
- */
157
- AstraRender._showSites( false, 'astra-api-post-loaded-on-scroll' );
158
- }
159
- }
160
- },
161
-
162
- _apiAddParam_status: function() {
163
- if( astraRenderGrid.sites && astraRenderGrid.sites.status ) {
164
- AstraRender._api_params['status'] = astraRenderGrid.sites.status;
165
- }
166
- },
167
-
168
- // Add 'search'
169
- _apiAddParam_search: function() {
170
- var search_val = jQuery('#wp-filter-search-input').val() || '';
171
- if( '' !== search_val ) {
172
- AstraRender._api_params['search'] = search_val;
173
- }
174
- },
175
-
176
- _apiAddParam_per_page: function() {
177
- // Add 'per_page'
178
- var per_page_val = 15;
179
- if( astraRenderGrid.sites && astraRenderGrid.sites["par-page"] ) {
180
- per_page_val = parseInt( astraRenderGrid.sites["par-page"] );
181
- }
182
- AstraRender._api_params['per_page'] = per_page_val;
183
- },
184
-
185
- _apiAddParam_astra_site_category: function() {
186
- // Add 'astra-site-category'
187
- var selected_category_id = jQuery('.filter-links.astra-site-category').find('.current').data('group') || '';
188
- if( '' !== selected_category_id && 'all' !== selected_category_id ) {
189
- AstraRender._api_params['astra-site-category'] = selected_category_id;
190
- } else if( 'astra-site-category' in astraRenderGrid.settings ) {
191
-
192
- if( $.inArray('all', astraRenderGrid.settings['astra-site-category']) !== -1 ) {
193
- var storedCategories = astraRenderGrid.settings['astra-site-category'];
194
- storedCategories = jQuery.grep(storedCategories, function(value) {
195
- return value != 'all';
196
- });
197
- AstraRender._api_params['astra-site-category'] = storedCategories.join(',');
198
- }
199
- }
200
- },
201
-
202
- _apiAddParam_astra_site_page_builder: function() {
203
- // Add 'astra-site-page-builder'
204
- var selected_page_builder_id = jQuery('.filter-links.astra-site-page-builder').find('.current').data('group') || '';
205
- if( '' !== selected_page_builder_id && 'all' !== selected_page_builder_id ) {
206
- AstraRender._api_params['astra-site-page-builder'] = selected_page_builder_id;
207
- } else if( 'astra-site-page-builder' in astraRenderGrid.settings ) {
208
- if( $.inArray('all', astraRenderGrid.settings['astra-site-page-builder']) !== -1 ) {
209
- var storedBuilders = astraRenderGrid.settings['astra-site-page-builder'];
210
- storedBuilders = jQuery.grep(storedBuilders, function(value) {
211
- return value != 'all';
212
- });
213
- AstraRender._api_params['astra-site-page-builder'] = storedBuilders.join(',');
214
- }
215
- }
216
- },
217
-
218
- _apiAddParam_page: function() {
219
- // Add 'page'
220
- var page_val = parseInt(jQuery('body').attr('data-astra-demo-paged')) || 1;
221
- AstraRender._api_params['page'] = page_val;
222
- },
223
-
224
- _apiAddParam_purchase_key: function() {
225
- if( astraRenderGrid.sites && astraRenderGrid.sites.purchase_key ) {
226
- AstraRender._api_params['purchase_key'] = astraRenderGrid.sites.purchase_key;
227
- }
228
- },
229
-
230
- _apiAddParam_site_url: function() {
231
- if( astraRenderGrid.sites && astraRenderGrid.sites.site_url ) {
232
- AstraRender._api_params['site_url'] = astraRenderGrid.sites.site_url;
233
- }
234
- },
235
-
236
- /**
237
- * Show Sites
238
- *
239
- * Params E.g. per_page=<page-id>&astra-site-category=<category-ids>&astra-site-page-builder=<page-builder-ids>&page=<page>
240
- *
241
- * @param {Boolean} resetPagedCount Reset Paged Count.
242
- * @param {String} trigger Filtered Trigger.
243
- */
244
- _showSites: function( resetPagedCount, trigger ) {
245
-
246
- if( undefined === resetPagedCount ) {
247
- resetPagedCount = true
248
- }
249
-
250
- if( undefined === trigger ) {
251
- trigger = 'astra-api-post-loaded';
252
- }
253
-
254
- if( resetPagedCount ) {
255
- AstraRender._resetPagedCount();
256
- }
257
-
258
- // Add Params for API request.
259
- AstraRender._api_params = {};
260
-
261
- AstraRender._apiAddParam_status();
262
- AstraRender._apiAddParam_search();
263
- AstraRender._apiAddParam_per_page();
264
- AstraRender._apiAddParam_astra_site_category();
265
- AstraRender._apiAddParam_astra_site_page_builder();
266
- AstraRender._apiAddParam_page();
267
- AstraRender._apiAddParam_site_url();
268
- AstraRender._apiAddParam_purchase_key();
269
-
270
- // API Request.
271
- var api_post = {
272
- slug: 'astra-sites?' + decodeURIComponent( $.param( AstraRender._api_params ) ),
273
- trigger: trigger,
274
- };
275
-
276
- AstraSitesAPI._api_request( api_post );
277
-
278
- },
279
-
280
- /**
281
- * Get Category Params
282
- *
283
- * @param {string} category_slug Category Slug.
284
- * @return {mixed} Add `include=<category-ids>` in API request.
285
- */
286
- _getCategoryParams: function( category_slug ) {
287
-
288
- // Has category?
289
- if( category_slug in astraRenderGrid.settings ) {
290
-
291
- var storedBuilders = astraRenderGrid.settings[ category_slug ];
292
-
293
- // Remove `all` from stored list?
294
- storedBuilders = jQuery.grep(storedBuilders, function(value) {
295
- return value != 'all';
296
- });
297
-
298
- return '?include='+storedBuilders.join(',');
299
- }
300
-
301
- return '';
302
- },
303
-
304
- /**
305
- * Get All Select Status
306
- *
307
- * @param {string} category_slug Category Slug.
308
- * @return {boolean} Return true/false.
309
- */
310
- _getCategoryAllSelectStatus: function( category_slug ) {
311
-
312
- // Has category?
313
- if( category_slug in astraRenderGrid.settings ) {
314
-
315
- // Has `all` in stored list?
316
- if( $.inArray('all', astraRenderGrid.settings[ category_slug ]) === -1 ) {
317
- return false;
318
- }
319
- }
320
-
321
- return true;
322
- },
323
-
324
- /**
325
- * Show Filters
326
- */
327
- _showFilters: function() {
328
-
329
- /**
330
- * Categories
331
- */
332
- var category_slug = 'astra-site-page-builder';
333
- var category = {
334
- slug : category_slug + AstraRender._getCategoryParams( category_slug ),
335
- id : category_slug,
336
- class : category_slug,
337
- trigger : 'astra-api-category-loaded',
338
- wrapper_class : 'filter-links',
339
- show_all : false,
340
- };
341
-
342
- AstraSitesAPI._api_request( category );
343
-
344
- /**
345
- * Page Builder
346
- */
347
- var category_slug = 'astra-site-category';
348
- var category = {
349
- slug : category_slug + AstraRender._getCategoryParams( category_slug ),
350
- id : category_slug,
351
- class : category_slug,
352
- trigger : 'astra-api-all-category-loaded',
353
- wrapper_class : 'filter-links',
354
- show_all : AstraRender._getCategoryAllSelectStatus( category_slug ),
355
- };
356
-
357
- AstraSitesAPI._api_request( category );
358
- },
359
-
360
- /**
361
- * Load First Grid.
362
- *
363
- * This is triggered after all category loaded.
364
- *
365
- * @param {object} event Event Object.
366
- */
367
- _loadFirstGrid: function( event, data ) {
368
- AstraRender._addFilters( event, data );
369
- setTimeout(function() {
370
- $('body').removeClass('loading-content');
371
- }, 100);
372
- },
373
-
374
- /**
375
- * Append filters.
376
- *
377
- * @param {object} event Object.
378
- * @param {object} data API response data.
379
- */
380
- _addFilters: function( event, data ) {
381
- event.preventDefault();
382
-
383
- if( jQuery('#' + data.args.id).length ) {
384
- var template = wp.template('astra-site-filters');
385
- jQuery('#' + data.args.id).html(template( data ));
386
- }
387
-
388
- },
389
-
390
- /**
391
- * Append sites on scroll.
392
- *
393
- * @param {object} event Object.
394
- * @param {object} data API response data.
395
- */
396
- _reinitGridScrolled: function( event, data ) {
397
-
398
- var template = wp.template('astra-sites-list');
399
-
400
- if( data.items.length > 0 ) {
401
-
402
- $('body').removeClass( 'loading-content' );
403
- $('.filter-count .count').text( data.items_count );
404
-
405
- setTimeout(function() {
406
- jQuery('#astra-sites').append(template( data ));
407
-
408
- AstraRender._imagesLoaded();
409
- }, 800);
410
- } else {
411
-
412
- $('body').addClass('listed-all-sites');
413
-
414
- // $('#astra-sites-admin').find('.spinner').removeClass('is-active');
415
- }
416
-
417
- },
418
-
419
- /**
420
- * Update Astra sites list.
421
- *
422
- * @param {object} event Object.
423
- * @param {object} data API response data.
424
- */
425
- _reinitGrid: function( event, data ) {
426
-
427
- var template = wp.template('astra-sites-list');
428
-
429
- $('body').addClass( 'page-builder-selected' );
430
- $('body').removeClass( 'loading-content' );
431
- $('.filter-count .count').text( data.items_count );
432
-
433
- jQuery('body').attr('data-astra-demo-last-request', data.items_count);
434
-
435
- jQuery('#astra-sites').show().html(template( data ));
436
-
437
- AstraRender._imagesLoaded();
438
-
439
- $('#astra-sites-admin').find('.spinner').removeClass('is-active');
440
-
441
- if( data.items_count <= 0 ) {
442
- $('#astra-sites-admin').find('.spinner').removeClass('is-active');
443
- $('.no-more-demos').addClass('hide-me');
444
- $('.astra-sites-suggestions').remove();
445
-
446
- } else {
447
- $('body').removeClass('listed-all-sites');
448
- }
449
-
450
-
451
- },
452
-
453
- /**
454
- * Check image loaded with function `imagesLoaded()`
455
- */
456
- _imagesLoaded: function() {
457
-
458
- var self = jQuery('#sites-filter.execute-only-one-time a');
459
-
460
- $('.astra-sites-grid').imagesLoaded()
461
- .always( function( instance ) {
462
- if( jQuery( window ).outerWidth() > AstraRender._breakpoint ) {
463
- // $('#astra-sites').masonry('reload');
464
- }
465
-
466
- $('#astra-sites-admin').find('.spinner').removeClass('is-active');
467
- })
468
- .progress( function( instance, image ) {
469
- var result = image.isLoaded ? 'loaded' : 'broken';
470
- });
471
-
472
- },
473
-
474
- /**
475
- * Add Suggestion Box
476
- */
477
- _addSuggestionBox: function() {
478
- $('#astra-sites-admin').find('.spinner').removeClass('is-active').addClass('hide-me');
479
-
480
- $('#astra-sites-admin').find('.no-more-demos').removeClass('hide-me');
481
- var template = wp.template('astra-sites-suggestions');
482
- if( ! $( '.astra-sites-suggestions').length ) {
483
- $('#astra-sites').append( template );
484
- }
485
- },
486
-
487
- /**
488
- * Update Page Count.
489
- */
490
- _updatedPagedCount: function() {
491
- paged = parseInt(jQuery('body').attr('data-astra-demo-paged'));
492
- jQuery('body').attr('data-astra-demo-paged', paged + 1);
493
- window.setTimeout(function () {
494
- jQuery('body').data('scrolling', false);
495
- }, 800);
496
- },
497
-
498
- /**
499
- * Reset Page Count.
500
- */
501
- _resetPagedCount: function() {
502
-
503
- jQuery('body').attr('data-astra-demo-last-request', '1');
504
- jQuery('body').attr('data-astra-demo-paged', '1');
505
- jQuery('body').attr('data-astra-demo-search', '');
506
- jQuery('body').attr('data-scrolling', false);
507
-
508
- }
509
-
510
- };
511
-
512
- /**
513
- * Initialize AstraRender
514
- */
515
- $(function(){
516
- AstraRender.init();
517
- });
518
-
519
ย  })(jQuery);
1
+ (function($){
2
+ AstraRender = {
3
+
4
+ _ref : null,
5
+
6
+ /**
7
+ * _api_params = {
8
+ * 'search' : '',
9
+ * 'per_page' : '',
10
+ * 'astra-site-category' : '',
11
+ * 'astra-site-page-builder' : '',
12
+ * 'page' : '',
13
+ * };
14
+ *
15
+ * E.g. per_page=<page-id>&astra-site-category=<category-ids>&astra-site-page-builder=<page-builder-ids>&page=<page>
16
+ */
17
+ _api_params : {},
18
+ _breakpoint : 768,
19
+
20
+ init: function()
21
+ {
22
+ this._resetPagedCount();
23
+ this._bind();
24
+ this._showFilters();
25
+ },
26
+
27
+ /**
28
+ * Binds events for the Astra Sites.
29
+ *
30
+ * @since 1.0.0
31
+ * @access private
32
+ * @method _bind
33
+ */
34
+ _bind: function()
35
+ {
36
+ $( document ).on('astra-sites-api-request-error' , AstraRender._addSuggestionBox );
37
+ $( document ).on('astra-sites-api-request-fail' , AstraRender._addSuggestionBox );
38
+ $( document ).on('astra-api-post-loaded-on-scroll' , AstraRender._reinitGridScrolled );
39
+ $( document ).on('astra-api-post-loaded' , AstraRender._reinitGrid );
40
+ $( document ).on('astra-api-category-loaded' , AstraRender._addFilters );
41
+ $( document ).on('astra-api-all-category-loaded' , AstraRender._loadFirstGrid );
42
+
43
+ // Event's for API request.
44
+ $( document ).on('click' , '.filter-links a', AstraRender._filterClick );
45
+ $( document ).on('keyup input' , '#wp-filter-search-input', AstraRender._search );
46
+ $( document ).on('scroll' , AstraRender._scroll );
47
+
48
+ },
49
+
50
+ /**
51
+ * On Filter Clicked
52
+ *
53
+ * Prepare Before API Request:
54
+ * - Empty search input field to avoid search term on filter click.
55
+ * - Remove Inline Height
56
+ * - Added 'hide-me' class to hide the 'No more sites!' string.
57
+ * - Added 'loading-content' for body.
58
+ * - Show spinner.
59
+ */
60
+ _filterClick: function( event ) {
61
+
62
+ event.preventDefault();
63
+
64
+ if( $( this ).parents('.astra-site-category').length && ! $('body').hasClass('page-builder-selected') ) {
65
+ return;
66
+ }
67
+
68
+ $(this).parents('.filter-links').find('a').removeClass('current');
69
+ $(this).addClass('current');
70
+
71
+ // Prepare Before Search.
72
+ $('.no-more-demos').addClass('hide-me');
73
+ $('.astra-sites-suggestions').remove();
74
+
75
+ // Empty the search input only click on category filter not on page builder filter.
76
+ if( $(this).parents('.filter-links').hasClass('astra-site-category') ) {
77
+ $('#wp-filter-search-input').val('');
78
+ }
79
+ $('#astra-sites').hide().css('height', '');
80
+
81
+ $('body').addClass('loading-content');
82
+ $('#astra-sites-admin').find('.spinner').removeClass('hide-me');
83
+
84
+ // Show sites.
85
+ AstraRender._showSites();
86
+ },
87
+
88
+ /**
89
+ * Search Site.
90
+ *
91
+ * Prepare Before API Request:
92
+ * - Remove Inline Height
93
+ * - Added 'hide-me' class to hide the 'No more sites!' string.
94
+ * - Added 'loading-content' for body.
95
+ * - Show spinner.
96
+ */
97
+ _search: function() {
98
+
99
+ if( ! $('body').hasClass('page-builder-selected') ) {
100
+ return;
101
+ }
102
+
103
+ $this = jQuery('#wp-filter-search-input').val();
104
+
105
+ // Prepare Before Search.
106
+ $('#astra-sites').hide().css('height', '');
107
+ $('.no-more-demos').addClass('hide-me');
108
+ $('.astra-sites-suggestions').remove();
109
+
110
+ $('body').addClass('loading-content');
111
+ $('#astra-sites-admin').find('.spinner').removeClass('hide-me');
112
+
113
+ window.clearTimeout(AstraRender._ref);
114
+ AstraRender._ref = window.setTimeout(function () {
115
+ AstraRender._ref = null;
116
+
117
+ AstraRender._resetPagedCount();
118
+ jQuery('body').addClass('loading-content');
119
+ jQuery('body').attr('data-astra-demo-search', $this);
120
+
121
+ AstraRender._showSites();
122
+
123
+ }, 500);
124
+
125
+ },
126
+
127
+ /**
128
+ * On Scroll
129
+ */
130
+ _scroll: function(event) {
131
+
132
+ if( ! $('body').hasClass('page-builder-selected') ) {
133
+ return;
134
+ }
135
+
136
+ if( ! $('body').hasClass('listed-all-sites') ) {
137
+
138
+ var scrollDistance = jQuery(window).scrollTop();
139
+
140
+ var themesBottom = Math.abs(jQuery(window).height() - jQuery('#astra-sites').offset().top - jQuery('#astra-sites').height());
141
+ themesBottom = themesBottom - 100;
142
+
143
+ ajaxLoading = jQuery('body').data('scrolling');
144
+
145
+ if (scrollDistance > themesBottom && ajaxLoading == false) {
146
+ AstraRender._updatedPagedCount();
147
+
148
+ if( ! $('#astra-sites .no-themes').length ) {
149
+ $('#astra-sites-admin').find('.spinner').addClass('is-active');
150
+ }
151
+
152
+ jQuery('body').data('scrolling', true);
153
+
154
+ /**
155
+ * @see _reinitGridScrolled() which called in trigger 'astra-api-post-loaded-on-scroll'
156
+ */
157
+ AstraRender._showSites( false, 'astra-api-post-loaded-on-scroll' );
158
+ }
159
+ }
160
+ },
161
+
162
+ _apiAddParam_status: function() {
163
+ if( astraRenderGrid.sites && astraRenderGrid.sites.status ) {
164
+ AstraRender._api_params['status'] = astraRenderGrid.sites.status;
165
+ }
166
+ },
167
+
168
+ // Add 'search'
169
+ _apiAddParam_search: function() {
170
+ var search_val = jQuery('#wp-filter-search-input').val() || '';
171
+ if( '' !== search_val ) {
172
+ AstraRender._api_params['search'] = search_val;
173
+ }
174
+ },
175
+
176
+ _apiAddParam_per_page: function() {
177
+ // Add 'per_page'
178
+ var per_page_val = 15;
179
+ if( astraRenderGrid.sites && astraRenderGrid.sites["par-page"] ) {
180
+ per_page_val = parseInt( astraRenderGrid.sites["par-page"] );
181
+ }
182
+ AstraRender._api_params['per_page'] = per_page_val;
183
+ },
184
+
185
+ _apiAddParam_astra_site_category: function() {
186
+ // Add 'astra-site-category'
187
+ var selected_category_id = jQuery('.filter-links.astra-site-category').find('.current').data('group') || '';
188
+ if( '' !== selected_category_id && 'all' !== selected_category_id ) {
189
+ AstraRender._api_params['astra-site-category'] = selected_category_id;
190
+ } else if( 'astra-site-category' in astraRenderGrid.settings ) {
191
+
192
+ if( $.inArray('all', astraRenderGrid.settings['astra-site-category']) !== -1 ) {
193
+ var storedCategories = astraRenderGrid.settings['astra-site-category'];
194
+ storedCategories = jQuery.grep(storedCategories, function(value) {
195
+ return value != 'all';
196
+ });
197
+ AstraRender._api_params['astra-site-category'] = storedCategories.join(',');
198
+ }
199
+ }
200
+ },
201
+
202
+ _apiAddParam_astra_site_page_builder: function() {
203
+ // Add 'astra-site-page-builder'
204
+ var selected_page_builder_id = jQuery('.filter-links.astra-site-page-builder').find('.current').data('group') || '';
205
+ if( '' !== selected_page_builder_id && 'all' !== selected_page_builder_id ) {
206
+ AstraRender._api_params['astra-site-page-builder'] = selected_page_builder_id;
207
+ } else if( 'astra-site-page-builder' in astraRenderGrid.settings ) {
208
+ if( $.inArray('all', astraRenderGrid.settings['astra-site-page-builder']) !== -1 ) {
209
+ var storedBuilders = astraRenderGrid.settings['astra-site-page-builder'];
210
+ storedBuilders = jQuery.grep(storedBuilders, function(value) {
211
+ return value != 'all';
212
+ });
213
+ AstraRender._api_params['astra-site-page-builder'] = storedBuilders.join(',');
214
+ }
215
+ }
216
+ },
217
+
218
+ _apiAddParam_page: function() {
219
+ // Add 'page'
220
+ var page_val = parseInt(jQuery('body').attr('data-astra-demo-paged')) || 1;
221
+ AstraRender._api_params['page'] = page_val;
222
+ },
223
+
224
+ _apiAddParam_purchase_key: function() {
225
+ if( astraRenderGrid.sites && astraRenderGrid.sites.purchase_key ) {
226
+ AstraRender._api_params['purchase_key'] = astraRenderGrid.sites.purchase_key;
227
+ }
228
+ },
229
+
230
+ _apiAddParam_site_url: function() {
231
+ if( astraRenderGrid.sites && astraRenderGrid.sites.site_url ) {
232
+ AstraRender._api_params['site_url'] = astraRenderGrid.sites.site_url;
233
+ }
234
+ },
235
+
236
+ /**
237
+ * Show Sites
238
+ *
239
+ * Params E.g. per_page=<page-id>&astra-site-category=<category-ids>&astra-site-page-builder=<page-builder-ids>&page=<page>
240
+ *
241
+ * @param {Boolean} resetPagedCount Reset Paged Count.
242
+ * @param {String} trigger Filtered Trigger.
243
+ */
244
+ _showSites: function( resetPagedCount, trigger ) {
245
+
246
+ if( undefined === resetPagedCount ) {
247
+ resetPagedCount = true
248
+ }
249
+
250
+ if( undefined === trigger ) {
251
+ trigger = 'astra-api-post-loaded';
252
+ }
253
+
254
+ if( resetPagedCount ) {
255
+ AstraRender._resetPagedCount();
256
+ }
257
+
258
+ // Add Params for API request.
259
+ AstraRender._api_params = {};
260
+
261
+ AstraRender._apiAddParam_status();
262
+ AstraRender._apiAddParam_search();
263
+ AstraRender._apiAddParam_per_page();
264
+ AstraRender._apiAddParam_astra_site_category();
265
+ AstraRender._apiAddParam_astra_site_page_builder();
266
+ AstraRender._apiAddParam_page();
267
+ AstraRender._apiAddParam_site_url();
268
+ AstraRender._apiAddParam_purchase_key();
269
+
270
+ // API Request.
271
+ var api_post = {
272
+ slug: 'astra-sites?' + decodeURIComponent( $.param( AstraRender._api_params ) ),
273
+ trigger: trigger,
274
+ };
275
+
276
+ AstraSitesAPI._api_request( api_post );
277
+
278
+ },
279
+
280
+ /**
281
+ * Get Category Params
282
+ *
283
+ * @param {string} category_slug Category Slug.
284
+ * @return {mixed} Add `include=<category-ids>` in API request.
285
+ */
286
+ _getCategoryParams: function( category_slug ) {
287
+
288
+ // Has category?
289
+ if( category_slug in astraRenderGrid.settings ) {
290
+
291
+ var storedBuilders = astraRenderGrid.settings[ category_slug ];
292
+
293
+ // Remove `all` from stored list?
294
+ storedBuilders = jQuery.grep(storedBuilders, function(value) {
295
+ return value != 'all';
296
+ });
297
+
298
+ return '?include='+storedBuilders.join(',');
299
+ }
300
+
301
+ return '';
302
+ },
303
+
304
+ /**
305
+ * Get All Select Status
306
+ *
307
+ * @param {string} category_slug Category Slug.
308
+ * @return {boolean} Return true/false.
309
+ */
310
+ _getCategoryAllSelectStatus: function( category_slug ) {
311
+
312
+ // Has category?
313
+ if( category_slug in astraRenderGrid.settings ) {
314
+
315
+ // Has `all` in stored list?
316
+ if( $.inArray('all', astraRenderGrid.settings[ category_slug ]) === -1 ) {
317
+ return false;
318
+ }
319
+ }
320
+
321
+ return true;
322
+ },
323
+
324
+ /**
325
+ * Show Filters
326
+ */
327
+ _showFilters: function() {
328
+
329
+ /**
330
+ * Categories
331
+ */
332
+ var category_slug = 'astra-site-page-builder';
333
+ var category = {
334
+ slug : category_slug + AstraRender._getCategoryParams( category_slug ),
335
+ id : category_slug,
336
+ class : category_slug,
337
+ trigger : 'astra-api-category-loaded',
338
+ wrapper_class : 'filter-links',
339
+ show_all : false,
340
+ };
341
+
342
+ AstraSitesAPI._api_request( category );
343
+
344
+ /**
345
+ * Page Builder
346
+ */
347
+ var category_slug = 'astra-site-category';
348
+ var category = {
349
+ slug : category_slug + AstraRender._getCategoryParams( category_slug ),
350
+ id : category_slug,
351
+ class : category_slug,
352
+ trigger : 'astra-api-all-category-loaded',
353
+ wrapper_class : 'filter-links',
354
+ show_all : AstraRender._getCategoryAllSelectStatus( category_slug ),
355
+ };
356
+
357
+ AstraSitesAPI._api_request( category );
358
+ },
359
+
360
+ /**
361
+ * Load First Grid.
362
+ *
363
+ * This is triggered after all category loaded.
364
+ *
365
+ * @param {object} event Event Object.
366
+ */
367
+ _loadFirstGrid: function( event, data ) {
368
+ AstraRender._addFilters( event, data );
369
+ setTimeout(function() {
370
+ $('body').removeClass('loading-content');
371
+ }, 100);
372
+ },
373
+
374
+ /**
375
+ * Append filters.
376
+ *
377
+ * @param {object} event Object.
378
+ * @param {object} data API response data.
379
+ */
380
+ _addFilters: function( event, data ) {
381
+ event.preventDefault();
382
+
383
+ if( jQuery('#' + data.args.id).length ) {
384
+ var template = wp.template('astra-site-filters');
385
+ jQuery('#' + data.args.id).html(template( data ));
386
+ }
387
+
388
+ },
389
+
390
+ /**
391
+ * Append sites on scroll.
392
+ *
393
+ * @param {object} event Object.
394
+ * @param {object} data API response data.
395
+ */
396
+ _reinitGridScrolled: function( event, data ) {
397
+
398
+ var template = wp.template('astra-sites-list');
399
+
400
+ if( data.items.length > 0 ) {
401
+
402
+ $('body').removeClass( 'loading-content' );
403
+ $('.filter-count .count').text( data.items_count );
404
+
405
+ setTimeout(function() {
406
+ jQuery('#astra-sites').append(template( data ));
407
+
408
+ AstraRender._imagesLoaded();
409
+ }, 800);
410
+ } else {
411
+
412
+ $('body').addClass('listed-all-sites');
413
+
414
+ // $('#astra-sites-admin').find('.spinner').removeClass('is-active');
415
+ }
416
+
417
+ },
418
+
419
+ /**
420
+ * Update Astra sites list.
421
+ *
422
+ * @param {object} event Object.
423
+ * @param {object} data API response data.
424
+ */
425
+ _reinitGrid: function( event, data ) {
426
+
427
+ var template = wp.template('astra-sites-list');
428
+
429
+ $('body').addClass( 'page-builder-selected' );
430
+ $('body').removeClass( 'loading-content' );
431
+ $('.filter-count .count').text( data.items_count );
432
+
433
+ jQuery('body').attr('data-astra-demo-last-request', data.items_count);
434
+
435
+ jQuery('#astra-sites').show().html(template( data ));
436
+
437
+ AstraRender._imagesLoaded();
438
+
439
+ $('#astra-sites-admin').find('.spinner').removeClass('is-active');
440
+
441
+ if( data.items_count <= 0 ) {
442
+ $('#astra-sites-admin').find('.spinner').removeClass('is-active');
443
+ $('.no-more-demos').addClass('hide-me');
444
+ $('.astra-sites-suggestions').remove();
445
+
446
+ } else {
447
+ $('body').removeClass('listed-all-sites');
448
+ }
449
+
450
+
451
+ },
452
+
453
+ /**
454
+ * Check image loaded with function `imagesLoaded()`
455
+ */
456
+ _imagesLoaded: function() {
457
+
458
+ var self = jQuery('#sites-filter.execute-only-one-time a');
459
+
460
+ $('.astra-sites-grid').imagesLoaded()
461
+ .always( function( instance ) {
462
+ if( jQuery( window ).outerWidth() > AstraRender._breakpoint ) {
463
+ // $('#astra-sites').masonry('reload');
464
+ }
465
+
466
+ $('#astra-sites-admin').find('.spinner').removeClass('is-active');
467
+ })
468
+ .progress( function( instance, image ) {
469
+ var result = image.isLoaded ? 'loaded' : 'broken';
470
+ });
471
+
472
+ },
473
+
474
+ /**
475
+ * Add Suggestion Box
476
+ */
477
+ _addSuggestionBox: function() {
478
+ $('#astra-sites-admin').find('.spinner').removeClass('is-active').addClass('hide-me');
479
+
480
+ $('#astra-sites-admin').find('.no-more-demos').removeClass('hide-me');
481
+ var template = wp.template('astra-sites-suggestions');
482
+ if( ! $( '.astra-sites-suggestions').length ) {
483
+ $('#astra-sites').append( template );
484
+ }
485
+ },
486
+
487
+ /**
488
+ * Update Page Count.
489
+ */
490
+ _updatedPagedCount: function() {
491
+ paged = parseInt(jQuery('body').attr('data-astra-demo-paged'));
492
+ jQuery('body').attr('data-astra-demo-paged', paged + 1);
493
+ window.setTimeout(function () {
494
+ jQuery('body').data('scrolling', false);
495
+ }, 800);
496
+ },
497
+
498
+ /**
499
+ * Reset Page Count.
500
+ */
501
+ _resetPagedCount: function() {
502
+
503
+ jQuery('body').attr('data-astra-demo-last-request', '1');
504
+ jQuery('body').attr('data-astra-demo-paged', '1');
505
+ jQuery('body').attr('data-astra-demo-search', '');
506
+ jQuery('body').attr('data-scrolling', false);
507
+
508
+ }
509
+
510
+ };
511
+
512
+ /**
513
+ * Initialize AstraRender
514
+ */
515
+ $(function(){
516
+ AstraRender.init();
517
+ });
518
+
519
ย  })(jQuery);
inc/importers/batch-processing/class-astra-sites-batch-processing-elementor-v2.php ADDED
@@ -0,0 +1,328 @@
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
1
+ <?php
2
+ /**
3
+ * Elementor Images Batch Processing
4
+ *
5
+ * @package Astra Sites
6
+ * @since 1.0.0
7
+ */
8
+
9
+ namespace Elementor;
10
+
11
+ // If plugin - 'Elementor' not exist then return.
12
+ if ( ! class_exists( '\Elementor\Plugin' ) ) {
13
+ return;
14
+ }
15
+
16
+ namespace Elementor\TemplateLibrary;
17
+
18
+ use Elementor\Controls_Stack;
19
+ use Elementor\Core\Settings\Manager as SettingsManager;
20
+ use Elementor\TemplateLibrary\Classes\Import_Images;
21
+ use Elementor\TemplateLibrary;
22
+ use Elementor\TemplateLibrary\Classes;
23
+ use Elementor\Api;
24
+ use Elementor\PageSettings\Page;
25
+
26
+ // For working protected methods defined in.
27
+ // file '/elementor/includes/template-library/sources/base.php'.
28
+ use Elementor\Plugin;
29
+ use Elementor\Utils;
30
+
31
+ if ( ! defined( 'ABSPATH' ) ) {
32
+ exit; // Exit if accessed directly.
33
+ }
34
+
35
+ /**
36
+ * Astra Source Remote
37
+ */
38
+ class Astra_Sites_Batch_Processing_Elementor extends Source_Base {
39
+
40
+
41
+ /**
42
+ * Get ID
43
+ *
44
+ * @since 1.0.4
45
+ *
46
+ * @return string
47
+ */
48
+ public function get_id() {
49
+ return 'remote';
50
+ }
51
+
52
+ /**
53
+ * Get Title.
54
+ *
55
+ * @since 1.0.4
56
+ *
57
+ * @return string
58
+ */
59
+ public function get_title() {
60
+ return __( 'Remote', 'astra-sites' );
61
+ }
62
+
63
+ /**
64
+ * Get Data
65
+ *
66
+ * @since 1.0.4
67
+ *
68
+ * @return void
69
+ */
70
+ public function register_data() {}
71
+
72
+ /**
73
+ * Get Items
74
+ *
75
+ * @since 1.0.4
76
+ *
77
+ * @param array $args Arguments.
78
+ * @return array
79
+ */
80
+ public function get_items( $args = array() ) {
81
+ $templates_data = Api::get_templates_data();
82
+
83
+ $templates = array();
84
+
85
+ if ( ! empty( $templates_data ) ) {
86
+ foreach ( $templates_data as $template_data ) {
87
+ $templates[] = $this->get_item( $template_data );
88
+ }
89
+ }
90
+
91
+ if ( ! empty( $args ) ) {
92
+ $templates = wp_list_filter( $templates, $args );
93
+ }
94
+
95
+ return $templates;
96
+ }
97
+
98
+ /**
99
+ * Get Item
100
+ *
101
+ * @param array $template_data Template Data.
102
+ *
103
+ * @return array
104
+ */
105
+ public function get_item( $template_data ) {
106
+ return array(
107
+ 'template_id' => $template_data['id'],
108
+ 'source' => $this->get_id(),
109
+ 'title' => $template_data['title'],
110
+ 'thumbnail' => $template_data['thumbnail'],
111
+ 'date' => date( get_option( 'date_format' ), $template_data['tmpl_created'] ),
112
+ 'author' => $template_data['author'],
113
+ 'categories' => array(),
114
+ 'keywords' => array(),
115
+ 'isPro' => ( '1' === $template_data['is_pro'] ),
116
+ 'hasPageSettings' => ( '1' === $template_data['has_page_settings'] ),
117
+ 'url' => $template_data['url'],
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Template Data
123
+ *
124
+ * @param boolean $template_data Template Data.
125
+ * @return boolean Return false.
126
+ */
127
+ public function save_item( $template_data ) {
128
+ return false;
129
+ }
130
+
131
+ /**
132
+ * Update Item
133
+ *
134
+ * @param boolean $new_data New Data.
135
+ * @return boolean Return false.
136
+ */
137
+ public function update_item( $new_data ) {
138
+ return false;
139
+ }
140
+
141
+ /**
142
+ * Delete Template
143
+ *
144
+ * @param boolean $template_id Template ID.
145
+ * @return boolean Return false.
146
+ */
147
+ public function delete_template( $template_id ) {
148
+ return false;
149
+ }
150
+
151
+ /**
152
+ * Delete Template
153
+ *
154
+ * @param boolean $template_id Template ID.
155
+ * @return boolean Return false.
156
+ */
157
+ public function export_template( $template_id ) {
158
+ return false;
159
+ }
160
+
161
+ /**
162
+ * Get Data
163
+ *
164
+ * @param array $args Arguments.
165
+ * @param string $context Context.
166
+ * @return array Data.
167
+ */
168
+ public function get_data( array $args, $context = 'display' ) {
169
+ $data = Api::get_template_content( $args['template_id'] );
170
+
171
+ if ( is_wp_error( $data ) ) {
172
+ return $data;
173
+ }
174
+
175
+ // TODO: since 1.5.0 to content container named `content` instead of `data`.
176
+ if ( ! empty( $data['data'] ) ) {
177
+ $data['content'] = $data['data'];
178
+ unset( $data['data'] );
179
+ }
180
+
181
+ $data['content'] = $this->replace_elements_ids( $data['content'] );
182
+ $data['content'] = $this->process_export_import_content( $data['content'], 'on_import' );
183
+
184
+ if ( ! empty( $args['page_settings'] ) && ! empty( $data['page_settings'] ) ) {
185
+ $page = new Page(
186
+ array(
187
+ 'settings' => $data['page_settings'],
188
+ )
189
+ );
190
+
191
+ $page_settings_data = $this->process_element_export_import_content( $page, 'on_import' );
192
+ $data['page_settings'] = $page_settings_data['settings'];
193
+ }
194
+
195
+ return $data;
196
+ }
197
+
198
+ /**
199
+ * Replace Elements Ids
200
+ *
201
+ * @param string $content Context.
202
+ * @return array Element.
203
+ */
204
+ public function replace_elements_ids( $content ) {
205
+ return Plugin::$instance->db->iterate_data(
206
+ $content, function( $element ) {
207
+ $element['id'] = Utils::generate_random_string();
208
+
209
+ return $element;
210
+ }
211
+ );
212
+ }
213
+
214
+ /**
215
+ * Process Import Content.
216
+ *
217
+ * @param array $content a set of elements.
218
+ * @param string $method (on_export|on_import).
219
+ *
220
+ * @return mixed
221
+ */
222
+ public function process_export_import_content( $content, $method ) {
223
+ return Plugin::$instance->db->iterate_data(
224
+ $content, function( $element_data ) use ( $method ) {
225
+ $element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
226
+
227
+ // If the widget/element isn't exist, like a plugin that creates a widget but deactivated.
228
+ if ( ! $element ) {
229
+ return null;
230
+ }
231
+
232
+ $instance = new Astra_Sites_Batch_Processing_Elementor();
233
+
234
+ return $instance->process_element_export_import_content( $element, $method );
235
+ }
236
+ );
237
+ }
238
+
239
+ /**
240
+ * Process Element/Export Import Content.
241
+ *
242
+ * @param \Elementor\Controls_Stack $element Element.
243
+ * @param string $method Method.
244
+ *
245
+ * @return array
246
+ */
247
+ public function process_element_export_import_content( Controls_Stack $element, $method ) {
248
+ $element_data = $element->get_data();
249
+
250
+ if ( method_exists( $element, $method ) ) {
251
+ // TODO: Use the internal element data without parameters.
252
+ $element_data = $element->{$method}( $element_data );
253
+ }
254
+
255
+ foreach ( $element->get_controls() as $control ) {
256
+ $control_class = Plugin::$instance->controls_manager->get_control( $control['type'] );
257
+
258
+ // If the control isn't exist, like a plugin that creates the control but deactivated.
259
+ if ( ! $control_class ) {
260
+ return $element_data;
261
+ }
262
+
263
+ if ( method_exists( $control_class, $method ) ) {
264
+ $element_data['settings'][ $control['name'] ] = $control_class->{$method}( $element->get_settings( $control['name'] ) );
265
+ }
266
+ }
267
+
268
+ return $element_data;
269
+ }
270
+
271
+ /**
272
+ * Import
273
+ *
274
+ * @since 1.0.14
275
+ * @return void
276
+ */
277
+ public function import() {
278
+
279
+ \Astra_Sites_Image_Importer::log( '---- Processing WordPress Posts / Pages - for Elementor ----' );
280
+
281
+ $post_ids = \Astra_Sites_Batch_Processing::get_pages();
282
+ if ( is_array( $post_ids ) ) {
283
+ foreach ( $post_ids as $post_id ) {
284
+ $this->import_single_post( $post_id );
285
+ }
286
+ }
287
+
288
+ }
289
+
290
+ /**
291
+ * Update post meta.
292
+ *
293
+ * @since 1.0.14
294
+ * @param integer $post_id Post ID.
295
+ * @return void
296
+ */
297
+ public function import_single_post( $post_id = 0 ) {
298
+
299
+ \Astra_Sites_Image_Importer::log( 'Post ID: ' . $post_id );
300
+
301
+ if ( ! empty( $post_id ) ) {
302
+
303
+ $hotlink_imported = get_post_meta( $post_id, '_astra_sites_hotlink_imported', true );
304
+
305
+ if ( empty( $hotlink_imported ) ) {
306
+
307
+ $data = get_post_meta( $post_id, '_elementor_data', true );
308
+
309
+ if ( ! empty( $data ) ) {
310
+
311
+ $data = json_decode( $data, true );
312
+
313
+ $data = $this->replace_elements_ids( $data );
314
+ $data = $this->process_export_import_content( $data, 'on_import' );
315
+
316
+ // Update processed meta.
317
+ update_metadata( 'post', $post_id, '_elementor_data', $data );
318
+ update_metadata( 'post', $post_id, '_astra_sites_hotlink_imported', true );
319
+
320
+ // !important, Clear the cache after images import.
321
+ Plugin::$instance->posts_css_manager->clear_cache();
322
+
323
+ }
324
+ }
325
+ }
326
+
327
+ }
328
+ }
inc/importers/batch-processing/class-astra-sites-batch-processing-elementor.php CHANGED
@@ -36,6 +36,7 @@ if ( ! defined( 'ABSPATH' ) ) {
36
ย  */
37
ย  class Astra_Sites_Batch_Processing_Elementor extends Source_Base {
38
ย 
ย 
39
ย  /**
40
ย  * Get ID
41
ย  *
36
ย  */
37
ย  class Astra_Sites_Batch_Processing_Elementor extends Source_Base {
38
ย 
39
+
40
ย  /**
41
ย  * Get ID
42
ย  *
inc/importers/batch-processing/class-astra-sites-batch-processing.php CHANGED
@@ -71,7 +71,12 @@ if ( ! class_exists( 'Astra_Sites_Batch_Processing' ) ) :
71
ย 
72
ย  // Prepare Page Builders.
73
ย  require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-beaver-builder.php';
74
- require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-elementor.php';
ย 
ย 
ย 
ย 
ย 
75
ย 
76
ย  // Prepare Misc.
77
ย  require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-misc.php';
71
ย 
72
ย  // Prepare Page Builders.
73
ย  require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-beaver-builder.php';
74
+
75
+ if ( defined( 'ELEMENTOR_VERSION' ) && version_compare( ELEMENTOR_VERSION, '2.0.0', '>=' ) ) {
76
+ require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-elementor-v2.php';
77
+ } else {
78
+ require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-elementor.php';
79
+ }
80
ย 
81
ย  // Prepare Misc.
82
ย  require_once ASTRA_SITES_DIR . 'inc/importers/batch-processing/class-astra-sites-batch-processing-misc.php';
inc/importers/batch-processing/helpers/class-wp-async-request.php CHANGED
@@ -1,164 +1,164 @@
1
- <?php
2
- /**
3
- * WP Async Request
4
- *
5
- * @see https://github.com/A5hleyRich/wp-background-processing/
6
- * @package WP-Background-Processing
7
- */
8
-
9
- if ( ! class_exists( 'WP_Async_Request' ) ) {
10
-
11
- /**
12
- * Abstract WP_Async_Request class.
13
- *
14
- * @abstract
15
- */
16
- abstract class WP_Async_Request {
17
-
18
- /**
19
- * Prefix
20
- *
21
- * (default value: 'wp')
22
- *
23
- * @var string
24
- * @access protected
25
- */
26
- protected $prefix = 'wp';
27
-
28
- /**
29
- * Action
30
- *
31
- * (default value: 'async_request')
32
- *
33
- * @var string
34
- * @access protected
35
- */
36
- protected $action = 'async_request';
37
-
38
- /**
39
- * Identifier
40
- *
41
- * @var mixed
42
- * @access protected
43
- */
44
- protected $identifier;
45
-
46
- /**
47
- * Data
48
- *
49
- * (default value: array())
50
- *
51
- * @var array
52
- * @access protected
53
- */
54
- protected $data = array();
55
-
56
- /**
57
- * Initiate new async request
58
- */
59
- public function __construct() {
60
- $this->identifier = $this->prefix . '_' . $this->action;
61
-
62
- add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
63
- add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
64
- }
65
-
66
- /**
67
- * Set data used during the request
68
- *
69
- * @param array $data Data.
70
- *
71
- * @return $this
72
- */
73
- public function data( $data ) {
74
- $this->data = $data;
75
-
76
- return $this;
77
- }
78
-
79
- /**
80
- * Dispatch the async request
81
- *
82
- * @return array|WP_Error
83
- */
84
- public function dispatch() {
85
- $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
86
- $args = $this->get_post_args();
87
-
88
- return wp_remote_post( esc_url_raw( $url ), $args );
89
- }
90
-
91
- /**
92
- * Get query args
93
- *
94
- * @return array
95
- */
96
- protected function get_query_args() {
97
- if ( property_exists( $this, 'query_args' ) ) {
98
- return $this->query_args;
99
- }
100
-
101
- return array(
102
- 'action' => $this->identifier,
103
- 'nonce' => wp_create_nonce( $this->identifier ),
104
- );
105
- }
106
-
107
- /**
108
- * Get query URL
109
- *
110
- * @return string
111
- */
112
- protected function get_query_url() {
113
- if ( property_exists( $this, 'query_url' ) ) {
114
- return $this->query_url;
115
- }
116
-
117
- return admin_url( 'admin-ajax.php' );
118
- }
119
-
120
- /**
121
- * Get post args
122
- *
123
- * @return array
124
- */
125
- protected function get_post_args() {
126
- if ( property_exists( $this, 'post_args' ) ) {
127
- return $this->post_args;
128
- }
129
-
130
- return array(
131
- 'timeout' => 0.01,
132
- 'blocking' => false,
133
- 'body' => $this->data,
134
- 'cookies' => $_COOKIE,
135
- 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
136
- );
137
- }
138
-
139
- /**
140
- * Maybe handle
141
- *
142
- * Check for correct nonce and pass to handler.
143
- */
144
- public function maybe_handle() {
145
- // Don't lock up other requests while processing.
146
- session_write_close();
147
-
148
- check_ajax_referer( $this->identifier, 'nonce' );
149
-
150
- $this->handle();
151
-
152
- wp_die();
153
- }
154
-
155
- /**
156
- * Handle
157
- *
158
- * Override this method to perform any actions required
159
- * during the async request.
160
- */
161
- abstract protected function handle();
162
-
163
- }
164
- }
1
+ <?php
2
+ /**
3
+ * WP Async Request
4
+ *
5
+ * @see https://github.com/A5hleyRich/wp-background-processing/
6
+ * @package WP-Background-Processing
7
+ */
8
+
9
+ if ( ! class_exists( 'WP_Async_Request' ) ) {
10
+
11
+ /**
12
+ * Abstract WP_Async_Request class.
13
+ *
14
+ * @abstract
15
+ */
16
+ abstract class WP_Async_Request {
17
+
18
+ /**
19
+ * Prefix
20
+ *
21
+ * (default value: 'wp')
22
+ *
23
+ * @var string
24
+ * @access protected
25
+ */
26
+ protected $prefix = 'wp';
27
+
28
+ /**
29
+ * Action
30
+ *
31
+ * (default value: 'async_request')
32
+ *
33
+ * @var string
34
+ * @access protected
35
+ */
36
+ protected $action = 'async_request';
37
+
38
+ /**
39
+ * Identifier
40
+ *
41
+ * @var mixed
42
+ * @access protected
43
+ */
44
+ protected $identifier;
45
+
46
+ /**
47
+ * Data
48
+ *
49
+ * (default value: array())
50
+ *
51
+ * @var array
52
+ * @access protected
53
+ */
54
+ protected $data = array();
55
+
56
+ /**
57
+ * Initiate new async request
58
+ */
59
+ public function __construct() {
60
+ $this->identifier = $this->prefix . '_' . $this->action;
61
+
62
+ add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
63
+ add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
64
+ }
65
+
66
+ /**
67
+ * Set data used during the request
68
+ *
69
+ * @param array $data Data.
70
+ *
71
+ * @return $this
72
+ */
73
+ public function data( $data ) {
74
+ $this->data = $data;
75
+
76
+ return $this;
77
+ }
78
+
79
+ /**
80
+ * Dispatch the async request
81
+ *
82
+ * @return array|WP_Error
83
+ */
84
+ public function dispatch() {
85
+ $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
86
+ $args = $this->get_post_args();
87
+
88
+ return wp_remote_post( esc_url_raw( $url ), $args );
89
+ }
90
+
91
+ /**
92
+ * Get query args
93
+ *
94
+ * @return array
95
+ */
96
+ protected function get_query_args() {
97
+ if ( property_exists( $this, 'query_args' ) ) {
98
+ return $this->query_args;
99
+ }
100
+
101
+ return array(
102
+ 'action' => $this->identifier,
103
+ 'nonce' => wp_create_nonce( $this->identifier ),
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Get query URL
109
+ *
110
+ * @return string
111
+ */
112
+ protected function get_query_url() {
113
+ if ( property_exists( $this, 'query_url' ) ) {
114
+ return $this->query_url;
115
+ }
116
+
117
+ return admin_url( 'admin-ajax.php' );
118
+ }
119
+
120
+ /**
121
+ * Get post args
122
+ *
123
+ * @return array
124
+ */
125
+ protected function get_post_args() {
126
+ if ( property_exists( $this, 'post_args' ) ) {
127
+ return $this->post_args;
128
+ }
129
+
130
+ return array(
131
+ 'timeout' => 0.01,
132
+ 'blocking' => false,
133
+ 'body' => $this->data,
134
+ 'cookies' => $_COOKIE,
135
+ 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Maybe handle
141
+ *
142
+ * Check for correct nonce and pass to handler.
143
+ */
144
+ public function maybe_handle() {
145
+ // Don't lock up other requests while processing.
146
+ session_write_close();
147
+
148
+ check_ajax_referer( $this->identifier, 'nonce' );
149
+
150
+ $this->handle();
151
+
152
+ wp_die();
153
+ }
154
+
155
+ /**
156
+ * Handle
157
+ *
158
+ * Override this method to perform any actions required
159
+ * during the async request.
160
+ */
161
+ abstract protected function handle();
162
+
163
+ }
164
+ }
inc/importers/batch-processing/helpers/class-wp-background-process.php CHANGED
@@ -1,513 +1,513 @@
1
- <?php
2
- /**
3
- * WP Background Process
4
- *
5
- * @see https://github.com/A5hleyRich/wp-background-processing/
6
- * @package WP-Background-Processing
7
- */
8
-
9
- if ( ! class_exists( 'WP_Background_Process' ) ) {
10
-
11
- /**
12
- * Abstract WP_Background_Process class.
13
- *
14
- * @abstract
15
- * @extends WP_Async_Request
16
- */
17
- abstract class WP_Background_Process extends WP_Async_Request {
18
-
19
- /**
20
- * Action
21
- *
22
- * (default value: 'background_process')
23
- *
24
- * @var string
25
- * @access protected
26
- */
27
- protected $action = 'background_process';
28
-
29
- /**
30
- * Start time of current process.
31
- *
32
- * (default value: 0)
33
- *
34
- * @var int
35
- * @access protected
36
- */
37
- protected $start_time = 0;
38
-
39
- /**
40
- * Cron_hook_identifier
41
- *
42
- * @var mixed
43
- * @access protected
44
- */
45
- protected $cron_hook_identifier;
46
-
47
- /**
48
- * Cron_interval_identifier
49
- *
50
- * @var mixed
51
- * @access protected
52
- */
53
- protected $cron_interval_identifier;
54
-
55
- /**
56
- * Initiate new background process
57
- */
58
- public function __construct() {
59
- parent::__construct();
60
-
61
- $this->cron_hook_identifier = $this->identifier . '_cron';
62
- $this->cron_interval_identifier = $this->identifier . '_cron_interval';
63
-
64
- add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
65
- add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
66
- }
67
-
68
- /**
69
- * Dispatch
70
- *
71
- * @return mixed dispatch event.
72
- */
73
- public function dispatch() {
74
- // Schedule the cron healthcheck.
75
- $this->schedule_event();
76
-
77
- // Perform remote post.
78
- return parent::dispatch();
79
- }
80
-
81
- /**
82
- * Push to queue
83
- *
84
- * @param mixed $data Data.
85
- *
86
- * @return $this
87
- */
88
- public function push_to_queue( $data ) {
89
- $this->data[] = $data;
90
-
91
- return $this;
92
- }
93
-
94
- /**
95
- * Save queue
96
- *
97
- * @return $this
98
- */
99
- public function save() {
100
- $key = $this->generate_key();
101
-
102
- if ( ! empty( $this->data ) ) {
103
- update_site_option( $key, $this->data );
104
- }
105
-
106
- return $this;
107
- }
108
-
109
- /**
110
- * Update queue
111
- *
112
- * @param string $key Key.
113
- * @param array $data Data.
114
- *
115
- * @return $this
116
- */
117
- public function update( $key, $data ) {
118
- if ( ! empty( $data ) ) {
119
- update_site_option( $key, $data );
120
- }
121
-
122
- return $this;
123
- }
124
-
125
- /**
126
- * Delete queue
127
- *
128
- * @param string $key Key.
129
- *
130
- * @return $this
131
- */
132
- public function delete( $key ) {
133
- delete_site_option( $key );
134
-
135
- return $this;
136
- }
137
-
138
- /**
139
- * Generate key
140
- *
141
- * Generates a unique key based on microtime. Queue items are
142
- * given a unique key so that they can be merged upon save.
143
- *
144
- * @param int $length Length.
145
- *
146
- * @return string
147
- */
148
- protected function generate_key( $length = 64 ) {
149
- $unique = md5( microtime() . rand() );
150
- $prepend = $this->identifier . '_batch_';
151
-
152
- return substr( $prepend . $unique, 0, $length );
153
- }
154
-
155
- /**
156
- * Maybe process queue
157
- *
158
- * Checks whether data exists within the queue and that
159
- * the process is not already running.
160
- */
161
- public function maybe_handle() {
162
- // Don't lock up other requests while processing.
163
- session_write_close();
164
-
165
- if ( $this->is_process_running() ) {
166
- // Background process already running.
167
- wp_die();
168
- }
169
-
170
- if ( $this->is_queue_empty() ) {
171
- // No data to process.
172
- wp_die();
173
- }
174
-
175
- check_ajax_referer( $this->identifier, 'nonce' );
176
-
177
- $this->handle();
178
-
179
- wp_die();
180
- }
181
-
182
- /**
183
- * Is queue empty
184
- *
185
- * @return bool
186
- */
187
- protected function is_queue_empty() {
188
- global $wpdb;
189
-
190
- $table = $wpdb->options;
191
- $column = 'option_name';
192
-
193
- if ( is_multisite() ) {
194
- $table = $wpdb->sitemeta;
195
- $column = 'meta_key';
196
- }
197
-
198
- $key = $this->identifier . '_batch_%';
199
-
200
- $count = $wpdb->get_var(
201
- $wpdb->prepare(
202
- "
203
- SELECT COUNT(*)
204
- FROM {$table}
205
- WHERE {$column} LIKE %s
206
- ", $key
207
- )
208
- );
209
-
210
- return ( $count > 0 ) ? false : true;
211
- }
212
-
213
- /**
214
- * Is process running
215
- *
216
- * Check whether the current process is already running
217
- * in a background process.
218
- */
219
- protected function is_process_running() {
220
- if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
221
- // Process already running.
222
- return true;
223
- }
224
-
225
- return false;
226
- }
227
-
228
- /**
229
- * Lock process
230
- *
231
- * Lock the process so that multiple instances can't run simultaneously.
232
- * Override if applicable, but the duration should be greater than that
233
- * defined in the time_exceeded() method.
234
- */
235
- protected function lock_process() {
236
- $this->start_time = time(); // Set start time of current process.
237
-
238
- $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
239
- $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
240
-
241
- set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
242
- }
243
-
244
- /**
245
- * Unlock process
246
- *
247
- * Unlock the process so that other instances can spawn.
248
- *
249
- * @return $this
250
- */
251
- protected function unlock_process() {
252
- delete_site_transient( $this->identifier . '_process_lock' );
253
-
254
- return $this;
255
- }
256
-
257
- /**
258
- * Get batch
259
- *
260
- * @return stdClass Return the first batch from the queue
261
- */
262
- protected function get_batch() {
263
- global $wpdb;
264
-
265
- $table = $wpdb->options;
266
- $column = 'option_name';
267
- $key_column = 'option_id';
268
- $value_column = 'option_value';
269
-
270
- if ( is_multisite() ) {
271
- $table = $wpdb->sitemeta;
272
- $column = 'meta_key';
273
- $key_column = 'meta_id';
274
- $value_column = 'meta_value';
275
- }
276
-
277
- $key = $this->identifier . '_batch_%';
278
-
279
- $query = $wpdb->get_row(
280
- $wpdb->prepare(
281
- "
282
- SELECT *
283
- FROM {$table}
284
- WHERE {$column} LIKE %s
285
- ORDER BY {$key_column} ASC
286
- LIMIT 1
287
- ", $key
288
- )
289
- );
290
-
291
- $batch = new stdClass();
292
- $batch->key = $query->$column;
293
- $batch->data = maybe_unserialize( $query->$value_column );
294
-
295
- return $batch;
296
- }
297
-
298
- /**
299
- * Handle
300
- *
301
- * Pass each queue item to the task handler, while remaining
302
- * within server memory and time limit constraints.
303
- */
304
- protected function handle() {
305
- $this->lock_process();
306
-
307
- do {
308
- $batch = $this->get_batch();
309
-
310
- foreach ( $batch->data as $key => $value ) {
311
- $task = $this->task( $value );
312
-
313
- if ( false !== $task ) {
314
- $batch->data[ $key ] = $task;
315
- } else {
316
- unset( $batch->data[ $key ] );
317
- }
318
-
319
- if ( $this->time_exceeded() || $this->memory_exceeded() ) {
320
- // Batch limits reached.
321
- break;
322
- }
323
- }
324
-
325
- // Update or delete current batch.
326
- if ( ! empty( $batch->data ) ) {
327
- $this->update( $batch->key, $batch->data );
328
- } else {
329
- $this->delete( $batch->key );
330
- }
331
- } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
332
-
333
- $this->unlock_process();
334
-
335
- // Start next batch or complete process.
336
- if ( ! $this->is_queue_empty() ) {
337
- $this->dispatch();
338
- } else {
339
- $this->complete();
340
- }
341
-
342
- wp_die();
343
- }
344
-
345
- /**
346
- * Memory exceeded
347
- *
348
- * Ensures the batch process never exceeds 90%
349
- * of the maximum WordPress memory.
350
- *
351
- * @return bool
352
- */
353
- protected function memory_exceeded() {
354
- $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
355
- $current_memory = memory_get_usage( true );
356
- $return = false;
357
-
358
- if ( $current_memory >= $memory_limit ) {
359
- $return = true;
360
- }
361
-
362
- return apply_filters( $this->identifier . '_memory_exceeded', $return );
363
- }
364
-
365
- /**
366
- * Get memory limit
367
- *
368
- * @return int
369
- */
370
- protected function get_memory_limit() {
371
- if ( function_exists( 'ini_get' ) ) {
372
- $memory_limit = ini_get( 'memory_limit' );
373
- } else {
374
- // Sensible default.
375
- $memory_limit = '128M';
376
- }
377
-
378
- if ( ! $memory_limit || -1 === $memory_limit ) {
379
- // Unlimited, set to 32GB.
380
- $memory_limit = '32000M';
381
- }
382
-
383
- return intval( $memory_limit ) * 1024 * 1024;
384
- }
385
-
386
- /**
387
- * Time exceeded.
388
- *
389
- * Ensures the batch never exceeds a sensible time limit.
390
- * A timeout limit of 30s is common on shared hosting.
391
- *
392
- * @return bool
393
- */
394
- protected function time_exceeded() {
395
- $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
396
- $return = false;
397
-
398
- if ( time() >= $finish ) {
399
- $return = true;
400
- }
401
-
402
- return apply_filters( $this->identifier . '_time_exceeded', $return );
403
- }
404
-
405
- /**
406
- * Complete.
407
- *
408
- * Override if applicable, but ensure that the below actions are
409
- * performed, or, call parent::complete().
410
- */
411
- protected function complete() {
412
- // Unschedule the cron healthcheck.
413
- $this->clear_scheduled_event();
414
- }
415
-
416
- /**
417
- * Schedule cron healthcheck
418
- *
419
- * @access public
420
- * @param mixed $schedules Schedules.
421
- * @return mixed
422
- */
423
- public function schedule_cron_healthcheck( $schedules ) {
424
- $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
425
-
426
- if ( property_exists( $this, 'cron_interval' ) ) {
427
- $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval_identifier );
428
- }
429
-
430
- // Adds every 5 minutes to the existing schedules.
431
- $schedules[ $this->identifier . '_cron_interval' ] = array(
432
- 'interval' => MINUTE_IN_SECONDS * $interval,
433
- 'display' => sprintf( __( 'Every %d Minutes', 'astra-sites' ), $interval ),
434
- );
435
-
436
- return $schedules;
437
- }
438
-
439
- /**
440
- * Handle cron healthcheck
441
- *
442
- * Restart the background process if not already running
443
- * and data exists in the queue.
444
- */
445
- public function handle_cron_healthcheck() {
446
- if ( $this->is_process_running() ) {
447
- // Background process already running.
448
- exit;
449
- }
450
-
451
- if ( $this->is_queue_empty() ) {
452
- // No data to process.
453
- $this->clear_scheduled_event();
454
- exit;
455
- }
456
-
457
- $this->handle();
458
-
459
- exit;
460
- }
461
-
462
- /**
463
- * Schedule event
464
- */
465
- protected function schedule_event() {
466
- if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
467
- wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
468
- }
469
- }
470
-
471
- /**
472
- * Clear scheduled event
473
- */
474
- protected function clear_scheduled_event() {
475
- $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
476
-
477
- if ( $timestamp ) {
478
- wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
479
- }
480
- }
481
-
482
- /**
483
- * Cancel Process
484
- *
485
- * Stop processing queue items, clear cronjob and delete batch.
486
- */
487
- public function cancel_process() {
488
- if ( ! $this->is_queue_empty() ) {
489
- $batch = $this->get_batch();
490
-
491
- $this->delete( $batch->key );
492
-
493
- wp_clear_scheduled_hook( $this->cron_hook_identifier );
494
- }
495
-
496
- }
497
-
498
- /**
499
- * Task
500
- *
501
- * Override this method to perform any actions required on each
502
- * queue item. Return the modified item for further processing
503
- * in the next pass through. Or, return false to remove the
504
- * item from the queue.
505
- *
506
- * @param mixed $item Queue item to iterate over.
507
- *
508
- * @return mixed
509
- */
510
- abstract protected function task( $item );
511
-
512
- }
513
- }
1
+ <?php
2
+ /**
3
+ * WP Background Process
4
+ *
5
+ * @see https://github.com/A5hleyRich/wp-background-processing/
6
+ * @package WP-Background-Processing
7
+ */
8
+
9
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
10
+
11
+ /**
12
+ * Abstract WP_Background_Process class.
13
+ *
14
+ * @abstract
15
+ * @extends WP_Async_Request
16
+ */
17
+ abstract class WP_Background_Process extends WP_Async_Request {
18
+
19
+ /**
20
+ * Action
21
+ *
22
+ * (default value: 'background_process')
23
+ *
24
+ * @var string
25
+ * @access protected
26
+ */
27
+ protected $action = 'background_process';
28
+
29
+ /**
30
+ * Start time of current process.
31
+ *
32
+ * (default value: 0)
33
+ *
34
+ * @var int
35
+ * @access protected
36
+ */
37
+ protected $start_time = 0;
38
+
39
+ /**
40
+ * Cron_hook_identifier
41
+ *
42
+ * @var mixed
43
+ * @access protected
44
+ */
45
+ protected $cron_hook_identifier;
46
+
47
+ /**
48
+ * Cron_interval_identifier
49
+ *
50
+ * @var mixed
51
+ * @access protected
52
+ */
53
+ protected $cron_interval_identifier;
54
+
55
+ /**
56
+ * Initiate new background process
57
+ */
58
+ public function __construct() {
59
+ parent::__construct();
60
+
61
+ $this->cron_hook_identifier = $this->identifier . '_cron';
62
+ $this->cron_interval_identifier = $this->identifier . '_cron_interval';
63
+
64
+ add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
65
+ add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
66
+ }
67
+
68
+ /**
69
+ * Dispatch
70
+ *
71
+ * @return mixed dispatch event.
72
+ */
73
+ public function dispatch() {
74
+ // Schedule the cron healthcheck.
75
+ $this->schedule_event();
76
+
77
+ // Perform remote post.
78
+ return parent::dispatch();
79
+ }
80
+
81
+ /**
82
+ * Push to queue
83
+ *
84
+ * @param mixed $data Data.
85
+ *
86
+ * @return $this
87
+ */
88
+ public function push_to_queue( $data ) {
89
+ $this->data[] = $data;
90
+
91
+ return $this;
92
+ }
93
+
94
+ /**
95
+ * Save queue
96
+ *
97
+ * @return $this
98
+ */
99
+ public function save() {
100
+ $key = $this->generate_key();
101
+
102
+ if ( ! empty( $this->data ) ) {
103
+ update_site_option( $key, $this->data );
104
+ }
105
+
106
+ return $this;
107
+ }
108
+
109
+ /**
110
+ * Update queue
111
+ *
112
+ * @param string $key Key.
113
+ * @param array $data Data.
114
+ *
115
+ * @return $this
116
+ */
117
+ public function update( $key, $data ) {
118
+ if ( ! empty( $data ) ) {
119
+ update_site_option( $key, $data );
120
+ }
121
+
122
+ return $this;
123
+ }
124
+
125
+ /**
126
+ * Delete queue
127
+ *
128
+ * @param string $key Key.
129
+ *
130
+ * @return $this
131
+ */
132
+ public function delete( $key ) {
133
+ delete_site_option( $key );
134
+
135
+ return $this;
136
+ }
137
+
138
+ /**
139
+ * Generate key
140
+ *
141
+ * Generates a unique key based on microtime. Queue items are
142
+ * given a unique key so that they can be merged upon save.
143
+ *
144
+ * @param int $length Length.
145
+ *
146
+ * @return string
147
+ */
148
+ protected function generate_key( $length = 64 ) {
149
+ $unique = md5( microtime() . rand() );
150
+ $prepend = $this->identifier . '_batch_';
151
+
152
+ return substr( $prepend . $unique, 0, $length );
153
+ }
154
+
155
+ /**
156
+ * Maybe process queue
157
+ *
158
+ * Checks whether data exists within the queue and that
159
+ * the process is not already running.
160
+ */
161
+ public function maybe_handle() {
162
+ // Don't lock up other requests while processing.
163
+ session_write_close();
164
+
165
+ if ( $this->is_process_running() ) {
166
+ // Background process already running.
167
+ wp_die();
168
+ }
169
+
170
+ if ( $this->is_queue_empty() ) {
171
+ // No data to process.
172
+ wp_die();
173
+ }
174
+
175
+ check_ajax_referer( $this->identifier, 'nonce' );
176
+
177
+ $this->handle();
178
+
179
+ wp_die();
180
+ }
181
+
182
+ /**
183
+ * Is queue empty
184
+ *
185
+ * @return bool
186
+ */
187
+ protected function is_queue_empty() {
188
+ global $wpdb;
189
+
190
+ $table = $wpdb->options;
191
+ $column = 'option_name';
192
+
193
+ if ( is_multisite() ) {
194
+ $table = $wpdb->sitemeta;
195
+ $column = 'meta_key';
196
+ }
197
+
198
+ $key = $this->identifier . '_batch_%';
199
+
200
+ $count = $wpdb->get_var(
201
+ $wpdb->prepare(
202
+ "
203
+ SELECT COUNT(*)
204
+ FROM {$table}
205
+ WHERE {$column} LIKE %s
206
+ ", $key
207
+ )
208
+ );
209
+
210
+ return ( $count > 0 ) ? false : true;
211
+ }
212
+
213
+ /**
214
+ * Is process running
215
+ *
216
+ * Check whether the current process is already running
217
+ * in a background process.
218
+ */
219
+ protected function is_process_running() {
220
+ if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
221
+ // Process already running.
222
+ return true;
223
+ }
224
+
225
+ return false;
226
+ }
227
+
228
+ /**
229
+ * Lock process
230
+ *
231
+ * Lock the process so that multiple instances can't run simultaneously.
232
+ * Override if applicable, but the duration should be greater than that
233
+ * defined in the time_exceeded() method.
234
+ */
235
+ protected function lock_process() {
236
+ $this->start_time = time(); // Set start time of current process.
237
+
238
+ $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
239
+ $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
240
+
241
+ set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
242
+ }
243
+
244
+ /**
245
+ * Unlock process
246
+ *
247
+ * Unlock the process so that other instances can spawn.
248
+ *
249
+ * @return $this
250
+ */
251
+ protected function unlock_process() {
252
+ delete_site_transient( $this->identifier . '_process_lock' );
253
+
254
+ return $this;
255
+ }
256
+
257
+ /**
258
+ * Get batch
259
+ *
260
+ * @return stdClass Return the first batch from the queue
261
+ */
262
+ protected function get_batch() {
263
+ global $wpdb;
264
+
265
+ $table = $wpdb->options;
266
+ $column = 'option_name';
267
+ $key_column = 'option_id';
268
+ $value_column = 'option_value';
269
+
270
+ if ( is_multisite() ) {
271
+ $table = $wpdb->sitemeta;
272
+ $column = 'meta_key';
273
+ $key_column = 'meta_id';
274
+ $value_column = 'meta_value';
275
+ }
276
+
277
+ $key = $this->identifier . '_batch_%';
278
+
279
+ $query = $wpdb->get_row(
280
+ $wpdb->prepare(
281
+ "
282
+ SELECT *
283
+ FROM {$table}
284
+ WHERE {$column} LIKE %s
285
+ ORDER BY {$key_column} ASC
286
+ LIMIT 1
287
+ ", $key
288
+ )
289
+ );
290
+
291
+ $batch = new stdClass();
292
+ $batch->key = $query->$column;
293
+ $batch->data = maybe_unserialize( $query->$value_column );
294
+
295
+ return $batch;
296
+ }
297
+
298
+ /**
299
+ * Handle
300
+ *
301
+ * Pass each queue item to the task handler, while remaining
302
+ * within server memory and time limit constraints.
303
+ */
304
+ protected function handle() {
305
+ $this->lock_process();
306
+
307
+ do {
308
+ $batch = $this->get_batch();
309
+
310
+ foreach ( $batch->data as $key => $value ) {
311
+ $task = $this->task( $value );
312
+
313
+ if ( false !== $task ) {
314
+ $batch->data[ $key ] = $task;
315
+ } else {
316
+ unset( $batch->data[ $key ] );
317
+ }
318
+
319
+ if ( $this->time_exceeded() || $this->memory_exceeded() ) {
320
+ // Batch limits reached.
321
+ break;
322
+ }
323
+ }
324
+
325
+ // Update or delete current batch.
326
+ if ( ! empty( $batch->data ) ) {
327
+ $this->update( $batch->key, $batch->data );
328
+ } else {
329
+ $this->delete( $batch->key );
330
+ }
331
+ } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
332
+
333
+ $this->unlock_process();
334
+
335
+ // Start next batch or complete process.
336
+ if ( ! $this->is_queue_empty() ) {
337
+ $this->dispatch();
338
+ } else {
339
+ $this->complete();
340
+ }
341
+
342
+ wp_die();
343
+ }
344
+
345
+ /**
346
+ * Memory exceeded
347
+ *
348
+ * Ensures the batch process never exceeds 90%
349
+ * of the maximum WordPress memory.
350
+ *
351
+ * @return bool
352
+ */
353
+ protected function memory_exceeded() {
354
+ $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
355
+ $current_memory = memory_get_usage( true );
356
+ $return = false;
357
+
358
+ if ( $current_memory >= $memory_limit ) {
359
+ $return = true;
360
+ }
361
+
362
+ return apply_filters( $this->identifier . '_memory_exceeded', $return );
363
+ }
364
+
365
+ /**
366
+ * Get memory limit
367
+ *
368
+ * @return int
369
+ */
370
+ protected function get_memory_limit() {
371
+ if ( function_exists( 'ini_get' ) ) {
372
+ $memory_limit = ini_get( 'memory_limit' );
373
+ } else {
374
+ // Sensible default.
375
+ $memory_limit = '128M';
376
+ }
377
+
378
+ if ( ! $memory_limit || -1 === $memory_limit ) {
379
+ // Unlimited, set to 32GB.
380
+ $memory_limit = '32000M';
381
+ }
382
+
383
+ return intval( $memory_limit ) * 1024 * 1024;
384
+ }
385
+
386
+ /**
387
+ * Time exceeded.
388
+ *
389
+ * Ensures the batch never exceeds a sensible time limit.
390
+ * A timeout limit of 30s is common on shared hosting.
391
+ *
392
+ * @return bool
393
+ */
394
+ protected function time_exceeded() {
395
+ $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
396
+ $return = false;
397
+
398
+ if ( time() >= $finish ) {
399
+ $return = true;
400
+ }
401
+
402
+ return apply_filters( $this->identifier . '_time_exceeded', $return );
403
+ }
404
+
405
+ /**
406
+ * Complete.
407
+ *
408
+ * Override if applicable, but ensure that the below actions are
409
+ * performed, or, call parent::complete().
410
+ */
411
+ protected function complete() {
412
+ // Unschedule the cron healthcheck.
413
+ $this->clear_scheduled_event();
414
+ }
415
+
416
+ /**
417
+ * Schedule cron healthcheck
418
+ *
419
+ * @access public
420
+ * @param mixed $schedules Schedules.
421
+ * @return mixed
422
+ */
423
+ public function schedule_cron_healthcheck( $schedules ) {
424
+ $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
425
+
426
+ if ( property_exists( $this, 'cron_interval' ) ) {
427
+ $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval_identifier );
428
+ }
429
+
430
+ // Adds every 5 minutes to the existing schedules.
431
+ $schedules[ $this->identifier . '_cron_interval' ] = array(
432
+ 'interval' => MINUTE_IN_SECONDS * $interval,
433
+ 'display' => sprintf( __( 'Every %d Minutes', 'astra-sites' ), $interval ),
434
+ );
435
+
436
+ return $schedules;
437
+ }
438
+
439
+ /**
440
+ * Handle cron healthcheck
441
+ *
442
+ * Restart the background process if not already running
443
+ * and data exists in the queue.
444
+ */
445
+ public function handle_cron_healthcheck() {
446
+ if ( $this->is_process_running() ) {
447
+ // Background process already running.
448
+ exit;
449
+ }
450
+
451
+ if ( $this->is_queue_empty() ) {
452
+ // No data to process.
453
+ $this->clear_scheduled_event();
454
+ exit;
455
+ }
456
+
457
+ $this->handle();
458
+
459
+ exit;
460
+ }
461
+
462
+ /**
463
+ * Schedule event
464
+ */
465
+ protected function schedule_event() {
466
+ if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
467
+ wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Clear scheduled event
473
+ */
474
+ protected function clear_scheduled_event() {
475
+ $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
476
+
477
+ if ( $timestamp ) {
478
+ wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
479
+ }
480
+ }
481
+
482
+ /**
483
+ * Cancel Process
484
+ *
485
+ * Stop processing queue items, clear cronjob and delete batch.
486
+ */
487
+ public function cancel_process() {
488
+ if ( ! $this->is_queue_empty() ) {
489
+ $batch = $this->get_batch();
490
+
491
+ $this->delete( $batch->key );
492
+
493
+ wp_clear_scheduled_hook( $this->cron_hook_identifier );
494
+ }
495
+
496
+ }
497
+
498
+ /**
499
+ * Task
500
+ *
501
+ * Override this method to perform any actions required on each
502
+ * queue item. Return the modified item for further processing
503
+ * in the next pass through. Or, return false to remove the
504
+ * item from the queue.
505
+ *
506
+ * @param mixed $item Queue item to iterate over.
507
+ *
508
+ * @return mixed
509
+ */
510
+ abstract protected function task( $item );
511
+
512
+ }
513
+ }
inc/importers/class-widgets-importer.php CHANGED
@@ -1,278 +1,278 @@
1
- <?php
2
- /**
3
- * Widget Data exporter class.
4
- *
5
- * @package Astra Addon
6
- * @see - https://wordpress.org/plugins/widget-importer-exporter/
7
- */
8
-
9
- defined( 'ABSPATH' ) or exit;
10
-
11
- /**
12
- * Widget Data exporter class.
13
- *
14
- * @see - https://wordpress.org/plugins/widget-importer-exporter/
15
- */
16
- class Astra_Widget_Importer {
17
-
18
- /**
19
- * Instance of Astra_Widget_Importer
20
- *
21
- * @var Astra_Widget_Importer
22
- */
23
- private static $_instance = null;
24
-
25
- public static function instance() {
26
-
27
- if ( ! isset( self::$_instance ) ) {
28
- self::$_instance = new self;
29
- }
30
-
31
- return self::$_instance;
32
- }
33
-
34
- /**
35
- * Available widgets
36
- *
37
- * Gather site's widgets into array with ID base, name, etc.
38
- * Used by export and import functions.
39
- *
40
- * @since 0.4
41
- * @global array $wp_registered_widget_updates
42
- * @return array Widget information
43
- */
44
- function wie_available_widgets() {
45
-
46
- global $wp_registered_widget_controls;
47
-
48
- $widget_controls = $wp_registered_widget_controls;
49
-
50
- $available_widgets = array();
51
-
52
- foreach ( $widget_controls as $widget ) {
53
-
54
- if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) { // no dupes
55
-
56
- $available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base'];
57
- $available_widgets[ $widget['id_base'] ]['name'] = $widget['name'];
58
-
59
- }
60
- }
61
-
62
- return apply_filters( 'wie_available_widgets', $available_widgets );
63
- }
64
-
65
- /**
66
- * Import widget JSON data
67
- *
68
- * @since 0.4
69
- * @global array $wp_registered_sidebars
70
- *
71
- * @param object $data JSON widget data from .wie file
72
- *
73
- * @return array Results array
74
- */
75
- function import_widgets_data( $data ) {
76
-
77
- global $wp_registered_sidebars;
78
-
79
- // Have valid data?
80
- // If no data or could not decode
81
- if ( empty( $data ) || ! is_object( $data ) ) {
82
- wp_die(
83
- esc_html__( 'Import data could not be read. Please try a different file.', 'astra-sites' ),
84
- '',
85
- array(
86
- 'back_link' => true,
87
- )
88
- );
89
- }
90
-
91
- // Hook before import
92
- do_action( 'wie_before_import' );
93
- $data = apply_filters( 'wie_import_data', $data );
94
-
95
- // Get all available widgets site supports
96
- $available_widgets = $this->wie_available_widgets();
97
-
98
- // Get all existing widget instances
99
- $widget_instances = array();
100
- foreach ( $available_widgets as $widget_data ) {
101
- $widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] );
102
- }
103
-
104
- // Begin results
105
- $results = array();
106
-
107
- // Loop import data's sidebars
108
- foreach ( $data as $sidebar_id => $widgets ) {
109
-
110
- // Skip inactive widgets
111
- // (should not be in export file)
112
- if ( 'wp_inactive_widgets' == $sidebar_id ) {
113
- continue;
114
- }
115
-
116
- // Check if sidebar is available on this site
117
- // Otherwise add widgets to inactive, and say so
118
- if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
119
- $sidebar_available = true;
120
- $use_sidebar_id = $sidebar_id;
121
- $sidebar_message_type = 'success';
122
- $sidebar_message = '';
123
- } else {
124
- $sidebar_available = false;
125
- $use_sidebar_id = 'wp_inactive_widgets'; // add to inactive if sidebar does not exist in theme
126
- $sidebar_message_type = 'error';
127
- $sidebar_message = esc_html__( 'Widget area does not exist in theme (using Inactive)', 'astra-sites' );
128
- }
129
-
130
- // Result for sidebar
131
- $results[ $sidebar_id ]['name'] = ! empty( $wp_registered_sidebars[ $sidebar_id ]['name'] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : $sidebar_id; // sidebar name if theme supports it; otherwise ID
132
- $results[ $sidebar_id ]['message_type'] = $sidebar_message_type;
133
- $results[ $sidebar_id ]['message'] = $sidebar_message;
134
- $results[ $sidebar_id ]['widgets'] = array();
135
-
136
- // Loop widgets
137
- foreach ( $widgets as $widget_instance_id => $widget ) {
138
-
139
- $fail = false;
140
-
141
- // Get id_base (remove -# from end) and instance ID number
142
- $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
143
- $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
144
-
145
- // Does site support this widget?
146
- if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) {
147
- $fail = true;
148
- $widget_message_type = 'error';
149
- $widget_message = esc_html__( 'Site does not support widget', 'astra-sites' ); // explain why widget not imported
150
- }
151
-
152
- // Filter to modify settings object before conversion to array and import
153
- // Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below)
154
- // Ideally the newer wie_widget_settings_array below will be used instead of this
155
- $widget = apply_filters( 'wie_widget_settings', $widget ); // object
156
-
157
- // Convert multidimensional objects to multidimensional arrays
158
- // Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays
159
- // Without this, they are imported as objects and cause fatal error on Widgets page
160
- // If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays
161
- // It is probably much more likely that arrays are used than objects, however
162
- $widget = json_decode( wp_json_encode( $widget ), true );
163
-
164
- // Filter to modify settings array
165
- // This is preferred over the older wie_widget_settings filter above
166
- // Do before identical check because changes may make it identical to end result (such as URL replacements)
167
- $widget = apply_filters( 'wie_widget_settings_array', $widget );
168
-
169
- // Does widget with identical settings already exist in same sidebar?
170
- if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) {
171
-
172
- // Get existing widgets in this sidebar
173
- $sidebars_widgets = get_option( 'sidebars_widgets' );
174
- $sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // check Inactive if that's where will go
175
-
176
- // Loop widgets with ID base
177
- $single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array();
178
- foreach ( $single_widget_instances as $check_id => $check_widget ) {
179
-
180
- // Is widget in same sidebar and has identical settings?
181
- if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
182
-
183
- $fail = true;
184
- $widget_message_type = 'warning';
185
- $widget_message = esc_html__( 'Widget already exists', 'astra-sites' ); // explain why widget not imported
186
-
187
- break;
188
-
189
- }
190
- }
191
- }
192
-
193
- // No failure
194
- if ( ! $fail ) {
195
-
196
- // Add widget instance
197
- $single_widget_instances = get_option( 'widget_' . $id_base ); // all instances for that widget ID base, get fresh every time
198
- $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array(
199
- '_multiwidget' => 1,
200
- ); // start fresh if have to
201
- $single_widget_instances[] = $widget; // add it
202
-
203
- // Get the key it was given
204
- end( $single_widget_instances );
205
- $new_instance_id_number = key( $single_widget_instances );
206
-
207
- // If key is 0, make it 1
208
- // When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it)
209
- if ( '0' === strval( $new_instance_id_number ) ) {
210
- $new_instance_id_number = 1;
211
- $single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0];
212
- unset( $single_widget_instances[0] );
213
- }
214
-
215
- // Move _multiwidget to end of array for uniformity
216
- if ( isset( $single_widget_instances['_multiwidget'] ) ) {
217
- $multiwidget = $single_widget_instances['_multiwidget'];
218
- unset( $single_widget_instances['_multiwidget'] );
219
- $single_widget_instances['_multiwidget'] = $multiwidget;
220
- }
221
-
222
- // Update option with new widget
223
- $result = update_option( 'widget_' . $id_base, $single_widget_instances );
224
-
225
- // Assign widget instance to sidebar
226
- $sidebars_widgets = get_option( 'sidebars_widgets' ); // which sidebars have which widgets, get fresh every time
227
-
228
- // Avoid rarely fatal error when the option is an empty string
229
- // https://github.com/churchthemes/widget-importer-exporter/pull/11
230
- if ( ! $sidebars_widgets ) {
231
- $sidebars_widgets = array();
232
- }
233
-
234
- $new_instance_id = $id_base . '-' . $new_instance_id_number; // use ID number from new widget instance
235
- $sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // add new instance to sidebar
236
- update_option( 'sidebars_widgets', $sidebars_widgets ); // save the amended data
237
-
238
- // After widget import action
239
- $after_widget_import = array(
240
- 'sidebar' => $use_sidebar_id,
241
- 'sidebar_old' => $sidebar_id,
242
- 'widget' => $widget,
243
- 'widget_type' => $id_base,
244
- 'widget_id' => $new_instance_id,
245
- 'widget_id_old' => $widget_instance_id,
246
- 'widget_id_num' => $new_instance_id_number,
247
- 'widget_id_num_old' => $instance_id_number,
248
- );
249
- do_action( 'wie_after_widget_import', $after_widget_import );
250
-
251
- // Success message
252
- if ( $sidebar_available ) {
253
- $widget_message_type = 'success';
254
- $widget_message = esc_html__( 'Imported', 'astra-sites' );
255
- } else {
256
- $widget_message_type = 'warning';
257
- $widget_message = esc_html__( 'Imported to Inactive', 'astra-sites' );
258
- }
259
- }// End if().
260
-
261
- // Result for widget instance
262
- $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['name'] = isset( $available_widgets[ $id_base ]['name'] ) ? $available_widgets[ $id_base ]['name'] : $id_base; // widget name or ID if name not available (not supported by site)
263
- $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : esc_html__( 'No Title', 'astra-sites' ); // show "No Title" if widget instance is untitled
264
- $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type;
265
- $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message;
266
-
267
- }// End foreach().
268
- }// End foreach().
269
-
270
- // Hook after import
271
- do_action( 'wie_after_import' );
272
-
273
- // Return results
274
- return apply_filters( 'wie_import_results', $results );
275
-
276
- }
277
-
278
- }
1
+ <?php
2
+ /**
3
+ * Widget Data exporter class.
4
+ *
5
+ * @package Astra Addon
6
+ * @see - https://wordpress.org/plugins/widget-importer-exporter/
7
+ */
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Widget Data exporter class.
13
+ *
14
+ * @see - https://wordpress.org/plugins/widget-importer-exporter/
15
+ */
16
+ class Astra_Widget_Importer {
17
+
18
+ /**
19
+ * Instance of Astra_Widget_Importer
20
+ *
21
+ * @var Astra_Widget_Importer
22
+ */
23
+ private static $_instance = null;
24
+
25
+ public static function instance() {
26
+
27
+ if ( ! isset( self::$_instance ) ) {
28
+ self::$_instance = new self;
29
+ }
30
+
31
+ return self::$_instance;
32
+ }
33
+
34
+ /**
35
+ * Available widgets
36
+ *
37
+ * Gather site's widgets into array with ID base, name, etc.
38
+ * Used by export and import functions.
39
+ *
40
+ * @since 0.4
41
+ * @global array $wp_registered_widget_updates
42
+ * @return array Widget information
43
+ */
44
+ function wie_available_widgets() {
45
+
46
+ global $wp_registered_widget_controls;
47
+
48
+ $widget_controls = $wp_registered_widget_controls;
49
+
50
+ $available_widgets = array();
51
+
52
+ foreach ( $widget_controls as $widget ) {
53
+
54
+ if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) { // no dupes
55
+
56
+ $available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base'];
57
+ $available_widgets[ $widget['id_base'] ]['name'] = $widget['name'];
58
+
59
+ }
60
+ }
61
+
62
+ return apply_filters( 'wie_available_widgets', $available_widgets );
63
+ }
64
+
65
+ /**
66
+ * Import widget JSON data
67
+ *
68
+ * @since 0.4
69
+ * @global array $wp_registered_sidebars
70
+ *
71
+ * @param object $data JSON widget data from .wie file
72
+ *
73
+ * @return array Results array
74
+ */
75
+ function import_widgets_data( $data ) {
76
+
77
+ global $wp_registered_sidebars;
78
+
79
+ // Have valid data?
80
+ // If no data or could not decode
81
+ if ( empty( $data ) || ! is_object( $data ) ) {
82
+ wp_die(
83
+ esc_html__( 'Import data could not be read. Please try a different file.', 'astra-sites' ),
84
+ '',
85
+ array(
86
+ 'back_link' => true,
87
+ )
88
+ );
89
+ }
90
+
91
+ // Hook before import
92
+ do_action( 'wie_before_import' );
93
+ $data = apply_filters( 'wie_import_data', $data );
94
+
95
+ // Get all available widgets site supports
96
+ $available_widgets = $this->wie_available_widgets();
97
+
98
+ // Get all existing widget instances
99
+ $widget_instances = array();
100
+ foreach ( $available_widgets as $widget_data ) {
101
+ $widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] );
102
+ }
103
+
104
+ // Begin results
105
+ $results = array();
106
+
107
+ // Loop import data's sidebars
108
+ foreach ( $data as $sidebar_id => $widgets ) {
109
+
110
+ // Skip inactive widgets
111
+ // (should not be in export file)
112
+ if ( 'wp_inactive_widgets' == $sidebar_id ) {
113
+ continue;
114
+ }
115
+
116
+ // Check if sidebar is available on this site
117
+ // Otherwise add widgets to inactive, and say so
118
+ if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
119
+ $sidebar_available = true;
120
+ $use_sidebar_id = $sidebar_id;
121
+ $sidebar_message_type = 'success';
122
+ $sidebar_message = '';
123
+ } else {
124
+ $sidebar_available = false;
125
+ $use_sidebar_id = 'wp_inactive_widgets'; // add to inactive if sidebar does not exist in theme
126
+ $sidebar_message_type = 'error';
127
+ $sidebar_message = esc_html__( 'Widget area does not exist in theme (using Inactive)', 'astra-sites' );
128
+ }
129
+
130
+ // Result for sidebar
131
+ $results[ $sidebar_id ]['name'] = ! empty( $wp_registered_sidebars[ $sidebar_id ]['name'] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : $sidebar_id; // sidebar name if theme supports it; otherwise ID
132
+ $results[ $sidebar_id ]['message_type'] = $sidebar_message_type;
133
+ $results[ $sidebar_id ]['message'] = $sidebar_message;
134
+ $results[ $sidebar_id ]['widgets'] = array();
135
+
136
+ // Loop widgets
137
+ foreach ( $widgets as $widget_instance_id => $widget ) {
138
+
139
+ $fail = false;
140
+
141
+ // Get id_base (remove -# from end) and instance ID number
142
+ $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
143
+ $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
144
+
145
+ // Does site support this widget?
146
+ if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) {
147
+ $fail = true;
148
+ $widget_message_type = 'error';
149
+ $widget_message = esc_html__( 'Site does not support widget', 'astra-sites' ); // explain why widget not imported
150
+ }
151
+
152
+ // Filter to modify settings object before conversion to array and import
153
+ // Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below)
154
+ // Ideally the newer wie_widget_settings_array below will be used instead of this
155
+ $widget = apply_filters( 'wie_widget_settings', $widget ); // object
156
+
157
+ // Convert multidimensional objects to multidimensional arrays
158
+ // Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays
159
+ // Without this, they are imported as objects and cause fatal error on Widgets page
160
+ // If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays
161
+ // It is probably much more likely that arrays are used than objects, however
162
+ $widget = json_decode( wp_json_encode( $widget ), true );
163
+
164
+ // Filter to modify settings array
165
+ // This is preferred over the older wie_widget_settings filter above
166
+ // Do before identical check because changes may make it identical to end result (such as URL replacements)
167
+ $widget = apply_filters( 'wie_widget_settings_array', $widget );
168
+
169
+ // Does widget with identical settings already exist in same sidebar?
170
+ if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) {
171
+
172
+ // Get existing widgets in this sidebar
173
+ $sidebars_widgets = get_option( 'sidebars_widgets' );
174
+ $sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // check Inactive if that's where will go
175
+
176
+ // Loop widgets with ID base
177
+ $single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array();
178
+ foreach ( $single_widget_instances as $check_id => $check_widget ) {
179
+
180
+ // Is widget in same sidebar and has identical settings?
181
+ if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
182
+
183
+ $fail = true;
184
+ $widget_message_type = 'warning';
185
+ $widget_message = esc_html__( 'Widget already exists', 'astra-sites' ); // explain why widget not imported
186
+
187
+ break;
188
+
189
+ }
190
+ }
191
+ }
192
+
193
+ // No failure
194
+ if ( ! $fail ) {
195
+
196
+ // Add widget instance
197
+ $single_widget_instances = get_option( 'widget_' . $id_base ); // all instances for that widget ID base, get fresh every time
198
+ $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array(
199
+ '_multiwidget' => 1,
200
+ ); // start fresh if have to
201
+ $single_widget_instances[] = $widget; // add it
202
+
203
+ // Get the key it was given
204
+ end( $single_widget_instances );
205
+ $new_instance_id_number = key( $single_widget_instances );
206
+
207
+ // If key is 0, make it 1
208
+ // When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it)
209
+ if ( '0' === strval( $new_instance_id_number ) ) {
210
+ $new_instance_id_number = 1;
211
+ $single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0];
212
+ unset( $single_widget_instances[0] );
213
+ }
214
+
215
+ // Move _multiwidget to end of array for uniformity
216
+ if ( isset( $single_widget_instances['_multiwidget'] ) ) {
217
+ $multiwidget = $single_widget_instances['_multiwidget'];
218
+ unset( $single_widget_instances['_multiwidget'] );
219
+ $single_widget_instances['_multiwidget'] = $multiwidget;
220
+ }
221
+
222
+ // Update option with new widget
223
+ $result = update_option( 'widget_' . $id_base, $single_widget_instances );
224
+
225
+ // Assign widget instance to sidebar
226
+ $sidebars_widgets = get_option( 'sidebars_widgets' ); // which sidebars have which widgets, get fresh every time
227
+
228
+ // Avoid rarely fatal error when the option is an empty string
229
+ // https://github.com/churchthemes/widget-importer-exporter/pull/11
230
+ if ( ! $sidebars_widgets ) {
231
+ $sidebars_widgets = array();
232
+ }
233
+
234
+ $new_instance_id = $id_base . '-' . $new_instance_id_number; // use ID number from new widget instance
235
+ $sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // add new instance to sidebar
236
+ update_option( 'sidebars_widgets', $sidebars_widgets ); // save the amended data
237
+
238
+ // After widget import action
239
+ $after_widget_import = array(
240
+ 'sidebar' => $use_sidebar_id,
241
+ 'sidebar_old' => $sidebar_id,
242
+ 'widget' => $widget,
243
+ 'widget_type' => $id_base,
244
+ 'widget_id' => $new_instance_id,
245
+ 'widget_id_old' => $widget_instance_id,
246
+ 'widget_id_num' => $new_instance_id_number,
247
+ 'widget_id_num_old' => $instance_id_number,
248
+ );
249
+ do_action( 'wie_after_widget_import', $after_widget_import );
250
+
251
+ // Success message
252
+ if ( $sidebar_available ) {
253
+ $widget_message_type = 'success';
254
+ $widget_message = esc_html__( 'Imported', 'astra-sites' );
255
+ } else {
256
+ $widget_message_type = 'warning';
257
+ $widget_message = esc_html__( 'Imported to Inactive', 'astra-sites' );
258
+ }
259
+ }// End if().
260
+
261
+ // Result for widget instance
262
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['name'] = isset( $available_widgets[ $id_base ]['name'] ) ? $available_widgets[ $id_base ]['name'] : $id_base; // widget name or ID if name not available (not supported by site)
263
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : esc_html__( 'No Title', 'astra-sites' ); // show "No Title" if widget instance is untitled
264
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type;
265
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message;
266
+
267
+ }// End foreach().
268
+ }// End foreach().
269
+
270
+ // Hook after import
271
+ do_action( 'wie_after_import' );
272
+
273
+ // Return results
274
+ return apply_filters( 'wie_import_results', $results );
275
+
276
+ }
277
+
278
+ }
inc/importers/wxr-importer/class-logger.php CHANGED
@@ -1,140 +1,140 @@
1
- <?php
2
-
3
- /**
4
- * Describes a logger instance
5
- *
6
- * Based on PSR-3: http://www.php-fig.org/psr/psr-3/
7
- *
8
- * The message MUST be a string or object implementing __toString().
9
- *
10
- * The message MAY contain placeholders in the form: {foo} where foo
11
- * will be replaced by the context data in key "foo".
12
- *
13
- * The context array can contain arbitrary data, the only assumption that
14
- * can be made by implementors is that if an Exception instance is given
15
- * to produce a stack trace, it MUST be in a key named "exception".
16
- *
17
- * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
18
- * for the full interface specification.
19
- */
20
- class WP_Importer_Logger {
21
- /**
22
- * System is unusable.
23
- *
24
- * @param string $message
25
- * @param array $context
26
- * @return null
27
- */
28
- public function emergency( $message, array $context = array() ) {
29
- return $this->log( 'emergency', $message, $context );
30
- }
31
-
32
- /**
33
- * Action must be taken immediately.
34
- *
35
- * Example: Entire website down, database unavailable, etc. This should
36
- * trigger the SMS alerts and wake you up.
37
- *
38
- * @param string $message
39
- * @param array $context
40
- * @return null
41
- */
42
- public function alert( $message, array $context = array() ) {
43
- return $this->log( 'alert', $message, $context );
44
- }
45
-
46
- /**
47
- * Critical conditions.
48
- *
49
- * Example: Application component unavailable, unexpected exception.
50
- *
51
- * @param string $message
52
- * @param array $context
53
- * @return null
54
- */
55
- public function critical( $message, array $context = array() ) {
56
- return $this->log( 'critical', $message, $context );
57
- }
58
-
59
- /**
60
- * Runtime errors that do not require immediate action but should typically
61
- * be logged and monitored.
62
- *
63
- * @param string $message
64
- * @param array $context
65
- * @return null
66
- */
67
- public function error( $message, array $context = array() ) {
68
- return $this->log( 'error', $message, $context );
69
- }
70
-
71
- /**
72
- * Exceptional occurrences that are not errors.
73
- *
74
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
75
- * that are not necessarily wrong.
76
- *
77
- * @param string $message
78
- * @param array $context
79
- * @return null
80
- */
81
- public function warning( $message, array $context = array() ) {
82
- return $this->log( 'warning', $message, $context );
83
- }
84
-
85
- /**
86
- * Normal but significant events.
87
- *
88
- * @param string $message
89
- * @param array $context
90
- * @return null
91
- */
92
- public function notice( $message, array $context = array() ) {
93
- return $this->log( 'notice', $message, $context );
94
- }
95
-
96
- /**
97
- * Interesting events.
98
- *
99
- * Example: User logs in, SQL logs.
100
- *
101
- * @param string $message
102
- * @param array $context
103
- * @return null
104
- */
105
- public function info( $message, array $context = array() ) {
106
- return $this->log( 'info', $message, $context );
107
- }
108
-
109
- /**
110
- * Detailed debug information.
111
- *
112
- * @param string $message
113
- * @param array $context
114
- * @return null
115
- */
116
- public function debug( $message, array $context = array() ) {
117
- return $this->log( 'debug', $message, $context );
118
- }
119
-
120
- /**
121
- * Logs with an arbitrary level.
122
- *
123
- * @param mixed $level
124
- * @param string $message
125
- * @param array $context
126
- * @return null
127
- */
128
- public function log( $level, $message, array $context = array() ) {
129
-
130
- // Log
131
- do_action( 'astra_sites_import_xml_log', $level, $message, $context );
132
-
133
- $this->messages[] = array(
134
- 'timestamp' => time(),
135
- 'level' => $level,
136
- 'message' => $message,
137
- 'context' => $context,
138
- );
139
- }
140
- }
1
+ <?php
2
+
3
+ /**
4
+ * Describes a logger instance
5
+ *
6
+ * Based on PSR-3: http://www.php-fig.org/psr/psr-3/
7
+ *
8
+ * The message MUST be a string or object implementing __toString().
9
+ *
10
+ * The message MAY contain placeholders in the form: {foo} where foo
11
+ * will be replaced by the context data in key "foo".
12
+ *
13
+ * The context array can contain arbitrary data, the only assumption that
14
+ * can be made by implementors is that if an Exception instance is given
15
+ * to produce a stack trace, it MUST be in a key named "exception".
16
+ *
17
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
18
+ * for the full interface specification.
19
+ */
20
+ class WP_Importer_Logger {
21
+ /**
22
+ * System is unusable.
23
+ *
24
+ * @param string $message
25
+ * @param array $context
26
+ * @return null
27
+ */
28
+ public function emergency( $message, array $context = array() ) {
29
+ return $this->log( 'emergency', $message, $context );
30
+ }
31
+
32
+ /**
33
+ * Action must be taken immediately.
34
+ *
35
+ * Example: Entire website down, database unavailable, etc. This should
36
+ * trigger the SMS alerts and wake you up.
37
+ *
38
+ * @param string $message
39
+ * @param array $context
40
+ * @return null
41
+ */
42
+ public function alert( $message, array $context = array() ) {
43
+ return $this->log( 'alert', $message, $context );
44
+ }
45
+
46
+ /**
47
+ * Critical conditions.
48
+ *
49
+ * Example: Application component unavailable, unexpected exception.
50
+ *
51
+ * @param string $message
52
+ * @param array $context
53
+ * @return null
54
+ */
55
+ public function critical( $message, array $context = array() ) {
56
+ return $this->log( 'critical', $message, $context );
57
+ }
58
+
59
+ /**
60
+ * Runtime errors that do not require immediate action but should typically
61
+ * be logged and monitored.
62
+ *
63
+ * @param string $message
64
+ * @param array $context
65
+ * @return null
66
+ */
67
+ public function error( $message, array $context = array() ) {
68
+ return $this->log( 'error', $message, $context );
69
+ }
70
+
71
+ /**
72
+ * Exceptional occurrences that are not errors.
73
+ *
74
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
75
+ * that are not necessarily wrong.
76
+ *
77
+ * @param string $message
78
+ * @param array $context
79
+ * @return null
80
+ */
81
+ public function warning( $message, array $context = array() ) {
82
+ return $this->log( 'warning', $message, $context );
83
+ }
84
+
85
+ /**
86
+ * Normal but significant events.
87
+ *
88
+ * @param string $message
89
+ * @param array $context
90
+ * @return null
91
+ */
92
+ public function notice( $message, array $context = array() ) {
93
+ return $this->log( 'notice', $message, $context );
94
+ }
95
+
96
+ /**
97
+ * Interesting events.
98
+ *
99
+ * Example: User logs in, SQL logs.
100
+ *
101
+ * @param string $message
102
+ * @param array $context
103
+ * @return null
104
+ */
105
+ public function info( $message, array $context = array() ) {
106
+ return $this->log( 'info', $message, $context );
107
+ }
108
+
109
+ /**
110
+ * Detailed debug information.
111
+ *
112
+ * @param string $message
113
+ * @param array $context
114
+ * @return null
115
+ */
116
+ public function debug( $message, array $context = array() ) {
117
+ return $this->log( 'debug', $message, $context );
118
+ }
119
+
120
+ /**
121
+ * Logs with an arbitrary level.
122
+ *
123
+ * @param mixed $level
124
+ * @param string $message
125
+ * @param array $context
126
+ * @return null
127
+ */
128
+ public function log( $level, $message, array $context = array() ) {
129
+
130
+ // Log
131
+ do_action( 'astra_sites_import_xml_log', $level, $message, $context );
132
+
133
+ $this->messages[] = array(
134
+ 'timestamp' => time(),
135
+ 'level' => $level,
136
+ 'message' => $message,
137
+ 'context' => $context,
138
+ );
139
+ }
140
+ }
inc/importers/wxr-importer/class-wp-importer-logger-serversentevents.php CHANGED
@@ -1,46 +1,46 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'WP_Importer_Logger_ServerSentEvents' ) && class_exists( 'WP_Importer_Logger' ) ) {
4
-
5
- class WP_Importer_Logger_ServerSentEvents extends WP_Importer_Logger {
6
-
7
- /**
8
- * Logs with an arbitrary level.
9
- *
10
- * @param mixed $level
11
- * @param string $message
12
- * @param array $context
13
- * @return null
14
- */
15
- public function log( $level, $message, array $context = array() ) {
16
-
17
- // Log
18
- do_action( 'astra_sites_import_xml_log', $level, $message, $context );
19
-
20
- $data = compact( 'level', 'message' );
21
-
22
- switch ( $level ) {
23
- case 'emergency':
24
- case 'alert':
25
- case 'critical':
26
- case 'error':
27
- case 'warning':
28
- case 'notice':
29
- case 'info':
30
- echo "event: log\n";
31
- echo 'data: ' . wp_json_encode( $data ) . "\n\n";
32
- flush();
33
- break;
34
-
35
- case 'debug':
36
- if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
37
- echo "event: log\n";
38
- echo 'data: ' . wp_json_encode( $data ) . "\n\n";
39
- flush();
40
- break;
41
- }
42
- break;
43
- }
44
- }
45
- }
46
- }
1
+ <?php
2
+
3
+ if ( ! class_exists( 'WP_Importer_Logger_ServerSentEvents' ) && class_exists( 'WP_Importer_Logger' ) ) {
4
+
5
+ class WP_Importer_Logger_ServerSentEvents extends WP_Importer_Logger {
6
+
7
+ /**
8
+ * Logs with an arbitrary level.
9
+ *
10
+ * @param mixed $level
11
+ * @param string $message
12
+ * @param array $context
13
+ * @return null
14
+ */
15
+ public function log( $level, $message, array $context = array() ) {
16
+
17
+ // Log
18
+ do_action( 'astra_sites_import_xml_log', $level, $message, $context );
19
+
20
+ $data = compact( 'level', 'message' );
21
+
22
+ switch ( $level ) {
23
+ case 'emergency':
24
+ case 'alert':
25
+ case 'critical':
26
+ case 'error':
27
+ case 'warning':
28
+ case 'notice':
29
+ case 'info':
30
+ echo "event: log\n";
31
+ echo 'data: ' . wp_json_encode( $data ) . "\n\n";
32
+ flush();
33
+ break;
34
+
35
+ case 'debug':
36
+ if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
37
+ echo "event: log\n";
38
+ echo 'data: ' . wp_json_encode( $data ) . "\n\n";
39
+ flush();
40
+ break;
41
+ }
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ }
inc/importers/wxr-importer/class-wxr-import-info.php CHANGED
@@ -1,21 +1,21 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'WXR_Import_Info' ) ) {
4
-
5
- class WXR_Import_Info {
6
- public $home;
7
- public $siteurl;
8
-
9
- public $title;
10
-
11
- public $users = array();
12
- public $post_count = 0;
13
- public $media_count = 0;
14
- public $comment_count = 0;
15
- public $term_count = 0;
16
-
17
- public $generator = '';
18
- public $version;
19
- }
20
-
21
ย  }
1
+ <?php
2
+
3
+ if ( ! class_exists( 'WXR_Import_Info' ) ) {
4
+
5
+ class WXR_Import_Info {
6
+ public $home;
7
+ public $siteurl;
8
+
9
+ public $title;
10
+
11
+ public $users = array();
12
+ public $post_count = 0;
13
+ public $media_count = 0;
14
+ public $comment_count = 0;
15
+ public $term_count = 0;
16
+
17
+ public $generator = '';
18
+ public $version;
19
+ }
20
+
21
ย  }
inc/importers/wxr-importer/class-wxr-importer.php CHANGED
@@ -1,2299 +1,2299 @@
1
- <?php
2
-
3
- class WXR_Importer extends WP_Importer {
4
- /**
5
- * Maximum supported WXR version
6
- */
7
- const MAX_WXR_VERSION = 1.2;
8
-
9
- /**
10
- * Regular expression for checking if a post references an attachment
11
- *
12
- * Note: This is a quick, weak check just to exclude text-only posts. More
13
- * vigorous checking is done later to verify.
14
- */
15
- const REGEX_HAS_ATTACHMENT_REFS = '!
16
- (
17
- # Match anything with an image or attachment class
18
- class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
19
- |
20
- # Match anything that looks like an upload URL
21
- src=[\'"][^\'"]*(
22
- [0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
23
- |
24
- content/uploads[^\'"]+
25
- )[\'"]
26
- )!ix';
27
-
28
- /**
29
- * Version of WXR we're importing.
30
- *
31
- * Defaults to 1.0 for compatibility. Typically overridden by a
32
- * `<wp:wxr_version>` tag at the start of the file.
33
- *
34
- * @var string
35
- */
36
- protected $version = '1.0';
37
-
38
- // information to import from WXR file
39
- protected $categories = array();
40
- protected $tags = array();
41
- protected $base_url = '';
42
-
43
- // TODO: REMOVE THESE
44
- protected $processed_terms = array();
45
- protected $processed_posts = array();
46
- protected $processed_menu_items = array();
47
- protected $menu_item_orphans = array();
48
- protected $missing_menu_items = array();
49
-
50
- // NEW STYLE
51
- protected $mapping = array();
52
- protected $requires_remapping = array();
53
- protected $exists = array();
54
- protected $user_slug_override = array();
55
-
56
- protected $url_remap = array();
57
- protected $featured_images = array();
58
-
59
- /**
60
- * Logger instance.
61
- *
62
- * @var WP_Importer_Logger
63
- */
64
- protected $logger;
65
-
66
- /**
67
- * Constructor
68
- *
69
- * @param array $options {
70
- * @var bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.)
71
- * @var bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.)
72
- * @var bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.)
73
- * @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
74
- * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
75
- * @var bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `<img class="wp-image-*">` only. Default is false.)
76
- * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
77
- * }
78
- */
79
- public function __construct( $options = array() ) {
80
- // Initialize some important variables
81
- $empty_types = array(
82
- 'post' => array(),
83
- 'comment' => array(),
84
- 'term' => array(),
85
- 'user' => array(),
86
- );
87
-
88
- $this->mapping = $empty_types;
89
- $this->mapping['user_slug'] = array();
90
- $this->mapping['term_id'] = array();
91
- $this->requires_remapping = $empty_types;
92
- $this->exists = $empty_types;
93
-
94
- $this->options = wp_parse_args( $options, array(
95
- 'prefill_existing_posts' => true,
96
- 'prefill_existing_comments' => true,
97
- 'prefill_existing_terms' => true,
98
- 'update_attachment_guids' => false,
99
- 'fetch_attachments' => false,
100
- 'aggressive_url_search' => false,
101
- 'default_author' => null,
102
- ) );
103
- }
104
-
105
- public function set_logger( $logger ) {
106
- $this->logger = $logger;
107
- }
108
-
109
- /**
110
- * Get a stream reader for the file.
111
- *
112
- * @param string $file Path to the XML file.
113
- * @return XMLReader|WP_Error Reader instance on success, error otherwise.
114
- */
115
- protected function get_reader( $file ) {
116
- // Avoid loading external entities for security
117
- $old_value = null;
118
- if ( function_exists( 'libxml_disable_entity_loader' ) ) {
119
- // $old_value = libxml_disable_entity_loader( true );
120
- }
121
-
122
- $reader = new XMLReader();
123
- $status = $reader->open( $file );
124
-
125
- if ( ! is_null( $old_value ) ) {
126
- // libxml_disable_entity_loader( $old_value );
127
- }
128
-
129
- if ( ! $status ) {
130
- return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
131
- }
132
-
133
- return $reader;
134
- }
135
-
136
- /**
137
- * The main controller for the actual import stage.
138
- *
139
- * @param string $file Path to the WXR file for importing
140
- */
141
- public function get_preliminary_information( $file ) {
142
- // Let's run the actual importer now, woot
143
- $reader = $this->get_reader( $file );
144
- if ( is_wp_error( $reader ) ) {
145
- return $reader;
146
- }
147
-
148
- // Set the version to compatibility mode first
149
- $this->version = '1.0';
150
-
151
- // Start parsing!
152
- $data = new WXR_Import_Info();
153
- while ( $reader->read() ) {
154
- // Only deal with element opens
155
- if ( $reader->nodeType !== XMLReader::ELEMENT ) {
156
- continue;
157
- }
158
-
159
- switch ( $reader->name ) {
160
- case 'wp:wxr_version':
161
- // Upgrade to the correct version
162
- $this->version = $reader->readString();
163
-
164
- if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
165
- $this->logger->warning( sprintf(
166
- __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
167
- $this->version,
168
- self::MAX_WXR_VERSION
169
- ) );
170
- }
171
-
172
- // Handled everything in this node, move on to the next
173
- $reader->next();
174
- break;
175
-
176
- case 'generator':
177
- $data->generator = $reader->readString();
178
- $reader->next();
179
- break;
180
-
181
- case 'title':
182
- $data->title = $reader->readString();
183
- $reader->next();
184
- break;
185
-
186
- case 'wp:base_site_url':
187
- $data->siteurl = $reader->readString();
188
- $reader->next();
189
- break;
190
-
191
- case 'wp:base_blog_url':
192
- $data->home = $reader->readString();
193
- $reader->next();
194
- break;
195
-
196
- case 'wp:author':
197
- $node = $reader->expand();
198
-
199
- $parsed = $this->parse_author_node( $node );
200
- if ( is_wp_error( $parsed ) ) {
201
- $this->log_error( $parsed );
202
-
203
- // Skip the rest of this post
204
- $reader->next();
205
- break;
206
- }
207
-
208
- $data->users[] = $parsed;
209
-
210
- // Handled everything in this node, move on to the next
211
- $reader->next();
212
- break;
213
-
214
- case 'item':
215
- $node = $reader->expand();
216
- $parsed = $this->parse_post_node( $node );
217
- if ( is_wp_error( $parsed ) ) {
218
- $this->log_error( $parsed );
219
-
220
- // Skip the rest of this post
221
- $reader->next();
222
- break;
223
- }
224
-
225
- if ( $parsed['data']['post_type'] === 'attachment' ) {
226
- $data->media_count++;
227
- } else {
228
- $data->post_count++;
229
- }
230
- $data->comment_count += count( $parsed['comments'] );
231
-
232
- // Handled everything in this node, move on to the next
233
- $reader->next();
234
- break;
235
-
236
- case 'wp:category':
237
- case 'wp:tag':
238
- case 'wp:term':
239
- $data->term_count++;
240
-
241
- // Handled everything in this node, move on to the next
242
- $reader->next();
243
- break;
244
- }// End switch().
245
- }// End while().
246
-
247
- $data->version = $this->version;
248
-
249
- return $data;
250
- }
251
-
252
- /**
253
- * The main controller for the actual import stage.
254
- *
255
- * @param string $file Path to the WXR file for importing
256
- */
257
- public function parse_authors( $file ) {
258
- // Let's run the actual importer now, woot
259
- $reader = $this->get_reader( $file );
260
- if ( is_wp_error( $reader ) ) {
261
- return $reader;
262
- }
263
-
264
- // Set the version to compatibility mode first
265
- $this->version = '1.0';
266
-
267
- // Start parsing!
268
- $authors = array();
269
- while ( $reader->read() ) {
270
- // Only deal with element opens
271
- if ( $reader->nodeType !== XMLReader::ELEMENT ) {
272
- continue;
273
- }
274
-
275
- switch ( $reader->name ) {
276
- case 'wp:wxr_version':
277
- // Upgrade to the correct version
278
- $this->version = $reader->readString();
279
-
280
- if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
281
- $this->logger->warning( sprintf(
282
- __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
283
- $this->version,
284
- self::MAX_WXR_VERSION
285
- ) );
286
- }
287
-
288
- // Handled everything in this node, move on to the next
289
- $reader->next();
290
- break;
291
-
292
- case 'wp:author':
293
- $node = $reader->expand();
294
-
295
- $parsed = $this->parse_author_node( $node );
296
- if ( is_wp_error( $parsed ) ) {
297
- $this->log_error( $parsed );
298
-
299
- // Skip the rest of this post
300
- $reader->next();
301
- break;
302
- }
303
-
304
- $authors[] = $parsed;
305
-
306
- // Handled everything in this node, move on to the next
307
- $reader->next();
308
- break;
309
- }
310
- }// End while().
311
-
312
- return $authors;
313
- }
314
-
315
- /**
316
- * The main controller for the actual import stage.
317
- *
318
- * @param string $file Path to the WXR file for importing
319
- */
320
- public function import( $file ) {
321
- add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
322
- add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
323
-
324
- $result = $this->import_start( $file );
325
- if ( is_wp_error( $result ) ) {
326
- return $result;
327
- }
328
-
329
- // Let's run the actual importer now, woot
330
- $reader = $this->get_reader( $file );
331
- if ( is_wp_error( $reader ) ) {
332
- return $reader;
333
- }
334
-
335
- // Set the version to compatibility mode first
336
- $this->version = '1.0';
337
-
338
- // Reset other variables
339
- $this->base_url = '';
340
-
341
- // Start parsing!
342
- while ( $reader->read() ) {
343
- // Only deal with element opens
344
- if ( $reader->nodeType !== XMLReader::ELEMENT ) {
345
- continue;
346
- }
347
-
348
- switch ( $reader->name ) {
349
- case 'wp:wxr_version':
350
- // Upgrade to the correct version
351
- $this->version = $reader->readString();
352
-
353
- if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
354
- $this->logger->warning( sprintf(
355
- __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
356
- $this->version,
357
- self::MAX_WXR_VERSION
358
- ) );
359
- }
360
-
361
- // Handled everything in this node, move on to the next
362
- $reader->next();
363
- break;
364
-
365
- case 'wp:base_site_url':
366
- $this->base_url = $reader->readString();
367
-
368
- // Handled everything in this node, move on to the next
369
- $reader->next();
370
- break;
371
-
372
- case 'item':
373
- $node = $reader->expand();
374
- $parsed = $this->parse_post_node( $node );
375
- if ( is_wp_error( $parsed ) ) {
376
- $this->log_error( $parsed );
377
-
378
- // Skip the rest of this post
379
- $reader->next();
380
- break;
381
- }
382
-
383
- $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
384
-
385
- // Handled everything in this node, move on to the next
386
- $reader->next();
387
- break;
388
-
389
- case 'wp:author':
390
- $node = $reader->expand();
391
-
392
- $parsed = $this->parse_author_node( $node );
393
- if ( is_wp_error( $parsed ) ) {
394
- $this->log_error( $parsed );
395
-
396
- // Skip the rest of this post
397
- $reader->next();
398
- break;
399
- }
400
-
401
- $status = $this->process_author( $parsed['data'], $parsed['meta'] );
402
- if ( is_wp_error( $status ) ) {
403
- $this->log_error( $status );
404
- }
405
-
406
- // Handled everything in this node, move on to the next
407
- $reader->next();
408
- break;
409
-
410
- case 'wp:category':
411
- $node = $reader->expand();
412
-
413
- $parsed = $this->parse_term_node( $node, 'category' );
414
- if ( is_wp_error( $parsed ) ) {
415
- $this->log_error( $parsed );
416
-
417
- // Skip the rest of this post
418
- $reader->next();
419
- break;
420
- }
421
-
422
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
423
-
424
- // Handled everything in this node, move on to the next
425
- $reader->next();
426
- break;
427
-
428
- case 'wp:tag':
429
- $node = $reader->expand();
430
-
431
- $parsed = $this->parse_term_node( $node, 'tag' );
432
- if ( is_wp_error( $parsed ) ) {
433
- $this->log_error( $parsed );
434
-
435
- // Skip the rest of this post
436
- $reader->next();
437
- break;
438
- }
439
-
440
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
441
-
442
- // Handled everything in this node, move on to the next
443
- $reader->next();
444
- break;
445
-
446
- case 'wp:term':
447
- $node = $reader->expand();
448
-
449
- $parsed = $this->parse_term_node( $node );
450
- if ( is_wp_error( $parsed ) ) {
451
- $this->log_error( $parsed );
452
-
453
- // Skip the rest of this post
454
- $reader->next();
455
- break;
456
- }
457
-
458
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
459
-
460
- // Handled everything in this node, move on to the next
461
- $reader->next();
462
- break;
463
-
464
- default:
465
- // Skip this node, probably handled by something already
466
- break;
467
- }// End switch().
468
- }// End while().
469
-
470
- // Now that we've done the main processing, do any required
471
- // post-processing and remapping.
472
- $this->post_process();
473
-
474
- if ( $this->options['aggressive_url_search'] ) {
475
- $this->replace_attachment_urls_in_content();
476
- }
477
- // $this->remap_featured_images();
478
- $this->import_end();
479
- }
480
-
481
- /**
482
- * Log an error instance to the logger.
483
- *
484
- * @param WP_Error $error Error instance to log.
485
- */
486
- protected function log_error( WP_Error $error ) {
487
- $this->logger->warning( $error->get_error_message() );
488
-
489
- // Log the data as debug info too
490
- $data = $error->get_error_data();
491
- if ( ! empty( $data ) ) {
492
- $this->logger->debug( var_export( $data, true ) );
493
- }
494
- }
495
-
496
- /**
497
- * Parses the WXR file and prepares us for the task of processing parsed data
498
- *
499
- * @param string $file Path to the WXR file for importing
500
- */
501
- protected function import_start( $file ) {
502
- if ( ! is_file( $file ) ) {
503
- return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
504
- }
505
-
506
- // Suspend bunches of stuff in WP core
507
- wp_defer_term_counting( true );
508
- wp_defer_comment_counting( true );
509
- wp_suspend_cache_invalidation( true );
510
-
511
- // Prefill exists calls if told to
512
- if ( $this->options['prefill_existing_posts'] ) {
513
- $this->prefill_existing_posts();
514
- }
515
- if ( $this->options['prefill_existing_comments'] ) {
516
- $this->prefill_existing_comments();
517
- }
518
- if ( $this->options['prefill_existing_terms'] ) {
519
- $this->prefill_existing_terms();
520
- }
521
-
522
- /**
523
- * Begin the import.
524
- *
525
- * Fires before the import process has begun. If you need to suspend
526
- * caching or heavy processing on hooks, do so here.
527
- */
528
- do_action( 'import_start' );
529
- }
530
-
531
- /**
532
- * Performs post-import cleanup of files and the cache
533
- */
534
- protected function import_end() {
535
- // Re-enable stuff in core
536
- wp_suspend_cache_invalidation( false );
537
- wp_cache_flush();
538
- foreach ( get_taxonomies() as $tax ) {
539
- delete_option( "{$tax}_children" );
540
- _get_term_hierarchy( $tax );
541
- }
542
-
543
- wp_defer_term_counting( false );
544
- wp_defer_comment_counting( false );
545
-
546
- /**
547
- * Complete the import.
548
- *
549
- * Fires after the import process has finished. If you need to update
550
- * your cache or re-enable processing, do so here.
551
- */
552
- do_action( 'import_end' );
553
- }
554
-
555
- /**
556
- * Set the user mapping.
557
- *
558
- * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
559
- */
560
- public function set_user_mapping( $mapping ) {
561
- foreach ( $mapping as $map ) {
562
- if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
563
- $this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) );
564
- $this->logger->debug( var_export( $map, true ) );
565
- continue;
566
- }
567
-
568
- $old_slug = $map['old_slug'];
569
- $old_id = $map['old_id'];
570
- $new_id = $map['new_id'];
571
-
572
- $this->mapping['user'][ $old_id ] = $new_id;
573
- $this->mapping['user_slug'][ $old_slug ] = $new_id;
574
- }
575
- }
576
-
577
- /**
578
- * Set the user slug overrides.
579
- *
580
- * Allows overriding the slug in the import with a custom/renamed version.
581
- *
582
- * @param string[] $overrides Map of old slug to new slug.
583
- */
584
- public function set_user_slug_overrides( $overrides ) {
585
- foreach ( $overrides as $original => $renamed ) {
586
- $this->user_slug_override[ $original ] = $renamed;
587
- }
588
- }
589
-
590
- /**
591
- * Parse a post node into post data.
592
- *
593
- * @param DOMElement $node Parent node of post data (typically `item`).
594
- * @return array|WP_Error Post data array on success, error otherwise.
595
- */
596
- protected function parse_post_node( $node ) {
597
- $data = array();
598
- $meta = array();
599
- $comments = array();
600
- $terms = array();
601
-
602
- foreach ( $node->childNodes as $child ) {
603
- // We only care about child elements
604
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
605
- continue;
606
- }
607
-
608
- switch ( $child->tagName ) {
609
- case 'wp:post_type':
610
- $data['post_type'] = $child->textContent;
611
- break;
612
-
613
- case 'title':
614
- $data['post_title'] = $child->textContent;
615
- break;
616
-
617
- case 'guid':
618
- $data['guid'] = $child->textContent;
619
- break;
620
-
621
- case 'dc:creator':
622
- $data['post_author'] = $child->textContent;
623
- break;
624
-
625
- case 'content:encoded':
626
- $data['post_content'] = $child->textContent;
627
- break;
628
-
629
- case 'excerpt:encoded':
630
- $data['post_excerpt'] = $child->textContent;
631
- break;
632
-
633
- case 'wp:post_id':
634
- $data['post_id'] = $child->textContent;
635
- break;
636
-
637
- case 'wp:post_date':
638
- $data['post_date'] = $child->textContent;
639
- break;
640
-
641
- case 'wp:post_date_gmt':
642
- $data['post_date_gmt'] = $child->textContent;
643
- break;
644
-
645
- case 'wp:comment_status':
646
- $data['comment_status'] = $child->textContent;
647
- break;
648
-
649
- case 'wp:ping_status':
650
- $data['ping_status'] = $child->textContent;
651
- break;
652
-
653
- case 'wp:post_name':
654
- $data['post_name'] = $child->textContent;
655
- break;
656
-
657
- case 'wp:status':
658
- $data['post_status'] = $child->textContent;
659
-
660
- if ( $data['post_status'] === 'auto-draft' ) {
661
- // Bail now
662
- return new WP_Error(
663
- 'wxr_importer.post.cannot_import_draft',
664
- __( 'Cannot import auto-draft posts' ),
665
- $data
666
- );
667
- }
668
- break;
669
-
670
- case 'wp:post_parent':
671
- $data['post_parent'] = $child->textContent;
672
- break;
673
-
674
- case 'wp:menu_order':
675
- $data['menu_order'] = $child->textContent;
676
- break;
677
-
678
- case 'wp:post_password':
679
- $data['post_password'] = $child->textContent;
680
- break;
681
-
682
- case 'wp:is_sticky':
683
- $data['is_sticky'] = $child->textContent;
684
- break;
685
-
686
- case 'wp:attachment_url':
687
- $data['attachment_url'] = $child->textContent;
688
- break;
689
-
690
- case 'wp:postmeta':
691
- $meta_item = $this->parse_meta_node( $child );
692
- if ( ! empty( $meta_item ) ) {
693
- $meta[] = $meta_item;
694
- }
695
- break;
696
-
697
- case 'wp:comment':
698
- $comment_item = $this->parse_comment_node( $child );
699
- if ( ! empty( $comment_item ) ) {
700
- $comments[] = $comment_item;
701
- }
702
- break;
703
-
704
- case 'category':
705
- $term_item = $this->parse_category_node( $child );
706
- if ( ! empty( $term_item ) ) {
707
- $terms[] = $term_item;
708
- }
709
- break;
710
- }// End switch().
711
- }// End foreach().
712
-
713
- return compact( 'data', 'meta', 'comments', 'terms' );
714
- }
715
-
716
- /**
717
- * Create new posts based on import information
718
- *
719
- * Posts marked as having a parent which doesn't exist will become top level items.
720
- * Doesn't create a new post if: the post type doesn't exist, the given post ID
721
- * is already noted as imported or a post with the same title and date already exists.
722
- * Note that new/updated terms, comments and meta are imported for the last of the above.
723
- */
724
- protected function process_post( $data, $meta, $comments, $terms ) {
725
- /**
726
- * Pre-process post data.
727
- *
728
- * @param array $data Post data. (Return empty to skip.)
729
- * @param array $meta Meta data.
730
- * @param array $comments Comments on the post.
731
- * @param array $terms Terms on the post.
732
- */
733
- $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
734
- if ( empty( $data ) ) {
735
- return false;
736
- }
737
-
738
- $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
739
- $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
740
- $author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
741
-
742
- // Have we already processed this?
743
- if ( isset( $this->mapping['post'][ $original_id ] ) ) {
744
- return;
745
- }
746
-
747
- $post_type_object = get_post_type_object( $data['post_type'] );
748
-
749
- // Is this type even valid?
750
- if ( ! $post_type_object ) {
751
- $this->logger->warning( sprintf(
752
- __( 'Failed to import "%1$s": Invalid post type %2$s', 'wordpress-importer' ),
753
- $data['post_title'],
754
- $data['post_type']
755
- ) );
756
- return false;
757
- }
758
-
759
- $post_exists = $this->post_exists( $data );
760
- if ( $post_exists ) {
761
- $this->logger->info( sprintf(
762
- __( '%1$s "%2$s" already exists.', 'wordpress-importer' ),
763
- $post_type_object->labels->singular_name,
764
- $data['post_title']
765
- ) );
766
-
767
- /**
768
- * Post processing already imported.
769
- *
770
- * @param array $data Raw data imported for the post.
771
- */
772
- do_action( 'wxr_importer.process_already_imported.post', $data );
773
-
774
- // Even though this post already exists, new comments might need importing
775
- $this->process_comments( $comments, $original_id, $data, $post_exists );
776
-
777
- return false;
778
- }
779
-
780
- // Map the parent post, or mark it as one we need to fix
781
- $requires_remapping = false;
782
- if ( $parent_id ) {
783
- if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
784
- $data['post_parent'] = $this->mapping['post'][ $parent_id ];
785
- } else {
786
- $meta[] = array(
787
- 'key' => '_wxr_import_parent',
788
- 'value' => $parent_id,
789
- );
790
- $requires_remapping = true;
791
-
792
- $data['post_parent'] = 0;
793
- }
794
- }
795
-
796
- // Map the author, or mark it as one we need to fix
797
- $author = sanitize_user( $data['post_author'], true );
798
- if ( empty( $author ) ) {
799
- // Missing or invalid author, use default if available.
800
- $data['post_author'] = $this->options['default_author'];
801
- } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
802
- $data['post_author'] = $this->mapping['user_slug'][ $author ];
803
- } else {
804
- $meta[] = array(
805
- 'key' => '_wxr_import_user_slug',
806
- 'value' => $author,
807
- );
808
- $requires_remapping = true;
809
-
810
- $data['post_author'] = (int) get_current_user_id();
811
- }
812
-
813
- // Does the post look like it contains attachment images?
814
- if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
815
- $meta[] = array(
816
- 'key' => '_wxr_import_has_attachment_refs',
817
- 'value' => true,
818
- );
819
- $requires_remapping = true;
820
- }
821
-
822
- // Whitelist to just the keys we allow
823
- $postdata = array(
824
- 'import_id' => $data['post_id'],
825
- );
826
- $allowed = array(
827
- 'post_author' => true,
828
- 'post_date' => true,
829
- 'post_date_gmt' => true,
830
- 'post_content' => true,
831
- 'post_excerpt' => true,
832
- 'post_title' => true,
833
- 'post_status' => true,
834
- 'post_name' => true,
835
- 'comment_status' => true,
836
- 'ping_status' => true,
837
- 'guid' => true,
838
- 'post_parent' => true,
839
- 'menu_order' => true,
840
- 'post_type' => true,
841
- 'post_password' => true,
842
- );
843
- foreach ( $data as $key => $value ) {
844
- if ( ! isset( $allowed[ $key ] ) ) {
845
- continue;
846
- }
847
-
848
- $postdata[ $key ] = $data[ $key ];
849
- }
850
-
851
- $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
852
-
853
- if ( 'attachment' === $postdata['post_type'] ) {
854
- if ( ! $this->options['fetch_attachments'] ) {
855
- $this->logger->notice( sprintf(
856
- __( 'Skipping attachment "%s", fetching attachments disabled' ),
857
- $data['post_title']
858
- ) );
859
- /**
860
- * Post processing skipped.
861
- *
862
- * @param array $data Raw data imported for the post.
863
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
864
- */
865
- do_action( 'wxr_importer.process_skipped.post', $data, $meta );
866
- return false;
867
- }
868
- $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
869
- $post_id = $this->process_attachment( $postdata, $meta, $remote_url );
870
- } else {
871
- $post_id = wp_insert_post( $postdata, true );
872
- do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
873
- }
874
-
875
- if ( is_wp_error( $post_id ) ) {
876
- $this->logger->error( sprintf(
877
- __( 'Failed to import "%1$s" (%2$s)', 'wordpress-importer' ),
878
- $data['post_title'],
879
- $post_type_object->labels->singular_name
880
- ) );
881
- $this->logger->debug( $post_id->get_error_message() );
882
-
883
- /**
884
- * Post processing failed.
885
- *
886
- * @param WP_Error $post_id Error object.
887
- * @param array $data Raw data imported for the post.
888
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
889
- * @param array $comments Raw comment data, already processed by {@see process_comments}.
890
- * @param array $terms Raw term data, already processed.
891
- */
892
- do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
893
- return false;
894
- }
895
-
896
- // Ensure stickiness is handled correctly too
897
- if ( $data['is_sticky'] === '1' ) {
898
- stick_post( $post_id );
899
- }
900
-
901
- // map pre-import ID to local ID
902
- $this->mapping['post'][ $original_id ] = (int) $post_id;
903
- if ( $requires_remapping ) {
904
- $this->requires_remapping['post'][ $post_id ] = true;
905
- }
906
- $this->mark_post_exists( $data, $post_id );
907
-
908
- $this->logger->info( sprintf(
909
- __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
910
- $data['post_title'],
911
- $post_type_object->labels->singular_name
912
- ) );
913
- $this->logger->debug( sprintf(
914
- __( 'Post %1$d remapped to %2$d', 'wordpress-importer' ),
915
- $original_id,
916
- $post_id
917
- ) );
918
-
919
- // Handle the terms too
920
- $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
921
-
922
- if ( ! empty( $terms ) ) {
923
- $term_ids = array();
924
- foreach ( $terms as $term ) {
925
- $taxonomy = $term['taxonomy'];
926
- $key = sha1( $taxonomy . ':' . $term['slug'] );
927
-
928
- if ( isset( $this->mapping['term'][ $key ] ) ) {
929
- $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
930
- } else {
931
- $meta[] = array(
932
- 'key' => '_wxr_import_term',
933
- 'value' => $term,
934
- );
935
- $requires_remapping = true;
936
- }
937
- }
938
-
939
- foreach ( $term_ids as $tax => $ids ) {
940
- $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
941
- do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
942
- }
943
- }
944
-
945
- $this->process_comments( $comments, $post_id, $data );
946
- $this->process_post_meta( $meta, $post_id, $data );
947
-
948
- if ( 'nav_menu_item' === $data['post_type'] ) {
949
- $this->process_menu_item_meta( $post_id, $data, $meta );
950
- }
951
-
952
- /**
953
- * Post processing completed.
954
- *
955
- * @param int $post_id New post ID.
956
- * @param array $data Raw data imported for the post.
957
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
958
- * @param array $comments Raw comment data, already processed by {@see process_comments}.
959
- * @param array $terms Raw term data, already processed.
960
- */
961
- do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
962
- }
963
-
964
- /**
965
- * Attempt to create a new menu item from import data
966
- *
967
- * Fails for draft, orphaned menu items and those without an associated nav_menu
968
- * or an invalid nav_menu term. If the post type or term object which the menu item
969
- * represents doesn't exist then the menu item will not be imported (waits until the
970
- * end of the import to retry again before discarding).
971
- *
972
- * @param array $item Menu item details from WXR file
973
- */
974
- protected function process_menu_item_meta( $post_id, $data, $meta ) {
975
-
976
- $item_type = get_post_meta( $post_id, '_menu_item_type', true );
977
- $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
978
- $object_id = null;
979
-
980
- $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
981
-
982
- $requires_remapping = false;
983
- switch ( $item_type ) {
984
- case 'taxonomy':
985
- if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
986
- $object_id = $this->mapping['term_id'][ $original_object_id ];
987
- } else {
988
- add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
989
- $requires_remapping = true;
990
- }
991
- break;
992
-
993
- case 'post_type':
994
- if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
995
- $object_id = $this->mapping['post'][ $original_object_id ];
996
- } else {
997
- add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
998
- $requires_remapping = true;
999
- }
1000
- break;
1001
-
1002
- case 'custom':
1003
- // Custom refers to itself, wonderfully easy.
1004
- $object_id = $post_id;
1005
- break;
1006
-
1007
- default:
1008
- // associated object is missing or not imported yet, we'll retry later
1009
- $this->missing_menu_items[] = $item;
1010
- $this->logger->debug( 'Unknown menu item type' );
1011
- break;
1012
- }
1013
-
1014
- if ( $requires_remapping ) {
1015
- $this->requires_remapping['post'][ $post_id ] = true;
1016
- }
1017
-
1018
- if ( empty( $object_id ) ) {
1019
- // Nothing needed here.
1020
- return;
1021
- }
1022
-
1023
- $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
1024
- update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
1025
- }
1026
-
1027
- /**
1028
- * If fetching attachments is enabled then attempt to create a new attachment
1029
- *
1030
- * @param array $post Attachment post details from WXR
1031
- * @param string $url URL to fetch attachment from
1032
- * @return int|WP_Error Post ID on success, WP_Error otherwise
1033
- */
1034
- protected function process_attachment( $post, $meta, $remote_url ) {
1035
- // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
1036
- // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
1037
- $post['upload_date'] = $post['post_date'];
1038
- foreach ( $meta as $meta_item ) {
1039
- if ( $meta_item['key'] !== '_wp_attached_file' ) {
1040
- continue;
1041
- }
1042
-
1043
- if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
1044
- $post['upload_date'] = $matches[0];
1045
- }
1046
- break;
1047
- }
1048
-
1049
- // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
1050
- if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
1051
- $remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
1052
- }
1053
-
1054
- $upload = $this->fetch_remote_file( $remote_url, $post );
1055
- if ( is_wp_error( $upload ) ) {
1056
- return $upload;
1057
- }
1058
-
1059
- $info = wp_check_filetype( $upload['file'] );
1060
- if ( ! $info ) {
1061
- return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
1062
- }
1063
-
1064
- $post['post_mime_type'] = $info['type'];
1065
-
1066
- // WP really likes using the GUID for display. Allow updating it.
1067
- // See https://core.trac.wordpress.org/ticket/33386
1068
- if ( $this->options['update_attachment_guids'] ) {
1069
- $post['guid'] = $upload['url'];
1070
- }
1071
-
1072
- // as per wp-admin/includes/upload.php
1073
- $post_id = wp_insert_attachment( $post, $upload['file'] );
1074
- if ( is_wp_error( $post_id ) ) {
1075
- return $post_id;
1076
- }
1077
-
1078
- $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
1079
- wp_update_attachment_metadata( $post_id, $attachment_metadata );
1080
-
1081
- // Map this image URL later if we need to
1082
- $this->url_remap[ $remote_url ] = $upload['url'];
1083
-
1084
- // If we have a HTTPS URL, ensure the HTTP URL gets replaced too
1085
- if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
1086
- $insecure_url = 'http' . substr( $remote_url, 5 );
1087
- $this->url_remap[ $insecure_url ] = $upload['url'];
1088
- }
1089
-
1090
- if ( $this->options['aggressive_url_search'] ) {
1091
- // remap resized image URLs, works by stripping the extension and remapping the URL stub.
1092
- /*
1093
- if ( preg_match( '!^image/!', $info['type'] ) ) {
1094
- $parts = pathinfo( $remote_url );
1095
- $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
1096
-
1097
- $parts_new = pathinfo( $upload['url'] );
1098
- $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
1099
-
1100
- $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
1101
- }*/
1102
- }
1103
-
1104
- return $post_id;
1105
- }
1106
-
1107
- /**
1108
- * Parse a meta node into meta data.
1109
- *
1110
- * @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
1111
- * @return array|null Meta data array on success, or null on error.
1112
- */
1113
- protected function parse_meta_node( $node ) {
1114
- foreach ( $node->childNodes as $child ) {
1115
- // We only care about child elements
1116
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1117
- continue;
1118
- }
1119
-
1120
- switch ( $child->tagName ) {
1121
- case 'wp:meta_key':
1122
- $key = $child->textContent;
1123
- break;
1124
-
1125
- case 'wp:meta_value':
1126
- $value = $child->textContent;
1127
- break;
1128
- }
1129
- }
1130
-
1131
- if ( empty( $key ) || empty( $value ) ) {
1132
- return null;
1133
- }
1134
-
1135
- return compact( 'key', 'value' );
1136
- }
1137
-
1138
- /**
1139
- * Process and import post meta items.
1140
- *
1141
- * @param array $meta List of meta data arrays
1142
- * @param int $post_id Post to associate with
1143
- * @param array $post Post data
1144
- * @return int|WP_Error Number of meta items imported on success, error otherwise.
1145
- */
1146
- protected function process_post_meta( $meta, $post_id, $post ) {
1147
- if ( empty( $meta ) ) {
1148
- return true;
1149
- }
1150
-
1151
- foreach ( $meta as $meta_item ) {
1152
- /**
1153
- * Pre-process post meta data.
1154
- *
1155
- * @param array $meta_item Meta data. (Return empty to skip.)
1156
- * @param int $post_id Post the meta is attached to.
1157
- */
1158
- $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
1159
- if ( empty( $meta_item ) ) {
1160
- return false;
1161
- }
1162
-
1163
- $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
1164
- $value = false;
1165
-
1166
- if ( '_edit_last' === $key ) {
1167
- $value = intval( $meta_item['value'] );
1168
- if ( ! isset( $this->mapping['user'][ $value ] ) ) {
1169
- // Skip!
1170
- continue;
1171
- }
1172
-
1173
- $value = $this->mapping['user'][ $value ];
1174
- }
1175
-
1176
- if ( $key ) {
1177
- // export gets meta straight from the DB so could have a serialized string
1178
- if ( ! $value ) {
1179
- $value = maybe_unserialize( $meta_item['value'] );
1180
- }
1181
-
1182
- add_post_meta( $post_id, $key, $value );
1183
- do_action( 'import_post_meta', $post_id, $key, $value );
1184
-
1185
- // if the post has a featured image, take note of this in case of remap
1186
- if ( '_thumbnail_id' === $key ) {
1187
- $this->featured_images[ $post_id ] = (int) $value;
1188
- }
1189
- }
1190
- }// End foreach().
1191
-
1192
- return true;
1193
- }
1194
-
1195
- /**
1196
- * Parse a comment node into comment data.
1197
- *
1198
- * @param DOMElement $node Parent node of comment data (typically `wp:comment`).
1199
- * @return array Comment data array.
1200
- */
1201
- protected function parse_comment_node( $node ) {
1202
- $data = array(
1203
- 'commentmeta' => array(),
1204
- );
1205
-
1206
- foreach ( $node->childNodes as $child ) {
1207
- // We only care about child elements
1208
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1209
- continue;
1210
- }
1211
-
1212
- switch ( $child->tagName ) {
1213
- case 'wp:comment_id':
1214
- $data['comment_id'] = $child->textContent;
1215
- break;
1216
- case 'wp:comment_author':
1217
- $data['comment_author'] = $child->textContent;
1218
- break;
1219
-
1220
- case 'wp:comment_author_email':
1221
- $data['comment_author_email'] = $child->textContent;
1222
- break;
1223
-
1224
- case 'wp:comment_author_IP':
1225
- $data['comment_author_IP'] = $child->textContent;
1226
- break;
1227
-
1228
- case 'wp:comment_author_url':
1229
- $data['comment_author_url'] = $child->textContent;
1230
- break;
1231
-
1232
- case 'wp:comment_user_id':
1233
- $data['comment_user_id'] = $child->textContent;
1234
- break;
1235
-
1236
- case 'wp:comment_date':
1237
- $data['comment_date'] = $child->textContent;
1238
- break;
1239
-
1240
- case 'wp:comment_date_gmt':
1241
- $data['comment_date_gmt'] = $child->textContent;
1242
- break;
1243
-
1244
- case 'wp:comment_content':
1245
- $data['comment_content'] = $child->textContent;
1246
- break;
1247
-
1248
- case 'wp:comment_approved':
1249
- $data['comment_approved'] = $child->textContent;
1250
- break;
1251
-
1252
- case 'wp:comment_type':
1253
- $data['comment_type'] = $child->textContent;
1254
- break;
1255
-
1256
- case 'wp:comment_parent':
1257
- $data['comment_parent'] = $child->textContent;
1258
- break;
1259
-
1260
- case 'wp:commentmeta':
1261
- $meta_item = $this->parse_meta_node( $child );
1262
- if ( ! empty( $meta_item ) ) {
1263
- $data['commentmeta'][] = $meta_item;
1264
- }
1265
- break;
1266
- }// End switch().
1267
- }// End foreach().
1268
-
1269
- return $data;
1270
- }
1271
-
1272
- /**
1273
- * Process and import comment data.
1274
- *
1275
- * @param array $comments List of comment data arrays.
1276
- * @param int $post_id Post to associate with.
1277
- * @param array $post Post data.
1278
- * @return int|WP_Error Number of comments imported on success, error otherwise.
1279
- */
1280
- protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
1281
-
1282
- $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
1283
- if ( empty( $comments ) ) {
1284
- return 0;
1285
- }
1286
-
1287
- $num_comments = 0;
1288
-
1289
- // Sort by ID to avoid excessive remapping later
1290
- usort( $comments, array( $this, 'sort_comments_by_id' ) );
1291
-
1292
- foreach ( $comments as $key => $comment ) {
1293
- /**
1294
- * Pre-process comment data
1295
- *
1296
- * @param array $comment Comment data. (Return empty to skip.)
1297
- * @param int $post_id Post the comment is attached to.
1298
- */
1299
- $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
1300
- if ( empty( $comment ) ) {
1301
- return false;
1302
- }
1303
-
1304
- $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
1305
- $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
1306
- $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
1307
-
1308
- // if this is a new post we can skip the comment_exists() check
1309
- // TODO: Check comment_exists for performance
1310
- if ( $post_exists ) {
1311
- $existing = $this->comment_exists( $comment );
1312
- if ( $existing ) {
1313
-
1314
- /**
1315
- * Comment processing already imported.
1316
- *
1317
- * @param array $comment Raw data imported for the comment.
1318
- */
1319
- do_action( 'wxr_importer.process_already_imported.comment', $comment );
1320
-
1321
- $this->mapping['comment'][ $original_id ] = $existing;
1322
- continue;
1323
- }
1324
- }
1325
-
1326
- // Remove meta from the main array
1327
- $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
1328
- unset( $comment['commentmeta'] );
1329
-
1330
- // Map the parent comment, or mark it as one we need to fix
1331
- $requires_remapping = false;
1332
- if ( $parent_id ) {
1333
- if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1334
- $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1335
- } else {
1336
- // Prepare for remapping later
1337
- $meta[] = array(
1338
- 'key' => '_wxr_import_parent',
1339
- 'value' => $parent_id,
1340
- );
1341
- $requires_remapping = true;
1342
-
1343
- // Wipe the parent for now
1344
- $comment['comment_parent'] = 0;
1345
- }
1346
- }
1347
-
1348
- // Map the author, or mark it as one we need to fix
1349
- if ( $author_id ) {
1350
- if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1351
- $comment['user_id'] = $this->mapping['user'][ $author_id ];
1352
- } else {
1353
- // Prepare for remapping later
1354
- $meta[] = array(
1355
- 'key' => '_wxr_import_user',
1356
- 'value' => $author_id,
1357
- );
1358
- $requires_remapping = true;
1359
-
1360
- // Wipe the user for now
1361
- $comment['user_id'] = 0;
1362
- }
1363
- }
1364
-
1365
- // Run standard core filters
1366
- $comment['comment_post_ID'] = $post_id;
1367
- $comment = wp_filter_comment( $comment );
1368
-
1369
- // wp_insert_comment expects slashed data
1370
- $comment_id = wp_insert_comment( wp_slash( $comment ) );
1371
- $this->mapping['comment'][ $original_id ] = $comment_id;
1372
- if ( $requires_remapping ) {
1373
- $this->requires_remapping['comment'][ $comment_id ] = true;
1374
- }
1375
- $this->mark_comment_exists( $comment, $comment_id );
1376
-
1377
- /**
1378
- * Comment has been imported.
1379
- *
1380
- * @param int $comment_id New comment ID
1381
- * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
1382
- * @param int $post_id Post parent of the comment
1383
- * @param array $post Post data
1384
- */
1385
- do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
1386
-
1387
- // Process the meta items
1388
- foreach ( $meta as $meta_item ) {
1389
- $value = maybe_unserialize( $meta_item['value'] );
1390
- add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
1391
- }
1392
-
1393
- /**
1394
- * Post processing completed.
1395
- *
1396
- * @param int $post_id New post ID.
1397
- * @param array $comment Raw data imported for the comment.
1398
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
1399
- * @param array $post_id Parent post ID.
1400
- */
1401
- do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
1402
-
1403
- $num_comments++;
1404
- }// End foreach().
1405
-
1406
- return $num_comments;
1407
- }
1408
-
1409
- protected function parse_category_node( $node ) {
1410
- $data = array(
1411
- // Default taxonomy to "category", since this is a `<category>` tag
1412
- 'taxonomy' => 'category',
1413
- );
1414
- $meta = array();
1415
-
1416
- if ( $node->hasAttribute( 'domain' ) ) {
1417
- $data['taxonomy'] = $node->getAttribute( 'domain' );
1418
- }
1419
- if ( $node->hasAttribute( 'nicename' ) ) {
1420
- $data['slug'] = $node->getAttribute( 'nicename' );
1421
- }
1422
-
1423
- $data['name'] = $node->textContent;
1424
-
1425
- if ( empty( $data['slug'] ) ) {
1426
- return null;
1427
- }
1428
-
1429
- // Just for extra compatibility
1430
- if ( $data['taxonomy'] === 'tag' ) {
1431
- $data['taxonomy'] = 'post_tag';
1432
- }
1433
-
1434
- return $data;
1435
- }
1436
-
1437
- /**
1438
- * Callback for `usort` to sort comments by ID
1439
- *
1440
- * @param array $a Comment data for the first comment
1441
- * @param array $b Comment data for the second comment
1442
- * @return int
1443
- */
1444
- public static function sort_comments_by_id( $a, $b ) {
1445
- if ( empty( $a['comment_id'] ) ) {
1446
- return 1;
1447
- }
1448
-
1449
- if ( empty( $b['comment_id'] ) ) {
1450
- return -1;
1451
- }
1452
-
1453
- return $a['comment_id'] - $b['comment_id'];
1454
- }
1455
-
1456
- protected function parse_author_node( $node ) {
1457
- $data = array();
1458
- $meta = array();
1459
- foreach ( $node->childNodes as $child ) {
1460
- // We only care about child elements
1461
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1462
- continue;
1463
- }
1464
-
1465
- switch ( $child->tagName ) {
1466
- case 'wp:author_login':
1467
- $data['user_login'] = $child->textContent;
1468
- break;
1469
-
1470
- case 'wp:author_id':
1471
- $data['ID'] = $child->textContent;
1472
- break;
1473
-
1474
- case 'wp:author_email':
1475
- $data['user_email'] = $child->textContent;
1476
- break;
1477
-
1478
- case 'wp:author_display_name':
1479
- $data['display_name'] = $child->textContent;
1480
- break;
1481
-
1482
- case 'wp:author_first_name':
1483
- $data['first_name'] = $child->textContent;
1484
- break;
1485
-
1486
- case 'wp:author_last_name':
1487
- $data['last_name'] = $child->textContent;
1488
- break;
1489
- }
1490
- }
1491
-
1492
- return compact( 'data', 'meta' );
1493
- }
1494
-
1495
- protected function process_author( $data, $meta ) {
1496
- /**
1497
- * Pre-process user data.
1498
- *
1499
- * @param array $data User data. (Return empty to skip.)
1500
- * @param array $meta Meta data.
1501
- */
1502
- $data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
1503
- if ( empty( $data ) ) {
1504
- return false;
1505
- }
1506
-
1507
- // Have we already handled this user?
1508
- $original_id = isset( $data['ID'] ) ? $data['ID'] : 0;
1509
- $original_slug = $data['user_login'];
1510
-
1511
- if ( isset( $this->mapping['user'][ $original_id ] ) ) {
1512
- $existing = $this->mapping['user'][ $original_id ];
1513
-
1514
- // Note the slug mapping if we need to too
1515
- if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1516
- $this->mapping['user_slug'][ $original_slug ] = $existing;
1517
- }
1518
-
1519
- return false;
1520
- }
1521
-
1522
- if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1523
- $existing = $this->mapping['user_slug'][ $original_slug ];
1524
-
1525
- // Ensure we note the mapping too
1526
- $this->mapping['user'][ $original_id ] = $existing;
1527
-
1528
- return false;
1529
- }
1530
-
1531
- // Allow overriding the user's slug
1532
- $login = $original_slug;
1533
- if ( isset( $this->user_slug_override[ $login ] ) ) {
1534
- $login = $this->user_slug_override[ $login ];
1535
- }
1536
-
1537
- $userdata = array(
1538
- 'user_login' => sanitize_user( $login, true ),
1539
- 'user_pass' => wp_generate_password(),
1540
- );
1541
-
1542
- $allowed = array(
1543
- 'user_email' => true,
1544
- 'display_name' => true,
1545
- 'first_name' => true,
1546
- 'last_name' => true,
1547
- );
1548
- foreach ( $data as $key => $value ) {
1549
- if ( ! isset( $allowed[ $key ] ) ) {
1550
- continue;
1551
- }
1552
-
1553
- $userdata[ $key ] = $data[ $key ];
1554
- }
1555
-
1556
- $user_id = wp_insert_user( wp_slash( $userdata ) );
1557
- if ( is_wp_error( $user_id ) ) {
1558
- $this->logger->error( sprintf(
1559
- __( 'Failed to import user "%s"', 'wordpress-importer' ),
1560
- $userdata['user_login']
1561
- ) );
1562
- $this->logger->debug( $user_id->get_error_message() );
1563
-
1564
- /**
1565
- * User processing failed.
1566
- *
1567
- * @param WP_Error $user_id Error object.
1568
- * @param array $userdata Raw data imported for the user.
1569
- */
1570
- do_action( 'wxr_importer.process_failed.user', $user_id, $userdata );
1571
- return false;
1572
- }
1573
-
1574
- if ( $original_id ) {
1575
- $this->mapping['user'][ $original_id ] = $user_id;
1576
- }
1577
- $this->mapping['user_slug'][ $original_slug ] = $user_id;
1578
-
1579
- $this->logger->info( sprintf(
1580
- __( 'Imported user "%s"', 'wordpress-importer' ),
1581
- $userdata['user_login']
1582
- ) );
1583
- $this->logger->debug( sprintf(
1584
- __( 'User %1$d remapped to %2$d', 'wordpress-importer' ),
1585
- $original_id,
1586
- $user_id
1587
- ) );
1588
-
1589
- // TODO: Implement meta handling once WXR includes it
1590
- /**
1591
- * User processing completed.
1592
- *
1593
- * @param int $user_id New user ID.
1594
- * @param array $userdata Raw data imported for the user.
1595
- */
1596
- do_action( 'wxr_importer.processed.user', $user_id, $userdata );
1597
- }
1598
-
1599
- protected function parse_term_node( $node, $type = 'term' ) {
1600
- $data = array();
1601
- $meta = array();
1602
-
1603
- $tag_name = array(
1604
- 'id' => 'wp:term_id',
1605
- 'taxonomy' => 'wp:term_taxonomy',
1606
- 'slug' => 'wp:term_slug',
1607
- 'parent' => 'wp:term_parent',
1608
- 'name' => 'wp:term_name',
1609
- 'description' => 'wp:term_description',
1610
- );
1611
- $taxonomy = null;
1612
-
1613
- // Special casing!
1614
- switch ( $type ) {
1615
- case 'category':
1616
- $tag_name['slug'] = 'wp:category_nicename';
1617
- $tag_name['parent'] = 'wp:category_parent';
1618
- $tag_name['name'] = 'wp:cat_name';
1619
- $tag_name['description'] = 'wp:category_description';
1620
- $tag_name['taxonomy'] = null;
1621
-
1622
- $data['taxonomy'] = 'category';
1623
- break;
1624
-
1625
- case 'tag':
1626
- $tag_name['slug'] = 'wp:tag_slug';
1627
- $tag_name['parent'] = null;
1628
- $tag_name['name'] = 'wp:tag_name';
1629
- $tag_name['description'] = 'wp:tag_description';
1630
- $tag_name['taxonomy'] = null;
1631
-
1632
- $data['taxonomy'] = 'post_tag';
1633
- break;
1634
- }
1635
-
1636
- foreach ( $node->childNodes as $child ) {
1637
- // We only care about child elements
1638
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1639
- continue;
1640
- }
1641
-
1642
- $key = array_search( $child->tagName, $tag_name );
1643
- if ( $key ) {
1644
- $data[ $key ] = $child->textContent;
1645
- }
1646
- }
1647
-
1648
- if ( empty( $data['taxonomy'] ) ) {
1649
- return null;
1650
- }
1651
-
1652
- // Compatibility with WXR 1.0
1653
- if ( $data['taxonomy'] === 'tag' ) {
1654
- $data['taxonomy'] = 'post_tag';
1655
- }
1656
-
1657
- return compact( 'data', 'meta' );
1658
- }
1659
-
1660
- protected function process_term( $data, $meta ) {
1661
- /**
1662
- * Pre-process term data.
1663
- *
1664
- * @param array $data Term data. (Return empty to skip.)
1665
- * @param array $meta Meta data.
1666
- */
1667
- $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
1668
- if ( empty( $data ) ) {
1669
- return false;
1670
- }
1671
-
1672
- $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
1673
- $parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
1674
-
1675
- $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
1676
- $existing = $this->term_exists( $data );
1677
- if ( $existing ) {
1678
-
1679
- /**
1680
- * Term processing already imported.
1681
- *
1682
- * @param array $data Raw data imported for the term.
1683
- */
1684
- do_action( 'wxr_importer.process_already_imported.term', $data );
1685
-
1686
- $this->mapping['term'][ $mapping_key ] = $existing;
1687
- $this->mapping['term_id'][ $original_id ] = $existing;
1688
- return false;
1689
- }
1690
-
1691
- // WP really likes to repeat itself in export files
1692
- if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
1693
- return false;
1694
- }
1695
-
1696
- $termdata = array();
1697
- $allowed = array(
1698
- 'slug' => true,
1699
- 'description' => true,
1700
- );
1701
-
1702
- // Map the parent comment, or mark it as one we need to fix
1703
- // TODO: add parent mapping and remapping
1704
- /*
1705
- $requires_remapping = false;
1706
- if ( $parent_id ) {
1707
- if ( isset( $this->mapping['term'][ $parent_id ] ) ) {
1708
- $data['parent'] = $this->mapping['term'][ $parent_id ];
1709
- } else {
1710
- // Prepare for remapping later
1711
- $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1712
- $requires_remapping = true;
1713
-
1714
- // Wipe the parent for now
1715
- $data['parent'] = 0;
1716
- }
1717
- }*/
1718
-
1719
- foreach ( $data as $key => $value ) {
1720
- if ( ! isset( $allowed[ $key ] ) ) {
1721
- continue;
1722
- }
1723
-
1724
- $termdata[ $key ] = $data[ $key ];
1725
- }
1726
-
1727
- $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
1728
- if ( is_wp_error( $result ) ) {
1729
- $this->logger->warning( sprintf(
1730
- __( 'Failed to import %1$s %2$s', 'wordpress-importer' ),
1731
- $data['taxonomy'],
1732
- $data['name']
1733
- ) );
1734
- $this->logger->debug( $result->get_error_message() );
1735
- do_action( 'wp_import_insert_term_failed', $result, $data );
1736
-
1737
- /**
1738
- * Term processing failed.
1739
- *
1740
- * @param WP_Error $result Error object.
1741
- * @param array $data Raw data imported for the term.
1742
- * @param array $meta Meta data supplied for the term.
1743
- */
1744
- do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
1745
- return false;
1746
- }
1747
-
1748
- $term_id = $result['term_id'];
1749
-
1750
- $this->mapping['term'][ $mapping_key ] = $term_id;
1751
- $this->mapping['term_id'][ $original_id ] = $term_id;
1752
-
1753
- $this->logger->info( sprintf(
1754
- __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
1755
- $data['name'],
1756
- $data['taxonomy']
1757
- ) );
1758
- $this->logger->debug( sprintf(
1759
- __( 'Term %1$d remapped to %2$d', 'wordpress-importer' ),
1760
- $original_id,
1761
- $term_id
1762
- ) );
1763
-
1764
- do_action( 'wp_import_insert_term', $term_id, $data );
1765
-
1766
- /**
1767
- * Term processing completed.
1768
- *
1769
- * @param int $term_id New term ID.
1770
- * @param array $data Raw data imported for the term.
1771
- */
1772
- do_action( 'wxr_importer.processed.term', $term_id, $data );
1773
- }
1774
-
1775
- /**
1776
- * Attempt to download a remote file attachment
1777
- *
1778
- * @param string $url URL of item to fetch
1779
- * @param array $post Attachment details
1780
- * @return array|WP_Error Local file location details on success, WP_Error otherwise
1781
- */
1782
- protected function fetch_remote_file( $url, $post ) {
1783
- // extract the file name and extension from the url
1784
- $file_name = basename( $url );
1785
-
1786
- // get placeholder file in the upload dir with a unique, sanitized filename
1787
- $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
1788
- if ( $upload['error'] ) {
1789
- return new WP_Error( 'upload_dir_error', $upload['error'] );
1790
- }
1791
-
1792
- // fetch the remote url and write it to the placeholder file
1793
- $response = wp_remote_get( $url, array(
1794
- 'stream' => true,
1795
- 'filename' => $upload['file'],
1796
- ) );
1797
-
1798
- // request failed
1799
- if ( is_wp_error( $response ) ) {
1800
- unlink( $upload['file'] );
1801
- return $response;
1802
- }
1803
-
1804
- $code = (int) wp_remote_retrieve_response_code( $response );
1805
-
1806
- // make sure the fetch was successful
1807
- if ( $code !== 200 ) {
1808
- unlink( $upload['file'] );
1809
- return new WP_Error(
1810
- 'import_file_error',
1811
- sprintf(
1812
- __( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ),
1813
- $code,
1814
- get_status_header_desc( $code ),
1815
- $url
1816
- )
1817
- );
1818
- }
1819
-
1820
- $filesize = filesize( $upload['file'] );
1821
- $headers = wp_remote_retrieve_headers( $response );
1822
-
1823
- if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1824
- unlink( $upload['file'] );
1825
- return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) );
1826
- }
1827
-
1828
- if ( 0 === $filesize ) {
1829
- unlink( $upload['file'] );
1830
- return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) );
1831
- }
1832
-
1833
- $max_size = (int) $this->max_attachment_size();
1834
- if ( ! empty( $max_size ) && $filesize > $max_size ) {
1835
- unlink( $upload['file'] );
1836
- $message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) );
1837
- return new WP_Error( 'import_file_error', $message );
1838
- }
1839
-
1840
- return $upload;
1841
- }
1842
-
1843
- protected function post_process() {
1844
- // Time to tackle any left-over bits
1845
- if ( ! empty( $this->requires_remapping['post'] ) ) {
1846
- $this->post_process_posts( $this->requires_remapping['post'] );
1847
- }
1848
- if ( ! empty( $this->requires_remapping['comment'] ) ) {
1849
- $this->post_process_comments( $this->requires_remapping['comment'] );
1850
- }
1851
- }
1852
-
1853
- protected function post_process_posts( $todo ) {
1854
- foreach ( $todo as $post_id => $_ ) {
1855
- $this->logger->debug( sprintf(
1856
- // Note: title intentionally not used to skip extra processing
1857
- // for when debug logging is off
1858
- __( 'Running post-processing for post %d', 'wordpress-importer' ),
1859
- $post_id
1860
- ) );
1861
-
1862
- $data = array();
1863
-
1864
- $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
1865
- if ( ! empty( $parent_id ) ) {
1866
- // Have we imported the parent now?
1867
- if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
1868
- $data['post_parent'] = $this->mapping['post'][ $parent_id ];
1869
- } else {
1870
- $this->logger->warning( sprintf(
1871
- __( 'Could not find the post parent for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1872
- get_the_title( $post_id ),
1873
- $post_id
1874
- ) );
1875
- $this->logger->debug( sprintf(
1876
- __( 'Post %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
1877
- $post_id,
1878
- $parent_id
1879
- ) );
1880
- }
1881
- }
1882
-
1883
- $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
1884
- if ( ! empty( $author_slug ) ) {
1885
- // Have we imported the user now?
1886
- if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
1887
- $data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
1888
- } else {
1889
- $this->logger->warning( sprintf(
1890
- __( 'Could not find the author for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1891
- get_the_title( $post_id ),
1892
- $post_id
1893
- ) );
1894
- $this->logger->debug( sprintf(
1895
- __( 'Post %1$d was imported with author "%2$s", but could not be found', 'wordpress-importer' ),
1896
- $post_id,
1897
- $author_slug
1898
- ) );
1899
- }
1900
- }
1901
-
1902
- $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
1903
- if ( ! empty( $has_attachments ) ) {
1904
- $post = get_post( $post_id );
1905
- $content = $post->post_content;
1906
-
1907
- // Replace all the URLs we've got
1908
- $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
1909
- if ( $new_content !== $content ) {
1910
- $data['post_content'] = $new_content;
1911
- }
1912
- }
1913
-
1914
- if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
1915
- $this->post_process_menu_item( $post_id );
1916
- }
1917
-
1918
- // Do we have updates to make?
1919
- if ( empty( $data ) ) {
1920
- $this->logger->debug( sprintf(
1921
- __( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
1922
- $post_id
1923
- ) );
1924
- continue;
1925
- }
1926
-
1927
- // Run the update
1928
- $data['ID'] = $post_id;
1929
- $result = wp_update_post( $data, true );
1930
- if ( is_wp_error( $result ) ) {
1931
- $this->logger->warning( sprintf(
1932
- __( 'Could not update "%1$s" (post #%2$d) with mapped data', 'wordpress-importer' ),
1933
- get_the_title( $post_id ),
1934
- $post_id
1935
- ) );
1936
- $this->logger->debug( $result->get_error_message() );
1937
- continue;
1938
- }
1939
-
1940
- // Clear out our temporary meta keys
1941
- delete_post_meta( $post_id, '_wxr_import_parent' );
1942
- delete_post_meta( $post_id, '_wxr_import_user_slug' );
1943
- delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
1944
- }// End foreach().
1945
- }
1946
-
1947
- protected function post_process_menu_item( $post_id ) {
1948
- $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
1949
- if ( empty( $menu_object_id ) ) {
1950
- // No processing needed!
1951
- return;
1952
- }
1953
-
1954
- $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
1955
- switch ( $menu_item_type ) {
1956
- case 'taxonomy':
1957
- if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
1958
- $menu_object = $this->mapping['term_id'][ $menu_object_id ];
1959
- }
1960
- break;
1961
-
1962
- case 'post_type':
1963
- if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
1964
- $menu_object = $this->mapping['post'][ $menu_object_id ];
1965
- }
1966
- break;
1967
-
1968
- default:
1969
- // Cannot handle this.
1970
- return;
1971
- }
1972
-
1973
- if ( ! empty( $menu_object ) ) {
1974
- update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
1975
- } else {
1976
- $this->logger->warning( sprintf(
1977
- __( 'Could not find the menu object for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1978
- get_the_title( $post_id ),
1979
- $post_id
1980
- ) );
1981
- $this->logger->debug( sprintf(
1982
- __( 'Post %1$d was imported with object "%2$d" of type "%3$s", but could not be found', 'wordpress-importer' ),
1983
- $post_id,
1984
- $menu_object_id,
1985
- $menu_item_type
1986
- ) );
1987
- }
1988
-
1989
- delete_post_meta( $post_id, '_wxr_import_menu_item' );
1990
- }
1991
-
1992
-
1993
- protected function post_process_comments( $todo ) {
1994
- foreach ( $todo as $comment_id => $_ ) {
1995
- $data = array();
1996
-
1997
- $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
1998
- if ( ! empty( $parent_id ) ) {
1999
- // Have we imported the parent now?
2000
- if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
2001
- $data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
2002
- } else {
2003
- $this->logger->warning( sprintf(
2004
- __( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
2005
- $comment_id
2006
- ) );
2007
- $this->logger->debug( sprintf(
2008
- __( 'Comment %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
2009
- $comment_id,
2010
- $parent_id
2011
- ) );
2012
- }
2013
- }
2014
-
2015
- $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
2016
- if ( ! empty( $author_id ) ) {
2017
- // Have we imported the user now?
2018
- if ( isset( $this->mapping['user'][ $author_id ] ) ) {
2019
- $data['user_id'] = $this->mapping['user'][ $author_id ];
2020
- } else {
2021
- $this->logger->warning( sprintf(
2022
- __( 'Could not find the author for comment #%d', 'wordpress-importer' ),
2023
- $comment_id
2024
- ) );
2025
- $this->logger->debug( sprintf(
2026
- __( 'Comment %1$d was imported with author %2$d, but could not be found', 'wordpress-importer' ),
2027
- $comment_id,
2028
- $author_id
2029
- ) );
2030
- }
2031
- }
2032
-
2033
- // Do we have updates to make?
2034
- if ( empty( $data ) ) {
2035
- continue;
2036
- }
2037
-
2038
- // Run the update
2039
- $data['comment_ID'] = $comment_ID;
2040
- $result = wp_update_comment( wp_slash( $data ) );
2041
- if ( empty( $result ) ) {
2042
- $this->logger->warning( sprintf(
2043
- __( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
2044
- $comment_id
2045
- ) );
2046
- continue;
2047
- }
2048
-
2049
- // Clear out our temporary meta keys
2050
- delete_comment_meta( $comment_id, '_wxr_import_parent' );
2051
- delete_comment_meta( $comment_id, '_wxr_import_user' );
2052
- }// End foreach().
2053
- }
2054
-
2055
- /**
2056
- * Use stored mapping information to update old attachment URLs
2057
- */
2058
- protected function replace_attachment_urls_in_content() {
2059
- global $wpdb;
2060
- // make sure we do the longest urls first, in case one is a substring of another
2061
- uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
2062
-
2063
- foreach ( $this->url_remap as $from_url => $to_url ) {
2064
- // remap urls in post_content
2065
- $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
2066
- $wpdb->query( $query );
2067
-
2068
- // remap enclosure urls
2069
- $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
2070
- $result = $wpdb->query( $query );
2071
- }
2072
- }
2073
-
2074
- /**
2075
- * Update _thumbnail_id meta to new, imported attachment IDs
2076
- */
2077
- function remap_featured_images() {
2078
- // cycle through posts that have a featured image
2079
- foreach ( $this->featured_images as $post_id => $value ) {
2080
- if ( isset( $this->processed_posts[ $value ] ) ) {
2081
- $new_id = $this->processed_posts[ $value ];
2082
-
2083
- // only update if there's a difference
2084
- if ( $new_id !== $value ) {
2085
- update_post_meta( $post_id, '_thumbnail_id', $new_id );
2086
- }
2087
- }
2088
- }
2089
- }
2090
-
2091
- /**
2092
- * Decide if the given meta key maps to information we will want to import
2093
- *
2094
- * @param string $key The meta key to check
2095
- * @return string|bool The key if we do want to import, false if not
2096
- */
2097
- public function is_valid_meta_key( $key ) {
2098
- // skip attachment metadata since we'll regenerate it from scratch
2099
- // skip _edit_lock as not relevant for import
2100
- if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
2101
- return false;
2102
- }
2103
-
2104
- return $key;
2105
- }
2106
-
2107
- /**
2108
- * Decide what the maximum file size for downloaded attachments is.
2109
- * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
2110
- *
2111
- * @return int Maximum attachment file size to import
2112
- */
2113
- protected function max_attachment_size() {
2114
- return apply_filters( 'import_attachment_size_limit', 0 );
2115
- }
2116
-
2117
- /**
2118
- * Added to http_request_timeout filter to force timeout at 60 seconds during import
2119
- *
2120
- * @access protected
2121
- * @return int 60
2122
- */
2123
- function bump_request_timeout( $val ) {
2124
- return 60;
2125
- }
2126
-
2127
- // return the difference in length between two strings
2128
- function cmpr_strlen( $a, $b ) {
2129
- return strlen( $b ) - strlen( $a );
2130
- }
2131
-
2132
- /**
2133
- * Prefill existing post data.
2134
- *
2135
- * This preloads all GUIDs into memory, allowing us to avoid hitting the
2136
- * database when we need to check for existence. With larger imports, this
2137
- * becomes prohibitively slow to perform SELECT queries on each.
2138
- *
2139
- * By preloading all this data into memory, it's a constant-time lookup in
2140
- * PHP instead. However, this does use a lot more memory, so for sites doing
2141
- * small imports onto a large site, it may be a better tradeoff to use
2142
- * on-the-fly checking instead.
2143
- */
2144
- protected function prefill_existing_posts() {
2145
- global $wpdb;
2146
- $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
2147
-
2148
- foreach ( $posts as $item ) {
2149
- $this->exists['post'][ $item->guid ] = $item->ID;
2150
- }
2151
- }
2152
-
2153
- /**
2154
- * Does the post exist?
2155
- *
2156
- * @param array $data Post data to check against.
2157
- * @return int|bool Existing post ID if it exists, false otherwise.
2158
- */
2159
- protected function post_exists( $data ) {
2160
- // Constant-time lookup if we prefilled
2161
- $exists_key = $data['guid'];
2162
-
2163
- if ( $this->options['prefill_existing_posts'] ) {
2164
- return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
2165
- }
2166
-
2167
- // No prefilling, but might have already handled it
2168
- if ( isset( $this->exists['post'][ $exists_key ] ) ) {
2169
- return $this->exists['post'][ $exists_key ];
2170
- }
2171
-
2172
- // Still nothing, try post_exists, and cache it
2173
- $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
2174
- $this->exists['post'][ $exists_key ] = $exists;
2175
-
2176
- return $exists;
2177
- }
2178
-
2179
- /**
2180
- * Mark the post as existing.
2181
- *
2182
- * @param array $data Post data to mark as existing.
2183
- * @param int $post_id Post ID.
2184
- */
2185
- protected function mark_post_exists( $data, $post_id ) {
2186
- $exists_key = $data['guid'];
2187
- $this->exists['post'][ $exists_key ] = $post_id;
2188
- }
2189
-
2190
- /**
2191
- * Prefill existing comment data.
2192
- *
2193
- * @see self::prefill_existing_posts() for justification of why this exists.
2194
- */
2195
- protected function prefill_existing_comments() {
2196
- global $wpdb;
2197
- $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
2198
-
2199
- foreach ( $posts as $item ) {
2200
- $exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
2201
- $this->exists['comment'][ $exists_key ] = $item->comment_ID;
2202
- }
2203
- }
2204
-
2205
- /**
2206
- * Does the comment exist?
2207
- *
2208
- * @param array $data Comment data to check against.
2209
- * @return int|bool Existing comment ID if it exists, false otherwise.
2210
- */
2211
- protected function comment_exists( $data ) {
2212
- $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2213
-
2214
- // Constant-time lookup if we prefilled
2215
- if ( $this->options['prefill_existing_comments'] ) {
2216
- return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
2217
- }
2218
-
2219
- // No prefilling, but might have already handled it
2220
- if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
2221
- return $this->exists['comment'][ $exists_key ];
2222
- }
2223
-
2224
- // Still nothing, try comment_exists, and cache it
2225
- $exists = comment_exists( $data['comment_author'], $data['comment_date'] );
2226
- $this->exists['comment'][ $exists_key ] = $exists;
2227
-
2228
- return $exists;
2229
- }
2230
-
2231
- /**
2232
- * Mark the comment as existing.
2233
- *
2234
- * @param array $data Comment data to mark as existing.
2235
- * @param int $comment_id Comment ID.
2236
- */
2237
- protected function mark_comment_exists( $data, $comment_id ) {
2238
- $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2239
- $this->exists['comment'][ $exists_key ] = $comment_id;
2240
- }
2241
-
2242
- /**
2243
- * Prefill existing term data.
2244
- *
2245
- * @see self::prefill_existing_posts() for justification of why this exists.
2246
- */
2247
- protected function prefill_existing_terms() {
2248
- global $wpdb;
2249
- $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
2250
- $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
2251
- $terms = $wpdb->get_results( $query );
2252
-
2253
- foreach ( $terms as $item ) {
2254
- $exists_key = sha1( $item->taxonomy . ':' . $item->slug );
2255
- $this->exists['term'][ $exists_key ] = $item->term_id;
2256
- }
2257
- }
2258
-
2259
- /**
2260
- * Does the term exist?
2261
- *
2262
- * @param array $data Term data to check against.
2263
- * @return int|bool Existing term ID if it exists, false otherwise.
2264
- */
2265
- protected function term_exists( $data ) {
2266
- $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2267
-
2268
- // Constant-time lookup if we prefilled
2269
- if ( $this->options['prefill_existing_terms'] ) {
2270
- return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
2271
- }
2272
-
2273
- // No prefilling, but might have already handled it
2274
- if ( isset( $this->exists['term'][ $exists_key ] ) ) {
2275
- return $this->exists['term'][ $exists_key ];
2276
- }
2277
-
2278
- // Still nothing, try comment_exists, and cache it
2279
- $exists = term_exists( $data['slug'], $data['taxonomy'] );
2280
- if ( is_array( $exists ) ) {
2281
- $exists = $exists['term_id'];
2282
- }
2283
-
2284
- $this->exists['term'][ $exists_key ] = $exists;
2285
-
2286
- return $exists;
2287
- }
2288
-
2289
- /**
2290
- * Mark the term as existing.
2291
- *
2292
- * @param array $data Term data to mark as existing.
2293
- * @param int $term_id Term ID.
2294
- */
2295
- protected function mark_term_exists( $data, $term_id ) {
2296
- $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2297
- $this->exists['term'][ $exists_key ] = $term_id;
2298
- }
2299
ย  }
1
+ <?php
2
+
3
+ class WXR_Importer extends WP_Importer {
4
+ /**
5
+ * Maximum supported WXR version
6
+ */
7
+ const MAX_WXR_VERSION = 1.2;
8
+
9
+ /**
10
+ * Regular expression for checking if a post references an attachment
11
+ *
12
+ * Note: This is a quick, weak check just to exclude text-only posts. More
13
+ * vigorous checking is done later to verify.
14
+ */
15
+ const REGEX_HAS_ATTACHMENT_REFS = '!
16
+ (
17
+ # Match anything with an image or attachment class
18
+ class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
19
+ |
20
+ # Match anything that looks like an upload URL
21
+ src=[\'"][^\'"]*(
22
+ [0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
23
+ |
24
+ content/uploads[^\'"]+
25
+ )[\'"]
26
+ )!ix';
27
+
28
+ /**
29
+ * Version of WXR we're importing.
30
+ *
31
+ * Defaults to 1.0 for compatibility. Typically overridden by a
32
+ * `<wp:wxr_version>` tag at the start of the file.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $version = '1.0';
37
+
38
+ // information to import from WXR file
39
+ protected $categories = array();
40
+ protected $tags = array();
41
+ protected $base_url = '';
42
+
43
+ // TODO: REMOVE THESE
44
+ protected $processed_terms = array();
45
+ protected $processed_posts = array();
46
+ protected $processed_menu_items = array();
47
+ protected $menu_item_orphans = array();
48
+ protected $missing_menu_items = array();
49
+
50
+ // NEW STYLE
51
+ protected $mapping = array();
52
+ protected $requires_remapping = array();
53
+ protected $exists = array();
54
+ protected $user_slug_override = array();
55
+
56
+ protected $url_remap = array();
57
+ protected $featured_images = array();
58
+
59
+ /**
60
+ * Logger instance.
61
+ *
62
+ * @var WP_Importer_Logger
63
+ */
64
+ protected $logger;
65
+
66
+ /**
67
+ * Constructor
68
+ *
69
+ * @param array $options {
70
+ * @var bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.)
71
+ * @var bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.)
72
+ * @var bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.)
73
+ * @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
74
+ * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
75
+ * @var bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `<img class="wp-image-*">` only. Default is false.)
76
+ * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
77
+ * }
78
+ */
79
+ public function __construct( $options = array() ) {
80
+ // Initialize some important variables
81
+ $empty_types = array(
82
+ 'post' => array(),
83
+ 'comment' => array(),
84
+ 'term' => array(),
85
+ 'user' => array(),
86
+ );
87
+
88
+ $this->mapping = $empty_types;
89
+ $this->mapping['user_slug'] = array();
90
+ $this->mapping['term_id'] = array();
91
+ $this->requires_remapping = $empty_types;
92
+ $this->exists = $empty_types;
93
+
94
+ $this->options = wp_parse_args( $options, array(
95
+ 'prefill_existing_posts' => true,
96
+ 'prefill_existing_comments' => true,
97
+ 'prefill_existing_terms' => true,
98
+ 'update_attachment_guids' => false,
99
+ 'fetch_attachments' => false,
100
+ 'aggressive_url_search' => false,
101
+ 'default_author' => null,
102
+ ) );
103
+ }
104
+
105
+ public function set_logger( $logger ) {
106
+ $this->logger = $logger;
107
+ }
108
+
109
+ /**
110
+ * Get a stream reader for the file.
111
+ *
112
+ * @param string $file Path to the XML file.
113
+ * @return XMLReader|WP_Error Reader instance on success, error otherwise.
114
+ */
115
+ protected function get_reader( $file ) {
116
+ // Avoid loading external entities for security
117
+ $old_value = null;
118
+ if ( function_exists( 'libxml_disable_entity_loader' ) ) {
119
+ // $old_value = libxml_disable_entity_loader( true );
120
+ }
121
+
122
+ $reader = new XMLReader();
123
+ $status = $reader->open( $file );
124
+
125
+ if ( ! is_null( $old_value ) ) {
126
+ // libxml_disable_entity_loader( $old_value );
127
+ }
128
+
129
+ if ( ! $status ) {
130
+ return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
131
+ }
132
+
133
+ return $reader;
134
+ }
135
+
136
+ /**
137
+ * The main controller for the actual import stage.
138
+ *
139
+ * @param string $file Path to the WXR file for importing
140
+ */
141
+ public function get_preliminary_information( $file ) {
142
+ // Let's run the actual importer now, woot
143
+ $reader = $this->get_reader( $file );
144
+ if ( is_wp_error( $reader ) ) {
145
+ return $reader;
146
+ }
147
+
148
+ // Set the version to compatibility mode first
149
+ $this->version = '1.0';
150
+
151
+ // Start parsing!
152
+ $data = new WXR_Import_Info();
153
+ while ( $reader->read() ) {
154
+ // Only deal with element opens
155
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
156
+ continue;
157
+ }
158
+
159
+ switch ( $reader->name ) {
160
+ case 'wp:wxr_version':
161
+ // Upgrade to the correct version
162
+ $this->version = $reader->readString();
163
+
164
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
165
+ $this->logger->warning( sprintf(
166
+ __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
167
+ $this->version,
168
+ self::MAX_WXR_VERSION
169
+ ) );
170
+ }
171
+
172
+ // Handled everything in this node, move on to the next
173
+ $reader->next();
174
+ break;
175
+
176
+ case 'generator':
177
+ $data->generator = $reader->readString();
178
+ $reader->next();
179
+ break;
180
+
181
+ case 'title':
182
+ $data->title = $reader->readString();
183
+ $reader->next();
184
+ break;
185
+
186
+ case 'wp:base_site_url':
187
+ $data->siteurl = $reader->readString();
188
+ $reader->next();
189
+ break;
190
+
191
+ case 'wp:base_blog_url':
192
+ $data->home = $reader->readString();
193
+ $reader->next();
194
+ break;
195
+
196
+ case 'wp:author':
197
+ $node = $reader->expand();
198
+
199
+ $parsed = $this->parse_author_node( $node );
200
+ if ( is_wp_error( $parsed ) ) {
201
+ $this->log_error( $parsed );
202
+
203
+ // Skip the rest of this post
204
+ $reader->next();
205
+ break;
206
+ }
207
+
208
+ $data->users[] = $parsed;
209
+
210
+ // Handled everything in this node, move on to the next
211
+ $reader->next();
212
+ break;
213
+
214
+ case 'item':
215
+ $node = $reader->expand();
216
+ $parsed = $this->parse_post_node( $node );
217
+ if ( is_wp_error( $parsed ) ) {
218
+ $this->log_error( $parsed );
219
+
220
+ // Skip the rest of this post
221
+ $reader->next();
222
+ break;
223
+ }
224
+
225
+ if ( $parsed['data']['post_type'] === 'attachment' ) {
226
+ $data->media_count++;
227
+ } else {
228
+ $data->post_count++;
229
+ }
230
+ $data->comment_count += count( $parsed['comments'] );
231
+
232
+ // Handled everything in this node, move on to the next
233
+ $reader->next();
234
+ break;
235
+
236
+ case 'wp:category':
237
+ case 'wp:tag':
238
+ case 'wp:term':
239
+ $data->term_count++;
240
+
241
+ // Handled everything in this node, move on to the next
242
+ $reader->next();
243
+ break;
244
+ }// End switch().
245
+ }// End while().
246
+
247
+ $data->version = $this->version;
248
+
249
+ return $data;
250
+ }
251
+
252
+ /**
253
+ * The main controller for the actual import stage.
254
+ *
255
+ * @param string $file Path to the WXR file for importing
256
+ */
257
+ public function parse_authors( $file ) {
258
+ // Let's run the actual importer now, woot
259
+ $reader = $this->get_reader( $file );
260
+ if ( is_wp_error( $reader ) ) {
261
+ return $reader;
262
+ }
263
+
264
+ // Set the version to compatibility mode first
265
+ $this->version = '1.0';
266
+
267
+ // Start parsing!
268
+ $authors = array();
269
+ while ( $reader->read() ) {
270
+ // Only deal with element opens
271
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
272
+ continue;
273
+ }
274
+
275
+ switch ( $reader->name ) {
276
+ case 'wp:wxr_version':
277
+ // Upgrade to the correct version
278
+ $this->version = $reader->readString();
279
+
280
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
281
+ $this->logger->warning( sprintf(
282
+ __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
283
+ $this->version,
284
+ self::MAX_WXR_VERSION
285
+ ) );
286
+ }
287
+
288
+ // Handled everything in this node, move on to the next
289
+ $reader->next();
290
+ break;
291
+
292
+ case 'wp:author':
293
+ $node = $reader->expand();
294
+
295
+ $parsed = $this->parse_author_node( $node );
296
+ if ( is_wp_error( $parsed ) ) {
297
+ $this->log_error( $parsed );
298
+
299
+ // Skip the rest of this post
300
+ $reader->next();
301
+ break;
302
+ }
303
+
304
+ $authors[] = $parsed;
305
+
306
+ // Handled everything in this node, move on to the next
307
+ $reader->next();
308
+ break;
309
+ }
310
+ }// End while().
311
+
312
+ return $authors;
313
+ }
314
+
315
+ /**
316
+ * The main controller for the actual import stage.
317
+ *
318
+ * @param string $file Path to the WXR file for importing
319
+ */
320
+ public function import( $file ) {
321
+ add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
322
+ add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
323
+
324
+ $result = $this->import_start( $file );
325
+ if ( is_wp_error( $result ) ) {
326
+ return $result;
327
+ }
328
+
329
+ // Let's run the actual importer now, woot
330
+ $reader = $this->get_reader( $file );
331
+ if ( is_wp_error( $reader ) ) {
332
+ return $reader;
333
+ }
334
+
335
+ // Set the version to compatibility mode first
336
+ $this->version = '1.0';
337
+
338
+ // Reset other variables
339
+ $this->base_url = '';
340
+
341
+ // Start parsing!
342
+ while ( $reader->read() ) {
343
+ // Only deal with element opens
344
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
345
+ continue;
346
+ }
347
+
348
+ switch ( $reader->name ) {
349
+ case 'wp:wxr_version':
350
+ // Upgrade to the correct version
351
+ $this->version = $reader->readString();
352
+
353
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
354
+ $this->logger->warning( sprintf(
355
+ __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
356
+ $this->version,
357
+ self::MAX_WXR_VERSION
358
+ ) );
359
+ }
360
+
361
+ // Handled everything in this node, move on to the next
362
+ $reader->next();
363
+ break;
364
+
365
+ case 'wp:base_site_url':
366
+ $this->base_url = $reader->readString();
367
+
368
+ // Handled everything in this node, move on to the next
369
+ $reader->next();
370
+ break;
371
+
372
+ case 'item':
373
+ $node = $reader->expand();
374
+ $parsed = $this->parse_post_node( $node );
375
+ if ( is_wp_error( $parsed ) ) {
376
+ $this->log_error( $parsed );
377
+
378
+ // Skip the rest of this post
379
+ $reader->next();
380
+ break;
381
+ }
382
+
383
+ $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
384
+
385
+ // Handled everything in this node, move on to the next
386
+ $reader->next();
387
+ break;
388
+
389
+ case 'wp:author':
390
+ $node = $reader->expand();
391
+
392
+ $parsed = $this->parse_author_node( $node );
393
+ if ( is_wp_error( $parsed ) ) {
394
+ $this->log_error( $parsed );
395
+
396
+ // Skip the rest of this post
397
+ $reader->next();
398
+ break;
399
+ }
400
+
401
+ $status = $this->process_author( $parsed['data'], $parsed['meta'] );
402
+ if ( is_wp_error( $status ) ) {
403
+ $this->log_error( $status );
404
+ }
405
+
406
+ // Handled everything in this node, move on to the next
407
+ $reader->next();
408
+ break;
409
+
410
+ case 'wp:category':
411
+ $node = $reader->expand();
412
+
413
+ $parsed = $this->parse_term_node( $node, 'category' );
414
+ if ( is_wp_error( $parsed ) ) {
415
+ $this->log_error( $parsed );
416
+
417
+ // Skip the rest of this post
418
+ $reader->next();
419
+ break;
420
+ }
421
+
422
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
423
+
424
+ // Handled everything in this node, move on to the next
425
+ $reader->next();
426
+ break;
427
+
428
+ case 'wp:tag':
429
+ $node = $reader->expand();
430
+
431
+ $parsed = $this->parse_term_node( $node, 'tag' );
432
+ if ( is_wp_error( $parsed ) ) {
433
+ $this->log_error( $parsed );
434
+
435
+ // Skip the rest of this post
436
+ $reader->next();
437
+ break;
438
+ }
439
+
440
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
441
+
442
+ // Handled everything in this node, move on to the next
443
+ $reader->next();
444
+ break;
445
+
446
+ case 'wp:term':
447
+ $node = $reader->expand();
448
+
449
+ $parsed = $this->parse_term_node( $node );
450
+ if ( is_wp_error( $parsed ) ) {
451
+ $this->log_error( $parsed );
452
+
453
+ // Skip the rest of this post
454
+ $reader->next();
455
+ break;
456
+ }
457
+
458
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
459
+
460
+ // Handled everything in this node, move on to the next
461
+ $reader->next();
462
+ break;
463
+
464
+ default:
465
+ // Skip this node, probably handled by something already
466
+ break;
467
+ }// End switch().
468
+ }// End while().
469
+
470
+ // Now that we've done the main processing, do any required
471
+ // post-processing and remapping.
472
+ $this->post_process();
473
+
474
+ if ( $this->options['aggressive_url_search'] ) {
475
+ $this->replace_attachment_urls_in_content();
476
+ }
477
+ // $this->remap_featured_images();
478
+ $this->import_end();
479
+ }
480
+
481
+ /**
482
+ * Log an error instance to the logger.
483
+ *
484
+ * @param WP_Error $error Error instance to log.
485
+ */
486
+ protected function log_error( WP_Error $error ) {
487
+ $this->logger->warning( $error->get_error_message() );
488
+
489
+ // Log the data as debug info too
490
+ $data = $error->get_error_data();
491
+ if ( ! empty( $data ) ) {
492
+ $this->logger->debug( var_export( $data, true ) );
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Parses the WXR file and prepares us for the task of processing parsed data
498
+ *
499
+ * @param string $file Path to the WXR file for importing
500
+ */
501
+ protected function import_start( $file ) {
502
+ if ( ! is_file( $file ) ) {
503
+ return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
504
+ }
505
+
506
+ // Suspend bunches of stuff in WP core
507
+ wp_defer_term_counting( true );
508
+ wp_defer_comment_counting( true );
509
+ wp_suspend_cache_invalidation( true );
510
+
511
+ // Prefill exists calls if told to
512
+ if ( $this->options['prefill_existing_posts'] ) {
513
+ $this->prefill_existing_posts();
514
+ }
515
+ if ( $this->options['prefill_existing_comments'] ) {
516
+ $this->prefill_existing_comments();
517
+ }
518
+ if ( $this->options['prefill_existing_terms'] ) {
519
+ $this->prefill_existing_terms();
520
+ }
521
+
522
+ /**
523
+ * Begin the import.
524
+ *
525
+ * Fires before the import process has begun. If you need to suspend
526
+ * caching or heavy processing on hooks, do so here.
527
+ */
528
+ do_action( 'import_start' );
529
+ }
530
+
531
+ /**
532
+ * Performs post-import cleanup of files and the cache
533
+ */
534
+ protected function import_end() {
535
+ // Re-enable stuff in core
536
+ wp_suspend_cache_invalidation( false );
537
+ wp_cache_flush();
538
+ foreach ( get_taxonomies() as $tax ) {
539
+ delete_option( "{$tax}_children" );
540
+ _get_term_hierarchy( $tax );
541
+ }
542
+
543
+ wp_defer_term_counting( false );
544
+ wp_defer_comment_counting( false );
545
+
546
+ /**
547
+ * Complete the import.
548
+ *
549
+ * Fires after the import process has finished. If you need to update
550
+ * your cache or re-enable processing, do so here.
551
+ */
552
+ do_action( 'import_end' );
553
+ }
554
+
555
+ /**
556
+ * Set the user mapping.
557
+ *
558
+ * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
559
+ */
560
+ public function set_user_mapping( $mapping ) {
561
+ foreach ( $mapping as $map ) {
562
+ if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
563
+ $this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) );
564
+ $this->logger->debug( var_export( $map, true ) );
565
+ continue;
566
+ }
567
+
568
+ $old_slug = $map['old_slug'];
569
+ $old_id = $map['old_id'];
570
+ $new_id = $map['new_id'];
571
+
572
+ $this->mapping['user'][ $old_id ] = $new_id;
573
+ $this->mapping['user_slug'][ $old_slug ] = $new_id;
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Set the user slug overrides.
579
+ *
580
+ * Allows overriding the slug in the import with a custom/renamed version.
581
+ *
582
+ * @param string[] $overrides Map of old slug to new slug.
583
+ */
584
+ public function set_user_slug_overrides( $overrides ) {
585
+ foreach ( $overrides as $original => $renamed ) {
586
+ $this->user_slug_override[ $original ] = $renamed;
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Parse a post node into post data.
592
+ *
593
+ * @param DOMElement $node Parent node of post data (typically `item`).
594
+ * @return array|WP_Error Post data array on success, error otherwise.
595
+ */
596
+ protected function parse_post_node( $node ) {
597
+ $data = array();
598
+ $meta = array();
599
+ $comments = array();
600
+ $terms = array();
601
+
602
+ foreach ( $node->childNodes as $child ) {
603
+ // We only care about child elements
604
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
605
+ continue;
606
+ }
607
+
608
+ switch ( $child->tagName ) {
609
+ case 'wp:post_type':
610
+ $data['post_type'] = $child->textContent;
611
+ break;
612
+
613
+ case 'title':
614
+ $data['post_title'] = $child->textContent;
615
+ break;
616
+
617
+ case 'guid':
618
+ $data['guid'] = $child->textContent;
619
+ break;
620
+
621
+ case 'dc:creator':
622
+ $data['post_author'] = $child->textContent;
623
+ break;
624
+
625
+ case 'content:encoded':
626
+ $data['post_content'] = $child->textContent;
627
+ break;
628
+
629
+ case 'excerpt:encoded':
630
+ $data['post_excerpt'] = $child->textContent;
631
+ break;
632
+
633
+ case 'wp:post_id':
634
+ $data['post_id'] = $child->textContent;
635
+ break;
636
+
637
+ case 'wp:post_date':
638
+ $data['post_date'] = $child->textContent;
639
+ break;
640
+
641
+ case 'wp:post_date_gmt':
642
+ $data['post_date_gmt'] = $child->textContent;
643
+ break;
644
+
645
+ case 'wp:comment_status':
646
+ $data['comment_status'] = $child->textContent;
647
+ break;
648
+
649
+ case 'wp:ping_status':
650
+ $data['ping_status'] = $child->textContent;
651
+ break;
652
+
653
+ case 'wp:post_name':
654
+ $data['post_name'] = $child->textContent;
655
+ break;
656
+
657
+ case 'wp:status':
658
+ $data['post_status'] = $child->textContent;
659
+
660
+ if ( $data['post_status'] === 'auto-draft' ) {
661
+ // Bail now
662
+ return new WP_Error(
663
+ 'wxr_importer.post.cannot_import_draft',
664
+ __( 'Cannot import auto-draft posts' ),
665
+ $data
666
+ );
667
+ }
668
+ break;
669
+
670
+ case 'wp:post_parent':
671
+ $data['post_parent'] = $child->textContent;
672
+ break;
673
+
674
+ case 'wp:menu_order':
675
+ $data['menu_order'] = $child->textContent;
676
+ break;
677
+
678
+ case 'wp:post_password':
679
+ $data['post_password'] = $child->textContent;
680
+ break;
681
+
682
+ case 'wp:is_sticky':
683
+ $data['is_sticky'] = $child->textContent;
684
+ break;
685
+
686
+ case 'wp:attachment_url':
687
+ $data['attachment_url'] = $child->textContent;
688
+ break;
689
+
690
+ case 'wp:postmeta':
691
+ $meta_item = $this->parse_meta_node( $child );
692
+ if ( ! empty( $meta_item ) ) {
693
+ $meta[] = $meta_item;
694
+ }
695
+ break;
696
+
697
+ case 'wp:comment':
698
+ $comment_item = $this->parse_comment_node( $child );
699
+ if ( ! empty( $comment_item ) ) {
700
+ $comments[] = $comment_item;
701
+ }
702
+ break;
703
+
704
+ case 'category':
705
+ $term_item = $this->parse_category_node( $child );
706
+ if ( ! empty( $term_item ) ) {
707
+ $terms[] = $term_item;
708
+ }
709
+ break;
710
+ }// End switch().
711
+ }// End foreach().
712
+
713
+ return compact( 'data', 'meta', 'comments', 'terms' );
714
+ }
715
+
716
+ /**
717
+ * Create new posts based on import information
718
+ *
719
+ * Posts marked as having a parent which doesn't exist will become top level items.
720
+ * Doesn't create a new post if: the post type doesn't exist, the given post ID
721
+ * is already noted as imported or a post with the same title and date already exists.
722
+ * Note that new/updated terms, comments and meta are imported for the last of the above.
723
+ */
724
+ protected function process_post( $data, $meta, $comments, $terms ) {
725
+ /**
726
+ * Pre-process post data.
727
+ *
728
+ * @param array $data Post data. (Return empty to skip.)
729
+ * @param array $meta Meta data.
730
+ * @param array $comments Comments on the post.
731
+ * @param array $terms Terms on the post.
732
+ */
733
+ $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
734
+ if ( empty( $data ) ) {
735
+ return false;
736
+ }
737
+
738
+ $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
739
+ $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
740
+ $author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
741
+
742
+ // Have we already processed this?
743
+ if ( isset( $this->mapping['post'][ $original_id ] ) ) {
744
+ return;
745
+ }
746
+
747
+ $post_type_object = get_post_type_object( $data['post_type'] );
748
+
749
+ // Is this type even valid?
750
+ if ( ! $post_type_object ) {
751
+ $this->logger->warning( sprintf(
752
+ __( 'Failed to import "%1$s": Invalid post type %2$s', 'wordpress-importer' ),
753
+ $data['post_title'],
754
+ $data['post_type']
755
+ ) );
756
+ return false;
757
+ }
758
+
759
+ $post_exists = $this->post_exists( $data );
760
+ if ( $post_exists ) {
761
+ $this->logger->info( sprintf(
762
+ __( '%1$s "%2$s" already exists.', 'wordpress-importer' ),
763
+ $post_type_object->labels->singular_name,
764
+ $data['post_title']
765
+ ) );
766
+
767
+ /**
768
+ * Post processing already imported.
769
+ *
770
+ * @param array $data Raw data imported for the post.
771
+ */
772
+ do_action( 'wxr_importer.process_already_imported.post', $data );
773
+
774
+ // Even though this post already exists, new comments might need importing
775
+ $this->process_comments( $comments, $original_id, $data, $post_exists );
776
+
777
+ return false;
778
+ }
779
+
780
+ // Map the parent post, or mark it as one we need to fix
781
+ $requires_remapping = false;
782
+ if ( $parent_id ) {
783
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
784
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
785
+ } else {
786
+ $meta[] = array(
787
+ 'key' => '_wxr_import_parent',
788
+ 'value' => $parent_id,
789
+ );
790
+ $requires_remapping = true;
791
+
792
+ $data['post_parent'] = 0;
793
+ }
794
+ }
795
+
796
+ // Map the author, or mark it as one we need to fix
797
+ $author = sanitize_user( $data['post_author'], true );
798
+ if ( empty( $author ) ) {
799
+ // Missing or invalid author, use default if available.
800
+ $data['post_author'] = $this->options['default_author'];
801
+ } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
802
+ $data['post_author'] = $this->mapping['user_slug'][ $author ];
803
+ } else {
804
+ $meta[] = array(
805
+ 'key' => '_wxr_import_user_slug',
806
+ 'value' => $author,
807
+ );
808
+ $requires_remapping = true;
809
+
810
+ $data['post_author'] = (int) get_current_user_id();
811
+ }
812
+
813
+ // Does the post look like it contains attachment images?
814
+ if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
815
+ $meta[] = array(
816
+ 'key' => '_wxr_import_has_attachment_refs',
817
+ 'value' => true,
818
+ );
819
+ $requires_remapping = true;
820
+ }
821
+
822
+ // Whitelist to just the keys we allow
823
+ $postdata = array(
824
+ 'import_id' => $data['post_id'],
825
+ );
826
+ $allowed = array(
827
+ 'post_author' => true,
828
+ 'post_date' => true,
829
+ 'post_date_gmt' => true,
830
+ 'post_content' => true,
831
+ 'post_excerpt' => true,
832
+ 'post_title' => true,
833
+ 'post_status' => true,
834
+ 'post_name' => true,
835
+ 'comment_status' => true,
836
+ 'ping_status' => true,
837
+ 'guid' => true,
838
+ 'post_parent' => true,
839
+ 'menu_order' => true,
840
+ 'post_type' => true,
841
+ 'post_password' => true,
842
+ );
843
+ foreach ( $data as $key => $value ) {
844
+ if ( ! isset( $allowed[ $key ] ) ) {
845
+ continue;
846
+ }
847
+
848
+ $postdata[ $key ] = $data[ $key ];
849
+ }
850
+
851
+ $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
852
+
853
+ if ( 'attachment' === $postdata['post_type'] ) {
854
+ if ( ! $this->options['fetch_attachments'] ) {
855
+ $this->logger->notice( sprintf(
856
+ __( 'Skipping attachment "%s", fetching attachments disabled' ),
857
+ $data['post_title']
858
+ ) );
859
+ /**
860
+ * Post processing skipped.
861
+ *
862
+ * @param array $data Raw data imported for the post.
863
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
864
+ */
865
+ do_action( 'wxr_importer.process_skipped.post', $data, $meta );
866
+ return false;
867
+ }
868
+ $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
869
+ $post_id = $this->process_attachment( $postdata, $meta, $remote_url );
870
+ } else {
871
+ $post_id = wp_insert_post( $postdata, true );
872
+ do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
873
+ }
874
+
875
+ if ( is_wp_error( $post_id ) ) {
876
+ $this->logger->error( sprintf(
877
+ __( 'Failed to import "%1$s" (%2$s)', 'wordpress-importer' ),
878
+ $data['post_title'],
879
+ $post_type_object->labels->singular_name
880
+ ) );
881
+ $this->logger->debug( $post_id->get_error_message() );
882
+
883
+ /**
884
+ * Post processing failed.
885
+ *
886
+ * @param WP_Error $post_id Error object.
887
+ * @param array $data Raw data imported for the post.
888
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
889
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
890
+ * @param array $terms Raw term data, already processed.
891
+ */
892
+ do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
893
+ return false;
894
+ }
895
+
896
+ // Ensure stickiness is handled correctly too
897
+ if ( $data['is_sticky'] === '1' ) {
898
+ stick_post( $post_id );
899
+ }
900
+
901
+ // map pre-import ID to local ID
902
+ $this->mapping['post'][ $original_id ] = (int) $post_id;
903
+ if ( $requires_remapping ) {
904
+ $this->requires_remapping['post'][ $post_id ] = true;
905
+ }
906
+ $this->mark_post_exists( $data, $post_id );
907
+
908
+ $this->logger->info( sprintf(
909
+ __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
910
+ $data['post_title'],
911
+ $post_type_object->labels->singular_name
912
+ ) );
913
+ $this->logger->debug( sprintf(
914
+ __( 'Post %1$d remapped to %2$d', 'wordpress-importer' ),
915
+ $original_id,
916
+ $post_id
917
+ ) );
918
+
919
+ // Handle the terms too
920
+ $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
921
+
922
+ if ( ! empty( $terms ) ) {
923
+ $term_ids = array();
924
+ foreach ( $terms as $term ) {
925
+ $taxonomy = $term['taxonomy'];
926
+ $key = sha1( $taxonomy . ':' . $term['slug'] );
927
+
928
+ if ( isset( $this->mapping['term'][ $key ] ) ) {
929
+ $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
930
+ } else {
931
+ $meta[] = array(
932
+ 'key' => '_wxr_import_term',
933
+ 'value' => $term,
934
+ );
935
+ $requires_remapping = true;
936
+ }
937
+ }
938
+
939
+ foreach ( $term_ids as $tax => $ids ) {
940
+ $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
941
+ do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
942
+ }
943
+ }
944
+
945
+ $this->process_comments( $comments, $post_id, $data );
946
+ $this->process_post_meta( $meta, $post_id, $data );
947
+
948
+ if ( 'nav_menu_item' === $data['post_type'] ) {
949
+ $this->process_menu_item_meta( $post_id, $data, $meta );
950
+ }
951
+
952
+ /**
953
+ * Post processing completed.
954
+ *
955
+ * @param int $post_id New post ID.
956
+ * @param array $data Raw data imported for the post.
957
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
958
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
959
+ * @param array $terms Raw term data, already processed.
960
+ */
961
+ do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
962
+ }
963
+
964
+ /**
965
+ * Attempt to create a new menu item from import data
966
+ *
967
+ * Fails for draft, orphaned menu items and those without an associated nav_menu
968
+ * or an invalid nav_menu term. If the post type or term object which the menu item
969
+ * represents doesn't exist then the menu item will not be imported (waits until the
970
+ * end of the import to retry again before discarding).
971
+ *
972
+ * @param array $item Menu item details from WXR file
973
+ */
974
+ protected function process_menu_item_meta( $post_id, $data, $meta ) {
975
+
976
+ $item_type = get_post_meta( $post_id, '_menu_item_type', true );
977
+ $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
978
+ $object_id = null;
979
+
980
+ $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
981
+
982
+ $requires_remapping = false;
983
+ switch ( $item_type ) {
984
+ case 'taxonomy':
985
+ if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
986
+ $object_id = $this->mapping['term_id'][ $original_object_id ];
987
+ } else {
988
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
989
+ $requires_remapping = true;
990
+ }
991
+ break;
992
+
993
+ case 'post_type':
994
+ if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
995
+ $object_id = $this->mapping['post'][ $original_object_id ];
996
+ } else {
997
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
998
+ $requires_remapping = true;
999
+ }
1000
+ break;
1001
+
1002
+ case 'custom':
1003
+ // Custom refers to itself, wonderfully easy.
1004
+ $object_id = $post_id;
1005
+ break;
1006
+
1007
+ default:
1008
+ // associated object is missing or not imported yet, we'll retry later
1009
+ $this->missing_menu_items[] = $item;
1010
+ $this->logger->debug( 'Unknown menu item type' );
1011
+ break;
1012
+ }
1013
+
1014
+ if ( $requires_remapping ) {
1015
+ $this->requires_remapping['post'][ $post_id ] = true;
1016
+ }
1017
+
1018
+ if ( empty( $object_id ) ) {
1019
+ // Nothing needed here.
1020
+ return;
1021
+ }
1022
+
1023
+ $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
1024
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
1025
+ }
1026
+
1027
+ /**
1028
+ * If fetching attachments is enabled then attempt to create a new attachment
1029
+ *
1030
+ * @param array $post Attachment post details from WXR
1031
+ * @param string $url URL to fetch attachment from
1032
+ * @return int|WP_Error Post ID on success, WP_Error otherwise
1033
+ */
1034
+ protected function process_attachment( $post, $meta, $remote_url ) {
1035
+ // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
1036
+ // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
1037
+ $post['upload_date'] = $post['post_date'];
1038
+ foreach ( $meta as $meta_item ) {
1039
+ if ( $meta_item['key'] !== '_wp_attached_file' ) {
1040
+ continue;
1041
+ }
1042
+
1043
+ if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
1044
+ $post['upload_date'] = $matches[0];
1045
+ }
1046
+ break;
1047
+ }
1048
+
1049
+ // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
1050
+ if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
1051
+ $remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
1052
+ }
1053
+
1054
+ $upload = $this->fetch_remote_file( $remote_url, $post );
1055
+ if ( is_wp_error( $upload ) ) {
1056
+ return $upload;
1057
+ }
1058
+
1059
+ $info = wp_check_filetype( $upload['file'] );
1060
+ if ( ! $info ) {
1061
+ return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
1062
+ }
1063
+
1064
+ $post['post_mime_type'] = $info['type'];
1065
+
1066
+ // WP really likes using the GUID for display. Allow updating it.
1067
+ // See https://core.trac.wordpress.org/ticket/33386
1068
+ if ( $this->options['update_attachment_guids'] ) {
1069
+ $post['guid'] = $upload['url'];
1070
+ }
1071
+
1072
+ // as per wp-admin/includes/upload.php
1073
+ $post_id = wp_insert_attachment( $post, $upload['file'] );
1074
+ if ( is_wp_error( $post_id ) ) {
1075
+ return $post_id;
1076
+ }
1077
+
1078
+ $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
1079
+ wp_update_attachment_metadata( $post_id, $attachment_metadata );
1080
+
1081
+ // Map this image URL later if we need to
1082
+ $this->url_remap[ $remote_url ] = $upload['url'];
1083
+
1084
+ // If we have a HTTPS URL, ensure the HTTP URL gets replaced too
1085
+ if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
1086
+ $insecure_url = 'http' . substr( $remote_url, 5 );
1087
+ $this->url_remap[ $insecure_url ] = $upload['url'];
1088
+ }
1089
+
1090
+ if ( $this->options['aggressive_url_search'] ) {
1091
+ // remap resized image URLs, works by stripping the extension and remapping the URL stub.
1092
+ /*
1093
+ if ( preg_match( '!^image/!', $info['type'] ) ) {
1094
+ $parts = pathinfo( $remote_url );
1095
+ $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
1096
+
1097
+ $parts_new = pathinfo( $upload['url'] );
1098
+ $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
1099
+
1100
+ $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
1101
+ }*/
1102
+ }
1103
+
1104
+ return $post_id;
1105
+ }
1106
+
1107
+ /**
1108
+ * Parse a meta node into meta data.
1109
+ *
1110
+ * @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
1111
+ * @return array|null Meta data array on success, or null on error.
1112
+ */
1113
+ protected function parse_meta_node( $node ) {
1114
+ foreach ( $node->childNodes as $child ) {
1115
+ // We only care about child elements
1116
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1117
+ continue;
1118
+ }
1119
+
1120
+ switch ( $child->tagName ) {
1121
+ case 'wp:meta_key':
1122
+ $key = $child->textContent;
1123
+ break;
1124
+
1125
+ case 'wp:meta_value':
1126
+ $value = $child->textContent;
1127
+ break;
1128
+ }
1129
+ }
1130
+
1131
+ if ( empty( $key ) || empty( $value ) ) {
1132
+ return null;
1133
+ }
1134
+
1135
+ return compact( 'key', 'value' );
1136
+ }
1137
+
1138
+ /**
1139
+ * Process and import post meta items.
1140
+ *
1141
+ * @param array $meta List of meta data arrays
1142
+ * @param int $post_id Post to associate with
1143
+ * @param array $post Post data
1144
+ * @return int|WP_Error Number of meta items imported on success, error otherwise.
1145
+ */
1146
+ protected function process_post_meta( $meta, $post_id, $post ) {
1147
+ if ( empty( $meta ) ) {
1148
+ return true;
1149
+ }
1150
+
1151
+ foreach ( $meta as $meta_item ) {
1152
+ /**
1153
+ * Pre-process post meta data.
1154
+ *
1155
+ * @param array $meta_item Meta data. (Return empty to skip.)
1156
+ * @param int $post_id Post the meta is attached to.
1157
+ */
1158
+ $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
1159
+ if ( empty( $meta_item ) ) {
1160
+ return false;
1161
+ }
1162
+
1163
+ $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
1164
+ $value = false;
1165
+
1166
+ if ( '_edit_last' === $key ) {
1167
+ $value = intval( $meta_item['value'] );
1168
+ if ( ! isset( $this->mapping['user'][ $value ] ) ) {
1169
+ // Skip!
1170
+ continue;
1171
+ }
1172
+
1173
+ $value = $this->mapping['user'][ $value ];
1174
+ }
1175
+
1176
+ if ( $key ) {
1177
+ // export gets meta straight from the DB so could have a serialized string
1178
+ if ( ! $value ) {
1179
+ $value = maybe_unserialize( $meta_item['value'] );
1180
+ }
1181
+
1182
+ add_post_meta( $post_id, $key, $value );
1183
+ do_action( 'import_post_meta', $post_id, $key, $value );
1184
+
1185
+ // if the post has a featured image, take note of this in case of remap
1186
+ if ( '_thumbnail_id' === $key ) {
1187
+ $this->featured_images[ $post_id ] = (int) $value;
1188
+ }
1189
+ }
1190
+ }// End foreach().
1191
+
1192
+ return true;
1193
+ }
1194
+
1195
+ /**
1196
+ * Parse a comment node into comment data.
1197
+ *
1198
+ * @param DOMElement $node Parent node of comment data (typically `wp:comment`).
1199
+ * @return array Comment data array.
1200
+ */
1201
+ protected function parse_comment_node( $node ) {
1202
+ $data = array(
1203
+ 'commentmeta' => array(),
1204
+ );
1205
+
1206
+ foreach ( $node->childNodes as $child ) {
1207
+ // We only care about child elements
1208
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1209
+ continue;
1210
+ }
1211
+
1212
+ switch ( $child->tagName ) {
1213
+ case 'wp:comment_id':
1214
+ $data['comment_id'] = $child->textContent;
1215
+ break;
1216
+ case 'wp:comment_author':
1217
+ $data['comment_author'] = $child->textContent;
1218
+ break;
1219
+
1220
+ case 'wp:comment_author_email':
1221
+ $data['comment_author_email'] = $child->textContent;
1222
+ break;
1223
+
1224
+ case 'wp:comment_author_IP':
1225
+ $data['comment_author_IP'] = $child->textContent;
1226
+ break;
1227
+
1228
+ case 'wp:comment_author_url':
1229
+ $data['comment_author_url'] = $child->textContent;
1230
+ break;
1231
+
1232
+ case 'wp:comment_user_id':
1233
+ $data['comment_user_id'] = $child->textContent;
1234
+ break;
1235
+
1236
+ case 'wp:comment_date':
1237
+ $data['comment_date'] = $child->textContent;
1238
+ break;
1239
+
1240
+ case 'wp:comment_date_gmt':
1241
+ $data['comment_date_gmt'] = $child->textContent;
1242
+ break;
1243
+
1244
+ case 'wp:comment_content':
1245
+ $data['comment_content'] = $child->textContent;
1246
+ break;
1247
+
1248
+ case 'wp:comment_approved':
1249
+ $data['comment_approved'] = $child->textContent;
1250
+ break;
1251
+
1252
+ case 'wp:comment_type':
1253
+ $data['comment_type'] = $child->textContent;
1254
+ break;
1255
+
1256
+ case 'wp:comment_parent':
1257
+ $data['comment_parent'] = $child->textContent;
1258
+ break;
1259
+
1260
+ case 'wp:commentmeta':
1261
+ $meta_item = $this->parse_meta_node( $child );
1262
+ if ( ! empty( $meta_item ) ) {
1263
+ $data['commentmeta'][] = $meta_item;
1264
+ }
1265
+ break;
1266
+ }// End switch().
1267
+ }// End foreach().
1268
+
1269
+ return $data;
1270
+ }
1271
+
1272
+ /**
1273
+ * Process and import comment data.
1274
+ *
1275
+ * @param array $comments List of comment data arrays.
1276
+ * @param int $post_id Post to associate with.
1277
+ * @param array $post Post data.
1278
+ * @return int|WP_Error Number of comments imported on success, error otherwise.
1279
+ */
1280
+ protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
1281
+
1282
+ $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
1283
+ if ( empty( $comments ) ) {
1284
+ return 0;
1285
+ }
1286
+
1287
+ $num_comments = 0;
1288
+
1289
+ // Sort by ID to avoid excessive remapping later
1290
+ usort( $comments, array( $this, 'sort_comments_by_id' ) );
1291
+
1292
+ foreach ( $comments as $key => $comment ) {
1293
+ /**
1294
+ * Pre-process comment data
1295
+ *
1296
+ * @param array $comment Comment data. (Return empty to skip.)
1297
+ * @param int $post_id Post the comment is attached to.
1298
+ */
1299
+ $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
1300
+ if ( empty( $comment ) ) {
1301
+ return false;
1302
+ }
1303
+
1304
+ $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
1305
+ $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
1306
+ $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
1307
+
1308
+ // if this is a new post we can skip the comment_exists() check
1309
+ // TODO: Check comment_exists for performance
1310
+ if ( $post_exists ) {
1311
+ $existing = $this->comment_exists( $comment );
1312
+ if ( $existing ) {
1313
+
1314
+ /**
1315
+ * Comment processing already imported.
1316
+ *
1317
+ * @param array $comment Raw data imported for the comment.
1318
+ */
1319
+ do_action( 'wxr_importer.process_already_imported.comment', $comment );
1320
+
1321
+ $this->mapping['comment'][ $original_id ] = $existing;
1322
+ continue;
1323
+ }
1324
+ }
1325
+
1326
+ // Remove meta from the main array
1327
+ $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
1328
+ unset( $comment['commentmeta'] );
1329
+
1330
+ // Map the parent comment, or mark it as one we need to fix
1331
+ $requires_remapping = false;
1332
+ if ( $parent_id ) {
1333
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1334
+ $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1335
+ } else {
1336
+ // Prepare for remapping later
1337
+ $meta[] = array(
1338
+ 'key' => '_wxr_import_parent',
1339
+ 'value' => $parent_id,
1340
+ );
1341
+ $requires_remapping = true;
1342
+
1343
+ // Wipe the parent for now
1344
+ $comment['comment_parent'] = 0;
1345
+ }
1346
+ }
1347
+
1348
+ // Map the author, or mark it as one we need to fix
1349
+ if ( $author_id ) {
1350
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1351
+ $comment['user_id'] = $this->mapping['user'][ $author_id ];
1352
+ } else {
1353
+ // Prepare for remapping later
1354
+ $meta[] = array(
1355
+ 'key' => '_wxr_import_user',
1356
+ 'value' => $author_id,
1357
+ );
1358
+ $requires_remapping = true;
1359
+
1360
+ // Wipe the user for now
1361
+ $comment['user_id'] = 0;
1362
+ }
1363
+ }
1364
+
1365
+ // Run standard core filters
1366
+ $comment['comment_post_ID'] = $post_id;
1367
+ $comment = wp_filter_comment( $comment );
1368
+
1369
+ // wp_insert_comment expects slashed data
1370
+ $comment_id = wp_insert_comment( wp_slash( $comment ) );
1371
+ $this->mapping['comment'][ $original_id ] = $comment_id;
1372
+ if ( $requires_remapping ) {
1373
+ $this->requires_remapping['comment'][ $comment_id ] = true;
1374
+ }
1375
+ $this->mark_comment_exists( $comment, $comment_id );
1376
+
1377
+ /**
1378
+ * Comment has been imported.
1379
+ *
1380
+ * @param int $comment_id New comment ID
1381
+ * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
1382
+ * @param int $post_id Post parent of the comment
1383
+ * @param array $post Post data
1384
+ */
1385
+ do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
1386
+
1387
+ // Process the meta items
1388
+ foreach ( $meta as $meta_item ) {
1389
+ $value = maybe_unserialize( $meta_item['value'] );
1390
+ add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
1391
+ }
1392
+
1393
+ /**
1394
+ * Post processing completed.
1395
+ *
1396
+ * @param int $post_id New post ID.
1397
+ * @param array $comment Raw data imported for the comment.
1398
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
1399
+ * @param array $post_id Parent post ID.
1400
+ */
1401
+ do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
1402
+
1403
+ $num_comments++;
1404
+ }// End foreach().
1405
+
1406
+ return $num_comments;
1407
+ }
1408
+
1409
+ protected function parse_category_node( $node ) {
1410
+ $data = array(
1411
+ // Default taxonomy to "category", since this is a `<category>` tag
1412
+ 'taxonomy' => 'category',
1413
+ );
1414
+ $meta = array();
1415
+
1416
+ if ( $node->hasAttribute( 'domain' ) ) {
1417
+ $data['taxonomy'] = $node->getAttribute( 'domain' );
1418
+ }
1419
+ if ( $node->hasAttribute( 'nicename' ) ) {
1420
+ $data['slug'] = $node->getAttribute( 'nicename' );
1421
+ }
1422
+
1423
+ $data['name'] = $node->textContent;
1424
+
1425
+ if ( empty( $data['slug'] ) ) {
1426
+ return null;
1427
+ }
1428
+
1429
+ // Just for extra compatibility
1430
+ if ( $data['taxonomy'] === 'tag' ) {
1431
+ $data['taxonomy'] = 'post_tag';
1432
+ }
1433
+
1434
+ return $data;
1435
+ }
1436
+
1437
+ /**
1438
+ * Callback for `usort` to sort comments by ID
1439
+ *
1440
+ * @param array $a Comment data for the first comment
1441
+ * @param array $b Comment data for the second comment
1442
+ * @return int
1443
+ */
1444
+ public static function sort_comments_by_id( $a, $b ) {
1445
+ if ( empty( $a['comment_id'] ) ) {
1446
+ return 1;
1447
+ }
1448
+
1449
+ if ( empty( $b['comment_id'] ) ) {
1450
+ return -1;
1451
+ }
1452
+
1453
+ return $a['comment_id'] - $b['comment_id'];
1454
+ }
1455
+
1456
+ protected function parse_author_node( $node ) {
1457
+ $data = array();
1458
+ $meta = array();
1459
+ foreach ( $node->childNodes as $child ) {
1460
+ // We only care about child elements
1461
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1462
+ continue;
1463
+ }
1464
+
1465
+ switch ( $child->tagName ) {
1466
+ case 'wp:author_login':
1467
+ $data['user_login'] = $child->textContent;
1468
+ break;
1469
+
1470
+ case 'wp:author_id':
1471
+ $data['ID'] = $child->textContent;
1472
+ break;
1473
+
1474
+ case 'wp:author_email':
1475
+ $data['user_email'] = $child->textContent;
1476
+ break;
1477
+
1478
+ case 'wp:author_display_name':
1479
+ $data['display_name'] = $child->textContent;
1480
+ break;
1481
+
1482
+ case 'wp:author_first_name':
1483
+ $data['first_name'] = $child->textContent;
1484
+ break;
1485
+
1486
+ case 'wp:author_last_name':
1487
+ $data['last_name'] = $child->textContent;
1488
+ break;
1489
+ }
1490
+ }
1491
+
1492
+ return compact( 'data', 'meta' );
1493
+ }
1494
+
1495
+ protected function process_author( $data, $meta ) {
1496
+ /**
1497
+ * Pre-process user data.
1498
+ *
1499
+ * @param array $data User data. (Return empty to skip.)
1500
+ * @param array $meta Meta data.
1501
+ */
1502
+ $data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
1503
+ if ( empty( $data ) ) {
1504
+ return false;
1505
+ }
1506
+
1507
+ // Have we already handled this user?
1508
+ $original_id = isset( $data['ID'] ) ? $data['ID'] : 0;
1509
+ $original_slug = $data['user_login'];
1510
+
1511
+ if ( isset( $this->mapping['user'][ $original_id ] ) ) {
1512
+ $existing = $this->mapping['user'][ $original_id ];
1513
+
1514
+ // Note the slug mapping if we need to too
1515
+ if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1516
+ $this->mapping['user_slug'][ $original_slug ] = $existing;
1517
+ }
1518
+
1519
+ return false;
1520
+ }
1521
+
1522
+ if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1523
+ $existing = $this->mapping['user_slug'][ $original_slug ];
1524
+
1525
+ // Ensure we note the mapping too
1526
+ $this->mapping['user'][ $original_id ] = $existing;
1527
+
1528
+ return false;
1529
+ }
1530
+
1531
+ // Allow overriding the user's slug
1532
+ $login = $original_slug;
1533
+ if ( isset( $this->user_slug_override[ $login ] ) ) {
1534
+ $login = $this->user_slug_override[ $login ];
1535
+ }
1536
+
1537
+ $userdata = array(
1538
+ 'user_login' => sanitize_user( $login, true ),
1539
+ 'user_pass' => wp_generate_password(),
1540
+ );
1541
+
1542
+ $allowed = array(
1543
+ 'user_email' => true,
1544
+ 'display_name' => true,
1545
+ 'first_name' => true,
1546
+ 'last_name' => true,
1547
+ );
1548
+ foreach ( $data as $key => $value ) {
1549
+ if ( ! isset( $allowed[ $key ] ) ) {
1550
+ continue;
1551
+ }
1552
+
1553
+ $userdata[ $key ] = $data[ $key ];
1554
+ }
1555
+
1556
+ $user_id = wp_insert_user( wp_slash( $userdata ) );
1557
+ if ( is_wp_error( $user_id ) ) {
1558
+ $this->logger->error( sprintf(
1559
+ __( 'Failed to import user "%s"', 'wordpress-importer' ),
1560
+ $userdata['user_login']
1561
+ ) );
1562
+ $this->logger->debug( $user_id->get_error_message() );
1563
+
1564
+ /**
1565
+ * User processing failed.
1566
+ *
1567
+ * @param WP_Error $user_id Error object.
1568
+ * @param array $userdata Raw data imported for the user.
1569
+ */
1570
+ do_action( 'wxr_importer.process_failed.user', $user_id, $userdata );
1571
+ return false;
1572
+ }
1573
+
1574
+ if ( $original_id ) {
1575
+ $this->mapping['user'][ $original_id ] = $user_id;
1576
+ }
1577
+ $this->mapping['user_slug'][ $original_slug ] = $user_id;
1578
+
1579
+ $this->logger->info( sprintf(
1580
+ __( 'Imported user "%s"', 'wordpress-importer' ),
1581
+ $userdata['user_login']
1582
+ ) );
1583
+ $this->logger->debug( sprintf(
1584
+ __( 'User %1$d remapped to %2$d', 'wordpress-importer' ),
1585
+ $original_id,
1586
+ $user_id
1587
+ ) );
1588
+
1589
+ // TODO: Implement meta handling once WXR includes it
1590
+ /**
1591
+ * User processing completed.
1592
+ *
1593
+ * @param int $user_id New user ID.
1594
+ * @param array $userdata Raw data imported for the user.
1595
+ */
1596
+ do_action( 'wxr_importer.processed.user', $user_id, $userdata );
1597
+ }
1598
+
1599
+ protected function parse_term_node( $node, $type = 'term' ) {
1600
+ $data = array();
1601
+ $meta = array();
1602
+
1603
+ $tag_name = array(
1604
+ 'id' => 'wp:term_id',
1605
+ 'taxonomy' => 'wp:term_taxonomy',
1606
+ 'slug' => 'wp:term_slug',
1607
+ 'parent' => 'wp:term_parent',
1608
+ 'name' => 'wp:term_name',
1609
+ 'description' => 'wp:term_description',
1610
+ );
1611
+ $taxonomy = null;
1612
+
1613
+ // Special casing!
1614
+ switch ( $type ) {
1615
+ case 'category':
1616
+ $tag_name['slug'] = 'wp:category_nicename';
1617
+ $tag_name['parent'] = 'wp:category_parent';
1618
+ $tag_name['name'] = 'wp:cat_name';
1619
+ $tag_name['description'] = 'wp:category_description';
1620
+ $tag_name['taxonomy'] = null;
1621
+
1622
+ $data['taxonomy'] = 'category';
1623
+ break;
1624
+
1625
+ case 'tag':
1626
+ $tag_name['slug'] = 'wp:tag_slug';
1627
+ $tag_name['parent'] = null;
1628
+ $tag_name['name'] = 'wp:tag_name';
1629
+ $tag_name['description'] = 'wp:tag_description';
1630
+ $tag_name['taxonomy'] = null;
1631
+
1632
+ $data['taxonomy'] = 'post_tag';
1633
+ break;
1634
+ }
1635
+
1636
+ foreach ( $node->childNodes as $child ) {
1637
+ // We only care about child elements
1638
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1639
+ continue;
1640
+ }
1641
+
1642
+ $key = array_search( $child->tagName, $tag_name );
1643
+ if ( $key ) {
1644
+ $data[ $key ] = $child->textContent;
1645
+ }
1646
+ }
1647
+
1648
+ if ( empty( $data['taxonomy'] ) ) {
1649
+ return null;
1650
+ }
1651
+
1652
+ // Compatibility with WXR 1.0
1653
+ if ( $data['taxonomy'] === 'tag' ) {
1654
+ $data['taxonomy'] = 'post_tag';
1655
+ }
1656
+
1657
+ return compact( 'data', 'meta' );
1658
+ }
1659
+
1660
+ protected function process_term( $data, $meta ) {
1661
+ /**
1662
+ * Pre-process term data.
1663
+ *
1664
+ * @param array $data Term data. (Return empty to skip.)
1665
+ * @param array $meta Meta data.
1666
+ */
1667
+ $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
1668
+ if ( empty( $data ) ) {
1669
+ return false;
1670
+ }
1671
+
1672
+ $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
1673
+ $parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
1674
+
1675
+ $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
1676
+ $existing = $this->term_exists( $data );
1677
+ if ( $existing ) {
1678
+
1679
+ /**
1680
+ * Term processing already imported.
1681
+ *
1682
+ * @param array $data Raw data imported for the term.
1683
+ */
1684
+ do_action( 'wxr_importer.process_already_imported.term', $data );
1685
+
1686
+ $this->mapping['term'][ $mapping_key ] = $existing;
1687
+ $this->mapping['term_id'][ $original_id ] = $existing;
1688
+ return false;
1689
+ }
1690
+
1691
+ // WP really likes to repeat itself in export files
1692
+ if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
1693
+ return false;
1694
+ }
1695
+
1696
+ $termdata = array();
1697
+ $allowed = array(
1698
+ 'slug' => true,
1699
+ 'description' => true,
1700
+ );
1701
+
1702
+ // Map the parent comment, or mark it as one we need to fix
1703
+ // TODO: add parent mapping and remapping
1704
+ /*
1705
+ $requires_remapping = false;
1706
+ if ( $parent_id ) {
1707
+ if ( isset( $this->mapping['term'][ $parent_id ] ) ) {
1708
+ $data['parent'] = $this->mapping['term'][ $parent_id ];
1709
+ } else {
1710
+ // Prepare for remapping later
1711
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1712
+ $requires_remapping = true;
1713
+
1714
+ // Wipe the parent for now
1715
+ $data['parent'] = 0;
1716
+ }
1717
+ }*/
1718
+
1719
+ foreach ( $data as $key => $value ) {
1720
+ if ( ! isset( $allowed[ $key ] ) ) {
1721
+ continue;
1722
+ }
1723
+
1724
+ $termdata[ $key ] = $data[ $key ];
1725
+ }
1726
+
1727
+ $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
1728
+ if ( is_wp_error( $result ) ) {
1729
+ $this->logger->warning( sprintf(
1730
+ __( 'Failed to import %1$s %2$s', 'wordpress-importer' ),
1731
+ $data['taxonomy'],
1732
+ $data['name']
1733
+ ) );
1734
+ $this->logger->debug( $result->get_error_message() );
1735
+ do_action( 'wp_import_insert_term_failed', $result, $data );
1736
+
1737
+ /**
1738
+ * Term processing failed.
1739
+ *
1740
+ * @param WP_Error $result Error object.
1741
+ * @param array $data Raw data imported for the term.
1742
+ * @param array $meta Meta data supplied for the term.
1743
+ */
1744
+ do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
1745
+ return false;
1746
+ }
1747
+
1748
+ $term_id = $result['term_id'];
1749
+
1750
+ $this->mapping['term'][ $mapping_key ] = $term_id;
1751
+ $this->mapping['term_id'][ $original_id ] = $term_id;
1752
+
1753
+ $this->logger->info( sprintf(
1754
+ __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ),
1755
+ $data['name'],
1756
+ $data['taxonomy']
1757
+ ) );
1758
+ $this->logger->debug( sprintf(
1759
+ __( 'Term %1$d remapped to %2$d', 'wordpress-importer' ),
1760
+ $original_id,
1761
+ $term_id
1762
+ ) );
1763
+
1764
+ do_action( 'wp_import_insert_term', $term_id, $data );
1765
+
1766
+ /**
1767
+ * Term processing completed.
1768
+ *
1769
+ * @param int $term_id New term ID.
1770
+ * @param array $data Raw data imported for the term.
1771
+ */
1772
+ do_action( 'wxr_importer.processed.term', $term_id, $data );
1773
+ }
1774
+
1775
+ /**
1776
+ * Attempt to download a remote file attachment
1777
+ *
1778
+ * @param string $url URL of item to fetch
1779
+ * @param array $post Attachment details
1780
+ * @return array|WP_Error Local file location details on success, WP_Error otherwise
1781
+ */
1782
+ protected function fetch_remote_file( $url, $post ) {
1783
+ // extract the file name and extension from the url
1784
+ $file_name = basename( $url );
1785
+
1786
+ // get placeholder file in the upload dir with a unique, sanitized filename
1787
+ $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
1788
+ if ( $upload['error'] ) {
1789
+ return new WP_Error( 'upload_dir_error', $upload['error'] );
1790
+ }
1791
+
1792
+ // fetch the remote url and write it to the placeholder file
1793
+ $response = wp_remote_get( $url, array(
1794
+ 'stream' => true,
1795
+ 'filename' => $upload['file'],
1796
+ ) );
1797
+
1798
+ // request failed
1799
+ if ( is_wp_error( $response ) ) {
1800
+ unlink( $upload['file'] );
1801
+ return $response;
1802
+ }
1803
+
1804
+ $code = (int) wp_remote_retrieve_response_code( $response );
1805
+
1806
+ // make sure the fetch was successful
1807
+ if ( $code !== 200 ) {
1808
+ unlink( $upload['file'] );
1809
+ return new WP_Error(
1810
+ 'import_file_error',
1811
+ sprintf(
1812
+ __( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ),
1813
+ $code,
1814
+ get_status_header_desc( $code ),
1815
+ $url
1816
+ )
1817
+ );
1818
+ }
1819
+
1820
+ $filesize = filesize( $upload['file'] );
1821
+ $headers = wp_remote_retrieve_headers( $response );
1822
+
1823
+ if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1824
+ unlink( $upload['file'] );
1825
+ return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) );
1826
+ }
1827
+
1828
+ if ( 0 === $filesize ) {
1829
+ unlink( $upload['file'] );
1830
+ return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) );
1831
+ }
1832
+
1833
+ $max_size = (int) $this->max_attachment_size();
1834
+ if ( ! empty( $max_size ) && $filesize > $max_size ) {
1835
+ unlink( $upload['file'] );
1836
+ $message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) );
1837
+ return new WP_Error( 'import_file_error', $message );
1838
+ }
1839
+
1840
+ return $upload;
1841
+ }
1842
+
1843
+ protected function post_process() {
1844
+ // Time to tackle any left-over bits
1845
+ if ( ! empty( $this->requires_remapping['post'] ) ) {
1846
+ $this->post_process_posts( $this->requires_remapping['post'] );
1847
+ }
1848
+ if ( ! empty( $this->requires_remapping['comment'] ) ) {
1849
+ $this->post_process_comments( $this->requires_remapping['comment'] );
1850
+ }
1851
+ }
1852
+
1853
+ protected function post_process_posts( $todo ) {
1854
+ foreach ( $todo as $post_id => $_ ) {
1855
+ $this->logger->debug( sprintf(
1856
+ // Note: title intentionally not used to skip extra processing
1857
+ // for when debug logging is off
1858
+ __( 'Running post-processing for post %d', 'wordpress-importer' ),
1859
+ $post_id
1860
+ ) );
1861
+
1862
+ $data = array();
1863
+
1864
+ $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
1865
+ if ( ! empty( $parent_id ) ) {
1866
+ // Have we imported the parent now?
1867
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
1868
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
1869
+ } else {
1870
+ $this->logger->warning( sprintf(
1871
+ __( 'Could not find the post parent for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1872
+ get_the_title( $post_id ),
1873
+ $post_id
1874
+ ) );
1875
+ $this->logger->debug( sprintf(
1876
+ __( 'Post %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
1877
+ $post_id,
1878
+ $parent_id
1879
+ ) );
1880
+ }
1881
+ }
1882
+
1883
+ $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
1884
+ if ( ! empty( $author_slug ) ) {
1885
+ // Have we imported the user now?
1886
+ if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
1887
+ $data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
1888
+ } else {
1889
+ $this->logger->warning( sprintf(
1890
+ __( 'Could not find the author for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1891
+ get_the_title( $post_id ),
1892
+ $post_id
1893
+ ) );
1894
+ $this->logger->debug( sprintf(
1895
+ __( 'Post %1$d was imported with author "%2$s", but could not be found', 'wordpress-importer' ),
1896
+ $post_id,
1897
+ $author_slug
1898
+ ) );
1899
+ }
1900
+ }
1901
+
1902
+ $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
1903
+ if ( ! empty( $has_attachments ) ) {
1904
+ $post = get_post( $post_id );
1905
+ $content = $post->post_content;
1906
+
1907
+ // Replace all the URLs we've got
1908
+ $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
1909
+ if ( $new_content !== $content ) {
1910
+ $data['post_content'] = $new_content;
1911
+ }
1912
+ }
1913
+
1914
+ if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
1915
+ $this->post_process_menu_item( $post_id );
1916
+ }
1917
+
1918
+ // Do we have updates to make?
1919
+ if ( empty( $data ) ) {
1920
+ $this->logger->debug( sprintf(
1921
+ __( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
1922
+ $post_id
1923
+ ) );
1924
+ continue;
1925
+ }
1926
+
1927
+ // Run the update
1928
+ $data['ID'] = $post_id;
1929
+ $result = wp_update_post( $data, true );
1930
+ if ( is_wp_error( $result ) ) {
1931
+ $this->logger->warning( sprintf(
1932
+ __( 'Could not update "%1$s" (post #%2$d) with mapped data', 'wordpress-importer' ),
1933
+ get_the_title( $post_id ),
1934
+ $post_id
1935
+ ) );
1936
+ $this->logger->debug( $result->get_error_message() );
1937
+ continue;
1938
+ }
1939
+
1940
+ // Clear out our temporary meta keys
1941
+ delete_post_meta( $post_id, '_wxr_import_parent' );
1942
+ delete_post_meta( $post_id, '_wxr_import_user_slug' );
1943
+ delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
1944
+ }// End foreach().
1945
+ }
1946
+
1947
+ protected function post_process_menu_item( $post_id ) {
1948
+ $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
1949
+ if ( empty( $menu_object_id ) ) {
1950
+ // No processing needed!
1951
+ return;
1952
+ }
1953
+
1954
+ $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
1955
+ switch ( $menu_item_type ) {
1956
+ case 'taxonomy':
1957
+ if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
1958
+ $menu_object = $this->mapping['term_id'][ $menu_object_id ];
1959
+ }
1960
+ break;
1961
+
1962
+ case 'post_type':
1963
+ if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
1964
+ $menu_object = $this->mapping['post'][ $menu_object_id ];
1965
+ }
1966
+ break;
1967
+
1968
+ default:
1969
+ // Cannot handle this.
1970
+ return;
1971
+ }
1972
+
1973
+ if ( ! empty( $menu_object ) ) {
1974
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
1975
+ } else {
1976
+ $this->logger->warning( sprintf(
1977
+ __( 'Could not find the menu object for "%1$s" (post #%2$d)', 'wordpress-importer' ),
1978
+ get_the_title( $post_id ),
1979
+ $post_id
1980
+ ) );
1981
+ $this->logger->debug( sprintf(
1982
+ __( 'Post %1$d was imported with object "%2$d" of type "%3$s", but could not be found', 'wordpress-importer' ),
1983
+ $post_id,
1984
+ $menu_object_id,
1985
+ $menu_item_type
1986
+ ) );
1987
+ }
1988
+
1989
+ delete_post_meta( $post_id, '_wxr_import_menu_item' );
1990
+ }
1991
+
1992
+
1993
+ protected function post_process_comments( $todo ) {
1994
+ foreach ( $todo as $comment_id => $_ ) {
1995
+ $data = array();
1996
+
1997
+ $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
1998
+ if ( ! empty( $parent_id ) ) {
1999
+ // Have we imported the parent now?
2000
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
2001
+ $data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
2002
+ } else {
2003
+ $this->logger->warning( sprintf(
2004
+ __( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
2005
+ $comment_id
2006
+ ) );
2007
+ $this->logger->debug( sprintf(
2008
+ __( 'Comment %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ),
2009
+ $comment_id,
2010
+ $parent_id
2011
+ ) );
2012
+ }
2013
+ }
2014
+
2015
+ $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
2016
+ if ( ! empty( $author_id ) ) {
2017
+ // Have we imported the user now?
2018
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
2019
+ $data['user_id'] = $this->mapping['user'][ $author_id ];
2020
+ } else {
2021
+ $this->logger->warning( sprintf(
2022
+ __( 'Could not find the author for comment #%d', 'wordpress-importer' ),
2023
+ $comment_id
2024
+ ) );
2025
+ $this->logger->debug( sprintf(
2026
+ __( 'Comment %1$d was imported with author %2$d, but could not be found', 'wordpress-importer' ),
2027
+ $comment_id,
2028
+ $author_id
2029
+ ) );
2030
+ }
2031
+ }
2032
+
2033
+ // Do we have updates to make?
2034
+ if ( empty( $data ) ) {
2035
+ continue;
2036
+ }
2037
+
2038
+ // Run the update
2039
+ $data['comment_ID'] = $comment_ID;
2040
+ $result = wp_update_comment( wp_slash( $data ) );
2041
+ if ( empty( $result ) ) {
2042
+ $this->logger->warning( sprintf(
2043
+ __( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
2044
+ $comment_id
2045
+ ) );
2046
+ continue;
2047
+ }
2048
+
2049
+ // Clear out our temporary meta keys
2050
+ delete_comment_meta( $comment_id, '_wxr_import_parent' );
2051
+ delete_comment_meta( $comment_id, '_wxr_import_user' );
2052
+ }// End foreach().
2053
+ }
2054
+
2055
+ /**
2056
+ * Use stored mapping information to update old attachment URLs
2057
+ */
2058
+ protected function replace_attachment_urls_in_content() {
2059
+ global $wpdb;
2060
+ // make sure we do the longest urls first, in case one is a substring of another
2061
+ uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
2062
+
2063
+ foreach ( $this->url_remap as $from_url => $to_url ) {
2064
+ // remap urls in post_content
2065
+ $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
2066
+ $wpdb->query( $query );
2067
+
2068
+ // remap enclosure urls
2069
+ $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
2070
+ $result = $wpdb->query( $query );
2071
+ }
2072
+ }
2073
+
2074
+ /**
2075
+ * Update _thumbnail_id meta to new, imported attachment IDs
2076
+ */
2077
+ function remap_featured_images() {
2078
+ // cycle through posts that have a featured image
2079
+ foreach ( $this->featured_images as $post_id => $value ) {
2080
+ if ( isset( $this->processed_posts[ $value ] ) ) {
2081
+ $new_id = $this->processed_posts[ $value ];
2082
+
2083
+ // only update if there's a difference
2084
+ if ( $new_id !== $value ) {
2085
+ update_post_meta( $post_id, '_thumbnail_id', $new_id );
2086
+ }
2087
+ }
2088
+ }
2089
+ }
2090
+
2091
+ /**
2092
+ * Decide if the given meta key maps to information we will want to import
2093
+ *
2094
+ * @param string $key The meta key to check
2095
+ * @return string|bool The key if we do want to import, false if not
2096
+ */
2097
+ public function is_valid_meta_key( $key ) {
2098
+ // skip attachment metadata since we'll regenerate it from scratch
2099
+ // skip _edit_lock as not relevant for import
2100
+ if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
2101
+ return false;
2102
+ }
2103
+
2104
+ return $key;
2105
+ }
2106
+
2107
+ /**
2108
+ * Decide what the maximum file size for downloaded attachments is.
2109
+ * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
2110
+ *
2111
+ * @return int Maximum attachment file size to import
2112
+ */
2113
+ protected function max_attachment_size() {
2114
+ return apply_filters( 'import_attachment_size_limit', 0 );
2115
+ }
2116
+
2117
+ /**
2118
+ * Added to http_request_timeout filter to force timeout at 60 seconds during import
2119
+ *
2120
+ * @access protected
2121
+ * @return int 60
2122
+ */
2123
+ function bump_request_timeout( $val ) {
2124
+ return 60;
2125
+ }
2126
+
2127
+ // return the difference in length between two strings
2128
+ function cmpr_strlen( $a, $b ) {
2129
+ return strlen( $b ) - strlen( $a );
2130
+ }
2131
+
2132
+ /**
2133
+ * Prefill existing post data.
2134
+ *
2135
+ * This preloads all GUIDs into memory, allowing us to avoid hitting the
2136
+ * database when we need to check for existence. With larger imports, this
2137
+ * becomes prohibitively slow to perform SELECT queries on each.
2138
+ *
2139
+ * By preloading all this data into memory, it's a constant-time lookup in
2140
+ * PHP instead. However, this does use a lot more memory, so for sites doing
2141
+ * small imports onto a large site, it may be a better tradeoff to use
2142
+ * on-the-fly checking instead.
2143
+ */
2144
+ protected function prefill_existing_posts() {
2145
+ global $wpdb;
2146
+ $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
2147
+
2148
+ foreach ( $posts as $item ) {
2149
+ $this->exists['post'][ $item->guid ] = $item->ID;
2150
+ }
2151
+ }
2152
+
2153
+ /**
2154
+ * Does the post exist?
2155
+ *
2156
+ * @param array $data Post data to check against.
2157
+ * @return int|bool Existing post ID if it exists, false otherwise.
2158
+ */
2159
+ protected function post_exists( $data ) {
2160
+ // Constant-time lookup if we prefilled
2161
+ $exists_key = $data['guid'];
2162
+
2163
+ if ( $this->options['prefill_existing_posts'] ) {
2164
+ return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
2165
+ }
2166
+
2167
+ // No prefilling, but might have already handled it
2168
+ if ( isset( $this->exists['post'][ $exists_key ] ) ) {
2169
+ return $this->exists['post'][ $exists_key ];
2170
+ }
2171
+
2172
+ // Still nothing, try post_exists, and cache it
2173
+ $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
2174
+ $this->exists['post'][ $exists_key ] = $exists;
2175
+
2176
+ return $exists;
2177
+ }
2178
+
2179
+ /**
2180
+ * Mark the post as existing.
2181
+ *
2182
+ * @param array $data Post data to mark as existing.
2183
+ * @param int $post_id Post ID.
2184
+ */
2185
+ protected function mark_post_exists( $data, $post_id ) {
2186
+ $exists_key = $data['guid'];
2187
+ $this->exists['post'][ $exists_key ] = $post_id;
2188
+ }
2189
+
2190
+ /**
2191
+ * Prefill existing comment data.
2192
+ *
2193
+ * @see self::prefill_existing_posts() for justification of why this exists.
2194
+ */
2195
+ protected function prefill_existing_comments() {
2196
+ global $wpdb;
2197
+ $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
2198
+
2199
+ foreach ( $posts as $item ) {
2200
+ $exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
2201
+ $this->exists['comment'][ $exists_key ] = $item->comment_ID;
2202
+ }
2203
+ }
2204
+
2205
+ /**
2206
+ * Does the comment exist?
2207
+ *
2208
+ * @param array $data Comment data to check against.
2209
+ * @return int|bool Existing comment ID if it exists, false otherwise.
2210
+ */
2211
+ protected function comment_exists( $data ) {
2212
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2213
+
2214
+ // Constant-time lookup if we prefilled
2215
+ if ( $this->options['prefill_existing_comments'] ) {
2216
+ return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
2217
+ }
2218
+
2219
+ // No prefilling, but might have already handled it
2220
+ if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
2221
+ return $this->exists['comment'][ $exists_key ];
2222
+ }
2223
+
2224
+ // Still nothing, try comment_exists, and cache it
2225
+ $exists = comment_exists( $data['comment_author'], $data['comment_date'] );
2226
+ $this->exists['comment'][ $exists_key ] = $exists;
2227
+
2228
+ return $exists;
2229
+ }
2230
+
2231
+ /**
2232
+ * Mark the comment as existing.
2233
+ *
2234
+ * @param array $data Comment data to mark as existing.
2235
+ * @param int $comment_id Comment ID.
2236
+ */
2237
+ protected function mark_comment_exists( $data, $comment_id ) {
2238
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2239
+ $this->exists['comment'][ $exists_key ] = $comment_id;
2240
+ }
2241
+
2242
+ /**
2243
+ * Prefill existing term data.
2244
+ *
2245
+ * @see self::prefill_existing_posts() for justification of why this exists.
2246
+ */
2247
+ protected function prefill_existing_terms() {
2248
+ global $wpdb;
2249
+ $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
2250
+ $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
2251
+ $terms = $wpdb->get_results( $query );
2252
+
2253
+ foreach ( $terms as $item ) {
2254
+ $exists_key = sha1( $item->taxonomy . ':' . $item->slug );
2255
+ $this->exists['term'][ $exists_key ] = $item->term_id;
2256
+ }
2257
+ }
2258
+
2259
+ /**
2260
+ * Does the term exist?
2261
+ *
2262
+ * @param array $data Term data to check against.
2263
+ * @return int|bool Existing term ID if it exists, false otherwise.
2264
+ */
2265
+ protected function term_exists( $data ) {
2266
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2267
+
2268
+ // Constant-time lookup if we prefilled
2269
+ if ( $this->options['prefill_existing_terms'] ) {
2270
+ return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
2271
+ }
2272
+
2273
+ // No prefilling, but might have already handled it
2274
+ if ( isset( $this->exists['term'][ $exists_key ] ) ) {
2275
+ return $this->exists['term'][ $exists_key ];
2276
+ }
2277
+
2278
+ // Still nothing, try comment_exists, and cache it
2279
+ $exists = term_exists( $data['slug'], $data['taxonomy'] );
2280
+ if ( is_array( $exists ) ) {
2281
+ $exists = $exists['term_id'];
2282
+ }
2283
+
2284
+ $this->exists['term'][ $exists_key ] = $exists;
2285
+
2286
+ return $exists;
2287
+ }
2288
+
2289
+ /**
2290
+ * Mark the term as existing.
2291
+ *
2292
+ * @param array $data Term data to mark as existing.
2293
+ * @param int $term_id Term ID.
2294
+ */
2295
+ protected function mark_term_exists( $data, $term_id ) {
2296
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2297
+ $this->exists['term'][ $exists_key ] = $term_id;
2298
+ }
2299
ย  }
inc/includes/admin-page.php CHANGED
@@ -1,288 +1,288 @@
1
- <?php
2
- /**
3
- * Shortcode Markup
4
- *
5
- * TMPL - Single Demo Preview
6
- * TMPL - No more demos
7
- * TMPL - Filters
8
- * TMPL - List
9
- *
10
- * @package Astra Sites
11
- * @since 1.0.0
12
- */
13
-
14
- defined( 'ABSPATH' ) or exit;
15
-
16
- ?>
17
-
18
- <div class="wrap" id="astra-sites-admin">
19
-
20
- <div id="astra-sites-filters" class="wp-filter hide-if-no-js">
21
-
22
- <div class="section-left">
23
-
24
- <!-- All Filters -->
25
- <div class="filter-count">
26
- <span class="count"></span>
27
- </div>
28
- <div class="filters-wrap">
29
- <div id="astra-site-page-builder"></div>
30
- </div>
31
-
32
- </div>
33
-
34
- <div class="section-right">
35
-
36
- <div class="filters-wrap">
37
- <div id="astra-site-category"></div>
38
- </div>
39
-
40
- <div class="search-form">
41
- <label class="screen-reader-text" for="wp-filter-search-input"><?php _e( 'Search Sites', 'astra-sites' ); ?> </label>
42
- <input placeholder="<?php _e( 'Search Sites...', 'astra-sites' ); ?>" type="search" aria-describedby="live-search-desc" id="wp-filter-search-input" class="wp-filter-search">
43
- </div>
44
-
45
- </div>
46
-
47
- </div>
48
-
49
- <?php do_action( 'astra_sites_before_site_grid' ); ?>
50
-
51
- <div class="theme-browser rendered">
52
- <div id="astra-sites" class="themes wp-clearfix"></div>
53
- </div>
54
-
55
- <div class="select-page-builder">
56
- <div class="note-wrap">
57
- <h3>
58
- <span class="up-arrow dashicons dashicons-editor-break"></span>
59
- <div class="note"><?php _e( 'Select Your Favorite Page Builder', 'astra-sites' ); ?></div>
60
- </h3>
61
- </div>
62
- <img src="<?php echo esc_url( ASTRA_SITES_URI . 'inc/assets/images/sites-screenshot.jpg' ); ?>" alt="<?php _e( 'Sites List..', 'astra-sites' ); ?>" title="<?php _e( 'Sites List..', 'astra-sites' ); ?>" />
63
- </div>
64
-
65
- <div class="spinner-wrap">
66
- <span class="spinner"></span>
67
- </div>
68
-
69
- <?php do_action( 'astra_sites_after_site_grid' ); ?>
70
-
71
- </div>
72
-
73
- <?php
74
- /**
75
- * TMPL - Single Demo Preview
76
- */
77
- ?>
78
- <script type="text/template" id="tmpl-astra-site-preview">
79
- <div class="astra-sites-preview theme-install-overlay wp-full-overlay expanded">
80
- <div class="wp-full-overlay-sidebar">
81
- <div class="wp-full-overlay-header"
82
- data-demo-id="{{{data.id}}}"
83
- data-demo-type="{{{data.astra_demo_type}}}"
84
- data-demo-url="{{{data.astra_demo_url}}}"
85
- data-demo-api="{{{data.demo_api}}}"
86
- data-demo-name="{{{data.demo_name}}}"
87
- data-demo-slug="{{{data.slug}}}"
88
- data-screenshot="{{{data.screenshot}}}"
89
- data-content="{{{data.content}}}"
90
- data-required-plugins="{{data.required_plugins}}">
91
- <input type="hidden" class="astra-site-options" value="{{data.astra_site_options}}" >
92
- <input type="hidden" class="astra-enabled-extensions" value="{{data.astra_enabled_extensions}}" >
93
- <button class="close-full-overlay"><span class="screen-reader-text"><?php esc_html_e( 'Close', 'astra-sites' ); ?></span></button>
94
- <button class="previous-theme"><span class="screen-reader-text"><?php esc_html_e( 'Previous', 'astra-sites' ); ?></span></button>
95
- <button class="next-theme"><span class="screen-reader-text"><?php esc_html_e( 'Next', 'astra-sites' ); ?></span></button>
96
- <a class="button hide-if-no-customize astra-demo-import" href="#" data-import="disabled"><?php esc_html_e( 'Install Plugins', 'astra-sites' ); ?></a>
97
-
98
- </div>
99
- <div class="wp-full-overlay-sidebar-content">
100
- <div class="install-theme-info">
101
-
102
- <span class="site-type {{{data.astra_demo_type}}}">{{{data.astra_demo_type}}}</span>
103
- <h3 class="theme-name">{{{data.demo_name}}}</h3>
104
-
105
- <# if ( data.screenshot.length ) { #>
106
- <img class="theme-screenshot" src="{{{data.screenshot}}}" alt="">
107
- <# } #>
108
-
109
- <div class="theme-details">
110
- {{{data.content}}}
111
- </div>
112
- <a href="#" class="theme-details-read-more"><?php _e( 'Read more', 'astra-sites' ); ?> &hellip;</a>
113
-
114
- <div class="required-plugins-wrap">
115
- <h4><?php _e( 'Required Plugins', 'astra-sites' ); ?> </h4>
116
- <div class="required-plugins"></div>
117
- </div>
118
- </div>
119
- </div>
120
-
121
- <div class="wp-full-overlay-footer">
122
- <div class="footer-import-button-wrap">
123
- <a class="button button-hero hide-if-no-customize astra-demo-import" href="#" data-import="disabled">
124
- <?php esc_html_e( 'Install Plugins', 'astra-sites' ); ?>
125
- </a>
126
- </div>
127
- <button type="button" class="collapse-sidebar button" aria-expanded="true"
128
- aria-label="Collapse Sidebar">
129
- <span class="collapse-sidebar-arrow"></span>
130
- <span class="collapse-sidebar-label"><?php esc_html_e( 'Collapse', 'astra-sites' ); ?></span>
131
- </button>
132
-
133
- <div class="devices-wrapper">
134
- <div class="devices">
135
- <button type="button" class="preview-desktop active" aria-pressed="true" data-device="desktop">
136
- <span class="screen-reader-text"><?php _e( 'Enter desktop preview mode', 'astra-sites' ); ?></span>
137
- </button>
138
- <button type="button" class="preview-tablet" aria-pressed="false" data-device="tablet">
139
- <span class="screen-reader-text"><?php _e( 'Enter tablet preview mode', 'astra-sites' ); ?></span>
140
- </button>
141
- <button type="button" class="preview-mobile" aria-pressed="false" data-device="mobile">
142
- <span class="screen-reader-text"><?php _e( 'Enter mobile preview mode', 'astra-sites' ); ?></span>
143
- </button>
144
- </div>
145
- </div>
146
-
147
- </div>
148
- </div>
149
- <div class="wp-full-overlay-main">
150
- <iframe src="{{{data.astra_demo_url}}}" title="<?php esc_attr_e( 'Preview', 'astra-sites' ); ?>"></iframe>
151
- </div>
152
- </div>
153
- </script>
154
-
155
- <?php
156
- /**
157
- * TMPL - No more demos
158
- */
159
- ?>
160
- <script type="text/template" id="tmpl-astra-site-api-request-failed">
161
- <div class="no-themes">
162
- <?php
163
-
164
- /* translators: %1$s & %2$s are a Demo API URL */
165
- printf( __( '<p> It seems the demo data server, <i><a href="%1$s">%2$s</a></i> is unreachable from your site.</p>', 'astra-sites' ), esc_url( Astra_Sites::$api_url ), esc_url( Astra_Sites::$api_url ) );
166
-
167
- _e( '<p class="left-margin"> 1. Sometimes, simple page reload fixes any temporary issues. No kidding!</p>', 'astra-sites' );
168
-
169
- _e( '<p class="left-margin"> 2. If that does not work, you will need to talk to your server administrator and check if demo server is being blocked by the firewall!</p>', 'astra-sites' );
170
-
171
- /* translators: %1$s is a support link */
172
- printf( __( '<p>If that does not help, please open up a <a href="%1$s" target="_blank">Support Ticket</a> and we will be glad take a closer look for you.</p>', 'astra-sites' ), esc_url( 'https://wpastra.com/support/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=api-request-failed' ) );
173
- ?>
174
- </div>
175
- </script>
176
-
177
- <?php
178
- /**
179
- * TMPL - Filters
180
- */
181
- ?>
182
- <script type="text/template" id="tmpl-astra-site-filters">
183
-
184
- <# if ( data ) { #>
185
-
186
- <ul class="{{ data.args.wrapper_class }} {{ data.args.class }}">
187
-
188
- <# if ( data.args.show_all ) { #>
189
- <li>
190
- <a href="#" data-group="all"> All </a>
191
- </li>
192
- <# } #>
193
-
194
- <# for ( key in data.items ) { #>
195
- <# if ( data.items[ key ].count ) { #>
196
- <li>
197
- <a href="#" data-group='{{ data.items[ key ].id }}' class="{{ data.items[ key ].name }}">
198
- {{ data.items[ key ].name }}
199
- </a>
200
- </li>
201
- <# } #>
202
- <# } #>
203
-
204
- </ul>
205
- <# } #>
206
- </script>
207
-
208
- <?php
209
- /**
210
- * TMPL - List
211
- */
212
- ?>
213
- <script type="text/template" id="tmpl-astra-sites-list">
214
-
215
- <# if ( data.items.length ) { #>
216
- <# for ( key in data.items ) { #>
217
-
218
- <div class="theme astra-theme site-single {{ data.items[ key ].status }}" tabindex="0" aria-describedby="astra-theme-action astra-theme-name"
219
- data-demo-id="{{{ data.items[ key ].id }}}"
220
- data-demo-type="{{{ data.items[ key ]['astra-site-type'] }}}"
221
- data-demo-url="{{{ data.items[ key ]['astra-site-url'] }}}"
222
- data-demo-api="{{{ data.items[ key ]['_links']['self'][0]['href'] }}}"
223
- data-demo-name="{{{ data.items[ key ].title.rendered }}}"
224
- data-demo-slug="{{{ data.items[ key ].slug }}}"
225
- data-screenshot="{{{ data.items[ key ]['featured-image-url'] }}}"
226
- data-content="{{{ data.items[ key ].content.rendered }}}"
227
- data-required-plugins="{{ JSON.stringify( data.items[ key ]['required-plugins'] ) }}"
228
- data-groups=["{{ data.items[ key ].tags }}"]>
229
- <input type="hidden" class="astra-site-options" value="{{ JSON.stringify(data.items[ key ]['astra-site-options-data'] ) }}" />
230
- <input type="hidden" class="astra-enabled-extensions" value="{{ JSON.stringify(data.items[ key ]['astra-enabled-extensions'] ) }}" />
231
-
232
- <div class="inner">
233
- <span class="site-preview" data-href="{{ data.items[ key ]['astra-site-url'] }}?TB_iframe=true&width=600&height=550" data-title="{{ data.items[ key ].title.rendered }}">
234
- <div class="theme-screenshot">
235
- <# if( '' !== data.items[ key ]['featured-image-url'] ) { #>
236
- <img src="{{ data.items[ key ]['featured-image-url'] }}" />
237
- <# } #>
238
- </div>
239
- </span>
240
- <span class="more-details"> <?php esc_html_e( 'Details &amp; Preview', 'astra-sites' ); ?> </span>
241
- <# if ( data.items[ key ]['astra-site-type'] ) { #>
242
- <# var type = ( data.items[ key ]['astra-site-type'] !== 'premium' ) ? ( data.items[ key ]['astra-site-type'] ) : 'agency'; #>
243
- <span class="site-type {{data.items[ key ]['astra-site-type']}}">{{ type }}</span>
244
- <# } #>
245
- <# if ( data.items[ key ].status ) { #>
246
- <span class="status {{data.items[ key ].status}}">{{data.items[ key ].status}}</span>
247
- <# } #>
248
- <div class="theme-id-container">
249
- <h3 class="theme-name" id="astra-theme-name"> {{{ data.items[ key ].title.rendered }}} </h3>
250
- <div class="theme-actions">
251
- <button class="button preview install-theme-preview"><?php esc_html_e( 'Preview', 'astra-sites' ); ?></button>
252
- </div>
253
- </div>
254
- </div>
255
- </div>
256
- <# } #>
257
- <# } else { #>
258
- <p class="no-themes" style="display:block;">
259
- <?php _e( 'No Demos found, Try a different search.', 'astra-sites' ); ?>
260
- <span class="description">
261
- <?php
262
- /* translators: %1$s External Link */
263
- printf( __( 'Don\'t see a site that you would like to import?<br><a target="_blank" href="%1$s">Please suggest us!</a>', 'astra-sites' ), esc_url( 'https://wpastra.com/sites-suggestions/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=suggestions' ) );
264
- ?>
265
- </span>
266
- </p>
267
- <# } #>
268
- </script>
269
-
270
- <?php
271
- /**
272
- * TMPL - List
273
- */
274
- ?>
275
- <script type="text/template" id="tmpl-astra-sites-suggestions">
276
- <div class="theme astra-theme site-single astra-sites-suggestions">
277
- <div class="inner">
278
- <p>
279
- <?php
280
- /* translators: %1$s External Link */
281
- printf( __( 'Don\'t see a site that you would like to import?<br><a target="_blank" href="%1$s">Please suggest us!</a>', 'astra-sites' ), esc_url( 'https://wpastra.com/sites-suggestions/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=suggestions' ) );
282
- ?>
283
- </p>
284
- </div>
285
- </div>
286
- </script>
287
- <?php
288
- wp_print_admin_notice_templates();
1
+ <?php
2
+ /**
3
+ * Shortcode Markup
4
+ *
5
+ * TMPL - Single Demo Preview
6
+ * TMPL - No more demos
7
+ * TMPL - Filters
8
+ * TMPL - List
9
+ *
10
+ * @package Astra Sites
11
+ * @since 1.0.0
12
+ */
13
+
14
+ defined( 'ABSPATH' ) or exit;
15
+
16
+ ?>
17
+
18
+ <div class="wrap" id="astra-sites-admin">
19
+
20
+ <div id="astra-sites-filters" class="wp-filter hide-if-no-js">
21
+
22
+ <div class="section-left">
23
+
24
+ <!-- All Filters -->
25
+ <div class="filter-count">
26
+ <span class="count"></span>
27
+ </div>
28
+ <div class="filters-wrap">
29
+ <div id="astra-site-page-builder"></div>
30
+ </div>
31
+
32
+ </div>
33
+
34
+ <div class="section-right">
35
+
36
+ <div class="filters-wrap">
37
+ <div id="astra-site-category"></div>
38
+ </div>
39
+
40
+ <div class="search-form">
41
+ <label class="screen-reader-text" for="wp-filter-search-input"><?php _e( 'Search Sites', 'astra-sites' ); ?> </label>
42
+ <input placeholder="<?php _e( 'Search Sites...', 'astra-sites' ); ?>" type="search" aria-describedby="live-search-desc" id="wp-filter-search-input" class="wp-filter-search">
43
+ </div>
44
+
45
+ </div>
46
+
47
+ </div>
48
+
49
+ <?php do_action( 'astra_sites_before_site_grid' ); ?>
50
+
51
+ <div class="theme-browser rendered">
52
+ <div id="astra-sites" class="themes wp-clearfix"></div>
53
+ </div>
54
+
55
+ <div class="select-page-builder">
56
+ <div class="note-wrap">
57
+ <h3>
58
+ <span class="up-arrow dashicons dashicons-editor-break"></span>
59
+ <div class="note"><?php _e( 'Select Your Favorite Page Builder', 'astra-sites' ); ?></div>
60
+ </h3>
61
+ </div>
62
+ <img src="<?php echo esc_url( ASTRA_SITES_URI . 'inc/assets/images/sites-screenshot.jpg' ); ?>" alt="<?php _e( 'Sites List..', 'astra-sites' ); ?>" title="<?php _e( 'Sites List..', 'astra-sites' ); ?>" />
63
+ </div>
64
+
65
+ <div class="spinner-wrap">
66
+ <span class="spinner"></span>
67
+ </div>
68
+
69
+ <?php do_action( 'astra_sites_after_site_grid' ); ?>
70
+
71
+ </div>
72
+
73
+ <?php
74
+ /**
75
+ * TMPL - Single Demo Preview
76
+ */
77
+ ?>
78
+ <script type="text/template" id="tmpl-astra-site-preview">
79
+ <div class="astra-sites-preview theme-install-overlay wp-full-overlay expanded">
80
+ <div class="wp-full-overlay-sidebar">
81
+ <div class="wp-full-overlay-header"
82
+ data-demo-id="{{{data.id}}}"
83
+ data-demo-type="{{{data.astra_demo_type}}}"
84
+ data-demo-url="{{{data.astra_demo_url}}}"
85
+ data-demo-api="{{{data.demo_api}}}"
86
+ data-demo-name="{{{data.demo_name}}}"
87
+ data-demo-slug="{{{data.slug}}}"
88
+ data-screenshot="{{{data.screenshot}}}"
89
+ data-content="{{{data.content}}}"
90
+ data-required-plugins="{{data.required_plugins}}">
91
+ <input type="hidden" class="astra-site-options" value="{{data.astra_site_options}}" >
92
+ <input type="hidden" class="astra-enabled-extensions" value="{{data.astra_enabled_extensions}}" >
93
+ <button class="close-full-overlay"><span class="screen-reader-text"><?php esc_html_e( 'Close', 'astra-sites' ); ?></span></button>
94
+ <button class="previous-theme"><span class="screen-reader-text"><?php esc_html_e( 'Previous', 'astra-sites' ); ?></span></button>
95
+ <button class="next-theme"><span class="screen-reader-text"><?php esc_html_e( 'Next', 'astra-sites' ); ?></span></button>
96
+ <a class="button hide-if-no-customize astra-demo-import" href="#" data-import="disabled"><?php esc_html_e( 'Install Plugins', 'astra-sites' ); ?></a>
97
+
98
+ </div>
99
+ <div class="wp-full-overlay-sidebar-content">
100
+ <div class="install-theme-info">
101
+
102
+ <span class="site-type {{{data.astra_demo_type}}}">{{{data.astra_demo_type}}}</span>
103
+ <h3 class="theme-name">{{{data.demo_name}}}</h3>
104
+
105
+ <# if ( data.screenshot.length ) { #>
106
+ <img class="theme-screenshot" src="{{{data.screenshot}}}" alt="">
107
+ <# } #>
108
+
109
+ <div class="theme-details">
110
+ {{{data.content}}}
111
+ </div>
112
+ <a href="#" class="theme-details-read-more"><?php _e( 'Read more', 'astra-sites' ); ?> &hellip;</a>
113
+
114
+ <div class="required-plugins-wrap">
115
+ <h4><?php _e( 'Required Plugins', 'astra-sites' ); ?> </h4>
116
+ <div class="required-plugins"></div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="wp-full-overlay-footer">
122
+ <div class="footer-import-button-wrap">
123
+ <a class="button button-hero hide-if-no-customize astra-demo-import" href="#" data-import="disabled">
124
+ <?php esc_html_e( 'Install Plugins', 'astra-sites' ); ?>
125
+ </a>
126
+ </div>
127
+ <button type="button" class="collapse-sidebar button" aria-expanded="true"
128
+ aria-label="Collapse Sidebar">
129
+ <span class="collapse-sidebar-arrow"></span>
130
+ <span class="collapse-sidebar-label"><?php esc_html_e( 'Collapse', 'astra-sites' ); ?></span>
131
+ </button>
132
+
133
+ <div class="devices-wrapper">
134
+ <div class="devices">
135
+ <button type="button" class="preview-desktop active" aria-pressed="true" data-device="desktop">
136
+ <span class="screen-reader-text"><?php _e( 'Enter desktop preview mode', 'astra-sites' ); ?></span>
137
+ </button>
138
+ <button type="button" class="preview-tablet" aria-pressed="false" data-device="tablet">
139
+ <span class="screen-reader-text"><?php _e( 'Enter tablet preview mode', 'astra-sites' ); ?></span>
140
+ </button>
141
+ <button type="button" class="preview-mobile" aria-pressed="false" data-device="mobile">
142
+ <span class="screen-reader-text"><?php _e( 'Enter mobile preview mode', 'astra-sites' ); ?></span>
143
+ </button>
144
+ </div>
145
+ </div>
146
+
147
+ </div>
148
+ </div>
149
+ <div class="wp-full-overlay-main">
150
+ <iframe src="{{{data.astra_demo_url}}}" title="<?php esc_attr_e( 'Preview', 'astra-sites' ); ?>"></iframe>
151
+ </div>
152
+ </div>
153
+ </script>
154
+
155
+ <?php
156
+ /**
157
+ * TMPL - No more demos
158
+ */
159
+ ?>
160
+ <script type="text/template" id="tmpl-astra-site-api-request-failed">
161
+ <div class="no-themes">
162
+ <?php
163
+
164
+ /* translators: %1$s & %2$s are a Demo API URL */
165
+ printf( __( '<p> It seems the demo data server, <i><a href="%1$s">%2$s</a></i> is unreachable from your site.</p>', 'astra-sites' ), esc_url( Astra_Sites::$api_url ), esc_url( Astra_Sites::$api_url ) );
166
+
167
+ _e( '<p class="left-margin"> 1. Sometimes, simple page reload fixes any temporary issues. No kidding!</p>', 'astra-sites' );
168
+
169
+ _e( '<p class="left-margin"> 2. If that does not work, you will need to talk to your server administrator and check if demo server is being blocked by the firewall!</p>', 'astra-sites' );
170
+
171
+ /* translators: %1$s is a support link */
172
+ printf( __( '<p>If that does not help, please open up a <a href="%1$s" target="_blank">Support Ticket</a> and we will be glad take a closer look for you.</p>', 'astra-sites' ), esc_url( 'https://wpastra.com/support/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=api-request-failed' ) );
173
+ ?>
174
+ </div>
175
+ </script>
176
+
177
+ <?php
178
+ /**
179
+ * TMPL - Filters
180
+ */
181
+ ?>
182
+ <script type="text/template" id="tmpl-astra-site-filters">
183
+
184
+ <# if ( data ) { #>
185
+
186
+ <ul class="{{ data.args.wrapper_class }} {{ data.args.class }}">
187
+
188
+ <# if ( data.args.show_all ) { #>
189
+ <li>
190
+ <a href="#" data-group="all"> All </a>
191
+ </li>
192
+ <# } #>
193
+
194
+ <# for ( key in data.items ) { #>
195
+ <# if ( data.items[ key ].count ) { #>
196
+ <li>
197
+ <a href="#" data-group='{{ data.items[ key ].id }}' class="{{ data.items[ key ].name }}">
198
+ {{ data.items[ key ].name }}
199
+ </a>
200
+ </li>
201
+ <# } #>
202
+ <# } #>
203
+
204
+ </ul>
205
+ <# } #>
206
+ </script>
207
+
208
+ <?php
209
+ /**
210
+ * TMPL - List
211
+ */
212
+ ?>
213
+ <script type="text/template" id="tmpl-astra-sites-list">
214
+
215
+ <# if ( data.items.length ) { #>
216
+ <# for ( key in data.items ) { #>
217
+
218
+ <div class="theme astra-theme site-single {{ data.items[ key ].status }}" tabindex="0" aria-describedby="astra-theme-action astra-theme-name"
219
+ data-demo-id="{{{ data.items[ key ].id }}}"
220
+ data-demo-type="{{{ data.items[ key ]['astra-site-type'] }}}"
221
+ data-demo-url="{{{ data.items[ key ]['astra-site-url'] }}}"
222
+ data-demo-api="{{{ data.items[ key ]['_links']['self'][0]['href'] }}}"
223
+ data-demo-name="{{{ data.items[ key ].title.rendered }}}"
224
+ data-demo-slug="{{{ data.items[ key ].slug }}}"
225
+ data-screenshot="{{{ data.items[ key ]['featured-image-url'] }}}"
226
+ data-content="{{{ data.items[ key ].content.rendered }}}"
227
+ data-required-plugins="{{ JSON.stringify( data.items[ key ]['required-plugins'] ) }}"
228
+ data-groups=["{{ data.items[ key ].tags }}"]>
229
+ <input type="hidden" class="astra-site-options" value="{{ JSON.stringify(data.items[ key ]['astra-site-options-data'] ) }}" />
230
+ <input type="hidden" class="astra-enabled-extensions" value="{{ JSON.stringify(data.items[ key ]['astra-enabled-extensions'] ) }}" />
231
+
232
+ <div class="inner">
233
+ <span class="site-preview" data-href="{{ data.items[ key ]['astra-site-url'] }}?TB_iframe=true&width=600&height=550" data-title="{{ data.items[ key ].title.rendered }}">
234
+ <div class="theme-screenshot">
235
+ <# if( '' !== data.items[ key ]['featured-image-url'] ) { #>
236
+ <img src="{{ data.items[ key ]['featured-image-url'] }}" />
237
+ <# } #>
238
+ </div>
239
+ </span>
240
+ <span class="more-details"> <?php esc_html_e( 'Details &amp; Preview', 'astra-sites' ); ?> </span>
241
+ <# if ( data.items[ key ]['astra-site-type'] ) { #>
242
+ <# var type = ( data.items[ key ]['astra-site-type'] !== 'premium' ) ? ( data.items[ key ]['astra-site-type'] ) : 'agency'; #>
243
+ <span class="site-type {{data.items[ key ]['astra-site-type']}}">{{ type }}</span>
244
+ <# } #>
245
+ <# if ( data.items[ key ].status ) { #>
246
+ <span class="status {{data.items[ key ].status}}">{{data.items[ key ].status}}</span>
247
+ <# } #>
248
+ <div class="theme-id-container">
249
+ <h3 class="theme-name" id="astra-theme-name"> {{{ data.items[ key ].title.rendered }}} </h3>
250
+ <div class="theme-actions">
251
+ <button class="button preview install-theme-preview"><?php esc_html_e( 'Preview', 'astra-sites' ); ?></button>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ <# } #>
257
+ <# } else { #>
258
+ <p class="no-themes" style="display:block;">
259
+ <?php _e( 'No Demos found, Try a different search.', 'astra-sites' ); ?>
260
+ <span class="description">
261
+ <?php
262
+ /* translators: %1$s External Link */
263
+ printf( __( 'Don\'t see a site that you would like to import?<br><a target="_blank" href="%1$s">Please suggest us!</a>', 'astra-sites' ), esc_url( 'https://wpastra.com/sites-suggestions/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=suggestions' ) );
264
+ ?>
265
+ </span>
266
+ </p>
267
+ <# } #>
268
+ </script>
269
+
270
+ <?php
271
+ /**
272
+ * TMPL - List
273
+ */
274
+ ?>
275
+ <script type="text/template" id="tmpl-astra-sites-suggestions">
276
+ <div class="theme astra-theme site-single astra-sites-suggestions">
277
+ <div class="inner">
278
+ <p>
279
+ <?php
280
+ /* translators: %1$s External Link */
281
+ printf( __( 'Don\'t see a site that you would like to import?<br><a target="_blank" href="%1$s">Please suggest us!</a>', 'astra-sites' ), esc_url( 'https://wpastra.com/sites-suggestions/?utm_source=demo-import-panel&utm_campaign=astra-sites&utm_medium=suggestions' ) );
282
+ ?>
283
+ </p>
284
+ </div>
285
+ </div>
286
+ </script>
287
+ <?php
288
+ wp_print_admin_notice_templates();
languages/astra-sites.pot CHANGED
@@ -1,624 +1,625 @@
1
- # Copyright (C) 2018 Brainstorm Force
2
- # This file is distributed under the same license as the Astra Starter Sites package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: Astra Starter Sites 1.1.9\n"
6
- "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/astra-sites\n"
7
- "POT-Creation-Date: 2018-03-12 13:23:13+00:00\n"
8
- "MIME-Version: 1.0\n"
9
- "Content-Type: text/plain; charset=utf-8\n"
10
- "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2018-MO-DA HO:MI+ZONE\n"
12
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
- "Language-Team: LANGUAGE <LL@li.org>\n"
14
- "Language: en\n"
15
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
16
- "X-Poedit-Country: United States\n"
17
- "X-Poedit-SourceCharset: UTF-8\n"
18
- "X-Poedit-KeywordsList: "
19
- "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
20
- "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
21
- "X-Poedit-Basepath: ../\n"
22
- "X-Poedit-SearchPath-0: .\n"
23
- "X-Poedit-Bookmarks: \n"
24
- "X-Textdomain-Support: yes\n"
25
- "X-Generator: grunt-wp-i18n1.0.0\n"
26
-
27
- #: astra-sites.php:18 inc/classes/class-astra-sites-page.php:85
28
- msgid "Astra Sites"
29
- msgstr ""
30
-
31
- #: inc/classes/class-astra-sites-importer-log.php:351
32
- msgid "Enabled"
33
- msgstr ""
34
-
35
- #: inc/classes/class-astra-sites-importer-log.php:354
36
- msgid "Disabled"
37
- msgstr ""
38
-
39
- #: inc/classes/class-astra-sites-importer-log.php:481
40
- msgid "Yes"
41
- msgstr ""
42
-
43
- #: inc/classes/class-astra-sites-importer-log.php:484
44
- msgid "No"
45
- msgstr ""
46
-
47
- #: inc/classes/class-astra-sites-importer.php:82
48
- msgid "You have not \"customize\" access to import the Astra site."
49
- msgstr ""
50
-
51
- #: inc/classes/class-astra-sites-importer.php:106
52
- msgid "Request site API URL is empty. Try again!"
53
- msgstr ""
54
-
55
- #: inc/classes/class-astra-sites-importer.php:129
56
- msgid "Customizer data is empty!"
57
- msgstr ""
58
-
59
- #: inc/classes/class-astra-sites-importer.php:158
60
- msgid "There was an error downloading the XML file."
61
- msgstr ""
62
-
63
- #: inc/classes/class-astra-sites-importer.php:164
64
- msgid "Invalid site XML file!"
65
- msgstr ""
66
-
67
- #: inc/classes/class-astra-sites-importer.php:186
68
- msgid "Site options are empty!"
69
- msgstr ""
70
-
71
- #: inc/classes/class-astra-sites-importer.php:208
72
- msgid "Widget data is empty!"
73
- msgstr ""
74
-
75
- #: inc/classes/class-astra-sites-page.php:192
76
- msgid "Settings saved successfully."
77
- msgstr ""
78
-
79
- #: inc/classes/class-astra-sites-white-label.php:176
80
- #. translators: %1$s product name
81
- msgid "%1$s Branding"
82
- msgstr ""
83
-
84
- #: inc/classes/class-astra-sites.php:81
85
- #. translators: 1: theme.php file
86
- msgid ""
87
- "Astra Theme needs to be active for you to use currently installed \"%1$s\" "
88
- "plugin. <a href=\"%2$s\">Install & Activate Now</a>"
89
- msgstr ""
90
-
91
- #: inc/classes/class-astra-sites.php:121
92
- msgid "See Library"
93
- msgstr ""
94
-
95
- #: inc/classes/class-astra-sites.php:172
96
- msgid "Page Builder"
97
- msgstr ""
98
-
99
- #: inc/classes/class-astra-sites.php:177
100
- msgid "Categories"
101
- msgstr ""
102
-
103
- #: inc/classes/class-astra-sites.php:210
104
- msgid "Purchase"
105
- msgstr ""
106
-
107
- #: inc/classes/class-astra-sites.php:212
108
- msgid "Upgrade"
109
- msgstr ""
110
-
111
- #: inc/classes/class-astra-sites.php:217
112
- msgid "Error!"
113
- msgstr ""
114
-
115
- #: inc/classes/class-astra-sites.php:218
116
- msgid "Error! Read Possibilities."
117
- msgstr ""
118
-
119
- #: inc/classes/class-astra-sites.php:220
120
- msgid "Done! View Site"
121
- msgstr ""
122
-
123
- #: inc/classes/class-astra-sites.php:221
124
- msgid "Activating"
125
- msgstr ""
126
-
127
- #: inc/classes/class-astra-sites.php:222
128
- msgid "Active"
129
- msgstr ""
130
-
131
- #: inc/classes/class-astra-sites.php:223
132
- msgid "Import failed."
133
- msgstr ""
134
-
135
- #: inc/classes/class-astra-sites.php:224
136
- msgid "Import failed. See error log."
137
- msgstr ""
138
-
139
- #: inc/classes/class-astra-sites.php:225
140
- msgid "Import This Site"
141
- msgstr ""
142
-
143
- #: inc/classes/class-astra-sites.php:226 inc/classes/class-astra-sites.php:243
144
- msgid "Importing.."
145
- msgstr ""
146
-
147
- #: inc/classes/class-astra-sites.php:227 inc/includes/admin-page.php:112
148
- msgid "Read more"
149
- msgstr ""
150
-
151
- #: inc/classes/class-astra-sites.php:228
152
- msgid "Hide"
153
- msgstr ""
154
-
155
- #: inc/classes/class-astra-sites.php:229
156
- msgid "There was a problem receiving a response from server."
157
- msgstr ""
158
-
159
- #: inc/classes/class-astra-sites.php:230 inc/includes/admin-page.php:259
160
- msgid "No Demos found, Try a different search."
161
- msgstr ""
162
-
163
- #: inc/classes/class-astra-sites.php:231
164
- msgid ""
165
- "Executing Demo Import will make your site similar as ours. Please bear in "
166
- "mind -\n"
167
- "\n"
168
- "1. It is recommended to run import on a fresh WordPress installation.\n"
169
- "\n"
170
- "2. Importing site does not delete any pages or posts. However, it can "
171
- "overwrite your existing content.\n"
172
- "\n"
173
- "3. Copyrighted media will not be imported. Instead it will be replaced with "
174
- "placeholders."
175
- msgstr ""
176
-
177
- #: inc/classes/class-astra-sites.php:234
178
- msgid "Installing plugin "
179
- msgstr ""
180
-
181
- #: inc/classes/class-astra-sites.php:235
182
- msgid "Successfully plugin installed!"
183
- msgstr ""
184
-
185
- #: inc/classes/class-astra-sites.php:236
186
- msgid "Activating plugin "
187
- msgstr ""
188
-
189
- #: inc/classes/class-astra-sites.php:237
190
- msgid "Successfully plugin activated "
191
- msgstr ""
192
-
193
- #: inc/classes/class-astra-sites.php:238
194
- msgid "Bulk plugin activation..."
195
- msgstr ""
196
-
197
- #: inc/classes/class-astra-sites.php:239
198
- msgid "Successfully plugin activate - "
199
- msgstr ""
200
-
201
- #: inc/classes/class-astra-sites.php:240
202
- msgid "Error! While activating plugin - "
203
- msgstr ""
204
-
205
- #: inc/classes/class-astra-sites.php:241
206
- msgid "Bulk plugin installation..."
207
- msgstr ""
208
-
209
- #: inc/classes/class-astra-sites.php:242
210
- msgid "Site API "
211
- msgstr ""
212
-
213
- #: inc/classes/class-astra-sites.php:244
214
- msgid "Processing requests..."
215
- msgstr ""
216
-
217
- #: inc/classes/class-astra-sites.php:245
218
- msgid "1) Importing \"Customizer Settings\"..."
219
- msgstr ""
220
-
221
- #: inc/classes/class-astra-sites.php:246
222
- msgid "Successfully imported customizer settings!"
223
- msgstr ""
224
-
225
- #: inc/classes/class-astra-sites.php:247
226
- msgid "2) Preparing \"XML\" Data..."
227
- msgstr ""
228
-
229
- #: inc/classes/class-astra-sites.php:248
230
- msgid "Successfully set XML data!"
231
- msgstr ""
232
-
233
- #: inc/classes/class-astra-sites.php:249
234
- msgid "3) Importing \"XML\"..."
235
- msgstr ""
236
-
237
- #: inc/classes/class-astra-sites.php:250
238
- msgid "Successfully imported XML!"
239
- msgstr ""
240
-
241
- #: inc/classes/class-astra-sites.php:251
242
- msgid "4) Importing \"Options\"..."
243
- msgstr ""
244
-
245
- #: inc/classes/class-astra-sites.php:252
246
- msgid "Successfully imported Options!"
247
- msgstr ""
248
-
249
- #: inc/classes/class-astra-sites.php:253
250
- msgid "5) Importing \"Widgets\"..."
251
- msgstr ""
252
-
253
- #: inc/classes/class-astra-sites.php:254
254
- msgid "Successfully imported Widgets!"
255
- msgstr ""
256
-
257
- #: inc/classes/class-astra-sites.php:256
258
- msgid "Site imported successfully! visit : "
259
- msgstr ""
260
-
261
- #: inc/classes/class-astra-sites.php:257
262
- msgid "Getting Site Information.."
263
- msgstr ""
264
-
265
- #: inc/classes/class-astra-sites.php:258
266
- msgid "Importing Customizer Settings.."
267
- msgstr ""
268
-
269
- #: inc/classes/class-astra-sites.php:259
270
- msgid "Setting up import data.."
271
- msgstr ""
272
-
273
- #: inc/classes/class-astra-sites.php:260
274
- msgid "Importing Pages, Posts & Media.."
275
- msgstr ""
276
-
277
- #: inc/classes/class-astra-sites.php:261
278
- msgid "Importing Site Options.."
279
- msgstr ""
280
-
281
- #: inc/classes/class-astra-sites.php:262
282
- msgid "Importing Widgets.."
283
- msgstr ""
284
-
285
- #: inc/classes/class-astra-sites.php:263
286
- msgid "Import Complete.."
287
- msgstr ""
288
-
289
- #: inc/classes/class-astra-sites.php:264
290
- msgid "Previewing "
291
- msgstr ""
292
-
293
- #: inc/classes/class-astra-sites.php:265
294
- msgid "See Error Log &rarr;"
295
- msgstr ""
296
-
297
- #: inc/classes/class-astra-sites.php:300
298
- msgid "No plugin specified"
299
- msgstr ""
300
-
301
- #: inc/classes/class-astra-sites.php:329
302
- msgid "Plugin Successfully Activated"
303
- msgstr ""
304
-
305
- #: inc/importers/batch-processing/class-astra-sites-batch-processing-elementor.php:58
306
- msgid "Remote"
307
- msgstr ""
308
-
309
- #: inc/importers/batch-processing/helpers/class-wp-background-process.php:433
310
- msgid "Every %d Minutes"
311
- msgstr ""
312
-
313
- #: inc/importers/class-widgets-importer.php:83
314
- msgid "Import data could not be read. Please try a different file."
315
- msgstr ""
316
-
317
- #: inc/importers/class-widgets-importer.php:127
318
- msgid "Widget area does not exist in theme (using Inactive)"
319
- msgstr ""
320
-
321
- #: inc/importers/class-widgets-importer.php:149
322
- msgid "Site does not support widget"
323
- msgstr ""
324
-
325
- #: inc/importers/class-widgets-importer.php:185
326
- msgid "Widget already exists"
327
- msgstr ""
328
-
329
- #: inc/importers/class-widgets-importer.php:254
330
- msgid "Imported"
331
- msgstr ""
332
-
333
- #: inc/importers/class-widgets-importer.php:257
334
- msgid "Imported to Inactive"
335
- msgstr ""
336
-
337
- #: inc/importers/class-widgets-importer.php:263
338
- msgid "No Title"
339
- msgstr ""
340
-
341
- #: inc/importers/wxr-importer/class-astra-wxr-importer.php:194
342
- msgid "Import complete!"
343
- msgstr ""
344
-
345
- #: inc/importers/wxr-importer/class-wxr-importer.php:130
346
- msgid "Could not open the file for parsing"
347
- msgstr ""
348
-
349
- #: inc/importers/wxr-importer/class-wxr-importer.php:166
350
- #: inc/importers/wxr-importer/class-wxr-importer.php:282
351
- #: inc/importers/wxr-importer/class-wxr-importer.php:355
352
- msgid ""
353
- "This WXR file (version %1$s) is newer than the importer (version %2$s) and "
354
- "may not be supported. Please consider updating."
355
- msgstr ""
356
-
357
- #: inc/importers/wxr-importer/class-wxr-importer.php:503
358
- msgid "The file does not exist, please try again."
359
- msgstr ""
360
-
361
- #: inc/importers/wxr-importer/class-wxr-importer.php:563
362
- msgid "Invalid author mapping"
363
- msgstr ""
364
-
365
- #: inc/importers/wxr-importer/class-wxr-importer.php:664
366
- msgid "Cannot import auto-draft posts"
367
- msgstr ""
368
-
369
- #: inc/importers/wxr-importer/class-wxr-importer.php:752
370
- msgid "Failed to import \"%1$s\": Invalid post type %2$s"
371
- msgstr ""
372
-
373
- #: inc/importers/wxr-importer/class-wxr-importer.php:762
374
- msgid "%1$s \"%2$s\" already exists."
375
- msgstr ""
376
-
377
- #: inc/importers/wxr-importer/class-wxr-importer.php:856
378
- msgid "Skipping attachment \"%s\", fetching attachments disabled"
379
- msgstr ""
380
-
381
- #: inc/importers/wxr-importer/class-wxr-importer.php:877
382
- msgid "Failed to import \"%1$s\" (%2$s)"
383
- msgstr ""
384
-
385
- #: inc/importers/wxr-importer/class-wxr-importer.php:909
386
- #: inc/importers/wxr-importer/class-wxr-importer.php:1754
387
- msgid "Imported \"%1$s\" (%2$s)"
388
- msgstr ""
389
-
390
- #: inc/importers/wxr-importer/class-wxr-importer.php:914
391
- msgid "Post %1$d remapped to %2$d"
392
- msgstr ""
393
-
394
- #: inc/importers/wxr-importer/class-wxr-importer.php:1061
395
- msgid "Invalid file type"
396
- msgstr ""
397
-
398
- #: inc/importers/wxr-importer/class-wxr-importer.php:1559
399
- msgid "Failed to import user \"%s\""
400
- msgstr ""
401
-
402
- #: inc/importers/wxr-importer/class-wxr-importer.php:1580
403
- msgid "Imported user \"%s\""
404
- msgstr ""
405
-
406
- #: inc/importers/wxr-importer/class-wxr-importer.php:1584
407
- msgid "User %1$d remapped to %2$d"
408
- msgstr ""
409
-
410
- #: inc/importers/wxr-importer/class-wxr-importer.php:1730
411
- msgid "Failed to import %1$s %2$s"
412
- msgstr ""
413
-
414
- #: inc/importers/wxr-importer/class-wxr-importer.php:1759
415
- msgid "Term %1$d remapped to %2$d"
416
- msgstr ""
417
-
418
- #: inc/importers/wxr-importer/class-wxr-importer.php:1812
419
- msgid "Remote server returned %1$d %2$s for %3$s"
420
- msgstr ""
421
-
422
- #: inc/importers/wxr-importer/class-wxr-importer.php:1825
423
- msgid "Remote file is incorrect size"
424
- msgstr ""
425
-
426
- #: inc/importers/wxr-importer/class-wxr-importer.php:1830
427
- msgid "Zero size file downloaded"
428
- msgstr ""
429
-
430
- #: inc/importers/wxr-importer/class-wxr-importer.php:1836
431
- msgid "Remote file is too large, limit is %s"
432
- msgstr ""
433
-
434
- #: inc/importers/wxr-importer/class-wxr-importer.php:1858
435
- msgid "Running post-processing for post %d"
436
- msgstr ""
437
-
438
- #: inc/importers/wxr-importer/class-wxr-importer.php:1871
439
- msgid "Could not find the post parent for \"%1$s\" (post #%2$d)"
440
- msgstr ""
441
-
442
- #: inc/importers/wxr-importer/class-wxr-importer.php:1876
443
- msgid "Post %1$d was imported with parent %2$d, but could not be found"
444
- msgstr ""
445
-
446
- #: inc/importers/wxr-importer/class-wxr-importer.php:1890
447
- msgid "Could not find the author for \"%1$s\" (post #%2$d)"
448
- msgstr ""
449
-
450
- #: inc/importers/wxr-importer/class-wxr-importer.php:1895
451
- msgid "Post %1$d was imported with author \"%2$s\", but could not be found"
452
- msgstr ""
453
-
454
- #: inc/importers/wxr-importer/class-wxr-importer.php:1921
455
- msgid "Post %d was marked for post-processing, but none was required."
456
- msgstr ""
457
-
458
- #: inc/importers/wxr-importer/class-wxr-importer.php:1932
459
- msgid "Could not update \"%1$s\" (post #%2$d) with mapped data"
460
- msgstr ""
461
-
462
- #: inc/importers/wxr-importer/class-wxr-importer.php:1977
463
- msgid "Could not find the menu object for \"%1$s\" (post #%2$d)"
464
- msgstr ""
465
-
466
- #: inc/importers/wxr-importer/class-wxr-importer.php:1982
467
- msgid ""
468
- "Post %1$d was imported with object \"%2$d\" of type \"%3$s\", but could not "
469
- "be found"
470
- msgstr ""
471
-
472
- #: inc/importers/wxr-importer/class-wxr-importer.php:2004
473
- msgid "Could not find the comment parent for comment #%d"
474
- msgstr ""
475
-
476
- #: inc/importers/wxr-importer/class-wxr-importer.php:2008
477
- msgid "Comment %1$d was imported with parent %2$d, but could not be found"
478
- msgstr ""
479
-
480
- #: inc/importers/wxr-importer/class-wxr-importer.php:2022
481
- msgid "Could not find the author for comment #%d"
482
- msgstr ""
483
-
484
- #: inc/importers/wxr-importer/class-wxr-importer.php:2026
485
- msgid "Comment %1$d was imported with author %2$d, but could not be found"
486
- msgstr ""
487
-
488
- #: inc/importers/wxr-importer/class-wxr-importer.php:2043
489
- msgid "Could not update comment #%d with mapped data"
490
- msgstr ""
491
-
492
- #: inc/includes/admin-page.php:41
493
- msgid "Search Sites"
494
- msgstr ""
495
-
496
- #: inc/includes/admin-page.php:42
497
- msgid "Search Sites..."
498
- msgstr ""
499
-
500
- #: inc/includes/admin-page.php:59
501
- msgid "Select Your Favorite Page Builder"
502
- msgstr ""
503
-
504
- #: inc/includes/admin-page.php:62
505
- msgid "Sites List.."
506
- msgstr ""
507
-
508
- #: inc/includes/admin-page.php:93
509
- msgid "Close"
510
- msgstr ""
511
-
512
- #: inc/includes/admin-page.php:94
513
- msgid "Previous"
514
- msgstr ""
515
-
516
- #: inc/includes/admin-page.php:95
517
- msgid "Next"
518
- msgstr ""
519
-
520
- #: inc/includes/admin-page.php:96 inc/includes/admin-page.php:124
521
- msgid "Install Plugins"
522
- msgstr ""
523
-
524
- #: inc/includes/admin-page.php:115
525
- msgid "Required Plugins"
526
- msgstr ""
527
-
528
- #: inc/includes/admin-page.php:130
529
- msgid "Collapse"
530
- msgstr ""
531
-
532
- #: inc/includes/admin-page.php:136
533
- msgid "Enter desktop preview mode"
534
- msgstr ""
535
-
536
- #: inc/includes/admin-page.php:139
537
- msgid "Enter tablet preview mode"
538
- msgstr ""
539
-
540
- #: inc/includes/admin-page.php:142
541
- msgid "Enter mobile preview mode"
542
- msgstr ""
543
-
544
- #: inc/includes/admin-page.php:150 inc/includes/admin-page.php:251
545
- msgid "Preview"
546
- msgstr ""
547
-
548
- #: inc/includes/admin-page.php:165
549
- #. translators: %1$s & %2$s are a Demo API URL
550
- msgid ""
551
- "<p> It seems the demo data server, <i><a href=\"%1$s\">%2$s</a></i> is "
552
- "unreachable from your site.</p>"
553
- msgstr ""
554
-
555
- #: inc/includes/admin-page.php:167
556
- msgid ""
557
- "<p class=\"left-margin\"> 1. Sometimes, simple page reload fixes any "
558
- "temporary issues. No kidding!</p>"
559
- msgstr ""
560
-
561
- #: inc/includes/admin-page.php:169
562
- msgid ""
563
- "<p class=\"left-margin\"> 2. If that does not work, you will need to talk "
564
- "to your server administrator and check if demo server is being blocked by "
565
- "the firewall!</p>"
566
- msgstr ""
567
-
568
- #: inc/includes/admin-page.php:172
569
- #. translators: %1$s is a support link
570
- msgid ""
571
- "<p>If that does not help, please open up a <a href=\"%1$s\" "
572
- "target=\"_blank\">Support Ticket</a> and we will be glad take a closer look "
573
- "for you.</p>"
574
- msgstr ""
575
-
576
- #: inc/includes/admin-page.php:240
577
- msgid "Details &amp; Preview"
578
- msgstr ""
579
-
580
- #: inc/includes/admin-page.php:263 inc/includes/admin-page.php:281
581
- #. translators: %1$s External Link
582
- msgid ""
583
- "Don't see a site that you would like to import?<br><a target=\"_blank\" "
584
- "href=\"%1$s\">Please suggest us!</a>"
585
- msgstr ""
586
-
587
- #: inc/includes/white-label.php:23
588
- msgid "Plugin Name:"
589
- msgstr ""
590
-
591
- #: inc/includes/white-label.php:28
592
- msgid "Plugin Description:"
593
- msgstr ""
594
-
595
- #. Plugin Name of the plugin/theme
596
- msgid "Astra Starter Sites"
597
- msgstr ""
598
-
599
- #. Plugin URI of the plugin/theme
600
- msgid "http://www.wpastra.com/pro/"
601
- msgstr ""
602
-
603
- #. Description of the plugin/theme
604
- msgid "Import free sites build with Astra theme."
605
- msgstr ""
606
-
607
- #. Author of the plugin/theme
608
- msgid "Brainstorm Force"
609
- msgstr ""
610
-
611
- #. Author URI of the plugin/theme
612
- msgid "http://www.brainstormforce.com"
613
- msgstr ""
614
-
615
- #: inc/classes/class-astra-sites-importer-log.php:372
616
- #. translators: %1$s Memory Limit, %2$s Recommended memory limit.
617
- msgctxt "Recommended Memory Limit"
618
- msgid "Current memory limit %1$s. We recommend setting memory to at least %2$s."
619
- msgstr ""
620
-
621
- #: inc/classes/class-astra-sites-importer-log.php:438
622
- msgctxt "PHP Version"
623
- msgid "We recommend to use php 5.4 or higher"
ย 
624
ย  msgstr ""
1
+ # Copyright (C) 2018 Brainstorm Force
2
+ # This file is distributed under the same license as the Astra Starter Sites package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: Astra Starter Sites 1.2.0\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/astra-sites\n"
7
+ "POT-Creation-Date: 2018-03-22 07:15:03+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=utf-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2018-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "Language: en\n"
15
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
16
+ "X-Poedit-Country: United States\n"
17
+ "X-Poedit-SourceCharset: UTF-8\n"
18
+ "X-Poedit-KeywordsList: "
19
+ "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
20
+ "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
21
+ "X-Poedit-Basepath: ../\n"
22
+ "X-Poedit-SearchPath-0: .\n"
23
+ "X-Poedit-Bookmarks: \n"
24
+ "X-Textdomain-Support: yes\n"
25
+ "X-Generator: grunt-wp-i18n1.0.0\n"
26
+
27
+ #: astra-sites.php:18 inc/classes/class-astra-sites-page.php:85
28
+ msgid "Astra Sites"
29
+ msgstr ""
30
+
31
+ #: inc/classes/class-astra-sites-importer-log.php:351
32
+ msgid "Enabled"
33
+ msgstr ""
34
+
35
+ #: inc/classes/class-astra-sites-importer-log.php:354
36
+ msgid "Disabled"
37
+ msgstr ""
38
+
39
+ #: inc/classes/class-astra-sites-importer-log.php:481
40
+ msgid "Yes"
41
+ msgstr ""
42
+
43
+ #: inc/classes/class-astra-sites-importer-log.php:484
44
+ msgid "No"
45
+ msgstr ""
46
+
47
+ #: inc/classes/class-astra-sites-importer.php:82
48
+ msgid "You have not \"customize\" access to import the Astra site."
49
+ msgstr ""
50
+
51
+ #: inc/classes/class-astra-sites-importer.php:106
52
+ msgid "Request site API URL is empty. Try again!"
53
+ msgstr ""
54
+
55
+ #: inc/classes/class-astra-sites-importer.php:129
56
+ msgid "Customizer data is empty!"
57
+ msgstr ""
58
+
59
+ #: inc/classes/class-astra-sites-importer.php:158
60
+ msgid "There was an error downloading the XML file."
61
+ msgstr ""
62
+
63
+ #: inc/classes/class-astra-sites-importer.php:164
64
+ msgid "Invalid site XML file!"
65
+ msgstr ""
66
+
67
+ #: inc/classes/class-astra-sites-importer.php:186
68
+ msgid "Site options are empty!"
69
+ msgstr ""
70
+
71
+ #: inc/classes/class-astra-sites-importer.php:208
72
+ msgid "Widget data is empty!"
73
+ msgstr ""
74
+
75
+ #: inc/classes/class-astra-sites-page.php:192
76
+ msgid "Settings saved successfully."
77
+ msgstr ""
78
+
79
+ #: inc/classes/class-astra-sites-white-label.php:176
80
+ #. translators: %1$s product name
81
+ msgid "%1$s Branding"
82
+ msgstr ""
83
+
84
+ #: inc/classes/class-astra-sites.php:81
85
+ #. translators: 1: theme.php file
86
+ msgid ""
87
+ "Astra Theme needs to be active for you to use currently installed \"%1$s\" "
88
+ "plugin. <a href=\"%2$s\">Install & Activate Now</a>"
89
+ msgstr ""
90
+
91
+ #: inc/classes/class-astra-sites.php:121
92
+ msgid "See Library"
93
+ msgstr ""
94
+
95
+ #: inc/classes/class-astra-sites.php:172
96
+ msgid "Page Builder"
97
+ msgstr ""
98
+
99
+ #: inc/classes/class-astra-sites.php:177
100
+ msgid "Categories"
101
+ msgstr ""
102
+
103
+ #: inc/classes/class-astra-sites.php:210
104
+ msgid "Purchase"
105
+ msgstr ""
106
+
107
+ #: inc/classes/class-astra-sites.php:212
108
+ msgid "Upgrade"
109
+ msgstr ""
110
+
111
+ #: inc/classes/class-astra-sites.php:217
112
+ msgid "Error!"
113
+ msgstr ""
114
+
115
+ #: inc/classes/class-astra-sites.php:218
116
+ msgid "Error! Read Possibilities."
117
+ msgstr ""
118
+
119
+ #: inc/classes/class-astra-sites.php:220
120
+ msgid "Done! View Site"
121
+ msgstr ""
122
+
123
+ #: inc/classes/class-astra-sites.php:221
124
+ msgid "Activating"
125
+ msgstr ""
126
+
127
+ #: inc/classes/class-astra-sites.php:222
128
+ msgid "Active"
129
+ msgstr ""
130
+
131
+ #: inc/classes/class-astra-sites.php:223
132
+ msgid "Import failed."
133
+ msgstr ""
134
+
135
+ #: inc/classes/class-astra-sites.php:224
136
+ msgid "Import failed. See error log."
137
+ msgstr ""
138
+
139
+ #: inc/classes/class-astra-sites.php:225
140
+ msgid "Import This Site"
141
+ msgstr ""
142
+
143
+ #: inc/classes/class-astra-sites.php:226 inc/classes/class-astra-sites.php:243
144
+ msgid "Importing.."
145
+ msgstr ""
146
+
147
+ #: inc/classes/class-astra-sites.php:227 inc/includes/admin-page.php:112
148
+ msgid "Read more"
149
+ msgstr ""
150
+
151
+ #: inc/classes/class-astra-sites.php:228
152
+ msgid "Hide"
153
+ msgstr ""
154
+
155
+ #: inc/classes/class-astra-sites.php:229
156
+ msgid "There was a problem receiving a response from server."
157
+ msgstr ""
158
+
159
+ #: inc/classes/class-astra-sites.php:230 inc/includes/admin-page.php:259
160
+ msgid "No Demos found, Try a different search."
161
+ msgstr ""
162
+
163
+ #: inc/classes/class-astra-sites.php:231
164
+ msgid ""
165
+ "Executing Demo Import will make your site similar as ours. Please bear in "
166
+ "mind -\n"
167
+ "\n"
168
+ "1. It is recommended to run import on a fresh WordPress installation.\n"
169
+ "\n"
170
+ "2. Importing site does not delete any pages or posts. However, it can "
171
+ "overwrite your existing content.\n"
172
+ "\n"
173
+ "3. Copyrighted media will not be imported. Instead it will be replaced with "
174
+ "placeholders."
175
+ msgstr ""
176
+
177
+ #: inc/classes/class-astra-sites.php:234
178
+ msgid "Installing plugin "
179
+ msgstr ""
180
+
181
+ #: inc/classes/class-astra-sites.php:235
182
+ msgid "Successfully plugin installed!"
183
+ msgstr ""
184
+
185
+ #: inc/classes/class-astra-sites.php:236
186
+ msgid "Activating plugin "
187
+ msgstr ""
188
+
189
+ #: inc/classes/class-astra-sites.php:237
190
+ msgid "Successfully plugin activated "
191
+ msgstr ""
192
+
193
+ #: inc/classes/class-astra-sites.php:238
194
+ msgid "Bulk plugin activation..."
195
+ msgstr ""
196
+
197
+ #: inc/classes/class-astra-sites.php:239
198
+ msgid "Successfully plugin activate - "
199
+ msgstr ""
200
+
201
+ #: inc/classes/class-astra-sites.php:240
202
+ msgid "Error! While activating plugin - "
203
+ msgstr ""
204
+
205
+ #: inc/classes/class-astra-sites.php:241
206
+ msgid "Bulk plugin installation..."
207
+ msgstr ""
208
+
209
+ #: inc/classes/class-astra-sites.php:242
210
+ msgid "Site API "
211
+ msgstr ""
212
+
213
+ #: inc/classes/class-astra-sites.php:244
214
+ msgid "Processing requests..."
215
+ msgstr ""
216
+
217
+ #: inc/classes/class-astra-sites.php:245
218
+ msgid "1) Importing \"Customizer Settings\"..."
219
+ msgstr ""
220
+
221
+ #: inc/classes/class-astra-sites.php:246
222
+ msgid "Successfully imported customizer settings!"
223
+ msgstr ""
224
+
225
+ #: inc/classes/class-astra-sites.php:247
226
+ msgid "2) Preparing \"XML\" Data..."
227
+ msgstr ""
228
+
229
+ #: inc/classes/class-astra-sites.php:248
230
+ msgid "Successfully set XML data!"
231
+ msgstr ""
232
+
233
+ #: inc/classes/class-astra-sites.php:249
234
+ msgid "3) Importing \"XML\"..."
235
+ msgstr ""
236
+
237
+ #: inc/classes/class-astra-sites.php:250
238
+ msgid "Successfully imported XML!"
239
+ msgstr ""
240
+
241
+ #: inc/classes/class-astra-sites.php:251
242
+ msgid "4) Importing \"Options\"..."
243
+ msgstr ""
244
+
245
+ #: inc/classes/class-astra-sites.php:252
246
+ msgid "Successfully imported Options!"
247
+ msgstr ""
248
+
249
+ #: inc/classes/class-astra-sites.php:253
250
+ msgid "5) Importing \"Widgets\"..."
251
+ msgstr ""
252
+
253
+ #: inc/classes/class-astra-sites.php:254
254
+ msgid "Successfully imported Widgets!"
255
+ msgstr ""
256
+
257
+ #: inc/classes/class-astra-sites.php:256
258
+ msgid "Site imported successfully! visit : "
259
+ msgstr ""
260
+
261
+ #: inc/classes/class-astra-sites.php:257
262
+ msgid "Getting Site Information.."
263
+ msgstr ""
264
+
265
+ #: inc/classes/class-astra-sites.php:258
266
+ msgid "Importing Customizer Settings.."
267
+ msgstr ""
268
+
269
+ #: inc/classes/class-astra-sites.php:259
270
+ msgid "Setting up import data.."
271
+ msgstr ""
272
+
273
+ #: inc/classes/class-astra-sites.php:260
274
+ msgid "Importing Pages, Posts & Media.."
275
+ msgstr ""
276
+
277
+ #: inc/classes/class-astra-sites.php:261
278
+ msgid "Importing Site Options.."
279
+ msgstr ""
280
+
281
+ #: inc/classes/class-astra-sites.php:262
282
+ msgid "Importing Widgets.."
283
+ msgstr ""
284
+
285
+ #: inc/classes/class-astra-sites.php:263
286
+ msgid "Import Complete.."
287
+ msgstr ""
288
+
289
+ #: inc/classes/class-astra-sites.php:264
290
+ msgid "Previewing "
291
+ msgstr ""
292
+
293
+ #: inc/classes/class-astra-sites.php:265
294
+ msgid "See Error Log &rarr;"
295
+ msgstr ""
296
+
297
+ #: inc/classes/class-astra-sites.php:300
298
+ msgid "No plugin specified"
299
+ msgstr ""
300
+
301
+ #: inc/classes/class-astra-sites.php:329
302
+ msgid "Plugin Successfully Activated"
303
+ msgstr ""
304
+
305
+ #: inc/importers/batch-processing/class-astra-sites-batch-processing-elementor-v2.php:60
306
+ #: inc/importers/batch-processing/class-astra-sites-batch-processing-elementor.php:59
307
+ msgid "Remote"
308
+ msgstr ""
309
+
310
+ #: inc/importers/batch-processing/helpers/class-wp-background-process.php:433
311
+ msgid "Every %d Minutes"
312
+ msgstr ""
313
+
314
+ #: inc/importers/class-widgets-importer.php:83
315
+ msgid "Import data could not be read. Please try a different file."
316
+ msgstr ""
317
+
318
+ #: inc/importers/class-widgets-importer.php:127
319
+ msgid "Widget area does not exist in theme (using Inactive)"
320
+ msgstr ""
321
+
322
+ #: inc/importers/class-widgets-importer.php:149
323
+ msgid "Site does not support widget"
324
+ msgstr ""
325
+
326
+ #: inc/importers/class-widgets-importer.php:185
327
+ msgid "Widget already exists"
328
+ msgstr ""
329
+
330
+ #: inc/importers/class-widgets-importer.php:254
331
+ msgid "Imported"
332
+ msgstr ""
333
+
334
+ #: inc/importers/class-widgets-importer.php:257
335
+ msgid "Imported to Inactive"
336
+ msgstr ""
337
+
338
+ #: inc/importers/class-widgets-importer.php:263
339
+ msgid "No Title"
340
+ msgstr ""
341
+
342
+ #: inc/importers/wxr-importer/class-astra-wxr-importer.php:194
343
+ msgid "Import complete!"
344
+ msgstr ""
345
+
346
+ #: inc/importers/wxr-importer/class-wxr-importer.php:130
347
+ msgid "Could not open the file for parsing"
348
+ msgstr ""
349
+
350
+ #: inc/importers/wxr-importer/class-wxr-importer.php:166
351
+ #: inc/importers/wxr-importer/class-wxr-importer.php:282
352
+ #: inc/importers/wxr-importer/class-wxr-importer.php:355
353
+ msgid ""
354
+ "This WXR file (version %1$s) is newer than the importer (version %2$s) and "
355
+ "may not be supported. Please consider updating."
356
+ msgstr ""
357
+
358
+ #: inc/importers/wxr-importer/class-wxr-importer.php:503
359
+ msgid "The file does not exist, please try again."
360
+ msgstr ""
361
+
362
+ #: inc/importers/wxr-importer/class-wxr-importer.php:563
363
+ msgid "Invalid author mapping"
364
+ msgstr ""
365
+
366
+ #: inc/importers/wxr-importer/class-wxr-importer.php:664
367
+ msgid "Cannot import auto-draft posts"
368
+ msgstr ""
369
+
370
+ #: inc/importers/wxr-importer/class-wxr-importer.php:752
371
+ msgid "Failed to import \"%1$s\": Invalid post type %2$s"
372
+ msgstr ""
373
+
374
+ #: inc/importers/wxr-importer/class-wxr-importer.php:762
375
+ msgid "%1$s \"%2$s\" already exists."
376
+ msgstr ""
377
+
378
+ #: inc/importers/wxr-importer/class-wxr-importer.php:856
379
+ msgid "Skipping attachment \"%s\", fetching attachments disabled"
380
+ msgstr ""
381
+
382
+ #: inc/importers/wxr-importer/class-wxr-importer.php:877
383
+ msgid "Failed to import \"%1$s\" (%2$s)"
384
+ msgstr ""
385
+
386
+ #: inc/importers/wxr-importer/class-wxr-importer.php:909
387
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1754
388
+ msgid "Imported \"%1$s\" (%2$s)"
389
+ msgstr ""
390
+
391
+ #: inc/importers/wxr-importer/class-wxr-importer.php:914
392
+ msgid "Post %1$d remapped to %2$d"
393
+ msgstr ""
394
+
395
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1061
396
+ msgid "Invalid file type"
397
+ msgstr ""
398
+
399
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1559
400
+ msgid "Failed to import user \"%s\""
401
+ msgstr ""
402
+
403
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1580
404
+ msgid "Imported user \"%s\""
405
+ msgstr ""
406
+
407
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1584
408
+ msgid "User %1$d remapped to %2$d"
409
+ msgstr ""
410
+
411
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1730
412
+ msgid "Failed to import %1$s %2$s"
413
+ msgstr ""
414
+
415
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1759
416
+ msgid "Term %1$d remapped to %2$d"
417
+ msgstr ""
418
+
419
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1812
420
+ msgid "Remote server returned %1$d %2$s for %3$s"
421
+ msgstr ""
422
+
423
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1825
424
+ msgid "Remote file is incorrect size"
425
+ msgstr ""
426
+
427
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1830
428
+ msgid "Zero size file downloaded"
429
+ msgstr ""
430
+
431
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1836
432
+ msgid "Remote file is too large, limit is %s"
433
+ msgstr ""
434
+
435
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1858
436
+ msgid "Running post-processing for post %d"
437
+ msgstr ""
438
+
439
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1871
440
+ msgid "Could not find the post parent for \"%1$s\" (post #%2$d)"
441
+ msgstr ""
442
+
443
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1876
444
+ msgid "Post %1$d was imported with parent %2$d, but could not be found"
445
+ msgstr ""
446
+
447
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1890
448
+ msgid "Could not find the author for \"%1$s\" (post #%2$d)"
449
+ msgstr ""
450
+
451
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1895
452
+ msgid "Post %1$d was imported with author \"%2$s\", but could not be found"
453
+ msgstr ""
454
+
455
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1921
456
+ msgid "Post %d was marked for post-processing, but none was required."
457
+ msgstr ""
458
+
459
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1932
460
+ msgid "Could not update \"%1$s\" (post #%2$d) with mapped data"
461
+ msgstr ""
462
+
463
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1977
464
+ msgid "Could not find the menu object for \"%1$s\" (post #%2$d)"
465
+ msgstr ""
466
+
467
+ #: inc/importers/wxr-importer/class-wxr-importer.php:1982
468
+ msgid ""
469
+ "Post %1$d was imported with object \"%2$d\" of type \"%3$s\", but could not "
470
+ "be found"
471
+ msgstr ""
472
+
473
+ #: inc/importers/wxr-importer/class-wxr-importer.php:2004
474
+ msgid "Could not find the comment parent for comment #%d"
475
+ msgstr ""
476
+
477
+ #: inc/importers/wxr-importer/class-wxr-importer.php:2008
478
+ msgid "Comment %1$d was imported with parent %2$d, but could not be found"
479
+ msgstr ""
480
+
481
+ #: inc/importers/wxr-importer/class-wxr-importer.php:2022
482
+ msgid "Could not find the author for comment #%d"
483
+ msgstr ""
484
+
485
+ #: inc/importers/wxr-importer/class-wxr-importer.php:2026
486
+ msgid "Comment %1$d was imported with author %2$d, but could not be found"
487
+ msgstr ""
488
+
489
+ #: inc/importers/wxr-importer/class-wxr-importer.php:2043
490
+ msgid "Could not update comment #%d with mapped data"
491
+ msgstr ""
492
+
493
+ #: inc/includes/admin-page.php:41
494
+ msgid "Search Sites"
495
+ msgstr ""
496
+
497
+ #: inc/includes/admin-page.php:42
498
+ msgid "Search Sites..."
499
+ msgstr ""
500
+
501
+ #: inc/includes/admin-page.php:59
502
+ msgid "Select Your Favorite Page Builder"
503
+ msgstr ""
504
+
505
+ #: inc/includes/admin-page.php:62
506
+ msgid "Sites List.."
507
+ msgstr ""
508
+
509
+ #: inc/includes/admin-page.php:93
510
+ msgid "Close"
511
+ msgstr ""
512
+
513
+ #: inc/includes/admin-page.php:94
514
+ msgid "Previous"
515
+ msgstr ""
516
+
517
+ #: inc/includes/admin-page.php:95
518
+ msgid "Next"
519
+ msgstr ""
520
+
521
+ #: inc/includes/admin-page.php:96 inc/includes/admin-page.php:124
522
+ msgid "Install Plugins"
523
+ msgstr ""
524
+
525
+ #: inc/includes/admin-page.php:115
526
+ msgid "Required Plugins"
527
+ msgstr ""
528
+
529
+ #: inc/includes/admin-page.php:130
530
+ msgid "Collapse"
531
+ msgstr ""
532
+
533
+ #: inc/includes/admin-page.php:136
534
+ msgid "Enter desktop preview mode"
535
+ msgstr ""
536
+
537
+ #: inc/includes/admin-page.php:139
538
+ msgid "Enter tablet preview mode"
539
+ msgstr ""
540
+
541
+ #: inc/includes/admin-page.php:142
542
+ msgid "Enter mobile preview mode"
543
+ msgstr ""
544
+
545
+ #: inc/includes/admin-page.php:150 inc/includes/admin-page.php:251
546
+ msgid "Preview"
547
+ msgstr ""
548
+
549
+ #: inc/includes/admin-page.php:165
550
+ #. translators: %1$s & %2$s are a Demo API URL
551
+ msgid ""
552
+ "<p> It seems the demo data server, <i><a href=\"%1$s\">%2$s</a></i> is "
553
+ "unreachable from your site.</p>"
554
+ msgstr ""
555
+
556
+ #: inc/includes/admin-page.php:167
557
+ msgid ""
558
+ "<p class=\"left-margin\"> 1. Sometimes, simple page reload fixes any "
559
+ "temporary issues. No kidding!</p>"
560
+ msgstr ""
561
+
562
+ #: inc/includes/admin-page.php:169
563
+ msgid ""
564
+ "<p class=\"left-margin\"> 2. If that does not work, you will need to talk "
565
+ "to your server administrator and check if demo server is being blocked by "
566
+ "the firewall!</p>"
567
+ msgstr ""
568
+
569
+ #: inc/includes/admin-page.php:172
570
+ #. translators: %1$s is a support link
571
+ msgid ""
572
+ "<p>If that does not help, please open up a <a href=\"%1$s\" "
573
+ "target=\"_blank\">Support Ticket</a> and we will be glad take a closer look "
574
+ "for you.</p>"
575
+ msgstr ""
576
+
577
+ #: inc/includes/admin-page.php:240
578
+ msgid "Details &amp; Preview"
579
+ msgstr ""
580
+
581
+ #: inc/includes/admin-page.php:263 inc/includes/admin-page.php:281
582
+ #. translators: %1$s External Link
583
+ msgid ""
584
+ "Don't see a site that you would like to import?<br><a target=\"_blank\" "
585
+ "href=\"%1$s\">Please suggest us!</a>"
586
+ msgstr ""
587
+
588
+ #: inc/includes/white-label.php:23
589
+ msgid "Plugin Name:"
590
+ msgstr ""
591
+
592
+ #: inc/includes/white-label.php:28
593
+ msgid "Plugin Description:"
594
+ msgstr ""
595
+
596
+ #. Plugin Name of the plugin/theme
597
+ msgid "Astra Starter Sites"
598
+ msgstr ""
599
+
600
+ #. Plugin URI of the plugin/theme
601
+ msgid "http://www.wpastra.com/pro/"
602
+ msgstr ""
603
+
604
+ #. Description of the plugin/theme
605
+ msgid "Import free sites build with Astra theme."
606
+ msgstr ""
607
+
608
+ #. Author of the plugin/theme
609
+ msgid "Brainstorm Force"
610
+ msgstr ""
611
+
612
+ #. Author URI of the plugin/theme
613
+ msgid "http://www.brainstormforce.com"
614
+ msgstr ""
615
+
616
+ #: inc/classes/class-astra-sites-importer-log.php:372
617
+ #. translators: %1$s Memory Limit, %2$s Recommended memory limit.
618
+ msgctxt "Recommended Memory Limit"
619
+ msgid "Current memory limit %1$s. We recommend setting memory to at least %2$s."
620
+ msgstr ""
621
+
622
+ #: inc/classes/class-astra-sites-importer-log.php:438
623
+ msgctxt "PHP Version"
624
+ msgid "We recommend to use php 5.4 or higher"
625
ย  msgstr ""
readme.txt CHANGED
@@ -1,171 +1,174 @@
1
- === Astra Starter Sites ===
2
- Contributors: brainstormforce
3
- Donate link: https://wpastra.com/pro/
4
- Tags: demo, theme demos, one click import
5
- Requires at least: 4.4
6
- Requires PHP: 5.3
7
- Tested up to: 4.9.1
8
- Stable tag: 1.1.9
9
- License: GPLv2 or later
10
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
-
12
- Import Astra Starter Sites with just one click.
13
-
14
- == Description ==
15
-
16
- This plugin is an add-on for the Astra WordPress Theme. It offers a library of ready sites that can be imported for your website easily. Here is how it works:
17
-
18
- 1. Browse through the library of ready sites right from your WordPress backend.
19
- 2. Pick a site you like.
20
- 3. Install required plugins in one click
21
- 4. Import the site data.
22
- 5. Done ;)
23
-
24
-
25
- Use this imported site as a base for your project and don't waste time starting from scratch!
26
-
27
- _<a href="https://wpastra.com/ready-websites/">See list of all available sites to import ยป</a>_
28
-
29
- #### Video Walkthrough by Adam from WPCrafter:
30
- [youtube https://www.youtube.com/watch?v=zYbz-jxE9_Q]
31
-
32
- == Installation ==
33
-
34
- 1. Upload the plugin files to the `/wp-content/plugins/astra-sites` directory, or install the plugin through the WordPress plugins screen directly.
35
- 2. Activate the plugin through the 'Plugins' screen in WordPress
36
- 3. Use the Appearance->Astra->Astra Free Sites to select the page to be displayed as header and footer.
37
-
38
- == Frequently Asked Questions ==
39
-
40
- = Is this really free? =
41
-
42
- Yup, we have dozens of free websites ready to import! We have a premium version as well that is required for importing premium sites.
43
-
44
- Learn More: https://wpastra.com/agency/
45
-
46
- = Can I suggest new websites that you I add? =
47
-
48
- Sure. We love suggestions! Please submit them here -
49
- https://wpastra.com/sites-suggestions/
50
-
51
-
52
- == Screenshots ==
53
-
54
- 1. Select the demo you want to import.
55
- 2. Install and activate the required plugins.
56
- 3. Import the demo.
57
-
58
- == Changelog ==
59
-
60
- v1.1.9 - 12-March-2018
61
- * Fix: String `Select Your Favorite Page Builder` jerk while loading the sites.
62
-
63
- v1.1.8 - 5-March-2018
64
- * Improvements: Updated processing button animation while installation/activating plugin and importing site.
65
- * Improvements: Updated suggestion box message.
66
- * Fix: PHP fatal errors for WXR importer classes `WXR_Import_Info`, `WXR_Importer`, `WP_Importer_Logger_ServerSentEvents` and `WP_Importer_Logger`.
67
-
68
- v1.1.7 - 2-February-2018
69
- * Improvements: Some users reported confusion in the default option of choosing page builder. We have made UX improvements so users now must select the Page Builder first before selecting any website.
70
-
71
- v1.1.6 - 22-January-2018
72
- * New: Added filter `astra_sites_xml_import_options` to change the XML import options.
73
- * Fix: Astra Pro plugin 'Custom Layouts' & 'Page Headers' not setting right display location due to different page, tax, category ids.
74
- * Fix: WooCommerce shop, checkout cart page ids not setting issue.
75
- * Fix: After site import updated demo url from the nav menus.
76
-
77
- v1.1.5 - 11-January-2018
78
- * New: Added SVG file support for importing the SVG images.
79
-
80
- v1.1.4 - 28-Dec-2017
81
- * Improvement: Importing WooCommerce product category images.
82
- * Improvement: Retain WooCommerce cart, checkout & my account pages when importing the ready WooCommerce sites.
83
- * Fix: Disabled WooCommerce plugin setup wizard after plugin install & activate.
84
-
85
- v1.1.3 - 20-Dec-2017
86
- * Improvement: Retain WooCommerce shop page when importing the ready WooCommerce sites.
87
-
88
- v1.1.2 - 24-Nov-2017
89
- * Fix: Handling plugin installation errors.
90
-
91
- v1.1.1 - 23-Nov-2017
92
- * New: Change the api url for Astra sites to https://websitedemos.net/ from https://sites.wpastra.com/
93
-
94
- v1.1.0 - 21-Nov-2017
95
- * New: Import the site content using Event Source (SSE) which ensures faithful imports.
96
- * New: Divided the site import process in separate AJAX calls to reduce the possibility of timeouts.
97
- * New: Generated the import log file. It will be displayed in the UI if the import fails.
98
- * Improvement: Validate all the possible errors.
99
- * Improvement: Updated Astra sites HTML grid structure for WordPress v4.9 compatibility.
100
- * Enhancement: Updated plugin name from Astra Sites - Lite with Astra Starter Sites.
101
-
102
- v1.0.14 - 9-Nov-2017
103
- * New: All the linked images on the Astra Sites will be downloaded to your site, No more loading images from external URLs.
104
- * New: Added suggestion box at as the last column in when listing sites so that you can add a suggest the sites you want.
105
- * New: Added site responsive preview buttons.
106
- * Improvement: Search string will not be removed when switching the page builder when scrolling through sites.
107
- * Improvement: Loading 15 sites instead of 6 Astra sites in the first load.
108
- * Improvement: Removed LazyLoad which is not useful in admin back-end for showing Astra Sites.
109
-
110
- v1.0.13 - 9-Oct-2017
111
- * New: Browsing the Astra Sites in the Admin panel is not faster with JS rendering.
112
-
113
- v1.0.12 - 29-Sept-2017
114
- * New: Added White Label support from <a href="https://wpastra.com/pro/">Astra Pro</a>.
115
- * Improvement: Don't display sites from both the page builders in the same view.
116
- * Fix: Astra Sites admin area not working in the Firefox.
117
-
118
- v1.0.11 - 22-Sept-2017
119
- * New: Single click Install & activate required plugins.
120
- * New: Added filter `astra_sites_menu_item` for adding extra tabs in admin page.
121
- * New: Added back image import feature for `elementor` page builder. In batch image import we import all images from astra site into client site.
122
- * Improvement: Updated JS code with object prototype.
123
- * Fix: Screen bounce on retina devices.
124
-
125
- v1.0.10 - 11-Sept-2017
126
- * Improvement: Added support for retina logo import.
127
- * Fix: Site logo image not displayed in customizer.
128
- * Fix: Updated `Astra Agency` purchase link.
129
-
130
- v1.0.9 - 8-Sept-2017
131
- * New: Added page builder categories for listing sites as per page builder.
132
-
133
- v1.0.8 - 6-Sept-2017
134
- * Fix: Beaver Builder option import.
135
- * Enhancement: Disabled dismiss-able notice visible once for each user.
136
- * Enhancement: Showing error message for for user who have not `manage_plugins` capability.
137
-
138
- v1.0.7 - 1-Sept-2017
139
- * Fix: Custom Menu widget not setting imported widget.
140
-
141
- v1.0.6 - 30-Aug-2017
142
- * New: Addd custom menu for Astra Sites.
143
- * Fix: Validate site options before storing in database.
144
-
145
- v1.0.5 - 29-Aug-2017
146
- * New: Added filter `astra_sites_api_args` for adding extra arguments in api call.
147
- * Enhancement: Plugin name updated from `Astra Sites` with `Astra Free Sites`.
148
- * Fix: PHP error while ignoring users.
149
-
150
- v1.0.4 - 21-Aug-2017
151
- * New: Added filter `astra_sites_api_params` for adding extra params in api call.
152
- * New: Added filter `astra_sites_api_args` for adding extra arguments in api call.
153
- * New: Added filter `astra_sites_category_hide_empty` for showing categories which are not set for any site.
154
-
155
- v1.0.3 - 11-Aug-2017
156
- * Fix: Avoided Astra users from site import process.
157
-
158
- v1.0.2 - 09-Aug-2017
159
- * Fix: Listing appropriate next and previous Astra sites.
160
- * Enhancement: Listing Astra sites though AJAX API call.
161
-
162
- v1.0.1 - 04-Aug-2017
163
- * New: Added Elementor plugin options support.
164
- * New: Added Customizer CSS support.
165
- * Enhancement: Avoided Lite Plugin version if Pro version is Installed. Now added support for Beaver Builder Plugin (Lite Version).
166
- * Enhancement: Astra sites API call validated before import.
167
- * Enhancement: Site logo imported from Astra sites.
168
- * Fix: Bug where widgets created with SiteOrigin plugin were not being imported.
169
-
170
- v1.0.0
171
- * Initial release
ย 
ย 
ย 
1
+ === Astra Starter Sites ===
2
+ Contributors: brainstormforce
3
+ Donate link: https://wpastra.com/pro/
4
+ Tags: demo, theme demos, one click import
5
+ Requires at least: 4.4
6
+ Requires PHP: 5.3
7
+ Tested up to: 4.9.1
8
+ Stable tag: 1.2.0
9
+ License: GPLv2 or later
10
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ Import Astra Starter Sites with just one click.
13
+
14
+ == Description ==
15
+
16
+ This plugin is an add-on for the Astra WordPress Theme. It offers a library of ready sites that can be imported for your website easily. Here is how it works:
17
+
18
+ 1. Browse through the library of ready sites right from your WordPress backend.
19
+ 2. Pick a site you like.
20
+ 3. Install required plugins in one click
21
+ 4. Import the site data.
22
+ 5. Done ;)
23
+
24
+
25
+ Use this imported site as a base for your project and don't waste time starting from scratch!
26
+
27
+ _<a href="https://wpastra.com/ready-websites/">See list of all available sites to import ยป</a>_
28
+
29
+ #### Video Walkthrough by Adam from WPCrafter:
30
+ [youtube https://www.youtube.com/watch?v=zYbz-jxE9_Q]
31
+
32
+ == Installation ==
33
+
34
+ 1. Upload the plugin files to the `/wp-content/plugins/astra-sites` directory, or install the plugin through the WordPress plugins screen directly.
35
+ 2. Activate the plugin through the 'Plugins' screen in WordPress
36
+ 3. Use the Appearance->Astra->Astra Free Sites to select the page to be displayed as header and footer.
37
+
38
+ == Frequently Asked Questions ==
39
+
40
+ = Is this really free? =
41
+
42
+ Yup, we have dozens of free websites ready to import! We have a premium version as well that is required for importing premium sites.
43
+
44
+ Learn More: https://wpastra.com/agency/
45
+
46
+ = Can I suggest new websites that you I add? =
47
+
48
+ Sure. We love suggestions! Please submit them here -
49
+ https://wpastra.com/sites-suggestions/
50
+
51
+
52
+ == Screenshots ==
53
+
54
+ 1. Select the demo you want to import.
55
+ 2. Install and activate the required plugins.
56
+ 3. Import the demo.
57
+
58
+ == Changelog ==
59
+
60
+ v1.2.0 - 22-March-2018
61
+ * Improvement: Added compatibility for Elemetor version 2.0.0. Older versions throw the PHP warning for function process_element_export_import_content().
62
+
63
+ v1.1.9 - 12-March-2018
64
+ * Fix: String `Select Your Favorite Page Builder` jerk while loading the sites.
65
+
66
+ v1.1.8 - 5-March-2018
67
+ * Improvements: Updated processing button animation while installation/activating plugin and importing site.
68
+ * Improvements: Updated suggestion box message.
69
+ * Fix: PHP fatal errors for WXR importer classes `WXR_Import_Info`, `WXR_Importer`, `WP_Importer_Logger_ServerSentEvents` and `WP_Importer_Logger`.
70
+
71
+ v1.1.7 - 2-February-2018
72
+ * Improvements: Some users reported confusion in the default option of choosing page builder. We have made UX improvements so users now must select the Page Builder first before selecting any website.
73
+
74
+ v1.1.6 - 22-January-2018
75
+ * New: Added filter `astra_sites_xml_import_options` to change the XML import options.
76
+ * Fix: Astra Pro plugin 'Custom Layouts' & 'Page Headers' not setting right display location due to different page, tax, category ids.
77
+ * Fix: WooCommerce shop, checkout cart page ids not setting issue.
78
+ * Fix: After site import updated demo url from the nav menus.
79
+
80
+ v1.1.5 - 11-January-2018
81
+ * New: Added SVG file support for importing the SVG images.
82
+
83
+ v1.1.4 - 28-Dec-2017
84
+ * Improvement: Importing WooCommerce product category images.
85
+ * Improvement: Retain WooCommerce cart, checkout & my account pages when importing the ready WooCommerce sites.
86
+ * Fix: Disabled WooCommerce plugin setup wizard after plugin install & activate.
87
+
88
+ v1.1.3 - 20-Dec-2017
89
+ * Improvement: Retain WooCommerce shop page when importing the ready WooCommerce sites.
90
+
91
+ v1.1.2 - 24-Nov-2017
92
+ * Fix: Handling plugin installation errors.
93
+
94
+ v1.1.1 - 23-Nov-2017
95
+ * New: Change the api url for Astra sites to https://websitedemos.net/ from https://sites.wpastra.com/
96
+
97
+ v1.1.0 - 21-Nov-2017
98
+ * New: Import the site content using Event Source (SSE) which ensures faithful imports.
99
+ * New: Divided the site import process in separate AJAX calls to reduce the possibility of timeouts.
100
+ * New: Generated the import log file. It will be displayed in the UI if the import fails.
101
+ * Improvement: Validate all the possible errors.
102
+ * Improvement: Updated Astra sites HTML grid structure for WordPress v4.9 compatibility.
103
+ * Enhancement: Updated plugin name from Astra Sites - Lite with Astra Starter Sites.
104
+
105
+ v1.0.14 - 9-Nov-2017
106
+ * New: All the linked images on the Astra Sites will be downloaded to your site, No more loading images from external URLs.
107
+ * New: Added suggestion box at as the last column in when listing sites so that you can add a suggest the sites you want.
108
+ * New: Added site responsive preview buttons.
109
+ * Improvement: Search string will not be removed when switching the page builder when scrolling through sites.
110
+ * Improvement: Loading 15 sites instead of 6 Astra sites in the first load.
111
+ * Improvement: Removed LazyLoad which is not useful in admin back-end for showing Astra Sites.
112
+
113
+ v1.0.13 - 9-Oct-2017
114
+ * New: Browsing the Astra Sites in the Admin panel is not faster with JS rendering.
115
+
116
+ v1.0.12 - 29-Sept-2017
117
+ * New: Added White Label support from <a href="https://wpastra.com/pro/">Astra Pro</a>.
118
+ * Improvement: Don't display sites from both the page builders in the same view.
119
+ * Fix: Astra Sites admin area not working in the Firefox.
120
+
121
+ v1.0.11 - 22-Sept-2017
122
+ * New: Single click Install & activate required plugins.
123
+ * New: Added filter `astra_sites_menu_item` for adding extra tabs in admin page.
124
+ * New: Added back image import feature for `elementor` page builder. In batch image import we import all images from astra site into client site.
125
+ * Improvement: Updated JS code with object prototype.
126
+ * Fix: Screen bounce on retina devices.
127
+
128
+ v1.0.10 - 11-Sept-2017
129
+ * Improvement: Added support for retina logo import.
130
+ * Fix: Site logo image not displayed in customizer.
131
+ * Fix: Updated `Astra Agency` purchase link.
132
+
133
+ v1.0.9 - 8-Sept-2017
134
+ * New: Added page builder categories for listing sites as per page builder.
135
+
136
+ v1.0.8 - 6-Sept-2017
137
+ * Fix: Beaver Builder option import.
138
+ * Enhancement: Disabled dismiss-able notice visible once for each user.
139
+ * Enhancement: Showing error message for for user who have not `manage_plugins` capability.
140
+
141
+ v1.0.7 - 1-Sept-2017
142
+ * Fix: Custom Menu widget not setting imported widget.
143
+
144
+ v1.0.6 - 30-Aug-2017
145
+ * New: Addd custom menu for Astra Sites.
146
+ * Fix: Validate site options before storing in database.
147
+
148
+ v1.0.5 - 29-Aug-2017
149
+ * New: Added filter `astra_sites_api_args` for adding extra arguments in api call.
150
+ * Enhancement: Plugin name updated from `Astra Sites` with `Astra Free Sites`.
151
+ * Fix: PHP error while ignoring users.
152
+
153
+ v1.0.4 - 21-Aug-2017
154
+ * New: Added filter `astra_sites_api_params` for adding extra params in api call.
155
+ * New: Added filter `astra_sites_api_args` for adding extra arguments in api call.
156
+ * New: Added filter `astra_sites_category_hide_empty` for showing categories which are not set for any site.
157
+
158
+ v1.0.3 - 11-Aug-2017
159
+ * Fix: Avoided Astra users from site import process.
160
+
161
+ v1.0.2 - 09-Aug-2017
162
+ * Fix: Listing appropriate next and previous Astra sites.
163
+ * Enhancement: Listing Astra sites though AJAX API call.
164
+
165
+ v1.0.1 - 04-Aug-2017
166
+ * New: Added Elementor plugin options support.
167
+ * New: Added Customizer CSS support.
168
+ * Enhancement: Avoided Lite Plugin version if Pro version is Installed. Now added support for Beaver Builder Plugin (Lite Version).
169
+ * Enhancement: Astra sites API call validated before import.
170
+ * Enhancement: Site logo imported from Astra sites.
171
+ * Fix: Bug where widgets created with SiteOrigin plugin were not being imported.
172
+
173
+ v1.0.0
174
+ * Initial release