Starter Templates by Kadence WP - Version 1.0.1

Version Description

  • Fix: Post Sanitize
Download this release

Release Info

Developer britner
Plugin Icon 128x128 Starter Templates by Kadence WP
Version 1.0.1
Comparing to
See all releases

Version 1.0.1

assets/css/main.css ADDED
@@ -0,0 +1,367 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Overriding WordPress native styles */
2
+
3
+ .ocdi {
4
+ max-width: none;
5
+ }
6
+
7
+ .ocdi h2 {
8
+ text-align: inherit;
9
+ }
10
+
11
+ .ocdi h2:first-child,
12
+ .ocdi h3:first-child {
13
+ margin-top: 0;
14
+ }
15
+
16
+ .ocdi hr {
17
+ margin: 2.62em 0;
18
+ }
19
+
20
+ .feature-section + hr {
21
+ margin-top: 0;
22
+ }
23
+
24
+ #wpbody select {
25
+ height: auto;
26
+ padding: .62em;
27
+ line-height: inherit;
28
+ }
29
+
30
+ .ocdi .notice {
31
+ display: block !important;
32
+ }
33
+
34
+ /* Plugin elements */
35
+
36
+ .ocdi__demo-import-files {
37
+ width: 100%;
38
+ }
39
+
40
+ .ocdi__demo-import-preview-image-message {
41
+ font-style: italic;
42
+ }
43
+
44
+ /* Plugin title */
45
+
46
+ .ocdi__title:before {
47
+ width: auto;
48
+ height: auto;
49
+ font-size: inherit;
50
+ }
51
+
52
+ /* Plugin intro text */
53
+
54
+ .ocdi__intro-text ul {
55
+ padding: 0 4%;
56
+ list-style-type: square;
57
+ }
58
+
59
+ /* Plugin multi select import and Plugin file upload containers */
60
+
61
+ .ocdi__file-upload,
62
+ .ocdi__multi-select-import,
63
+ .ocdi__demo-import-notice:not(:empty) {
64
+ padding: 4%;
65
+ margin: 1.62em 0;
66
+ background-color: #ffffff;
67
+ border: 1px solid #e5e5e5;
68
+ }
69
+
70
+ .ocdi__file-upload {
71
+ margin: 0;
72
+ margin-bottom: -1px;
73
+ }
74
+
75
+ .ocdi__file-upload span {
76
+ font-size: .81em;
77
+ font-weight: normal;
78
+ opacity: .66;
79
+ }
80
+
81
+ .ocdi__demo-import-notice:not(:empty) {
82
+ border: 0;
83
+ border-left: 4px solid #00a0d2;
84
+ -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
85
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
86
+ }
87
+
88
+ [dir="rtl"] .ocdi__demo-import-notice:not(:empty) {
89
+ border: 0;
90
+ border-right: 4px solid #00a0d2;
91
+ }
92
+
93
+ /* Plugin button */
94
+
95
+ .ocdi__button-container {
96
+ margin-top: 1.62em;
97
+ }
98
+
99
+ /* AJAX loader */
100
+
101
+ p.ocdi__ajax-loader {
102
+ font-size: 1.5em;
103
+ display: none;
104
+ }
105
+
106
+ .ocdi__ajax-loader .spinner {
107
+ display: inline-block;
108
+ float: none;
109
+ visibility: visible;
110
+ margin-bottom: 6px;
111
+ }
112
+
113
+
114
+ /* New grid layout */
115
+
116
+ .ocdi__gl-navigation li a {
117
+ -webkit-box-shadow: none;
118
+ box-shadow: none;
119
+ }
120
+
121
+ .ocdi__gl-item {
122
+ float: left;
123
+ width: 100%;
124
+ margin: 0 0 20px 0;
125
+ position: relative;
126
+ border: 1px solid #ddd;
127
+ -webkit-box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
128
+ box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
129
+ -webkit-box-sizing: border-box;
130
+ -moz-box-sizing: border-box;
131
+ box-sizing: border-box;
132
+ }
133
+
134
+ .ocdi__gl-item-image-container {
135
+ display: block;
136
+ overflow: hidden;
137
+ position: relative;
138
+ -webkit-backface-visibility: hidden;
139
+ -webkit-transition: opacity 0.2s ease-in-out;
140
+ transition: opacity 0.2s ease-in-out;
141
+ }
142
+
143
+ .ocdi__gl-item-image-container::after {
144
+ content: "";
145
+ display: block;
146
+ padding-top: 66.66666%;
147
+ }
148
+
149
+ .ocdi__gl-item-image {
150
+ height: auto;
151
+ position: absolute;
152
+ left: 0;
153
+ top: 0;
154
+ width: 100%;
155
+ -webkit-transition: opacity 0.2s ease-in-out;
156
+ transition: opacity 0.2s ease-in-out;
157
+ }
158
+
159
+ .ocdi__gl-item-image--no-image {
160
+ display: inline-block;
161
+ width: 50%;
162
+ text-align: center;
163
+ position: absolute;
164
+ top: 45%;
165
+ right: 25%;
166
+ left: 25%;
167
+ }
168
+
169
+ .ocdi__gl-item-footer {
170
+ height: 30px;
171
+ margin: 0;
172
+ padding: 10px;
173
+ -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
174
+ box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
175
+ background: #ffffff;
176
+ background: rgba(255,255,255,0.65);
177
+ }
178
+
179
+ h4.ocdi__gl-item-title {
180
+ width: 50%;
181
+ overflow: hidden;
182
+ white-space: nowrap;
183
+ text-overflow: ellipsis;
184
+ display: inline-block;
185
+ margin: 6px 0 0 0;
186
+ }
187
+
188
+ .ocdi__gl-item-button {
189
+ float: right;
190
+ }
191
+ .ocdi__gl-item-button + .ocdi__gl-item-button {
192
+ margin-right: 5px;
193
+ }
194
+
195
+ @media (max-width: 782px) {
196
+ h4.ocdi__gl-item-title {
197
+ width: 100%;
198
+ margin-bottom: 10px;
199
+ }
200
+
201
+ .ocdi__gl-item-button {
202
+ width: calc(50% - 10px);
203
+ margin-bottom: 10px;
204
+ }
205
+
206
+ .ocdi__gl-item-button + .ocdi__gl-item-button {
207
+ float: left;
208
+ }
209
+
210
+ .ocdi__gl-item-footer {
211
+ height: 72px;
212
+ }
213
+ }
214
+ .ocdi__gl-header {
215
+ display: inline-block;
216
+ width: calc(100% - 40px);
217
+ background-color: #ffffff;
218
+ margin-bottom: 20px;
219
+ padding: 0 20px;
220
+ }
221
+
222
+ .ocdi__gl-navigation {
223
+ font-size: 13px;
224
+ width: 100%;
225
+ float: left;
226
+ }
227
+
228
+ .ocdi__gl-navigation ul {
229
+ list-style-type: none;
230
+ margin: 0;
231
+ padding: 0;
232
+ overflow: hidden;
233
+ }
234
+
235
+ .ocdi__gl-navigation li {
236
+ float: left;
237
+ margin: 0 15px;
238
+ }
239
+
240
+ .ocdi__gl-navigation li.active a,
241
+ .ocdi__gl-navigation li.active a:hover {
242
+ border-bottom: 4px solid #666666;
243
+ }
244
+
245
+ .ocdi__gl-navigation li a {
246
+ display: block;
247
+ text-align: center;
248
+ text-decoration: none;
249
+ color: #444444;
250
+ border-bottom: 4px solid #ffffff;
251
+ padding: 15px 0;
252
+ }
253
+
254
+ .ocdi__gl-navigation li a:hover {
255
+ color: #00a0d2;
256
+ border-bottom: 4px solid #ffffff;
257
+ cursor:pointer;
258
+ }
259
+
260
+ .ocdi__gl-search-input {
261
+ width: 100%;
262
+ margin: 10px 0;
263
+ }
264
+
265
+ @media (min-width: 640px) {
266
+ .ocdi__gl-navigation {
267
+ width: calc(100% - 180px);
268
+ }
269
+
270
+ .ocdi__gl-navigation li {
271
+ margin: 0;
272
+ }
273
+
274
+ .ocdi__gl-navigation li a {
275
+ padding: 15px;
276
+ }
277
+
278
+ .ocdi__gl-search-input {
279
+ display: inline-block;
280
+ width: 180px;
281
+ height: 30px;
282
+ margin: 0;
283
+ margin-top: 11px;
284
+ }
285
+
286
+ .ocdi__gl-item-container {
287
+ margin-right: -20px;
288
+ }
289
+
290
+ .ocdi__gl-item {
291
+ width: calc(50% - 20px);
292
+ margin: 0 20px 20px 0;
293
+ }
294
+ }
295
+
296
+ @media (min-width: 1120px) {
297
+ .ocdi__gl-item-container {
298
+ margin-right: -30px;
299
+ }
300
+
301
+ .ocdi__gl-item {
302
+ width: calc(33.333% - 30px);
303
+ margin: 0 30px 30px 0;
304
+ }
305
+ }
306
+
307
+ /* Grid animations */
308
+ @keyframes ocdi-fade {
309
+ from {
310
+ opacity: 1;
311
+ }
312
+
313
+ to {
314
+ opacity: 0;
315
+ }
316
+ }
317
+
318
+ .ocdi-is-fadeout {
319
+ animation: ocdi-fade linear 200ms 1 forwards;
320
+ }
321
+
322
+ .ocdi-is-fadein {
323
+ animation: ocdi-fade linear 200ms 1 reverse forwards;
324
+ }
325
+
326
+ /* Grid layout modal window */
327
+
328
+ .ocdi__modal-image-container {
329
+ width: 100%;
330
+ height: 240px;
331
+ margin: 0;
332
+ overflow: hidden;
333
+ }
334
+ .kt-install-action-button.kt-no-install {
335
+ display: none;
336
+ }
337
+ .kt-import-action-button.kt-no-import {
338
+ display: none;
339
+ }
340
+
341
+ .ocdi__modal-item-title {
342
+ margin-top: 0.5em;
343
+ font-weight: bold;
344
+ }
345
+
346
+ .ocdi__modal-image-container img {
347
+ width: 100%;
348
+ }
349
+
350
+ .ocdi__modal-notice.ocdi__demo-import-notice:not(:empty) {
351
+ border: 1px solid #e5e5e5;
352
+ border-left: 4px solid #00a0d2;
353
+ margin: 1em 0 0;
354
+ }
355
+
356
+ /* Redux */
357
+ .ocdi__redux-option-name-label {
358
+ margin-right: 5px;
359
+ }
360
+
361
+ .kt-import-plugin {
362
+ padding: 5px 10px;
363
+ line-height: 20px;
364
+ }
365
+ .kt-import-plugin .spinner {
366
+ margin-top: 0;
367
+ }
assets/css/starter-templates.css ADDED
@@ -0,0 +1 @@
 
1
+ .appearance_page_kadence-starter-templates #wpcontent{padding:0}.kadence_theme_dash_head{background:#fff;padding:10px 20px;height:50px}.kadence_theme_dash_head h1{color:#2d3748;line-height:50px;padding:0;height:50px;margin:0;display:flex;align-items:center}.kadence_theme_dash_head .subtext{font-size:16px;color:#718096;display:inline-block;padding-left:10px}.kadence_theme_dash_head .kadence_theme_dash_head_container{margin:0 auto;display:flex;align-items:center}.kadence_theme_dash_head .kadence_theme_dash_logo{width:50px;height:50px;padding-right:10px}.kadence_theme_dash_head .kadence_theme_dash_logo img{width:50px}.kadence_theme_dash_head .kadence_theme_dash_version{flex-grow:1;text-align:right}.kadence_theme_dash_head .kadence_theme_dash_version span{padding:5px;background:#4a5568;color:#fff}.wrap.kadence_theme_dash{margin:20px 20px 0}.kadence_theme_dashboard{margin:0 auto}.templates-grid{display:grid;grid-template-columns:minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);grid-gap:2em}.kst-template-item .components-button.kst-import-btn{height:100%;width:100%;padding:0;display:block;border:0;background:transparent;position:absolute;box-shadow:0 0px 0px 0 rgba(0,0,0,0)}.kst-template-item .components-button.kst-import-btn img{max-width:100%;height:auto}.kst-grid-single-item{max-width:800px}.kst-grid-single-item .kst-template-item{overflow:hidden}.kst-grid-single-item .kst-import-btn{height:100%;width:100%;padding:0;display:block;border:0;background:transparent;position:absolute;box-shadow:0 0px 0px 0 rgba(0,0,0,0)}.kst-grid-single-item .kst-import-btn img{max-width:100%;height:auto}.kadence_starter_templates_notice{background:#ebf8ff;margin-bottom:20px;border:1px solid #4299e1;padding:10px;color:#2b6cb0;font-weight:bold}.kst-import-modal ul.kadence-required-wrap{margin-bottom:20px}.components-modal__frame.kst-import-modal{-webkit-animation:none !important;animation:none !important}.kst-import-modal h3{font-size:16px}.kadence_starter_templates_response{background:#fffaf0;margin-bottom:20px;border:1px solid #ed8936;padding:10px;color:#c05621;font-weight:bold}.kst-template-item{position:relative;height:0;padding-bottom:65%}.sidebar-section .components-panel__body.is-opened{padding:20px}.sidebar-section h2:first-child,.tab-section h2:first-child{margin-top:0}.side-panel .components-panel+.components-panel{margin-top:1rem}.tab-section .components-panel__body.is-opened{padding:25px}.kadence-dashboard-tab-panel .components-tab-panel__tabs .components-button{border:1px solid transparent;background:transparent;border:none;box-shadow:none;border-radius:0;cursor:pointer;height:48px;padding:3px 16px;margin-left:0;font-weight:500}.kadence-dashboard-tab-panel .components-tab-panel__tabs .components-button:hover{box-shadow:none !important}.kadence-dashboard-tab-panel .components-tab-panel__tabs .components-button:not(.active-tab):hover{color:#007cba !important;background:transparent !important}.kadence-dashboard-tab-panel .components-tab-panel__tabs .components-button.active-tab{background:#fff;border:1px solid #e2e4e7;border-bottom-color:transparent}.kadence-dashboard-tab-panel .components-tab-panel__tabs{margin-bottom:-1px}.two-col-grid{display:grid;grid-gap:1rem;grid-template-columns:1fr 1fr 1fr}h3.section-sub-head{background:#edf2f7;padding:10px;color:#4a5568;font-size:14px;text-transform:uppercase;margin-bottom:1rem;margin-top:2rem}.link-item{border:1px solid #e2e8f0;padding:20px;border-radius:4px;display:flex;flex-flow:column nowrap}.link-item h4{margin:0}.dashboard-pro-settings{margin-top:2rem}.link-item .link-item-foot{margin-top:auto;display:flex;align-items:center}.link-item .link-item-foot .components-spinner{margin-top:0}.link-item .link-item-foot .components-toggle-control .components-base-control__field{margin-bottom:0}.link-item .link-item-foot .components-toggle-control .components-base-control__field .components-form-toggle{margin-right:0}.link-item .link-item-foot>*:first-child{flex-grow:2}.link-item a{display:block;background:transparent;color:#4a5568}.link-item a:hover{color:#007cba}span.kt-license-status{padding:4px;margin-left:10px;font-size:14px;text-transform:uppercase}span.kt-license-status.k-inactive{color:#c05621 !important;background:#fffaf0 !important}span.kt-license-status.k-active{color:#2b6cb0 !important;background:#ebf8ff !important}.license-section h2{display:flex;margin-top:0;align-items:center;justify-content:space-between}.license-section table.form-table{display:block}.license-section table.form-table tbody{display:block}.license-section table.form-table td,.license-section table.form-table tr{display:block;padding:0;width:100%}.license-section .form-table th{padding:0;width:100%;margin-bottom:4px;display:block;color:#4a5568}.license-section p.submit{padding:0;margin-top:10px}.license-section table.form-table input[type=text]{width:100%}.kadence-starter-templates-preview .install-theme-info .theme-name{font-size:20px}.appearance_page_kadence-starter-templates .kadence-starter-templates-preview-actions .button{width:100%;line-height:1;min-height:35px}.appearance_page_kadence-starter-templates .kadence-starter-templates-preview-actions{padding:5px}.kadence-starter-templates-preview button.components-button.kst-palette-btn{border-radius:4px !important;padding:5px 5px 5px 7px;height:auto;display:inline-flex;align-items:flex-end;justify-content:center;border:1px solid transparent;box-shadow:none !important}.kst-template-item .components-button.kst-import-btn:hover,.kst-template-item .components-button.kst-import-btn:focus{box-shadow:0 7px 15px 0 rgba(0,0,0,.15) !important}.kadence-starter-templates-preview button.components-button.kst-palette-btn:hover,.kadence-starter-templates-preview button.components-button.kst-palette-btn:focus{border:1px solid #444;box-shadow:none !important}.kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary,.kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary:hover,.kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary:focus{background:#eee;border:1px solid #aaa;box-shadow:none}.demo-title{height:30px;margin:0;padding:10px 0;box-shadow:inset 0 1px 0 rgba(0,0,0,.1);background:rgba(255,255,255,.95);position:absolute;bottom:0;left:0;width:100%}.demo-title h4{margin:0;font-size:18px;text-align:left;line-height:30px;padding:0 20px}.kadence-starter-templates-preview .components-button-group.kst-palette-group{display:grid;grid-template-columns:1fr 1fr;grid-gap:5px}.palette-title-wrap{display:flex;align-items:center;justify-content:space-between}@media(max-width: 1760px){.kadence-starter-templates-preview .kadence-swatche-item-wrap{width:22px !important;height:22px !important}h2.palette-title{font-size:15px}}@media(max-width: 1460px){.templates-grid{grid-template-columns:minmax(0, 1fr) minmax(0, 1fr)}}.kadence-starter-required-plugins{padding:10px 20px 10px}.kadence-required-wrap{font-weight:bold;list-style:disc;padding-left:1.4em}span.plugin-status{text-transform:uppercase;color:#777}.kst-palette-btn.kst-selected-color-palette{display:flex;margin-bottom:20px}.finshed-notice-success .button-primary.button.kadence-starter-templates-finish-button{padding:10px 16px;font-size:18px}
assets/images/kadence_logo.png ADDED
Binary file
assets/js/starter-templates.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('react', 'react-dom', 'wp-element', 'wp-polyfill'), 'version' => 'afa2b5183b6905d2837635b2cb6be972');
assets/js/starter-templates.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=46)}([function(e,t){!function(){e.exports=this.wp.element}()},function(e,t,n){var r=n(28),o="object"==typeof self&&self&&self.Object===Object&&self,a=r||o||Function("return this")();e.exports=a},function(e,t){var n=Array.isArray;e.exports=n},function(e,t,n){var r=n(24),o=n(49),a=n(128),i=n(2);e.exports=function(e,t){return(i(e)?r:a)(e,o(t,3))}},function(e,t,n){var r=n(62),o=n(67);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},function(e,t,n){var r=n(10),o=n(63),a=n(64),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?o(e):a(e)}},function(e,t){e.exports=function(e){return null!=e&&"object"==typeof e}},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},function(e,t,n){var r=n(52),o=n(53),a=n(54),i=n(55),c=n(56);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=o,s.prototype.get=a,s.prototype.has=i,s.prototype.set=c,e.exports=s},function(e,t,n){var r=n(26);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},function(e,t,n){var r=n(1).Symbol;e.exports=r},function(e,t,n){var r=n(4)(Object,"create");e.exports=r},function(e,t,n){var r=n(76);e.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},function(e,t,n){var r=n(22);e.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}},function(e,t,n){var r=n(4)(n(1),"Map");e.exports=r},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t,n){var r=n(68),o=n(75),a=n(77),i=n(78),c=n(79);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=o,s.prototype.get=a,s.prototype.has=i,s.prototype.set=c,e.exports=s},function(e,t,n){var r=n(97),o=n(104),a=n(20);e.exports=function(e){return a(e)?r(e):o(e)}},function(e,t){e.exports=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}},function(e,t,n){var r=n(27),o=n(19);e.exports=function(e){return null!=e&&o(e.length)&&!r(e)}},function(e,t,n){var r=n(2),o=n(22),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||(i.test(e)||!a.test(e)||null!=t&&e in Object(t))}},function(e,t,n){var r=n(5),o=n(6);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},function(e,t){function n(t){return e.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},n(t)}e.exports=n},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n<r;)o[n]=t(e[n],n,e);return o}},function(e,t,n){var r=n(8),o=n(57),a=n(58),i=n(59),c=n(60),s=n(61);function u(e){var t=this.__data__=new r(e);this.size=t.size}u.prototype.clear=o,u.prototype.delete=a,u.prototype.get=i,u.prototype.has=c,u.prototype.set=s,e.exports=u},function(e,t){e.exports=function(e,t){return e===t||e!=e&&t!=t}},function(e,t,n){var r=n(5),o=n(16);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},function(e,t,n){(function(t){var n="object"==typeof t&&t&&t.Object===Object&&t;e.exports=n}).call(this,n(15))},function(e,t){var n=Function.prototype.toString;e.exports=function(e){if(null!=e){try{return n.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},function(e,t,n){var r=n(80),o=n(6);e.exports=function e(t,n,a,i,c){return t===n||(null==t||null==n||!o(t)&&!o(n)?t!=t&&n!=n:r(t,n,a,i,e,c))}},function(e,t,n){var r=n(81),o=n(84),a=n(85);e.exports=function(e,t,n,i,c,s){var u=1&n,l=e.length,f=t.length;if(l!=f&&!(u&&f>l))return!1;var p=s.get(e);if(p&&s.get(t))return p==t;var d=-1,v=!0,m=2&n?new r:void 0;for(s.set(e,t),s.set(t,e);++d<l;){var b=e[d],h=t[d];if(i)var y=u?i(h,b,d,t,e,s):i(b,h,d,e,t,s);if(void 0!==y){if(y)continue;v=!1;break}if(m){if(!o(t,(function(e,t){if(!a(m,t)&&(b===e||c(b,e,n,i,s)))return m.push(t)}))){v=!1;break}}else if(b!==h&&!c(b,h,n,i,s)){v=!1;break}}return s.delete(e),s.delete(t),v}},function(e,t,n){var r=n(99),o=n(6),a=Object.prototype,i=a.hasOwnProperty,c=a.propertyIsEnumerable,s=r(function(){return arguments}())?r:function(e){return o(e)&&i.call(e,"callee")&&!c.call(e,"callee")};e.exports=s},function(e,t,n){(function(e){var r=n(1),o=n(100),a=t&&!t.nodeType&&t,i=a&&"object"==typeof e&&e&&!e.nodeType&&e,c=i&&i.exports===a?r.Buffer:void 0,s=(c?c.isBuffer:void 0)||o;e.exports=s}).call(this,n(34)(e))},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){var n=/^(?:0|[1-9]\d*)$/;e.exports=function(e,t){var r=typeof e;return!!(t=null==t?9007199254740991:t)&&("number"==r||"symbol"!=r&&n.test(e))&&e>-1&&e%1==0&&e<t}},function(e,t,n){var r=n(101),o=n(102),a=n(103),i=a&&a.isTypedArray,c=i?o(i):r;e.exports=c},function(e,t,n){var r=n(16);e.exports=function(e){return e==e&&!r(e)}},function(e,t){e.exports=function(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}},function(e,t,n){var r=n(40),o=n(13);e.exports=function(e,t){for(var n=0,a=(t=r(t,e)).length;null!=e&&n<a;)e=e[o(t[n++])];return n&&n==a?e:void 0}},function(e,t,n){var r=n(2),o=n(21),a=n(116),i=n(119);e.exports=function(e,t){return r(e)?e:o(e,t)?[e]:a(i(e))}},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}e.exports=function(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),e}},function(e,t,n){var r=n(47);e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&r(e,t)}},function(e,t,n){var r=n(48),o=n(7);e.exports=function(e,t){return!t||"object"!==r(t)&&"function"!=typeof t?o(e):t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(134),a=d(o),i=d(n(135)),c=n(138),s=n(139),u=d(n(140)),l=d(n(141)),f=d(n(142)),p=d(n(143));function d(e){return e&&e.__esModule?e:{default:e}}var v=function(e){function t(e){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var n=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.lazyLoadHandler=n.lazyLoadHandler.bind(n),e.throttle>0&&(e.debounce?n.lazyLoadHandler=(0,u.default)(n.lazyLoadHandler,e.throttle):n.lazyLoadHandler=(0,l.default)(n.lazyLoadHandler,e.throttle)),n.state={visible:!1},n}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"componentDidMount",value:function(){this._mounted=!0;var e=this.getEventNode();this.lazyLoadHandler(),this.lazyLoadHandler.flush&&this.lazyLoadHandler.flush(),(0,s.add)(window,"resize",this.lazyLoadHandler),(0,s.add)(e,"scroll",this.lazyLoadHandler),e!==window&&(0,s.add)(window,"scroll",this.lazyLoadHandler)}},{key:"componentWillReceiveProps",value:function(){this.state.visible||this.lazyLoadHandler()}},{key:"shouldComponentUpdate",value:function(e,t){return t.visible}},{key:"componentWillUnmount",value:function(){this._mounted=!1,this.lazyLoadHandler.cancel&&this.lazyLoadHandler.cancel(),this.detachListeners()}},{key:"getEventNode",value:function(){return(0,f.default)((0,c.findDOMNode)(this))}},{key:"getOffset",value:function(){var e=this.props,t=e.offset,n=e.offsetVertical,r=e.offsetHorizontal,o=e.offsetTop,a=e.offsetBottom,i=e.offsetLeft,c=e.offsetRight,s=e.threshold||t,u=n||s,l=r||s;return{top:o||u,bottom:a||u,left:i||l,right:c||l}}},{key:"lazyLoadHandler",value:function(){if(this._mounted){var e=this.getOffset(),t=(0,c.findDOMNode)(this),n=this.getEventNode();if((0,p.default)(t,n,e)){var r=this.props.onContentVisible;this.setState({visible:!0},(function(){r&&r()})),this.detachListeners()}}}},{key:"detachListeners",value:function(){var e=this.getEventNode();(0,s.remove)(window,"resize",this.lazyLoadHandler),(0,s.remove)(e,"scroll",this.lazyLoadHandler),e!==window&&(0,s.remove)(window,"scroll",this.lazyLoadHandler)}},{key:"render",value:function(){var e=this.props,t=e.children,n=e.className,r=e.height,i=e.width,c=this.state.visible,s={height:r,width:i},u="LazyLoad"+(c?" is-visible":"")+(n?" "+n:"");return a.default.createElement(this.props.elementType,{className:u,style:s},c&&o.Children.only(t))}}]),t}(o.Component);t.default=v,v.propTypes={children:i.default.node.isRequired,className:i.default.string,debounce:i.default.bool,elementType:i.default.string,height:i.default.oneOfType([i.default.string,i.default.number]),offset:i.default.number,offsetBottom:i.default.number,offsetHorizontal:i.default.number,offsetLeft:i.default.number,offsetRight:i.default.number,offsetTop:i.default.number,offsetVertical:i.default.number,threshold:i.default.number,throttle:i.default.number,width:i.default.oneOfType([i.default.string,i.default.number]),onContentVisible:i.default.func},v.defaultProps={elementType:"div",debounce:!0,offset:0,offsetBottom:0,offsetHorizontal:0,offsetLeft:0,offsetRight:0,offsetTop:0,offsetVertical:0,throttle:250}},function(e,t,n){"use strict";n.r(t);var r=n(41),o=n.n(r),a=n(42),i=n.n(a),c=n(7),s=n.n(c),u=n(43),l=n.n(u),f=n(44),p=n.n(f),d=n(23),v=n.n(d),m=n(0),b=n(3),h=n.n(b),y=n(45),g=n.n(y);function j(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=v()(e);if(t){var o=v()(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return p()(this,n)}}var _=wp.i18n,x=_.__,O=(_.sprintf,wp.element),w=O.Fragment,k=O.Component,E=O.render,P=wp.components,S=P.Modal,N=P.Spinner,T=P.ButtonGroup,z=P.Button,A=P.ExternalLink,L=wp.apiFetch,M=function(e){l()(n,e);var t=j(n);function n(){var e;return o()(this,n),(e=t.apply(this,arguments)).runAjax=e.runAjax.bind(s()(e)),e.runPluginInstall=e.runPluginInstall.bind(s()(e)),e.focusMode=e.focusMode.bind(s()(e)),e.state={category:"all",activeTemplate:"",colorPalette:"",search:null,isFetching:!1,isImporting:!1,progress:"",focusMode:!1,finished:!1,templates:kadenceStarterParams.templates?kadenceStarterParams.templates:[],palettes:kadenceStarterParams.palettes?kadenceStarterParams.palettes:[]},L.setFetchHandler((function(e){var t=e.url,n=e.path,r=e.data,o=e.method;return axios({url:t||n,method:o,data:r})})),e}return i()(n,[{key:"capitalizeFirstLetter",value:function(e){return e.charAt(0).toUpperCase()+e.slice(1)}},{key:"focusMode",value:function(e){this.setState({activeTemplate:e,focusMode:!0})}},{key:"runPluginInstall",value:function(e){this.setState({progress:"plugins",isFetching:!0});var t=new FormData;t.append("action","kadence_import_install_plugins"),t.append("security",kadenceStarterParams.ajax_nonce),t.append("selected",e),this.runAjax(t)}},{key:"runAjax",value:function(e){var t=this;jQuery.ajax({method:"POST",url:kadenceStarterParams.ajax_url,data:e,contentType:!1,processData:!1}).done((function(n,r,o){if(void 0!==n.status&&"newAJAX"===n.status)t.state.progress="contentNew",t.runAjax(e);else if(void 0!==n.status&&"customizerAJAX"===n.status){t.setState({progress:"customizer"}),(a=new FormData).append("action","kadence_import_customizer_data"),a.append("security",kadenceStarterParams.ajax_nonce),a.append("wp_customize","on"),t.runAjax(a)}else if(void 0!==n.status&&"afterAllImportAJAX"===n.status){t.setState({progress:"widgets"}),(a=new FormData).append("action","kadence_after_import_data"),a.append("security",kadenceStarterParams.ajax_nonce),t.runAjax(a)}else if(void 0!==n.status&&"pluginSuccess"===n.status){var a;t.setState({progress:"content"}),(a=new FormData).append("action","kadence_import_demo_data"),a.append("security",kadenceStarterParams.ajax_nonce),a.append("selected",t.state.activeTemplate),a.append("palette",t.state.colorPalette),t.runAjax(a)}else void 0!==n.message?(jQuery(".kadence_starter_templates_finished").append("<p>"+n.message+"</p>"),t.setState({finished:!0,isFetching:!1,activeTemplate:"",focusMode:!1,isImporting:!1,progress:""})):(jQuery(".kadence_starter_templates_error").append('<div class="notice kadence_starter_templates_response notice-error"><p>'+n+"</p></div>"),t.setState({finished:!0,isFetching:!1,activeTemplate:"",focusMode:!1,isImporting:!1,progress:""}))})).fail((function(e){jQuery(".kadence_starter_templates_error").append('<div class="notice kadence_starter_templates_response notice-error"><p>Error: '+e.statusText+" ("+e.status+")</p></div>"),t.setState({finished:!0,isFetching:!1,activeTemplate:"",focusMode:!1,isImporting:!1,progress:""})}))}},{key:"render",value:function(){for(var e=this,t=["all"],n=0;n<this.state.templates.length;n++)for(var r=0;r<this.state.templates[n].categories.length;r++)t.includes(this.state.templates[n].categories[r])||t.push(this.state.templates[n].categories[r]);t.map((function(t){return{value:t,label:e.capitalizeFirstLetter(t)}}));var o=function(){var t=e.state.templates.filter((function(t){return t.key===e.state.activeTemplate}))[0],n=!0,r=e.state.colorPalette?t.url+"?previewcolor="+e.state.colorPalette:t.url;return Object(m.createElement)("div",{className:"kadence-starter-templates-preview theme-install-overlay wp-full-overlay expanded",style:{display:"block"}},Object(m.createElement)("div",{className:"wp-full-overlay-sidebar"},Object(m.createElement)("div",{className:"wp-full-overlay-header"},Object(m.createElement)("button",{className:"kst-close-focus-btn close-full-overlay",onClick:function(){return e.setState({activeTemplate:"",colorPalette:"",focusMode:!1})}})),Object(m.createElement)("div",{className:"wp-full-overlay-sidebar-content"},Object(m.createElement)("div",{className:"install-theme-info"},Object(m.createElement)("h3",{className:"theme-name"},t.name),Object(m.createElement)("div",{className:"theme-by"},t.categories.map((function(t){return e.capitalizeFirstLetter(t)})).join(", ")),Object(m.createElement)("img",{className:"theme-screenshot",src:t.image,alt:t.name}),Object(m.createElement)("div",{className:"palette-title-wrap"},Object(m.createElement)("h2",{className:"palette-title"},x("Optional: Choose Color Scheme","kadence-starter-templates")),Object(m.createElement)(z,{label:x("clear"),className:"kst-clear-palette",disabled:!e.state.colorPalette,icon:"image-rotate",iconSize:10,onClick:function(){return e.setState({colorPalette:""})}})),Object(m.createElement)(T,{className:"kst-palette-group","aria-label":x("Select a Palette","kadence-starter-templates")},h()(e.state.palettes,(function(t){var n=t.palette,r=t.colors;return Object(m.createElement)(z,{className:"kst-palette-btn",isPrimary:n===e.state.colorPalette,"aria-pressed":n===e.state.colorPalette,onClick:function(){return e.setState({colorPalette:n})}},h()(r,(function(e,t){return Object(m.createElement)("div",{key:t,style:{width:30,height:30,marginBottom:0,marginRight:"3px",transform:"scale(1)",transition:"100ms transform ease"},className:"kadence-swatche-item-wrap"},Object(m.createElement)("span",{className:"kadence-swatch-item",style:{height:"100%",display:"block",width:"100%",border:"1px solid rgb(218, 218, 218)",borderRadius:"50%",color:"".concat(e),boxShadow:"inset 0 0 0 ".concat(15,"px"),transition:"100ms box-shadow ease"}}))})))}))),Object(m.createElement)("p",{className:"desc-small"},x("*You can change this after import.","kadence-starter-templates"))),Object(m.createElement)("div",{className:"kadence-starter-required-plugins"},Object(m.createElement)("h3",null,x("Required Plugins","kadence-starter-templates")),Object(m.createElement)("ul",{className:"kadence-required-wrap"},h()(t.plugins,(function(e){var t=e.state,r=e.title;return"active"!==t&&(n=!1),Object(m.createElement)("li",{className:"plugin-required"},r," - ",Object(m.createElement)("span",{class:"plugin-status"},"notactive"===t?x("Not Installed","kadence-starter-templates"):t))}))),!n&&Object(m.createElement)("p",{className:"desc-small"},x("*Missing/Inactive plugins will be installed on import.","kadence-starter-templates")))),Object(m.createElement)("div",{class:"wp-full-overlay-footer"},Object(m.createElement)("div",{class:"kadence-starter-templates-preview-actions"},Object(m.createElement)("button",{className:"kst-import-btn button-hero button button-primary",isDisabled:void 0!==t.pro&&t.pro&&"true"!==kadenceStarterParams.pro,onClick:function(){return e.setState({isImporting:!0})}},x("Import","kadence-starter-templates"))))),Object(m.createElement)("div",{class:"wp-full-overlay-main"},Object(m.createElement)("iframe",{id:"kadence-starter-preview",src:r})))},a=function(){var t=e.state.templates.filter((function(t){return t.key===e.state.activeTemplate}))[0];return Object(m.createElement)(w,null,Object(m.createElement)("div",{className:"kst-grid-single-item"},Object(m.createElement)("div",{className:"kst-template-item"},Object(m.createElement)("div",{className:"kst-import-btn"},Object(m.createElement)("img",{src:t.image,alt:t.name}),Object(m.createElement)("div",{className:"demo-title"},Object(m.createElement)("h4",null,t.name))))),Object(m.createElement)(S,{className:"kst-import-modal",title:x("Import Starter Template"),onRequestClose:function(){return!e.state.isFetching&&e.setState({activeTemplate:"",colorPalette:"",focusMode:!1,isImporting:!1,progress:""})}},Object(m.createElement)("div",{className:"kadence_starter_templates_notice"},kadenceStarterParams.notice),Object(m.createElement)("h3",null,x("Starter Template Plugins","kadence-starter-templates")),Object(m.createElement)("ul",{className:"kadence-required-wrap"},h()(t.plugins,(function(e){e.state;var t=e.title;return Object(m.createElement)("li",{className:"plugin-required"},t)}))),e.state.colorPalette&&Object(m.createElement)(w,null,Object(m.createElement)("h3",null,x("Selected Color Palette","kadence-starter-templates")),h()(e.state.palettes,(function(t){var n=t.palette,r=t.colors;if(n===e.state.colorPalette)return Object(m.createElement)("div",{className:"kst-palette-btn kst-selected-color-palette"},h()(r,(function(e,t){return Object(m.createElement)("div",{key:t,style:{width:22,height:22,marginBottom:0,marginRight:"3px",transform:"scale(1)",transition:"100ms transform ease"},className:"kadence-swatche-item-wrap"},Object(m.createElement)("span",{className:"kadence-swatch-item",style:{height:"100%",display:"block",width:"100%",border:"1px solid rgb(218, 218, 218)",borderRadius:"50%",color:"".concat(e),boxShadow:"inset 0 0 0 ".concat(15,"px"),transition:"100ms box-shadow ease"}}))})))}))),"plugins"===e.state.progress&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},kadenceStarterParams.plugin_progress),"content"===e.state.progress&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},kadenceStarterParams.content_progress),"contentNew"===e.state.progress&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},kadenceStarterParams.content_new_progress),"customizer"===e.state.progress&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},kadenceStarterParams.customizer_progress),"widgets"===e.state.progress&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},kadenceStarterParams.widgets_progress),e.state.isFetching&&Object(m.createElement)(N,null),!kadenceStarterParams.isKadence&&Object(m.createElement)("div",{class:"kadence_starter_templates_response"},Object(m.createElement)("h2",null,x("This Starter Template Requires the Kadence Theme","kadence-starter-templates")),Object(m.createElement)(A,{href:"https://www.kadencewp.com/kadence-theme/"},x("Get Free Theme","kadence-blocks"))),kadenceStarterParams.isKadence&&Object(m.createElement)(z,{className:"kt-defaults-save",isPrimary:!0,disabled:e.state.isFetching,onClick:function(){e.runPluginInstall(t.key)}},x("Start Importing"))))},i=function(){return Object(m.createElement)("div",{className:"templates-grid"},h()(e.state.templates,(function(t){var n=t.name,r=t.key,o=t.image,a=(t.content,t.categories),i=t.keywords,c=t.pro;if(("all"===e.state.category||a.includes(e.state.category))&&(!e.state.search||i&&i.some((function(t){return t.toLowerCase().includes(e.state.search.toLowerCase())}))))return Object(m.createElement)("div",{className:"kst-template-item"},Object(m.createElement)(z,{key:r,className:"kst-import-btn",isSmall:!0,isDisabled:void 0!==c&&c&&"true"!==kadenceStarterParams.pro,onClick:function(){return e.focusMode(r)}},Object(m.createElement)(g.a,null,Object(m.createElement)("img",{src:o,alt:n})),Object(m.createElement)("div",{className:"demo-title"},Object(m.createElement)("h4",null,n))),void 0!==c&&c&&Object(m.createElement)(w,null,Object(m.createElement)("span",{className:"kb-pro-template"},x("Pro","kadence-blocks")),"true"!==kadenceStarterParams.pro&&Object(m.createElement)("div",{className:"kt-popover-pro-notice"},Object(m.createElement)("h2",null,x("Kadence Pro required for this item","kadence-starter-sites")," "),Object(m.createElement)(A,{href:"https://www.kadencewp.com/pro/"},x("Upgrade to Pro","kadence-blocks")))))})))};return Object(m.createElement)(w,null,Object(m.createElement)((function(){return Object(m.createElement)("div",{className:"main-panel"},e.state.focusMode&&Object(m.createElement)(w,null,e.state.isImporting&&Object(m.createElement)(a,null),!e.state.isImporting&&Object(m.createElement)(o,null)),!e.state.focusMode&&!e.state.finished&&Object(m.createElement)(i,null))}),null))}}]),n}(k);wp.domReady((function(){E(Object(m.createElement)(M,null),document.querySelector(".kadence_starter_dashboard_main"))}))},function(e,t){function n(t,r){return e.exports=n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},n(t,r)}e.exports=n},function(e,t){function n(t){return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?e.exports=n=function(e){return typeof e}:e.exports=n=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(t)}e.exports=n},function(e,t,n){var r=n(50),o=n(114),a=n(124),i=n(2),c=n(125);e.exports=function(e){return"function"==typeof e?e:null==e?a:"object"==typeof e?i(e)?o(e[0],e[1]):r(e):c(e)}},function(e,t,n){var r=n(51),o=n(113),a=n(38);e.exports=function(e){var t=o(e);return 1==t.length&&t[0][2]?a(t[0][0],t[0][1]):function(n){return n===e||r(n,e,t)}}},function(e,t,n){var r=n(25),o=n(30);e.exports=function(e,t,n,a){var i=n.length,c=i,s=!a;if(null==e)return!c;for(e=Object(e);i--;){var u=n[i];if(s&&u[2]?u[1]!==e[u[0]]:!(u[0]in e))return!1}for(;++i<c;){var l=(u=n[i])[0],f=e[l],p=u[1];if(s&&u[2]){if(void 0===f&&!(l in e))return!1}else{var d=new r;if(a)var v=a(f,p,l,e,t,d);if(!(void 0===v?o(p,f,3,a,d):v))return!1}}return!0}},function(e,t){e.exports=function(){this.__data__=[],this.size=0}},function(e,t,n){var r=n(9),o=Array.prototype.splice;e.exports=function(e){var t=this.__data__,n=r(t,e);return!(n<0)&&(n==t.length-1?t.pop():o.call(t,n,1),--this.size,!0)}},function(e,t,n){var r=n(9);e.exports=function(e){var t=this.__data__,n=r(t,e);return n<0?void 0:t[n][1]}},function(e,t,n){var r=n(9);e.exports=function(e){return r(this.__data__,e)>-1}},function(e,t,n){var r=n(9);e.exports=function(e,t){var n=this.__data__,o=r(n,e);return o<0?(++this.size,n.push([e,t])):n[o][1]=t,this}},function(e,t,n){var r=n(8);e.exports=function(){this.__data__=new r,this.size=0}},function(e,t){e.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},function(e,t){e.exports=function(e){return this.__data__.get(e)}},function(e,t){e.exports=function(e){return this.__data__.has(e)}},function(e,t,n){var r=n(8),o=n(14),a=n(17);e.exports=function(e,t){var n=this.__data__;if(n instanceof r){var i=n.__data__;if(!o||i.length<199)return i.push([e,t]),this.size=++n.size,this;n=this.__data__=new a(i)}return n.set(e,t),this.size=n.size,this}},function(e,t,n){var r=n(27),o=n(65),a=n(16),i=n(29),c=/^\[object .+?Constructor\]$/,s=Function.prototype,u=Object.prototype,l=s.toString,f=u.hasOwnProperty,p=RegExp("^"+l.call(f).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");e.exports=function(e){return!(!a(e)||o(e))&&(r(e)?p:c).test(i(e))}},function(e,t,n){var r=n(10),o=Object.prototype,a=o.hasOwnProperty,i=o.toString,c=r?r.toStringTag:void 0;e.exports=function(e){var t=a.call(e,c),n=e[c];try{e[c]=void 0;var r=!0}catch(e){}var o=i.call(e);return r&&(t?e[c]=n:delete e[c]),o}},function(e,t){var n=Object.prototype.toString;e.exports=function(e){return n.call(e)}},function(e,t,n){var r,o=n(66),a=(r=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";e.exports=function(e){return!!a&&a in e}},function(e,t,n){var r=n(1)["__core-js_shared__"];e.exports=r},function(e,t){e.exports=function(e,t){return null==e?void 0:e[t]}},function(e,t,n){var r=n(69),o=n(8),a=n(14);e.exports=function(){this.size=0,this.__data__={hash:new r,map:new(a||o),string:new r}}},function(e,t,n){var r=n(70),o=n(71),a=n(72),i=n(73),c=n(74);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=o,s.prototype.get=a,s.prototype.has=i,s.prototype.set=c,e.exports=s},function(e,t,n){var r=n(11);e.exports=function(){this.__data__=r?r(null):{},this.size=0}},function(e,t){e.exports=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}},function(e,t,n){var r=n(11),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;if(r){var n=t[e];return"__lodash_hash_undefined__"===n?void 0:n}return o.call(t,e)?t[e]:void 0}},function(e,t,n){var r=n(11),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:o.call(t,e)}},function(e,t,n){var r=n(11);e.exports=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=r&&void 0===t?"__lodash_hash_undefined__":t,this}},function(e,t,n){var r=n(12);e.exports=function(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}},function(e,t){e.exports=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},function(e,t,n){var r=n(12);e.exports=function(e){return r(this,e).get(e)}},function(e,t,n){var r=n(12);e.exports=function(e){return r(this,e).has(e)}},function(e,t,n){var r=n(12);e.exports=function(e,t){var n=r(this,e),o=n.size;return n.set(e,t),this.size+=n.size==o?0:1,this}},function(e,t,n){var r=n(25),o=n(31),a=n(86),i=n(90),c=n(108),s=n(2),u=n(33),l=n(36),f="[object Object]",p=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,d,v,m){var b=s(e),h=s(t),y=b?"[object Array]":c(e),g=h?"[object Array]":c(t),j=(y="[object Arguments]"==y?f:y)==f,_=(g="[object Arguments]"==g?f:g)==f,x=y==g;if(x&&u(e)){if(!u(t))return!1;b=!0,j=!1}if(x&&!j)return m||(m=new r),b||l(e)?o(e,t,n,d,v,m):a(e,t,y,n,d,v,m);if(!(1&n)){var O=j&&p.call(e,"__wrapped__"),w=_&&p.call(t,"__wrapped__");if(O||w){var k=O?e.value():e,E=w?t.value():t;return m||(m=new r),v(k,E,n,d,m)}}return!!x&&(m||(m=new r),i(e,t,n,d,v,m))}},function(e,t,n){var r=n(17),o=n(82),a=n(83);function i(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t<n;)this.add(e[t])}i.prototype.add=i.prototype.push=o,i.prototype.has=a,e.exports=i},function(e,t){e.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},function(e,t){e.exports=function(e){return this.__data__.has(e)}},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(t(e[n],n,e))return!0;return!1}},function(e,t){e.exports=function(e,t){return e.has(t)}},function(e,t,n){var r=n(10),o=n(87),a=n(26),i=n(31),c=n(88),s=n(89),u=r?r.prototype:void 0,l=u?u.valueOf:void 0;e.exports=function(e,t,n,r,u,f,p){switch(n){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!f(new o(e),new o(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return a(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var d=c;case"[object Set]":var v=1&r;if(d||(d=s),e.size!=t.size&&!v)return!1;var m=p.get(e);if(m)return m==t;r|=2,p.set(e,t);var b=i(d(e),d(t),r,u,f,p);return p.delete(e),b;case"[object Symbol]":if(l)return l.call(e)==l.call(t)}return!1}},function(e,t,n){var r=n(1).Uint8Array;e.exports=r},function(e,t){e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}},function(e,t){e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}},function(e,t,n){var r=n(91),o=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,a,i,c){var s=1&n,u=r(e),l=u.length;if(l!=r(t).length&&!s)return!1;for(var f=l;f--;){var p=u[f];if(!(s?p in t:o.call(t,p)))return!1}var d=c.get(e);if(d&&c.get(t))return d==t;var v=!0;c.set(e,t),c.set(t,e);for(var m=s;++f<l;){var b=e[p=u[f]],h=t[p];if(a)var y=s?a(h,b,p,t,e,c):a(b,h,p,e,t,c);if(!(void 0===y?b===h||i(b,h,n,a,c):y)){v=!1;break}m||(m="constructor"==p)}if(v&&!m){var g=e.constructor,j=t.constructor;g==j||!("constructor"in e)||!("constructor"in t)||"function"==typeof g&&g instanceof g&&"function"==typeof j&&j instanceof j||(v=!1)}return c.delete(e),c.delete(t),v}},function(e,t,n){var r=n(92),o=n(94),a=n(18);e.exports=function(e){return r(e,a,o)}},function(e,t,n){var r=n(93),o=n(2);e.exports=function(e,t,n){var a=t(e);return o(e)?a:r(a,n(e))}},function(e,t){e.exports=function(e,t){for(var n=-1,r=t.length,o=e.length;++n<r;)e[o+n]=t[n];return e}},function(e,t,n){var r=n(95),o=n(96),a=Object.prototype.propertyIsEnumerable,i=Object.getOwnPropertySymbols,c=i?function(e){return null==e?[]:(e=Object(e),r(i(e),(function(t){return a.call(e,t)})))}:o;e.exports=c},function(e,t){e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=0,a=[];++n<r;){var i=e[n];t(i,n,e)&&(a[o++]=i)}return a}},function(e,t){e.exports=function(){return[]}},function(e,t,n){var r=n(98),o=n(32),a=n(2),i=n(33),c=n(35),s=n(36),u=Object.prototype.hasOwnProperty;e.exports=function(e,t){var n=a(e),l=!n&&o(e),f=!n&&!l&&i(e),p=!n&&!l&&!f&&s(e),d=n||l||f||p,v=d?r(e.length,String):[],m=v.length;for(var b in e)!t&&!u.call(e,b)||d&&("length"==b||f&&("offset"==b||"parent"==b)||p&&("buffer"==b||"byteLength"==b||"byteOffset"==b)||c(b,m))||v.push(b);return v}},function(e,t){e.exports=function(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}},function(e,t,n){var r=n(5),o=n(6);e.exports=function(e){return o(e)&&"[object Arguments]"==r(e)}},function(e,t){e.exports=function(){return!1}},function(e,t,n){var r=n(5),o=n(19),a=n(6),i={};i["[object Float32Array]"]=i["[object Float64Array]"]=i["[object Int8Array]"]=i["[object Int16Array]"]=i["[object Int32Array]"]=i["[object Uint8Array]"]=i["[object Uint8ClampedArray]"]=i["[object Uint16Array]"]=i["[object Uint32Array]"]=!0,i["[object Arguments]"]=i["[object Array]"]=i["[object ArrayBuffer]"]=i["[object Boolean]"]=i["[object DataView]"]=i["[object Date]"]=i["[object Error]"]=i["[object Function]"]=i["[object Map]"]=i["[object Number]"]=i["[object Object]"]=i["[object RegExp]"]=i["[object Set]"]=i["[object String]"]=i["[object WeakMap]"]=!1,e.exports=function(e){return a(e)&&o(e.length)&&!!i[r(e)]}},function(e,t){e.exports=function(e){return function(t){return e(t)}}},function(e,t,n){(function(e){var r=n(28),o=t&&!t.nodeType&&t,a=o&&"object"==typeof e&&e&&!e.nodeType&&e,i=a&&a.exports===o&&r.process,c=function(){try{var e=a&&a.require&&a.require("util").types;return e||i&&i.binding&&i.binding("util")}catch(e){}}();e.exports=c}).call(this,n(34)(e))},function(e,t,n){var r=n(105),o=n(106),a=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return o(e);var t=[];for(var n in Object(e))a.call(e,n)&&"constructor"!=n&&t.push(n);return t}},function(e,t){var n=Object.prototype;e.exports=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||n)}},function(e,t,n){var r=n(107)(Object.keys,Object);e.exports=r},function(e,t){e.exports=function(e,t){return function(n){return e(t(n))}}},function(e,t,n){var r=n(109),o=n(14),a=n(110),i=n(111),c=n(112),s=n(5),u=n(29),l=u(r),f=u(o),p=u(a),d=u(i),v=u(c),m=s;(r&&"[object DataView]"!=m(new r(new ArrayBuffer(1)))||o&&"[object Map]"!=m(new o)||a&&"[object Promise]"!=m(a.resolve())||i&&"[object Set]"!=m(new i)||c&&"[object WeakMap]"!=m(new c))&&(m=function(e){var t=s(e),n="[object Object]"==t?e.constructor:void 0,r=n?u(n):"";if(r)switch(r){case l:return"[object DataView]";case f:return"[object Map]";case p:return"[object Promise]";case d:return"[object Set]";case v:return"[object WeakMap]"}return t}),e.exports=m},function(e,t,n){var r=n(4)(n(1),"DataView");e.exports=r},function(e,t,n){var r=n(4)(n(1),"Promise");e.exports=r},function(e,t,n){var r=n(4)(n(1),"Set");e.exports=r},function(e,t,n){var r=n(4)(n(1),"WeakMap");e.exports=r},function(e,t,n){var r=n(37),o=n(18);e.exports=function(e){for(var t=o(e),n=t.length;n--;){var a=t[n],i=e[a];t[n]=[a,i,r(i)]}return t}},function(e,t,n){var r=n(30),o=n(115),a=n(121),i=n(21),c=n(37),s=n(38),u=n(13);e.exports=function(e,t){return i(e)&&c(t)?s(u(e),t):function(n){var i=o(n,e);return void 0===i&&i===t?a(n,e):r(t,i,3)}}},function(e,t,n){var r=n(39);e.exports=function(e,t,n){var o=null==e?void 0:r(e,t);return void 0===o?n:o}},function(e,t,n){var r=n(117),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g,i=r((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(o,(function(e,n,r,o){t.push(r?o.replace(a,"$1"):n||e)})),t}));e.exports=i},function(e,t,n){var r=n(118);e.exports=function(e){var t=r(e,(function(e){return 500===n.size&&n.clear(),e})),n=t.cache;return t}},function(e,t,n){var r=n(17);function o(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError("Expected a function");var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],a=n.cache;if(a.has(o))return a.get(o);var i=e.apply(this,r);return n.cache=a.set(o,i)||a,i};return n.cache=new(o.Cache||r),n}o.Cache=r,e.exports=o},function(e,t,n){var r=n(120);e.exports=function(e){return null==e?"":r(e)}},function(e,t,n){var r=n(10),o=n(24),a=n(2),i=n(22),c=r?r.prototype:void 0,s=c?c.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(a(t))return o(t,e)+"";if(i(t))return s?s.call(t):"";var n=t+"";return"0"==n&&1/t==-1/0?"-0":n}},function(e,t,n){var r=n(122),o=n(123);e.exports=function(e,t){return null!=e&&o(e,t,r)}},function(e,t){e.exports=function(e,t){return null!=e&&t in Object(e)}},function(e,t,n){var r=n(40),o=n(32),a=n(2),i=n(35),c=n(19),s=n(13);e.exports=function(e,t,n){for(var u=-1,l=(t=r(t,e)).length,f=!1;++u<l;){var p=s(t[u]);if(!(f=null!=e&&n(e,p)))break;e=e[p]}return f||++u!=l?f:!!(l=null==e?0:e.length)&&c(l)&&i(p,l)&&(a(e)||o(e))}},function(e,t){e.exports=function(e){return e}},function(e,t,n){var r=n(126),o=n(127),a=n(21),i=n(13);e.exports=function(e){return a(e)?r(i(e)):o(e)}},function(e,t){e.exports=function(e){return function(t){return null==t?void 0:t[e]}}},function(e,t,n){var r=n(39);e.exports=function(e){return function(t){return r(t,e)}}},function(e,t,n){var r=n(129),o=n(20);e.exports=function(e,t){var n=-1,a=o(e)?Array(e.length):[];return r(e,(function(e,r,o){a[++n]=t(e,r,o)})),a}},function(e,t,n){var r=n(130),o=n(133)(r);e.exports=o},function(e,t,n){var r=n(131),o=n(18);e.exports=function(e,t){return e&&r(e,t,o)}},function(e,t,n){var r=n(132)();e.exports=r},function(e,t){e.exports=function(e){return function(t,n,r){for(var o=-1,a=Object(t),i=r(t),c=i.length;c--;){var s=i[e?c:++o];if(!1===n(a[s],s,a))break}return t}}},function(e,t,n){var r=n(20);e.exports=function(e,t){return function(n,o){if(null==n)return n;if(!r(n))return e(n,o);for(var a=n.length,i=t?a:-1,c=Object(n);(t?i--:++i<a)&&!1!==o(c[i],i,c););return n}}},function(e,t){!function(){e.exports=this.React}()},function(e,t,n){e.exports=n(136)()},function(e,t,n){"use strict";var r=n(137);function o(){}function a(){}a.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,a,i){if(i!==r){var c=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw c.name="Invariant Violation",c}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:a,resetWarningCache:o};return n.PropTypes=n,n}},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t){!function(){e.exports=this.ReactDOM}()},function(e,t,n){var r,o;void 0===(o="function"==typeof(r=function(){function e(e,t){return function(n,r,o,a){n[e]?n[e](r,o,a):n[t]&&n[t]("on"+r,o)}}return{add:e("addEventListener","attachEvent"),remove:e("removeEventListener","detachEvent")}})?r.call(t,n,t,e):r)||(e.exports=o)},function(e,t,n){(function(t){var n=/^\s+|\s+$/g,r=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,a=/^0o[0-7]+$/i,i=parseInt,c="object"==typeof t&&t&&t.Object===Object&&t,s="object"==typeof self&&self&&self.Object===Object&&self,u=c||s||Function("return this")(),l=Object.prototype.toString,f=Math.max,p=Math.min,d=function(){return u.Date.now()};function v(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function m(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Symbol]"==l.call(e)}(e))return NaN;if(v(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=v(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(n,"");var c=o.test(e);return c||a.test(e)?i(e.slice(2),c?2:8):r.test(e)?NaN:+e}e.exports=function(e,t,n){var r,o,a,i,c,s,u=0,l=!1,b=!1,h=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function y(t){var n=r,a=o;return r=o=void 0,u=t,i=e.apply(a,n)}function g(e){return u=e,c=setTimeout(_,t),l?y(e):i}function j(e){var n=e-s;return void 0===s||n>=t||n<0||b&&e-u>=a}function _(){var e=d();if(j(e))return x(e);c=setTimeout(_,function(e){var n=t-(e-s);return b?p(n,a-(e-u)):n}(e))}function x(e){return c=void 0,h&&r?y(e):(r=o=void 0,i)}function O(){var e=d(),n=j(e);if(r=arguments,o=this,s=e,n){if(void 0===c)return g(s);if(b)return c=setTimeout(_,t),y(s)}return void 0===c&&(c=setTimeout(_,t)),i}return t=m(t)||0,v(n)&&(l=!!n.leading,a=(b="maxWait"in n)?f(m(n.maxWait)||0,t):a,h="trailing"in n?!!n.trailing:h),O.cancel=function(){void 0!==c&&clearTimeout(c),u=0,r=s=o=c=void 0},O.flush=function(){return void 0===c?i:x(d())},O}}).call(this,n(15))},function(e,t,n){(function(t){var n=/^\s+|\s+$/g,r=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,a=/^0o[0-7]+$/i,i=parseInt,c="object"==typeof t&&t&&t.Object===Object&&t,s="object"==typeof self&&self&&self.Object===Object&&self,u=c||s||Function("return this")(),l=Object.prototype.toString,f=Math.max,p=Math.min,d=function(){return u.Date.now()};function v(e,t,n){var r,o,a,i,c,s,u=0,l=!1,v=!1,h=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function y(t){var n=r,a=o;return r=o=void 0,u=t,i=e.apply(a,n)}function g(e){return u=e,c=setTimeout(_,t),l?y(e):i}function j(e){var n=e-s;return void 0===s||n>=t||n<0||v&&e-u>=a}function _(){var e=d();if(j(e))return x(e);c=setTimeout(_,function(e){var n=t-(e-s);return v?p(n,a-(e-u)):n}(e))}function x(e){return c=void 0,h&&r?y(e):(r=o=void 0,i)}function O(){var e=d(),n=j(e);if(r=arguments,o=this,s=e,n){if(void 0===c)return g(s);if(v)return c=setTimeout(_,t),y(s)}return void 0===c&&(c=setTimeout(_,t)),i}return t=b(t)||0,m(n)&&(l=!!n.leading,a=(v="maxWait"in n)?f(b(n.maxWait)||0,t):a,h="trailing"in n?!!n.trailing:h),O.cancel=function(){void 0!==c&&clearTimeout(c),u=0,r=s=o=c=void 0},O.flush=function(){return void 0===c?i:x(d())},O}function m(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function b(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Symbol]"==l.call(e)}(e))return NaN;if(m(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=m(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(n,"");var c=o.test(e);return c||a.test(e)?i(e.slice(2),c?2:8):r.test(e)?NaN:+e}e.exports=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new TypeError("Expected a function");return m(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),v(e,t,{leading:r,maxWait:t,trailing:o})}}).call(this,n(15))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e,t){return"undefined"!=typeof getComputedStyle?getComputedStyle(e,null).getPropertyValue(t):e.style[t]},o=function(e){return r(e,"overflow")+r(e,"overflow-y")+r(e,"overflow-x")};t.default=function(e){if(!(e instanceof HTMLElement))return window;for(var t=e;t&&t!==document.body&&t!==document.documentElement&&t.parentNode;){if(/(scroll|auto)/.test(o(t)))return t;t=t.parentNode}return window}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function e(t,n,r){if(function(e){return null===e.offsetParent}(t))return!1;var o=void 0,i=void 0,c=void 0,s=void 0;if(void 0===n||n===window)o=window.pageYOffset,c=window.pageXOffset,i=o+window.innerHeight,s=c+window.innerWidth;else{if(!e(n,window,r))return!1;var u=(0,a.default)(n);o=u.top,c=u.left,i=o+n.offsetHeight,s=c+n.offsetWidth}var l=(0,a.default)(t);return o<=l.top+t.offsetHeight+r.top&&i>=l.top-r.bottom&&c<=l.left+t.offsetWidth+r.left&&s>=l.left-r.right};var r,o=n(144),a=(r=o)&&r.__esModule?r:{default:r}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=e.getBoundingClientRect();return{top:t.top+window.pageYOffset,left:t.left+window.pageXOffset}}}]);
class-kadence-starter-templates.php ADDED
@@ -0,0 +1,1207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Importer class.
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ use function activate_plugin;
11
+ use function plugins_api;
12
+ use function wp_send_json_error;
13
+
14
+ /**
15
+ * Block direct access to the main plugin file.
16
+ */
17
+ if ( ! defined( 'ABSPATH' ) ) {
18
+ exit;
19
+ }
20
+ /**
21
+ * Main plugin class with initialization tasks.
22
+ */
23
+ class Starter_Templates {
24
+ /**
25
+ * Instance of this class
26
+ *
27
+ * @var null
28
+ */
29
+ private static $instance = null;
30
+
31
+ /**
32
+ * The instance of the Importer class.
33
+ *
34
+ * @var object
35
+ */
36
+ public $importer;
37
+
38
+ /**
39
+ * The resulting page's hook_suffix, or false if the user does not have the capability required.
40
+ *
41
+ * @var boolean or string
42
+ */
43
+ private $plugin_page;
44
+
45
+ /**
46
+ * Holds the verified import files.
47
+ *
48
+ * @var array
49
+ */
50
+ public $import_files;
51
+
52
+ /**
53
+ * The path of the log file.
54
+ *
55
+ * @var string
56
+ */
57
+ public $log_file_path;
58
+
59
+ /**
60
+ * The index of the `import_files` array (which import files was selected).
61
+ *
62
+ * @var int
63
+ */
64
+ private $selected_index;
65
+
66
+ /**
67
+ * The palette for the import.
68
+ *
69
+ * @var string
70
+ */
71
+ private $selected_palette;
72
+
73
+ /**
74
+ * The paths of the actual import files to be used in the import.
75
+ *
76
+ * @var array
77
+ */
78
+ private $selected_import_files;
79
+
80
+ /**
81
+ * Holds any error messages, that should be printed out at the end of the import.
82
+ *
83
+ * @var string
84
+ */
85
+ public $frontend_error_messages = array();
86
+
87
+ /**
88
+ * Was the before content import already triggered?
89
+ *
90
+ * @var boolean
91
+ */
92
+ private $before_import_executed = false;
93
+
94
+ /**
95
+ * Make plugin page options available to other methods.
96
+ *
97
+ * @var array
98
+ */
99
+ private $plugin_page_setup = array();
100
+
101
+ /**
102
+ * Instance Control
103
+ */
104
+ public static function get_instance() {
105
+ if ( is_null( self::$instance ) ) {
106
+ self::$instance = new self();
107
+ }
108
+ return self::$instance;
109
+ }
110
+ /**
111
+ * Construct function
112
+ */
113
+ public function __construct() {
114
+ // Set plugin constants.
115
+ $this->set_plugin_constants();
116
+ $this->include_plugin_files();
117
+ add_action( 'init', array( $this, 'init_config' ) );
118
+ add_action( 'wp_ajax_kadence_import_demo_data', array( $this, 'import_demo_data_ajax_callback' ) );
119
+ add_action( 'wp_ajax_kadence_import_install_plugins', array( $this, 'install_plugins_ajax_callback' ) );
120
+ add_action( 'wp_ajax_kadence_import_customizer_data', array( $this, 'import_customizer_data_ajax_callback' ) );
121
+ add_action( 'wp_ajax_kadence_after_import_data', array( $this, 'after_all_import_data_ajax_callback' ) );
122
+ add_action( 'after_setup_theme', array( $this, 'setup_plugin_with_filter_data' ) );
123
+ add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
124
+ add_filter( 'kadence-starter-templates/import_files', array( $this, 'kadence_import_kadence_theme_files' ) );
125
+ add_action( 'kadence-starter-templates/after_import', array( $this, 'kadence_kadence_theme_after_import' ), 10, 2 );
126
+
127
+ add_filter( 'plugin_action_links_kadence-starter-templates/kadence-starter-templates.php', array( $this, 'add_settings_link' ) );
128
+ }
129
+ /**
130
+ * Add a little css for submenu items.
131
+ */
132
+ public function basic_css_menu_support() {
133
+ wp_register_style( 'kadence-import-admin', false );
134
+ wp_enqueue_style( 'kadence-import-admin' );
135
+ $css = '#menu-appearance .wp-submenu a[href^="themes.php?page=kadence-"]:before {content: "\21B3";margin-right: 0.5em;opacity: 0.5;}';
136
+ wp_add_inline_style( 'kadence-import-admin', $css );
137
+ }
138
+ /**
139
+ * Kadence Import
140
+ */
141
+ public function init_config() {
142
+ if ( class_exists( 'Kadence\Theme' ) ) {
143
+ add_action( 'kadence_theme_admin_menu', array( $this, 'create_admin_page' ) );
144
+ add_action( 'admin_enqueue_scripts', array( $this, 'basic_css_menu_support' ) );
145
+ } else {
146
+ add_action( 'admin_menu', array( $this, 'create_admin_page' ) );
147
+ }
148
+ }
149
+ /**
150
+ * Kadence After Import functions.
151
+ *
152
+ * @param array $selected_import the selected import.
153
+ */
154
+ public function kadence_kadence_theme_after_import( $selected_import, $selected_palette ) {
155
+ if ( 'agency' === $selected_import['import_file_name'] ) {
156
+
157
+ // Assign menus to their locations.
158
+ $main_menu = get_term_by( 'name', 'Agency Menu', 'nav_menu' );
159
+
160
+ set_theme_mod(
161
+ 'nav_menu_locations',
162
+ array(
163
+ 'primary' => $main_menu->term_id,
164
+ 'mobile' => $main_menu->term_id,
165
+ )
166
+ );
167
+
168
+ // Assign front page.
169
+ $homepage = get_page_by_title( 'Home' );
170
+ if ( isset( $homepage ) && $homepage->ID ) {
171
+ update_option( 'show_on_front', 'page' );
172
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
173
+ }
174
+ wp_delete_post( 1 );
175
+
176
+ } elseif ( 'agency_free' === $selected_import['import_file_name'] ) {
177
+
178
+ // Assign menus to their locations.
179
+ $main_menu = get_term_by( 'name', 'Agency Menu', 'nav_menu' );
180
+
181
+ set_theme_mod(
182
+ 'nav_menu_locations',
183
+ array(
184
+ 'primary' => $main_menu->term_id,
185
+ 'mobile' => $main_menu->term_id,
186
+ )
187
+ );
188
+
189
+ // Assign front page.
190
+ $homepage = get_page_by_title( 'Home' );
191
+ if ( isset( $homepage ) && $homepage->ID ) {
192
+ update_option( 'show_on_front', 'page' );
193
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
194
+ $blogpage = get_page_by_title( 'Blog' );
195
+ update_option( 'page_for_posts', $blogpage->ID ); // Blog Page.
196
+ }
197
+ wp_delete_post( 1 );
198
+
199
+ } elseif ( 'food' === $selected_import['import_file_name'] ) {
200
+
201
+ // Assign menus to their locations.
202
+ $main_menu = get_term_by( 'name', 'Food Primary', 'nav_menu' );
203
+
204
+ set_theme_mod(
205
+ 'nav_menu_locations',
206
+ array(
207
+ 'primary' => $main_menu->term_id,
208
+ 'mobile' => $main_menu->term_id,
209
+ 'footer' => $main_menu->term_id,
210
+ )
211
+ );
212
+
213
+ // Assign front page.
214
+ $homepage = get_page_by_title( 'Home' );
215
+ if ( isset( $homepage ) && $homepage->ID ) {
216
+ update_option( 'show_on_front', 'page' );
217
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
218
+ $blogpage = get_page_by_title( 'Recipes' );
219
+ update_option( 'page_for_posts', $blogpage->ID ); // Blog Page.
220
+ }
221
+ // Remove Hello Post.
222
+ wp_delete_post( 1 );
223
+
224
+ } elseif ( 'shopping' === $selected_import['import_file_name'] ) {
225
+ // Assign Woo Pages.
226
+ if ( class_exists( 'woocommerce' ) ) {
227
+ $this->import_demo_woocommerce();
228
+ }
229
+
230
+ // Assign menus to their locations.
231
+ $main_menu = get_term_by( 'name', 'Shop Menu', 'nav_menu' );
232
+
233
+ set_theme_mod(
234
+ 'nav_menu_locations',
235
+ array(
236
+ 'primary' => $main_menu->term_id,
237
+ 'mobile' => $main_menu->term_id,
238
+ )
239
+ );
240
+
241
+ // Assign front page.
242
+ $homepage = get_page_by_title( 'Home' );
243
+ if ( isset( $homepage ) && $homepage->ID ) {
244
+ update_option( 'show_on_front', 'page' );
245
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
246
+ $blogpage = get_page_by_title( 'Shop News' );
247
+ update_option( 'page_for_posts', $blogpage->ID ); // Blog Page.
248
+ }
249
+ // Remove Hello Post.
250
+ wp_delete_post( 1 );
251
+
252
+ } elseif ( 'sass' === $selected_import['import_file_name'] ) {
253
+ // Assign menus to their locations.
254
+ $main_menu = get_term_by( 'name', 'Sass Menu', 'nav_menu' );
255
+ $footer_menu = get_term_by( 'name', 'Sass Footer', 'nav_menu' );
256
+
257
+ set_theme_mod(
258
+ 'nav_menu_locations',
259
+ array(
260
+ 'primary' => $main_menu->term_id,
261
+ 'mobile' => $main_menu->term_id,
262
+ 'footer' => $footer_menu->term_id,
263
+ )
264
+ );
265
+
266
+ // Assign front page.
267
+ $homepage = get_page_by_title( 'Home' );
268
+ if ( isset( $homepage ) && $homepage->ID ) {
269
+ update_option( 'show_on_front', 'page' );
270
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
271
+ }
272
+ // Remove Hello Post.
273
+ wp_delete_post( 1 );
274
+
275
+ } elseif ( 'yoga' === $selected_import['import_file_name'] ) {
276
+ // Assign menus to their locations.
277
+ $main_menu = get_term_by( 'name', 'Yoga Menu', 'nav_menu' );
278
+
279
+ set_theme_mod(
280
+ 'nav_menu_locations',
281
+ array(
282
+ 'primary' => $main_menu->term_id,
283
+ 'mobile' => $main_menu->term_id,
284
+ )
285
+ );
286
+
287
+ // Assign front page.
288
+ $homepage = get_page_by_title( 'Home' );
289
+ if ( isset( $homepage ) && $homepage->ID ) {
290
+ update_option( 'show_on_front', 'page' );
291
+ update_option( 'page_on_front', $homepage->ID ); // Front Page.
292
+ }
293
+ // Remove Hello Post.
294
+ wp_delete_post( 1 );
295
+
296
+ }
297
+ if ( $selected_palette && ! empty( $selected_palette ) ) {
298
+ $palette_presets = json_decode( '{"basic":[{"color":"#2B6CB0"},{"color":"#265E9A"},{"color":"#222222"},{"color":"#3B3B3B"},{"color":"#515151"},{"color":"#626262"},{"color":"#E1E1E1"},{"color":"#F7F7F7"},{"color":"#ffffff"}],"bright":[{"color":"#255FDD"},{"color":"#00F2FF"},{"color":"#1A202C"},{"color":"#2D3748"},{"color":"#4A5568"},{"color":"#718096"},{"color":"#EDF2F7"},{"color":"#F7FAFC"},{"color":"#ffffff"}],"darkmode":[{"color":"#3296ff"},{"color":"#003174"},{"color":"#ffffff"},{"color":"#f7fafc"},{"color":"#edf2f7"},{"color":"#cbd2d9"},{"color":"#2d3748"},{"color":"#252c39"},{"color":"#1a202c"}],"orange":[{"color":"#e47b02"},{"color":"#ed8f0c"},{"color":"#1f2933"},{"color":"#3e4c59"},{"color":"#52606d"},{"color":"#7b8794"},{"color":"#f3f4f7"},{"color":"#f9f9fb"},{"color":"#ffffff"}],"pinkish":[{"color":"#E21E51"},{"color":"#4d40ff"},{"color":"#040037"},{"color":"#032075"},{"color":"#514d7c"},{"color":"#666699"},{"color":"#deddeb"},{"color":"#efeff5"},{"color":"#f8f9fa"}],"pinkishdark":[{"color":"#E21E51"},{"color":"#4d40ff"},{"color":"#f8f9fa"},{"color":"#efeff5"},{"color":"#deddeb"},{"color":"#c3c2d6"},{"color":"#514d7c"},{"color":"#221e5b"},{"color":"#040037"}],"green":[{"color":"#049f82"},{"color":"#008f72"},{"color":"#222222"},{"color":"#353535"},{"color":"#454545"},{"color":"#676767"},{"color":"#eeeeee"},{"color":"#f7f7f7"},{"color":"#ffffff"}],"fire":[{"color":"#dd6b20"},{"color":"#cf3033"},{"color":"#27241d"},{"color":"#423d33"},{"color":"#504a40"},{"color":"#625d52"},{"color":"#e8e6e1"},{"color":"#faf9f7"},{"color":"#ffffff"}]}', true );
299
+ if ( isset( $palette_presets[ $selected_palette ] ) ) {
300
+ $default = json_decode( '{"palette":[{"color":"#3182CE","slug":"palette1","name":"Palette Color 1"},{"color":"#2B6CB0","slug":"palette2","name":"Palette Color 2"},{"color":"#1A202C","slug":"palette3","name":"Palette Color 3"},{"color":"#2D3748","slug":"palette4","name":"Palette Color 4"},{"color":"#4A5568","slug":"palette5","name":"Palette Color 5"},{"color":"#718096","slug":"palette6","name":"Palette Color 6"},{"color":"#EDF2F7","slug":"palette7","name":"Palette Color 7"},{"color":"#F7FAFC","slug":"palette8","name":"Palette Color 8"},{"color":"#ffffff","slug":"palette9","name":"Palette Color 9"}],"second-palette":[{"color":"#3182CE","slug":"palette1","name":"Palette Color 1"},{"color":"#2B6CB0","slug":"palette2","name":"Palette Color 2"},{"color":"#1A202C","slug":"palette3","name":"Palette Color 3"},{"color":"#2D3748","slug":"palette4","name":"Palette Color 4"},{"color":"#4A5568","slug":"palette5","name":"Palette Color 5"},{"color":"#718096","slug":"palette6","name":"Palette Color 6"},{"color":"#EDF2F7","slug":"palette7","name":"Palette Color 7"},{"color":"#F7FAFC","slug":"palette8","name":"Palette Color 8"},{"color":"#ffffff","slug":"palette9","name":"Palette Color 9"}],"third-palette":[{"color":"#3182CE","slug":"palette1","name":"Palette Color 1"},{"color":"#2B6CB0","slug":"palette2","name":"Palette Color 2"},{"color":"#1A202C","slug":"palette3","name":"Palette Color 3"},{"color":"#2D3748","slug":"palette4","name":"Palette Color 4"},{"color":"#4A5568","slug":"palette5","name":"Palette Color 5"},{"color":"#718096","slug":"palette6","name":"Palette Color 6"},{"color":"#EDF2F7","slug":"palette7","name":"Palette Color 7"},{"color":"#F7FAFC","slug":"palette8","name":"Palette Color 8"},{"color":"#ffffff","slug":"palette9","name":"Palette Color 9"}],"active":"palette"}', true );
301
+ $default['palette'][0]['color'] = $palette_presets[ $selected_palette ][0]['color'];
302
+ $default['palette'][1]['color'] = $palette_presets[ $selected_palette ][1]['color'];
303
+ $default['palette'][2]['color'] = $palette_presets[ $selected_palette ][2]['color'];
304
+ $default['palette'][3]['color'] = $palette_presets[ $selected_palette ][3]['color'];
305
+ $default['palette'][4]['color'] = $palette_presets[ $selected_palette ][4]['color'];
306
+ $default['palette'][5]['color'] = $palette_presets[ $selected_palette ][5]['color'];
307
+ $default['palette'][6]['color'] = $palette_presets[ $selected_palette ][6]['color'];
308
+ $default['palette'][7]['color'] = $palette_presets[ $selected_palette ][7]['color'];
309
+ $default['palette'][8]['color'] = $palette_presets[ $selected_palette ][8]['color'];
310
+ update_option( 'kadence_global_palette', json_encode( $default ) );
311
+ }
312
+ }
313
+ }
314
+ /**
315
+ * Kadence Import function.
316
+ */
317
+ public function import_demo_woocommerce( $shop = 'Shop', $cart = 'Cart', $checkout = 'Checkout', $myaccount = 'My Account' ) {
318
+ $woopages = array(
319
+ 'woocommerce_shop_page_id' => $shop,
320
+ 'woocommerce_cart_page_id' => $cart,
321
+ 'woocommerce_checkout_page_id' => $checkout,
322
+ 'woocommerce_myaccount_page_id' => $myaccount,
323
+ );
324
+ foreach ( $woopages as $woo_page_name => $woo_page_title ) {
325
+ $woopage = get_page_by_title( $woo_page_title );
326
+ if ( isset( $woopage ) && $woopage->ID ) {
327
+ update_option( $woo_page_name, $woopage->ID );
328
+ }
329
+ }
330
+
331
+ // We no longer need to install pages.
332
+ delete_option( '_wc_needs_pages' );
333
+ delete_transient( '_wc_activation_redirect' );
334
+
335
+ // Flush rules after install.
336
+ flush_rewrite_rules();
337
+ }
338
+ /**
339
+ * Kadence Import function.
340
+ */
341
+ public function kadence_import_kadence_theme_files() {
342
+ $woocommerce = array(
343
+ 'base' => 'woocommerce',
344
+ 'slug' => 'woocommerce',
345
+ 'path' => 'woocommerce/woocommerce.php',
346
+ 'title' => 'Woocommerce',
347
+ 'bundled' => '0',
348
+ 'state' => Plugin_Check::active_check( 'woocommerce/woocommerce.php' ),
349
+ );
350
+ $kadence_blocks = array(
351
+ 'base' => 'kadence-blocks',
352
+ 'slug' => 'kadence-blocks',
353
+ 'path' => 'kadence-blocks/kadence-blocks.php',
354
+ 'title' => 'Kadence Blocks',
355
+ 'bundled' => '0',
356
+ 'state' => Plugin_Check::active_check( 'kadence-blocks/kadence-blocks.php' ),
357
+ );
358
+ $kadence_blocks_pro = array(
359
+ 'base' => 'kadence-blocks-pro',
360
+ 'slug' => 'kadence-blocks-pro',
361
+ 'path' => 'kadence-blocks-pro/kadence-blocks-pro.php',
362
+ 'title' => 'Kadence Block Pro',
363
+ 'bundled' => '1',
364
+ 'state' => Plugin_Check::active_check( 'kadence-blocks-pro/kadence-blocks-pro.php' ),
365
+ );
366
+ $wpzoom_recipe_card = array(
367
+ 'base' => 'recipe-card-blocks-by-wpzoom',
368
+ 'slug' => 'wpzoom-recipe-card',
369
+ 'path' => 'recipe-card-blocks-by-wpzoom/wpzoom-recipe-card.php',
370
+ 'title' => 'Recipe Card Blocks by WPZOOM',
371
+ 'bundled' => '0',
372
+ 'state' => Plugin_Check::active_check( 'recipe-card-blocks-by-wpzoom/wpzoom-recipe-card.php' ),
373
+ );
374
+ if ( 'notactive' !== $kadence_blocks_pro['state'] ) {
375
+ $agency = array(
376
+ 'import_file_name' => 'agency',
377
+ 'categories' => array( 'Kadence Blocks Pro' ),
378
+ 'import_file_url' => 'https://kadence.design/importer/kadence/agency_pro/demo_content.xml',
379
+ 'import_widget_file_url' => '',
380
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/agency_pro/theme_options.json',
381
+ 'preview_url' => 'https://demos.kadencewp.com/blocks-agency/',
382
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/agency_pro/preview-image.jpg',
383
+ 'import_notice' => '',
384
+ 'plugins' => array(
385
+ $kadence_blocks,
386
+ $kadence_blocks_pro
387
+ ),
388
+ );
389
+ } else {
390
+ $agency = array(
391
+ 'import_file_name' => 'agency_free',
392
+ 'categories' => array(),
393
+ 'import_file_url' => 'https://kadence.design/importer/kadence/agency/demo_content.xml',
394
+ 'import_widget_file_url' => '',
395
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/agency/theme_options.json',
396
+ 'preview_url' => 'https://demos.kadencewp.com/agency-free/',
397
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/agency/preview-image.jpg',
398
+ 'import_notice' => '',
399
+ 'plugins' => array(
400
+ $kadence_blocks,
401
+ ),
402
+ );
403
+ }
404
+ $demos = array(
405
+ $agency,
406
+ array(
407
+ 'import_file_name' => 'food',
408
+ 'categories' => array(),
409
+ 'import_file_url' => 'https://kadence.design/importer/kadence/recipe_blog/demo_content.xml',
410
+ 'import_widget_file_url' => '',
411
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/recipe_blog/theme_options.json',
412
+ 'preview_url' => 'https://demos.kadencewp.com/food/',
413
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/recipe_blog/preview-image.jpg',
414
+ 'import_notice' => '',
415
+ 'plugins' => array(
416
+ $kadence_blocks,
417
+ $wpzoom_recipe_card,
418
+ ),
419
+ ),
420
+ array(
421
+ 'import_file_name' => 'shopping',
422
+ 'categories' => array( 'Woocommerce' ),
423
+ 'import_file_url' => 'https://kadence.design/importer/kadence/shopping_site/demo_content.xml',
424
+ 'import_widget_file_url' => 'https://kadence.design/importer/kadence/shopping_site/widget_data.json',
425
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/shopping_site/theme_options.json',
426
+ 'preview_url' => 'https://demos.kadencewp.com/blocks-store/',
427
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/shopping_site/preview-image.jpg',
428
+ 'import_notice' => '',
429
+ 'plugins' => array(
430
+ $kadence_blocks,
431
+ $woocommerce,
432
+ ),
433
+ ),
434
+ array(
435
+ 'import_file_name' => 'yoga',
436
+ 'categories' => array( 'Business' ),
437
+ 'import_file_url' => 'https://kadence.design/importer/kadence/yoga_site/demo_content.xml',
438
+ 'import_widget_file_url' => '',
439
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/yoga_site/theme_options.json',
440
+ 'preview_url' => 'https://demos.kadencewp.com/blocks-active/',
441
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/yoga_site/preview-image.jpg',
442
+ 'import_notice' => '',
443
+ 'plugins' => array(
444
+ $kadence_blocks,
445
+ ),
446
+ ),
447
+ array(
448
+ 'import_file_name' => 'sass',
449
+ 'categories' => array( 'Business' ),
450
+ 'import_file_url' => 'https://kadence.design/importer/kadence/sass_site/demo_content.xml',
451
+ 'import_widget_file_url' => '',
452
+ 'import_customizer_file_url' => 'https://kadence.design/importer/kadence/sass_site/theme_options.json',
453
+ 'preview_url' => 'https://demos.kadencewp.com/blocks-saas/',
454
+ 'import_preview_image_url' => 'https://kadence.design/importer/kadence/sass_site/preview-image.jpg',
455
+ 'import_notice' => '',
456
+ 'plugins' => array(
457
+ $kadence_blocks,
458
+ ),
459
+ ),
460
+ );
461
+ return $demos;
462
+ }
463
+ /**
464
+ * Private clone method to prevent cloning of the instance of the *Singleton* instance.
465
+ *
466
+ * @return void
467
+ */
468
+ private function __clone() {}
469
+
470
+
471
+ /**
472
+ * Private unserialize method to prevent unserializing of the *Singleton* instance.
473
+ *
474
+ * @return void
475
+ */
476
+ private function __wakeup() {}
477
+
478
+ /**
479
+ * Set plugin constants.
480
+ *
481
+ * Path/URL to root of this plugin, with trailing slash and plugin version.
482
+ */
483
+ private function set_plugin_constants() {
484
+ // Path/URL to root of this plugin, with trailing slash.
485
+ if ( ! defined( 'KADENCE_STARTER_TEMPLATES_PATH' ) ) {
486
+ define( 'KADENCE_STARTER_TEMPLATES_PATH', plugin_dir_path( __FILE__ ) );
487
+ }
488
+ if ( ! defined( 'KADENCE_STARTER_TEMPLATES_URL' ) ) {
489
+ define( 'KADENCE_STARTER_TEMPLATES_URL', trailingslashit( plugin_dir_url( __FILE__ ) ) );
490
+ }
491
+ if ( ! defined( 'KADENCE_STARTER_TEMPLATES_VERSION' ) ) {
492
+ define( 'KADENCE_STARTER_TEMPLATES_VERSION', '1.0.0' );
493
+ }
494
+ }
495
+ /**
496
+ * Include all plugin files.
497
+ */
498
+ private function include_plugin_files() {
499
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-plugin-check.php';
500
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-helpers.php';
501
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-import-actions.php';
502
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-widget-importer.php';
503
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-logger.php';
504
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-logger-cli.php';
505
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-importer.php';
506
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-downloader.php';
507
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-customizer-importer.php';
508
+ }
509
+
510
+ /**
511
+ * Add settings link
512
+ *
513
+ * @param array $links holds plugin links.
514
+ */
515
+ public function add_settings_link( $links ) {
516
+ $settings_link = '<a href="' . admin_url( 'themes.php?page=kadence-starter-templates' ) . '">' . __( 'View Template Library', 'kadence-starter-templates' ) . '</a>';
517
+ array_unshift( $links, $settings_link );
518
+ return $links;
519
+ }
520
+
521
+ /**
522
+ * Creates the plugin page and a submenu item in WP Appearance menu.
523
+ */
524
+ public function create_admin_page() {
525
+ $page = add_theme_page(
526
+ esc_html__( 'Starter Templates by Kadence WP', 'kadence-starter-templates' ),
527
+ esc_html__( 'Starter Templates', 'kadence-starter-templates' ),
528
+ 'import',
529
+ 'kadence-starter-templates',
530
+ array( $this, 'render_admin_page' )
531
+ );
532
+ add_action( 'admin_print_styles-' . $page, array( $this, 'scripts' ) );
533
+ }
534
+
535
+ /**
536
+ * Plugin page display.
537
+ * Output (HTML) is in another file.
538
+ */
539
+ public function render_admin_page() {
540
+ ?>
541
+ <div class="kadence_theme_dash_head">
542
+ <div class="kadence_theme_dash_head_container">
543
+ <div class="kadence_theme_dash_logo">
544
+ <img src="<?php echo esc_attr( KADENCE_STARTER_TEMPLATES_URL . 'assets/images/kadence_logo.png' ); ?>">
545
+ </div>
546
+ <div class="kadence_theme_dash_logo_title">
547
+ <h1>
548
+ KADENCE STARTER TEMPLATES
549
+ </h1>
550
+ </div>
551
+ <div class="kadence_theme_dash_version">
552
+ <span>
553
+ <?php echo esc_html( KADENCE_STARTER_TEMPLATES_VERSION ); ?>
554
+ </span>
555
+ </div>
556
+ </div>
557
+ </div>
558
+ <div class="wrap kadence_theme_dash">
559
+ <div class="kadence_theme_dashboard">
560
+ <h2 class="notices" style="display:none;"></h2>
561
+ <?php settings_errors(); ?>
562
+ <div class="kadence_starter_templates_finished"></div>
563
+ <div class="kadence_starter_templates_error"></div>
564
+ <div class="page-grid">
565
+ <div class="kadence_starter_dashboard_main">
566
+ </div>
567
+ </div>
568
+ </div>
569
+ <?php
570
+ }
571
+
572
+ /**
573
+ * Loads admin style sheets and scripts
574
+ */
575
+ public function scripts() {
576
+ $woocommerce = array(
577
+ 'title' => 'Woocommerce',
578
+ 'state' => Plugin_Check::active_check( 'woocommerce/woocommerce.php' ),
579
+ );
580
+ $kadence_blocks = array(
581
+ 'title' => 'Kadence Blocks',
582
+ 'state' => Plugin_Check::active_check( 'kadence-blocks/kadence-blocks.php' ),
583
+ );
584
+ $kadence_blocks_pro = array(
585
+ 'title' => 'Kadence Block Pro',
586
+ 'state' => Plugin_Check::active_check( 'kadence-blocks-pro/kadence-blocks-pro.php' ),
587
+ );
588
+ $wpzoom_recipe_card = array(
589
+ 'title' => 'Recipe Card Blocks by WPZOOM',
590
+ 'state' => Plugin_Check::active_check( 'recipe-card-blocks-by-wpzoom/wpzoom-recipe-card.php' ),
591
+ );
592
+ if ( 'notactive' !== $kadence_blocks_pro['state'] ) {
593
+ $agency = array(
594
+ 'key' => 0,
595
+ 'slug' => 'agency',
596
+ 'name' => __( 'Agency', 'kadence-starter-templates' ),
597
+ 'keywords' => array(
598
+ __( 'portfolio', 'kadence-starter-templates' ),
599
+ __( 'services', 'kadence-starter-templates' ),
600
+ __( 'business', 'kadence-starter-templates' ),
601
+ __( 'transparent', 'kadence-starter-templates' ),
602
+ ),
603
+ 'url' => 'https://demos.kadencewp.com/blocks-agency/',
604
+ 'categories' => array( 'business' ),
605
+ 'plugins' => array(
606
+ $kadence_blocks,
607
+ $kadence_blocks_pro,
608
+ ),
609
+ 'image' => 'https://kadence.design/importer/kadence/agency/preview-image.jpg',
610
+ );
611
+ } else {
612
+ $agency = array(
613
+ 'key' => 0,
614
+ 'slug' => 'agency_free',
615
+ 'name' => __( 'Agency', 'kadence-starter-templates' ),
616
+ 'keywords' => array(
617
+ __( 'portfolio', 'kadence-starter-templates' ),
618
+ __( 'services', 'kadence-starter-templates' ),
619
+ __( 'business', 'kadence-starter-templates' ),
620
+ __( 'transparent', 'kadence-starter-templates' ),
621
+ ),
622
+ 'url' => 'https://demos.kadencewp.com/agency-free/',
623
+ 'categories' => array( 'business' ),
624
+ 'plugins' => array(
625
+ $kadence_blocks,
626
+ ),
627
+ 'image' => 'https://kadence.design/importer/kadence/agency/preview-image.jpg',
628
+ );
629
+ }
630
+ $templates = array(
631
+ $agency,
632
+ array(
633
+ 'key' => 1,
634
+ 'slug' => 'food',
635
+ 'name' => __( 'Recipe Blog', 'kadence-starter-templates' ),
636
+ 'keywords' => array(
637
+ __( 'blog', 'kadence-starter-templates' ),
638
+ __( 'food', 'kadence-starter-templates' ),
639
+ __( 'recipe', 'kadence-starter-templates' ),
640
+ ),
641
+ 'url' => 'https://demos.kadencewp.com/food/',
642
+ 'categories' => array( 'blog' ),
643
+ 'plugins' => array(
644
+ $kadence_blocks,
645
+ $wpzoom_recipe_card,
646
+ ),
647
+ 'image' => 'https://kadence.design/importer/kadence/recipe_blog/preview-image.jpg',
648
+ ),
649
+ array(
650
+ 'key' => 2,
651
+ 'slug' => 'shopping',
652
+ 'name' => __( 'Shopping', 'kadence-starter-templates' ),
653
+ 'keywords' => array(
654
+ __( 'ecommerce', 'kadence-starter-templates' ),
655
+ __( 'shopping', 'kadence-starter-templates' ),
656
+ __( 'business', 'kadence-starter-templates' ),
657
+ ),
658
+ 'url' => 'https://demos.kadencewp.com/blocks-store/',
659
+ 'categories' => array( 'ecommerce' ),
660
+ 'plugins' => array(
661
+ $kadence_blocks,
662
+ $woocommerce,
663
+ ),
664
+ 'image' => 'https://kadence.design/importer/kadence/shopping_site/preview-image.jpg',
665
+ ),
666
+ array(
667
+ 'key' => 3,
668
+ 'slug' => 'yogo',
669
+ 'name' => __( 'Yoga Studio', 'kadence-starter-templates' ),
670
+ 'keywords' => array(
671
+ __( 'yoga', 'kadence-starter-templates' ),
672
+ __( 'gym', 'kadence-starter-templates' ),
673
+ __( 'business', 'kadence-starter-templates' ),
674
+ ),
675
+ 'url' => 'https://demos.kadencewp.com/blocks-active/',
676
+ 'categories' => array( 'business' ),
677
+ 'plugins' => array(
678
+ $kadence_blocks,
679
+ ),
680
+ 'image' => 'https://kadence.design/importer/kadence/yoga_site/preview-image.jpg',
681
+ ),
682
+ array(
683
+ 'key' => 4,
684
+ 'slug' => 'sass',
685
+ 'name' => __( 'Sass', 'kadence-starter-templates' ),
686
+ 'keywords' => array(
687
+ __( 'sass', 'kadence-starter-templates' ),
688
+ __( 'pricing', 'kadence-starter-templates' ),
689
+ __( 'business', 'kadence-starter-templates' ),
690
+ ),
691
+ 'url' => 'https://demos.kadencewp.com/blocks-saas/',
692
+ 'categories' => array( 'business' ),
693
+ 'plugins' => array(
694
+ $kadence_blocks,
695
+ ),
696
+ 'image' => 'https://kadence.design/importer/kadence/sass_site/preview-image.jpg',
697
+ ),
698
+ );
699
+ $palettes = array(
700
+ array(
701
+ 'palette' => 'base',
702
+ 'colors' => array(
703
+ '#2B6CB0',
704
+ '#265E9A',
705
+ '#222222',
706
+ '#ffffff',
707
+ ),
708
+ ),
709
+ array(
710
+ 'palette' => 'bright',
711
+ 'colors' => array(
712
+ '#255FDD',
713
+ '#00F2FF',
714
+ '#1A202C',
715
+ '#ffffff',
716
+ ),
717
+ ),
718
+ array(
719
+ 'palette' => 'darkmode',
720
+ 'colors' => array(
721
+ '#3296ff',
722
+ '#003174',
723
+ '#ffffff',
724
+ '#1a202c',
725
+ ),
726
+ ),
727
+ array(
728
+ 'palette' => 'orange',
729
+ 'colors' => array(
730
+ '#e47b02',
731
+ '#ed8f0c',
732
+ '#1f2933',
733
+ '#ffffff',
734
+ ),
735
+ ),
736
+ array(
737
+ 'palette' => 'pinkish',
738
+ 'colors' => array(
739
+ '#E21E51',
740
+ '#4d40ff',
741
+ '#040037',
742
+ '#ffffff',
743
+ ),
744
+ ),
745
+ array(
746
+ 'palette' => 'pinkishdark',
747
+ 'colors' => array(
748
+ '#E21E51',
749
+ '#4d40ff',
750
+ '#ffffff',
751
+ '#040037',
752
+ ),
753
+ ),
754
+ array(
755
+ 'palette' => 'green',
756
+ 'colors' => array(
757
+ '#049f82',
758
+ '#008f72',
759
+ '#222222',
760
+ '#ffffff',
761
+ ),
762
+ ),
763
+ array(
764
+ 'palette' => 'fire',
765
+ 'colors' => array(
766
+ '#dd6b20',
767
+ '#cf3033',
768
+ '#27241d',
769
+ '#ffffff',
770
+ ),
771
+ ),
772
+ );
773
+ wp_enqueue_style( 'kadence-starter-templates', KADENCE_STARTER_TEMPLATES_URL . 'assets/css/starter-templates.css', array( 'wp-components' ), KADENCE_STARTER_TEMPLATES_VERSION );
774
+ wp_enqueue_script( 'kadence-starter-templates', KADENCE_STARTER_TEMPLATES_URL . 'assets/js/starter-templates.js', array( 'jquery', 'wp-i18n', 'wp-element', 'wp-plugins', 'wp-components', 'wp-api', 'wp-hooks', 'wp-edit-post', 'lodash', 'wp-block-library', 'wp-block-editor', 'wp-editor' ), KADENCE_STARTER_TEMPLATES_VERSION, true );
775
+ wp_localize_script(
776
+ 'kadence-starter-templates',
777
+ 'kadenceStarterParams',
778
+ array(
779
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
780
+ 'ajax_nonce' => wp_create_nonce( 'kadence-ajax-verification' ),
781
+ 'pro' => false,
782
+ 'isKadence' => class_exists( 'Kadence\Theme' ),
783
+ 'templates' => $templates,
784
+ 'palettes' => $palettes,
785
+ 'notice' => esc_html__( 'Please Note: This importer is designed for new/empty sites with no content.', 'kadence-starter-templates' ),
786
+ 'plugin_progress' => esc_html__( 'Checking/Installing/Activating Required Plugins', 'kadence-starter-templates' ),
787
+ 'content_progress' => esc_html__( 'Importing Demo Content...', 'kadence-starter-templates' ),
788
+ 'content_new_progress' => esc_html__( 'Importing Demo Content... Still Importing.', 'kadence-starter-templates' ),
789
+ 'widget_progress' => esc_html__( 'Importing Menus/Widgets...', 'kadence-starter-templates' ),
790
+ 'customizer_progress' => esc_html__( 'Importing Customizer Settings...', 'kadence-starter-templates' ),
791
+ )
792
+ );
793
+ }
794
+ /**
795
+ * AJAX callback to install a plugin.
796
+ */
797
+ public function install_plugins_ajax_callback() {
798
+ Helpers::verify_ajax_call();
799
+
800
+ if ( ! current_user_can( 'install_plugins' ) || ! isset( $_POST['selected'] ) ) {
801
+ wp_send_json_error();
802
+ }
803
+ // Get selected file index or set it to 0.
804
+ $selected_index = empty( $_POST['selected'] ) ? 0 : absint( $_POST['selected'] );
805
+ $info = $this->import_files[ $selected_index ];
806
+ $install = true;
807
+
808
+ if ( isset( $info['plugins'] ) && ! empty( $info['plugins'] ) ) {
809
+
810
+ if ( ! function_exists( 'plugins_api' ) ) {
811
+ require_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
812
+ }
813
+ if ( ! class_exists( 'WP_Upgrader' ) ) {
814
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
815
+ }
816
+
817
+ foreach( $info['plugins'] as $key => $plugin ) {
818
+ if ( 'notactive' === $plugin['state'] ) {
819
+ $api = plugins_api(
820
+ 'plugin_information',
821
+ array(
822
+ 'slug' => $plugin['base'],
823
+ 'fields' => array(
824
+ 'short_description' => false,
825
+ 'sections' => false,
826
+ 'requires' => false,
827
+ 'rating' => false,
828
+ 'ratings' => false,
829
+ 'downloaded' => false,
830
+ 'last_updated' => false,
831
+ 'added' => false,
832
+ 'tags' => false,
833
+ 'compatibility' => false,
834
+ 'homepage' => false,
835
+ 'donate_link' => false,
836
+ ),
837
+ )
838
+ );
839
+ if ( ! is_wp_error( $api ) ) {
840
+
841
+ // Use AJAX upgrader skin instead of plugin installer skin.
842
+ // ref: function wp_ajax_install_plugin().
843
+ $upgrader = new \Plugin_Upgrader( new \WP_Ajax_Upgrader_Skin() );
844
+
845
+ $installed = $upgrader->install( $api->download_link );
846
+ if ( $installed ) {
847
+ $activate = activate_plugin( $plugin['path'], '', false, true );
848
+ if ( is_wp_error( $activate ) ) {
849
+ $install = false;
850
+ }
851
+ } else {
852
+ $install = false;
853
+ }
854
+ } else {
855
+ $install = false;
856
+ }
857
+ } elseif ( 'installed' === $plugin['state'] ) {
858
+ $activate = activate_plugin( $plugin['path'], '', false, true );
859
+ if ( is_wp_error( $activate ) ) {
860
+ $install = false;
861
+ }
862
+ }
863
+ }
864
+ }
865
+
866
+ if ( false === $install ) {
867
+ wp_send_json_error();
868
+ } else {
869
+ wp_send_json( array( 'status' => 'pluginSuccess' ) );
870
+ }
871
+ }
872
+
873
+ /**
874
+ * Main AJAX callback function for:
875
+ * 1). prepare import files (uploaded or predefined via filters)
876
+ * 2). execute 'before content import' actions (before import WP action)
877
+ * 3). import content
878
+ * 4). execute 'after content import' actions (before widget import WP action, widget import, customizer import, after import WP action)
879
+ */
880
+ public function import_demo_data_ajax_callback() {
881
+ // Try to update PHP memory limit (so that it does not run out of it).
882
+ ini_set( 'memory_limit', apply_filters( 'kadence-starter-templates/import_memory_limit', '350M' ) );
883
+
884
+ // Verify if the AJAX call is valid (checks nonce and current_user_can).
885
+ Helpers::verify_ajax_call();
886
+
887
+ // Is this a new AJAX call to continue the previous import?
888
+ $use_existing_importer_data = $this->use_existing_importer_data();
889
+
890
+ if ( ! $use_existing_importer_data ) {
891
+ // Create a date and time string to use for demo and log file names.
892
+ Helpers::set_demo_import_start_time();
893
+
894
+ // Define log file path.
895
+ $this->log_file_path = Helpers::get_log_path();
896
+
897
+ // Get selected file index or set it to 0.
898
+ $this->selected_index = empty( $_POST['selected'] ) ? 0 : absint( $_POST['selected'] );
899
+ $this->selected_palette = empty( $_POST['palette'] ) ? '' : sanitize_text_field( $_POST['palette'] );
900
+ /**
901
+ * 1). Prepare import files.
902
+ * Predefined import files via filter: kadence-starter-templates/import_files
903
+ */
904
+ if ( ! empty( $this->import_files[ $this->selected_index ] ) ) { // Use predefined import files from wp filter: kadence-starter-templates/import_files.
905
+
906
+ // Download the import files (content, widgets and customizer files).
907
+ $this->selected_import_files = Helpers::download_import_files( $this->import_files[ $this->selected_index ] );
908
+
909
+ // Check Errors.
910
+ if ( is_wp_error( $this->selected_import_files ) ) {
911
+ // Write error to log file and send an AJAX response with the error.
912
+ Helpers::log_error_and_send_ajax_response(
913
+ $this->selected_import_files->get_error_message(),
914
+ $this->log_file_path,
915
+ esc_html__( 'Downloaded files', 'kadence-starter-templates' )
916
+ );
917
+ }
918
+
919
+ // Add this message to log file.
920
+ $log_added = Helpers::append_to_file(
921
+ sprintf(
922
+ __( 'The import files for: %s were successfully downloaded!', 'kadence-starter-templates' ),
923
+ $this->import_files[ $this->selected_index ]['import_file_name']
924
+ ) . Helpers::import_file_info( $this->selected_import_files ),
925
+ $this->log_file_path,
926
+ esc_html__( 'Downloaded files' , 'kadence-starter-templates' )
927
+ );
928
+ } else {
929
+ // Send JSON Error response to the AJAX call.
930
+ wp_send_json( esc_html__( 'No import files specified!', 'kadence-starter-templates' ) );
931
+ }
932
+ }
933
+
934
+ // Save the initial import data as a transient, so other import parts (in new AJAX calls) can use that data.
935
+ Helpers::set_import_data_transient( $this->get_current_importer_data() );
936
+
937
+ if ( ! $this->before_import_executed ) {
938
+ $this->before_import_executed = true;
939
+
940
+ /**
941
+ * 2). Execute the actions hooked to the 'kadence-starter-templates/before_content_import_execution' action:
942
+ *
943
+ * Default actions:
944
+ * 1 - Before content import WP action (with priority 10).
945
+ */
946
+ do_action( 'kadence-starter-templates/before_content_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index, $this->selected_palette );
947
+ }
948
+
949
+ /**
950
+ * 3). Import content (if the content XML file is set for this import).
951
+ * Returns any errors greater then the "warning" logger level, that will be displayed on front page.
952
+ */
953
+ if ( ! empty( $this->selected_import_files['content'] ) ) {
954
+ $this->append_to_frontend_error_messages( $this->importer->import_content( $this->selected_import_files['content'] ) );
955
+ }
956
+
957
+ /**
958
+ * 4). Execute the actions hooked to the 'kadence-starter-templates/after_content_import_execution' action:
959
+ *
960
+ * Default actions:
961
+ * 1 - Before widgets import setup (with priority 10).
962
+ * 2 - Import widgets (with priority 20).
963
+ * 3 - Import Redux data (with priority 30).
964
+ */
965
+ do_action( 'kadence-starter-templates/after_content_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index, $this->selected_palette );
966
+
967
+ // Save the import data as a transient, so other import parts (in new AJAX calls) can use that data.
968
+ Helpers::set_import_data_transient( $this->get_current_importer_data() );
969
+
970
+ // Request the customizer import AJAX call.
971
+ if ( ! empty( $this->selected_import_files['customizer'] ) ) {
972
+ wp_send_json( array( 'status' => 'customizerAJAX' ) );
973
+ }
974
+
975
+ // Request the after all import AJAX call.
976
+ if ( false !== has_action( 'kadence-starter-templates/after_all_import_execution' ) ) {
977
+ wp_send_json( array( 'status' => 'afterAllImportAJAX' ) );
978
+ }
979
+
980
+ // Send a JSON response with final report.
981
+ $this->final_response();
982
+ }
983
+
984
+
985
+ /**
986
+ * AJAX callback for importing the customizer data.
987
+ * This request has the wp_customize set to 'on', so that the customizer hooks can be called
988
+ * (they can only be called with the $wp_customize instance). But if the $wp_customize is defined,
989
+ * then the widgets do not import correctly, that's why the customizer import has its own AJAX call.
990
+ */
991
+ public function import_customizer_data_ajax_callback() {
992
+ // Verify if the AJAX call is valid (checks nonce and current_user_can).
993
+ Helpers::verify_ajax_call();
994
+
995
+ // Get existing import data.
996
+ if ( $this->use_existing_importer_data() ) {
997
+ /**
998
+ * Execute the customizer import actions.
999
+ *
1000
+ * Default actions:
1001
+ * 1 - Customizer import (with priority 10).
1002
+ */
1003
+ do_action( 'kadence-starter-templates/customizer_import_execution', $this->selected_import_files );
1004
+ }
1005
+
1006
+ // Request the after all import AJAX call.
1007
+ if ( false !== has_action( 'kadence-starter-templates/after_all_import_execution' ) ) {
1008
+ wp_send_json( array( 'status' => 'afterAllImportAJAX' ) );
1009
+ }
1010
+
1011
+ // Send a JSON response with final report.
1012
+ $this->final_response();
1013
+ }
1014
+
1015
+
1016
+ /**
1017
+ * AJAX callback for the after all import action.
1018
+ */
1019
+ public function after_all_import_data_ajax_callback() {
1020
+ // Verify if the AJAX call is valid (checks nonce and current_user_can).
1021
+ Helpers::verify_ajax_call();
1022
+
1023
+ // Get existing import data.
1024
+ if ( $this->use_existing_importer_data() ) {
1025
+ /**
1026
+ * Execute the after all import actions.
1027
+ *
1028
+ * Default actions:
1029
+ * 1 - after_import action (with priority 10).
1030
+ */
1031
+ do_action( 'kadence-starter-templates/after_all_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index, $this->selected_palette );
1032
+ }
1033
+
1034
+ // Send a JSON response with final report.
1035
+ $this->final_response();
1036
+ }
1037
+
1038
+
1039
+ /**
1040
+ * Send a JSON response with final report.
1041
+ */
1042
+ private function final_response() {
1043
+ // Delete importer data transient for current import.
1044
+ delete_transient( 'kadence_importer_data' );
1045
+
1046
+ // Display final messages (success or error messages).
1047
+ if ( empty( $this->frontend_error_messages ) ) {
1048
+ $response['message'] = '';
1049
+
1050
+ $response['message'] .= sprintf(
1051
+ __( '%1$sFinished! View your site%2$s', 'kadence-starter-templates' ),
1052
+ '<div class="finshed-notice-success"><p><a href="'. esc_url( home_url( '/' ) ) . '" class="button-primary button kadence-starter-templates-finish-button">',
1053
+ '</a></p></div>'
1054
+ );
1055
+ } else {
1056
+ $response['message'] = $this->frontend_error_messages_display() . '<br>';
1057
+ $response['message'] .= sprintf(
1058
+ __( '%1$sThe demo import has finished, but there were some import errors.%2$sMore details about the errors can be found in this %3$s%5$slog file%6$s%4$s%7$s', 'kadence-starter-templates' ),
1059
+ '<div class="notice notice-warning"><p>',
1060
+ '<br>',
1061
+ '<strong>',
1062
+ '</strong>',
1063
+ '<a href="' . Helpers::get_log_url( $this->log_file_path ) .'" target="_blank">',
1064
+ '</a>',
1065
+ '</p></div>'
1066
+ );
1067
+ }
1068
+
1069
+ wp_send_json( $response );
1070
+ }
1071
+
1072
+
1073
+ /**
1074
+ * Get content importer data, so we can continue the import with this new AJAX request.
1075
+ *
1076
+ * @return boolean
1077
+ */
1078
+ private function use_existing_importer_data() {
1079
+ if ( $data = get_transient( 'kadence_importer_data' ) ) {
1080
+ $this->frontend_error_messages = empty( $data['frontend_error_messages'] ) ? array() : $data['frontend_error_messages'];
1081
+ $this->log_file_path = empty( $data['log_file_path'] ) ? '' : $data['log_file_path'];
1082
+ $this->selected_index = empty( $data['selected_index'] ) ? 0 : $data['selected_index'];
1083
+ $this->selected_palette = empty( $data['selected_palette'] ) ? '' : $data['selected_palette'];
1084
+ $this->selected_import_files = empty( $data['selected_import_files'] ) ? array() : $data['selected_import_files'];
1085
+ $this->import_files = empty( $data['import_files'] ) ? array() : $data['import_files'];
1086
+ $this->before_import_executed = empty( $data['before_import_executed'] ) ? false : $data['before_import_executed'];
1087
+ $this->importer->set_importer_data( $data );
1088
+
1089
+ return true;
1090
+ }
1091
+ return false;
1092
+ }
1093
+
1094
+
1095
+ /**
1096
+ * Get the current state of selected data.
1097
+ *
1098
+ * @return array
1099
+ */
1100
+ public function get_current_importer_data() {
1101
+ return array(
1102
+ 'frontend_error_messages' => $this->frontend_error_messages,
1103
+ 'log_file_path' => $this->log_file_path,
1104
+ 'selected_index' => $this->selected_index,
1105
+ 'selected_palette' => $this->selected_palette,
1106
+ 'selected_import_files' => $this->selected_import_files,
1107
+ 'import_files' => $this->import_files,
1108
+ 'before_import_executed' => $this->before_import_executed,
1109
+ );
1110
+ }
1111
+
1112
+
1113
+ /**
1114
+ * Getter function to retrieve the private log_file_path value.
1115
+ *
1116
+ * @return string The log_file_path value.
1117
+ */
1118
+ public function get_log_file_path() {
1119
+ return $this->log_file_path;
1120
+ }
1121
+
1122
+
1123
+ /**
1124
+ * Setter function to append additional value to the private frontend_error_messages value.
1125
+ *
1126
+ * @param string $additional_value The additional value that will be appended to the existing frontend_error_messages.
1127
+ */
1128
+ public function append_to_frontend_error_messages( $text ) {
1129
+ $lines = array();
1130
+
1131
+ if ( ! empty( $text ) ) {
1132
+ $text = str_replace( '<br>', PHP_EOL, $text );
1133
+ $lines = explode( PHP_EOL, $text );
1134
+ }
1135
+
1136
+ foreach ( $lines as $line ) {
1137
+ if ( ! empty( $line ) && ! in_array( $line , $this->frontend_error_messages ) ) {
1138
+ $this->frontend_error_messages[] = $line;
1139
+ }
1140
+ }
1141
+ }
1142
+
1143
+
1144
+ /**
1145
+ * Display the frontend error messages.
1146
+ *
1147
+ * @return string Text with HTML markup.
1148
+ */
1149
+ public function frontend_error_messages_display() {
1150
+ $output = '';
1151
+
1152
+ if ( ! empty( $this->frontend_error_messages ) ) {
1153
+ foreach ( $this->frontend_error_messages as $line ) {
1154
+ $output .= esc_html( $line );
1155
+ $output .= '<br>';
1156
+ }
1157
+ }
1158
+
1159
+ return $output;
1160
+ }
1161
+
1162
+
1163
+ /**
1164
+ * Load the plugin textdomain, so that translations can be made.
1165
+ */
1166
+ public function load_textdomain() {
1167
+ load_plugin_textdomain( 'kadence-starter-templates', false, plugin_basename( dirname( dirname( __FILE__ ) ) ) . '/languages' );
1168
+ }
1169
+
1170
+
1171
+ /**
1172
+ * Get data from filters, after the theme has loaded and instantiate the importer.
1173
+ */
1174
+ public function setup_plugin_with_filter_data() {
1175
+ if ( ! ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) ) {
1176
+ return;
1177
+ }
1178
+
1179
+ // Get info of import data files and filter it.
1180
+ $this->import_files = Helpers::validate_import_file_info( apply_filters( 'kadence-starter-templates/import_files', array() ) );
1181
+
1182
+ /**
1183
+ * Register all default actions (before content import, widget, customizer import and other actions)
1184
+ * to the 'before_content_import_execution' and the 'kadence-starter-templates/after_content_import_execution' action hook.
1185
+ */
1186
+ $import_actions = new ImportActions();
1187
+ $import_actions->register_hooks();
1188
+
1189
+ // Importer options array.
1190
+ $importer_options = apply_filters( 'kadence-starter-templates/importer_options', array(
1191
+ 'fetch_attachments' => true,
1192
+ ) );
1193
+
1194
+ // Logger options for the logger used in the importer.
1195
+ $logger_options = apply_filters( 'kadence-starter-templates/logger_options', array(
1196
+ 'logger_min_level' => 'warning',
1197
+ ) );
1198
+
1199
+ // Configure logger instance and set it to the importer.
1200
+ $logger = new Logger();
1201
+ $logger->min_level = $logger_options['logger_min_level'];
1202
+
1203
+ // Create importer instance with proper parameters.
1204
+ $this->importer = new Importer( $importer_options, $logger );
1205
+ }
1206
+ }
1207
+ Starter_Templates::get_instance();
inc/class-customizer-importer.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for downloading a file from a given URL.
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ class CustomizerImporter {
11
+ /**
12
+ * Import customizer from a DAT file, generated by the Customizer Export/Import plugin.
13
+ *
14
+ * @param string $customizer_import_file_path path to the customizer import file.
15
+ */
16
+ public static function import( $customizer_import_file_path ) {
17
+ $starter_templates = Starter_Templates::get_instance();
18
+ $log_file_path = $starter_templates->get_log_file_path();
19
+
20
+ // Try to import the customizer settings.
21
+ $results = self::import_customizer_options( $customizer_import_file_path );
22
+
23
+ // Check for errors, else write the results to the log file.
24
+ if ( is_wp_error( $results ) ) {
25
+ $error_message = $results->get_error_message();
26
+
27
+ // Add any error messages to the frontend_error_messages variable in starter_templates main class.
28
+ $starter_templates->append_to_frontend_error_messages( $error_message );
29
+
30
+ // Write error to log file.
31
+ Helpers::append_to_file(
32
+ $error_message,
33
+ $log_file_path,
34
+ esc_html__( 'Importing customizer settings', 'kadence-starter-templates' )
35
+ );
36
+ } else {
37
+ // Add this message to log file.
38
+ $log_added = Helpers::append_to_file(
39
+ esc_html__( 'Customizer settings import finished!', 'kadence-starter-templates' ),
40
+ $log_file_path,
41
+ esc_html__( 'Importing customizer settings' , 'kadence-starter-templates' )
42
+ );
43
+ }
44
+ }
45
+
46
+
47
+ /**
48
+ * Imports uploaded mods and calls WordPress core customize_save actions so
49
+ * themes that hook into them can act before mods are saved to the database.
50
+ *
51
+ * Update: WP core customize_save actions were removed, because of some errors.
52
+ *
53
+ * @since 1.1.1
54
+ * @param string $import_file_path Path to the import file.
55
+ * @return void|WP_Error
56
+ */
57
+ public static function import_customizer_options( $import_file_path ) {
58
+ // Setup global vars.
59
+ global $wp_customize;
60
+
61
+ // Setup internal vars.
62
+ $template = get_template();
63
+
64
+ // Make sure we have an import file.
65
+ if ( ! file_exists( $import_file_path ) ) {
66
+ return new \WP_Error(
67
+ 'missing_cutomizer_import_file',
68
+ sprintf(
69
+ esc_html__( 'Error: The customizer import file is missing! File path: %s', 'kadence-starter-templates' ),
70
+ $import_file_path
71
+ )
72
+ );
73
+ }
74
+ // Get the upload data.
75
+ $raw = Helpers::data_from_file( $import_file_path );
76
+
77
+ // Make sure we got the data.
78
+ if ( is_wp_error( $raw ) ) {
79
+ return $raw;
80
+ }
81
+
82
+ $data = unserialize( $raw );
83
+
84
+ // Data checks.
85
+ if ( 'array' != gettype( $data ) && ( ! isset( $data['template'] ) || ! isset( $data['mods'] ) ) ) {
86
+ return new \WP_Error(
87
+ 'customizer_import_data_error',
88
+ esc_html__( 'Error: The customizer import file is not in a correct format. Please make sure to use the correct customizer import file.', 'kadence-starter-templates' )
89
+ );
90
+ }
91
+ if ( $data['template'] !== $template ) {
92
+ return new \WP_Error(
93
+ 'customizer_import_wrong_theme',
94
+ esc_html__( 'Error: The customizer import file is not suitable for current theme. You can only import customizer settings for the same theme or a child theme.', 'kadence-starter-templates' )
95
+ );
96
+ }
97
+
98
+ // Import images.
99
+ if ( apply_filters( 'kadence-starter-templates/customizer_import_images', true ) ) {
100
+ $data['mods'] = self::import_customizer_images( $data['mods'] );
101
+ }
102
+
103
+ // Import custom options.
104
+ if ( isset( $data['options'] ) ) {
105
+ // Require modified customizer options class.
106
+ if ( ! class_exists( '\WP_Customize_Setting' ) ) {
107
+ require_once ABSPATH . 'wp-includes/class-wp-customize-setting.php';
108
+ }
109
+ if ( ! class_exists( 'Kadence_Starter_Templates\CustomizerOption' ) ) {
110
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-customizer-option.php';
111
+ }
112
+
113
+ foreach ( $data['options'] as $option_key => $option_value ) {
114
+ $option = new CustomizerOption( $wp_customize, $option_key, array(
115
+ 'default' => '',
116
+ 'type' => 'option',
117
+ 'capability' => 'edit_theme_options',
118
+ ) );
119
+
120
+ $option->import( $option_value );
121
+ }
122
+ }
123
+
124
+ // Should the customizer import use the WP customize_save* hooks?
125
+ $use_wp_customize_save_hooks = apply_filters( 'kadence-starter-templates/enable_wp_customize_save_hooks', false );
126
+
127
+ if ( $use_wp_customize_save_hooks ) {
128
+ do_action( 'customize_save', $wp_customize );
129
+ }
130
+
131
+ // Loop through the mods and save the mods.
132
+ foreach ( $data['mods'] as $key => $val ) {
133
+ if ( $use_wp_customize_save_hooks ) {
134
+ do_action( 'customize_save_' . $key, $wp_customize );
135
+ }
136
+
137
+ set_theme_mod( $key, $val );
138
+ }
139
+
140
+ if ( $use_wp_customize_save_hooks ) {
141
+ do_action( 'customize_save_after', $wp_customize );
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Helper function: Customizer import - imports images for settings saved as mods.
147
+ *
148
+ * @since 1.1.1
149
+ * @param array $mods An array of customizer mods.
150
+ * @return array The mods array with any new import data.
151
+ */
152
+ private static function import_customizer_images( $mods ) {
153
+ foreach ( $mods as $key => $val ) {
154
+ if ( self::customizer_is_image_url( $val ) ) {
155
+ $data = self::customizer_sideload_image( $val );
156
+ if ( ! is_wp_error( $data ) ) {
157
+ $mods[ $key ] = $data->url;
158
+
159
+ // Handle header image controls.
160
+ if ( isset( $mods[ $key . '_data' ] ) ) {
161
+ $mods[ $key . '_data' ] = $data;
162
+ update_post_meta( $data->attachment_id, '_wp_attachment_is_custom_header', get_stylesheet() );
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ return $mods;
169
+ }
170
+
171
+ /**
172
+ * Helper function: Customizer import
173
+ * Taken from the core media_sideload_image function and
174
+ * modified to return an array of data instead of html.
175
+ *
176
+ * @since 1.1.1.
177
+ * @param string $file The image file path.
178
+ * @return array An array of image data.
179
+ */
180
+ private static function customizer_sideload_image( $file ) {
181
+ $data = new \stdClass();
182
+
183
+ if ( ! function_exists( 'media_handle_sideload' ) ) {
184
+ require_once( ABSPATH . 'wp-admin/includes/media.php' );
185
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
186
+ require_once( ABSPATH . 'wp-admin/includes/image.php' );
187
+ }
188
+ if ( ! empty( $file ) ) {
189
+ // Set variables for storage, fix file filename for query strings.
190
+ preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches );
191
+ $file_array = array();
192
+ $file_array['name'] = basename( $matches[0] );
193
+
194
+ // Download file to temp location.
195
+ $file_array['tmp_name'] = download_url( $file );
196
+
197
+ // If error storing temporarily, return the error.
198
+ if ( is_wp_error( $file_array['tmp_name'] ) ) {
199
+ return $file_array['tmp_name'];
200
+ }
201
+
202
+ // Do the validation and storage stuff.
203
+ $id = media_handle_sideload( $file_array, 0 );
204
+
205
+ // If error storing permanently, unlink.
206
+ if ( is_wp_error( $id ) ) {
207
+ unlink( $file_array['tmp_name'] );
208
+ return $id;
209
+ }
210
+
211
+ // Build the object to return.
212
+ $meta = wp_get_attachment_metadata( $id );
213
+ $data->attachment_id = $id;
214
+ $data->url = wp_get_attachment_url( $id );
215
+ $data->thumbnail_url = wp_get_attachment_thumb_url( $id );
216
+ $data->height = $meta['height'];
217
+ $data->width = $meta['width'];
218
+ }
219
+
220
+ return $data;
221
+ }
222
+
223
+ /**
224
+ * Checks to see whether a string is an image url or not.
225
+ *
226
+ * @since 1.1.1
227
+ * @param string $string The string to check.
228
+ * @return bool Whether the string is an image url or not.
229
+ */
230
+ private static function customizer_is_image_url( $string = '' ) {
231
+ if ( is_string( $string ) ) {
232
+ if ( preg_match( '/\.(jpg|jpeg|png|gif)/i', $string ) ) {
233
+ return true;
234
+ }
235
+ }
236
+
237
+ return false;
238
+ }
239
+ }
inc/class-customizer-option.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * A class that extends WP_Customize_Setting so we can access
4
+ * the protected updated method when importing options.
5
+ *
6
+ * Used in the Customizer importer.
7
+ *
8
+ * @since 1.1.1
9
+ * @package Kadence Starter Templates
10
+ */
11
+
12
+ namespace Kadence_Starter_Templates;
13
+
14
+ final class CustomizerOption extends \WP_Customize_Setting {
15
+ /**
16
+ * Import an option value for this setting.
17
+ *
18
+ * @since 1.1.1
19
+ * @param mixed $value The option value.
20
+ * @return void
21
+ */
22
+ public function import( $value ) {
23
+ $this->update( $value );
24
+ }
25
+ }
inc/class-downloader.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for downloading a file from a given URL.
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ class Downloader {
11
+ /**
12
+ * Holds full path to where the files will be saved.
13
+ *
14
+ * @var string
15
+ */
16
+ private $download_directory_path = '';
17
+
18
+ /**
19
+ * Constructor method.
20
+ *
21
+ * @param string $download_directory_path Full path to where the files will be saved.
22
+ */
23
+ public function __construct( $download_directory_path = '' ) {
24
+ $this->set_download_directory_path( $download_directory_path );
25
+ }
26
+
27
+
28
+ /**
29
+ * Download file from a given URL.
30
+ *
31
+ * @param string $url URL of file to download.
32
+ * @param string $filename Filename of the file to save.
33
+ * @return string|WP_Error Full path to the downloaded file or WP_Error object with error message.
34
+ */
35
+ public function download_file( $url, $filename ) {
36
+ $content = $this->get_content_from_url( $url );
37
+
38
+ // Check if there was an error and break out.
39
+ if ( is_wp_error( $content ) ) {
40
+ return $content;
41
+ }
42
+
43
+ return Helpers::write_to_file( $content, $this->download_directory_path . $filename );
44
+ }
45
+
46
+
47
+ /**
48
+ * Helper function: get content from an URL.
49
+ *
50
+ * @param string $url URL to the content file.
51
+ * @return string|WP_Error, content from the URL or WP_Error object with error message.
52
+ */
53
+ private function get_content_from_url( $url ) {
54
+ // Test if the URL to the file is defined.
55
+ if ( empty( $url ) ) {
56
+ return new \WP_Error(
57
+ 'missing_url',
58
+ __( 'Missing URL for downloading a file!', 'pt-ocdi' )
59
+ );
60
+ }
61
+
62
+ // Get file content from the server.
63
+ $response = wp_remote_get(
64
+ $url,
65
+ array( 'timeout' => apply_filters( 'kadence-starter-templates/timeout_for_downloading_import_file', 20 ) )
66
+ );
67
+
68
+ // Test if the get request was not successful.
69
+ if ( is_wp_error( $response ) || 200 !== $response['response']['code'] ) {
70
+ // Collect the right format of error data (array or WP_Error).
71
+ $response_error = $this->get_error_from_response( $response );
72
+
73
+ return new \WP_Error(
74
+ 'download_error',
75
+ sprintf(
76
+ __( 'An error occurred while fetching file from: %1$s%2$s%3$s!%4$sReason: %5$s - %6$s.', 'pt-ocdi' ),
77
+ '<strong>',
78
+ $url,
79
+ '</strong>',
80
+ '<br>',
81
+ $response_error['error_code'],
82
+ $response_error['error_message']
83
+ ) . '<br>' .
84
+ apply_filters( 'kadence-starter-templates/message_after_file_fetching_error', '' )
85
+ );
86
+ }
87
+
88
+ // Return content retrieved from the URL.
89
+ return wp_remote_retrieve_body( $response );
90
+ }
91
+
92
+
93
+ /**
94
+ * Helper function: get the right format of response errors.
95
+ *
96
+ * @param array|WP_Error $response Array or WP_Error or the response.
97
+ * @return array Error code and error message.
98
+ */
99
+ private function get_error_from_response( $response ) {
100
+ $response_error = array();
101
+
102
+ if ( is_array( $response ) ) {
103
+ $response_error['error_code'] = $response['response']['code'];
104
+ $response_error['error_message'] = $response['response']['message'];
105
+ }
106
+ else {
107
+ $response_error['error_code'] = $response->get_error_code();
108
+ $response_error['error_message'] = $response->get_error_message();
109
+ }
110
+
111
+ return $response_error;
112
+ }
113
+
114
+
115
+ /**
116
+ * Get download_directory_path attribute.
117
+ */
118
+ public function get_download_directory_path() {
119
+ return $this->download_directory_path;
120
+ }
121
+
122
+
123
+ /**
124
+ * Set download_directory_path attribute.
125
+ * If no valid path is specified, the default WP upload directory will be used.
126
+ *
127
+ * @param string $download_directory_path Path, where the files will be saved.
128
+ */
129
+ public function set_download_directory_path( $download_directory_path ) {
130
+ if ( file_exists( $download_directory_path ) ) {
131
+ $this->download_directory_path = $download_directory_path;
132
+ }
133
+ else {
134
+ $upload_dir = wp_upload_dir();
135
+ $this->download_directory_path = apply_filters( 'kadence-starter-templates/upload_file_path', trailingslashit( $upload_dir['path'] ) );
136
+ }
137
+ }
138
+ }
inc/class-helpers.php ADDED
@@ -0,0 +1,660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Static functions used in the Kadence Starter Templates plugin.
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ /**
11
+ * Class with static helper functions.
12
+ */
13
+ class Helpers {
14
+ /**
15
+ * Holds the date and time string for demo import and log file.
16
+ *
17
+ * @var string
18
+ */
19
+ public static $demo_import_start_time = '';
20
+
21
+ /**
22
+ * Filter through the array of import files and get rid of those who do not comply.
23
+ *
24
+ * @param array $import_files list of arrays with import file details.
25
+ * @return array list of filtered arrays.
26
+ */
27
+ public static function validate_import_file_info( $import_files ) {
28
+ $filtered_import_file_info = array();
29
+
30
+ foreach ( $import_files as $import_file ) {
31
+ if ( self::is_import_file_info_format_correct( $import_file ) ) {
32
+ $filtered_import_file_info[] = $import_file;
33
+ }
34
+ }
35
+
36
+ return $filtered_import_file_info;
37
+ }
38
+
39
+
40
+ /**
41
+ * Helper function: a simple check for valid import file format.
42
+ *
43
+ * @param array $import_file_info array with import file details.
44
+ * @return boolean
45
+ */
46
+ private static function is_import_file_info_format_correct( $import_file_info ) {
47
+ if ( empty( $import_file_info['import_file_name'] ) ) {
48
+ return false;
49
+ }
50
+
51
+ return true;
52
+ }
53
+
54
+
55
+ /**
56
+ * Download import files. Content .xml and widgets .wie|.json files.
57
+ *
58
+ * @param array $import_file_info array with import file details.
59
+ * @return array|WP_Error array of paths to the downloaded files or WP_Error object with error message.
60
+ */
61
+ public static function download_import_files( $import_file_info ) {
62
+ $downloaded_files = array(
63
+ 'content' => '',
64
+ 'widgets' => '',
65
+ 'customizer' => '',
66
+ 'redux' => '',
67
+ );
68
+ $downloader = new Downloader();
69
+
70
+ // ----- Set content file path -----
71
+ // Check if 'import_file_url' is not defined. That would mean a local file.
72
+ if ( empty( $import_file_info['import_file_url'] ) ) {
73
+ if ( file_exists( $import_file_info['local_import_file'] ) ) {
74
+ $downloaded_files['content'] = $import_file_info['local_import_file'];
75
+ }
76
+ } else {
77
+ // Set the filename string for content import file.
78
+ $content_filename = apply_filters( 'kaence-starter-templates/downloaded_content_file_prefix', 'demo-content-import-file_' ) . self::$demo_import_start_time . apply_filters( 'kaence-starter-templates/downloaded_content_file_suffix_and_file_extension', '.xml' );
79
+
80
+ // Download the content import file.
81
+ $downloaded_files['content'] = $downloader->download_file( $import_file_info['import_file_url'], $content_filename );
82
+
83
+ // Return from this function if there was an error.
84
+ if ( is_wp_error( $downloaded_files['content'] ) ) {
85
+ return $downloaded_files['content'];
86
+ }
87
+ }
88
+
89
+ // ----- Set widget file path -----
90
+ // Get widgets file as well. If defined!
91
+ if ( ! empty( $import_file_info['import_widget_file_url'] ) ) {
92
+ // Set the filename string for widgets import file.
93
+ $widget_filename = apply_filters( 'kaence-starter-templates/downloaded_widgets_file_prefix', 'demo-widgets-import-file_' ) . self::$demo_import_start_time . apply_filters( 'kaence-starter-templates/downloaded_widgets_file_suffix_and_file_extension', '.json' );
94
+
95
+ // Download the widgets import file.
96
+ $downloaded_files['widgets'] = $downloader->download_file( $import_file_info['import_widget_file_url'], $widget_filename );
97
+
98
+ // Return from this function if there was an error.
99
+ if ( is_wp_error( $downloaded_files['widgets'] ) ) {
100
+ return $downloaded_files['widgets'];
101
+ }
102
+ }
103
+ else if ( ! empty( $import_file_info['local_import_widget_file'] ) ) {
104
+ if ( file_exists( $import_file_info['local_import_widget_file'] ) ) {
105
+ $downloaded_files['widgets'] = $import_file_info['local_import_widget_file'];
106
+ }
107
+ }
108
+
109
+ // ----- Set customizer file path -----
110
+ // Get customizer import file as well. If defined!
111
+ if ( ! empty( $import_file_info['import_customizer_file_url'] ) ) {
112
+ // Setup filename path to save the customizer content.
113
+ $customizer_filename = apply_filters( 'kaence-starter-templates/downloaded_customizer_file_prefix', 'demo-customizer-import-file_' ) . self::$demo_import_start_time . apply_filters( 'kaence-starter-templates/downloaded_customizer_file_suffix_and_file_extension', '.dat' );
114
+
115
+ // Download the customizer import file.
116
+ $downloaded_files['customizer'] = $downloader->download_file( $import_file_info['import_customizer_file_url'], $customizer_filename );
117
+
118
+ // Return from this function if there was an error.
119
+ if ( is_wp_error( $downloaded_files['customizer'] ) ) {
120
+ return $downloaded_files['customizer'];
121
+ }
122
+ }
123
+ else if ( ! empty( $import_file_info['local_import_customizer_file'] ) ) {
124
+ if ( file_exists( $import_file_info['local_import_customizer_file'] ) ) {
125
+ $downloaded_files['customizer'] = $import_file_info['local_import_customizer_file'];
126
+ }
127
+ }
128
+
129
+ // ----- Set Redux file paths -----
130
+ // Get Redux import file as well. If defined!
131
+ if ( ! empty( $import_file_info['import_redux'] ) && is_array( $import_file_info['import_redux'] ) ) {
132
+ $redux_items = array();
133
+
134
+ // Setup filename paths to save the Redux content.
135
+ foreach ( $import_file_info['import_redux'] as $index => $redux_item ) {
136
+ $redux_filename = apply_filters( 'kaence-starter-templates/downloaded_redux_file_prefix', 'demo-redux-import-file_' ) . $index . '-' . self::$demo_import_start_time . apply_filters( 'kaence-starter-templates/downloaded_redux_file_suffix_and_file_extension', '.json' );
137
+
138
+ // Download the Redux import file.
139
+ $file_path = $downloader->download_file( $redux_item['file_url'], $redux_filename );
140
+
141
+ // Return from this function if there was an error.
142
+ if ( is_wp_error( $file_path ) ) {
143
+ return $file_path;
144
+ }
145
+
146
+ $redux_items[] = array(
147
+ 'option_name' => $redux_item['option_name'],
148
+ 'file_path' => $file_path,
149
+ );
150
+ }
151
+
152
+ // Download the Redux import file.
153
+ $downloaded_files['redux'] = $redux_items;
154
+ } else if ( ! empty( $import_file_info['local_import_redux'] ) ) {
155
+
156
+ $redux_items = array();
157
+
158
+ // Setup filename paths to save the Redux content.
159
+ foreach ( $import_file_info['local_import_redux'] as $redux_item ) {
160
+ if ( file_exists( $redux_item['file_path'] ) ) {
161
+ $redux_items[] = $redux_item;
162
+ }
163
+ }
164
+
165
+ // Download the Redux import file.
166
+ $downloaded_files['redux'] = $redux_items;
167
+ }
168
+
169
+ return $downloaded_files;
170
+ }
171
+
172
+
173
+ /**
174
+ * Write content to a file.
175
+ *
176
+ * @param string $content content to be saved to the file.
177
+ * @param string $file_path file path where the content should be saved.
178
+ * @return string|WP_Error path to the saved file or WP_Error object with error message.
179
+ */
180
+ public static function write_to_file( $content, $file_path ) {
181
+ // Verify WP file-system credentials.
182
+ $verified_credentials = self::check_wp_filesystem_credentials();
183
+
184
+ if ( is_wp_error( $verified_credentials ) ) {
185
+ return $verified_credentials;
186
+ }
187
+
188
+ // By this point, the $wp_filesystem global should be working, so let's use it to create a file.
189
+ global $wp_filesystem;
190
+
191
+ if ( ! $wp_filesystem->put_contents( $file_path, $content ) ) {
192
+ return new \WP_Error(
193
+ 'failed_writing_file_to_server',
194
+ sprintf(
195
+ __( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'kadence-starter-templates' ),
196
+ '<br>',
197
+ $file_path
198
+ )
199
+ );
200
+ }
201
+
202
+ // Return the file path on successful file write.
203
+ return $file_path;
204
+ }
205
+
206
+
207
+ /**
208
+ * Append content to the file.
209
+ *
210
+ * @param string $content content to be saved to the file.
211
+ * @param string $file_path file path where the content should be saved.
212
+ * @param string $separator_text separates the existing content of the file with the new content.
213
+ * @return boolean|WP_Error, path to the saved file or WP_Error object with error message.
214
+ */
215
+ public static function append_to_file( $content, $file_path, $separator_text = '' ) {
216
+ // Verify WP file-system credentials.
217
+ $verified_credentials = self::check_wp_filesystem_credentials();
218
+
219
+ if ( is_wp_error( $verified_credentials ) ) {
220
+ return $verified_credentials;
221
+ }
222
+
223
+ // By this point, the $wp_filesystem global should be working, so let's use it to create a file.
224
+ global $wp_filesystem;
225
+
226
+ $existing_data = '';
227
+ if ( file_exists( $file_path ) ) {
228
+ $existing_data = $wp_filesystem->get_contents( $file_path );
229
+ }
230
+
231
+ // Style separator.
232
+ $separator = PHP_EOL . '---' . $separator_text . '---' . PHP_EOL;
233
+
234
+ if ( ! $wp_filesystem->put_contents( $file_path, $existing_data . $separator . $content . PHP_EOL ) ) {
235
+ return new \WP_Error(
236
+ 'failed_writing_file_to_server',
237
+ sprintf(
238
+ __( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'kadence-starter-templates' ),
239
+ '<br>',
240
+ $file_path
241
+ )
242
+ );
243
+ }
244
+
245
+ return true;
246
+ }
247
+
248
+
249
+ /**
250
+ * Get data from a file
251
+ *
252
+ * @param string $file_path file path where the content should be saved.
253
+ * @return string $data, content of the file or WP_Error object with error message.
254
+ */
255
+ public static function data_from_file( $file_path ) {
256
+ // Verify WP file-system credentials.
257
+ $verified_credentials = self::check_wp_filesystem_credentials();
258
+
259
+ if ( is_wp_error( $verified_credentials ) ) {
260
+ return $verified_credentials;
261
+ }
262
+
263
+ // By this point, the $wp_filesystem global should be working, so let's use it to read a file.
264
+ global $wp_filesystem;
265
+
266
+ $data = $wp_filesystem->get_contents( $file_path );
267
+
268
+ if ( ! $data ) {
269
+ return new \WP_Error(
270
+ 'failed_reading_file_from_server',
271
+ sprintf(
272
+ __( 'An error occurred while reading a file from your server! Tried reading file from path: %s%s.', 'kadence-starter-templates' ),
273
+ '<br>',
274
+ $file_path
275
+ )
276
+ );
277
+ }
278
+
279
+ // Return the file data.
280
+ return $data;
281
+ }
282
+
283
+
284
+ /**
285
+ * Helper function: check for WP file-system credentials needed for reading and writing to a file.
286
+ *
287
+ * @return boolean|WP_Error
288
+ */
289
+ private static function check_wp_filesystem_credentials() {
290
+ // Check if the file-system method is 'direct', if not display an error.
291
+ if ( ! ( 'direct' === get_filesystem_method() ) ) {
292
+ return new \WP_Error(
293
+ 'no_direct_file_access',
294
+ sprintf(
295
+ __( 'This WordPress page does not have %sdirect%s write file access. This plugin needs it in order to save the demo import xml file to the upload directory of your site. You can change this setting with these instructions: %s.', 'kadence-starter-templates' ),
296
+ '<strong>',
297
+ '</strong>',
298
+ '<a href="http://gregorcapuder.com/wordpress-how-to-set-direct-filesystem-method/" target="_blank">How to set <strong>direct</strong> filesystem method</a>'
299
+ )
300
+ );
301
+ }
302
+
303
+ // Get plugin page settings.
304
+ $plugin_page_setup = apply_filters( 'kaence-starter-templates/plugin_page_setup', array(
305
+ 'parent_slug' => 'themes.php',
306
+ 'page_title' => esc_html__( 'One Click Demo Import' , 'kadence-starter-templates' ),
307
+ 'menu_title' => esc_html__( 'Import Demo Data' , 'kadence-starter-templates' ),
308
+ 'capability' => 'import',
309
+ 'menu_slug' => 'pt-one-click-demo-import',
310
+ )
311
+ );
312
+
313
+ // Get user credentials for WP file-system API.
314
+ $demo_import_page_url = wp_nonce_url( $plugin_page_setup['parent_slug'] . '?page=' . $plugin_page_setup['menu_slug'], $plugin_page_setup['menu_slug'] );
315
+
316
+ if ( false === ( $creds = request_filesystem_credentials( $demo_import_page_url, '', false, false, null ) ) ) {
317
+ return new \WP_error(
318
+ 'filesystem_credentials_could_not_be_retrieved',
319
+ __( 'An error occurred while retrieving reading/writing permissions to your server (could not retrieve WP filesystem credentials)!', 'kadence-starter-templates' )
320
+ );
321
+ }
322
+
323
+ // Now we have credentials, try to get the wp_filesystem running.
324
+ if ( ! WP_Filesystem( $creds ) ) {
325
+ return new \WP_Error(
326
+ 'wrong_login_credentials',
327
+ __( 'Your WordPress login credentials don\'t allow to use WP_Filesystem!', 'kadence-starter-templates' )
328
+ );
329
+ }
330
+
331
+ return true;
332
+ }
333
+
334
+
335
+ /**
336
+ * Get log file path
337
+ *
338
+ * @return string, path to the log file
339
+ */
340
+ public static function get_log_path() {
341
+ $upload_dir = wp_upload_dir();
342
+ $upload_path = apply_filters( 'kaence-starter-templates/upload_file_path', trailingslashit( $upload_dir['path'] ) );
343
+
344
+ $log_path = $upload_path . apply_filters( 'kaence-starter-templates/log_file_prefix', 'log_file_' ) . self::$demo_import_start_time . apply_filters( 'kaence-starter-templates/log_file_suffix_and_file_extension', '.txt' );
345
+
346
+ self::register_file_as_media_attachment( $log_path );
347
+
348
+ return $log_path;
349
+ }
350
+
351
+
352
+ /**
353
+ * Register file as attachment to the Media page.
354
+ *
355
+ * @param string $log_path log file path.
356
+ * @return void
357
+ */
358
+ public static function register_file_as_media_attachment( $log_path ) {
359
+ // Check the type of file.
360
+ $log_mimes = array( 'txt' => 'text/plain' );
361
+ $filetype = wp_check_filetype( basename( $log_path ), apply_filters( 'kaence-starter-templates/file_mimes', $log_mimes ) );
362
+
363
+ // Prepare an array of post data for the attachment.
364
+ $attachment = array(
365
+ 'guid' => self::get_log_url( $log_path ),
366
+ 'post_mime_type' => $filetype['type'],
367
+ 'post_title' => apply_filters( 'kaence-starter-templates/attachment_prefix', esc_html__( 'One Click Demo Import - ', 'kadence-starter-templates' ) ) . preg_replace( '/\.[^.]+$/', '', basename( $log_path ) ),
368
+ 'post_content' => '',
369
+ 'post_status' => 'inherit',
370
+ );
371
+
372
+ // Insert the file as attachment in Media page.
373
+ $attach_id = wp_insert_attachment( $attachment, $log_path );
374
+ }
375
+
376
+
377
+ /**
378
+ * Get log file url
379
+ *
380
+ * @param string $log_path log path to use for the log filename.
381
+ * @return string, url to the log file.
382
+ */
383
+ public static function get_log_url( $log_path ) {
384
+ $upload_dir = wp_upload_dir();
385
+ $upload_url = apply_filters( 'kaence-starter-templates/upload_file_url', trailingslashit( $upload_dir['url'] ) );
386
+
387
+ return $upload_url . basename( $log_path );
388
+ }
389
+
390
+
391
+ /**
392
+ * Check if the AJAX call is valid.
393
+ */
394
+ public static function verify_ajax_call() {
395
+ check_ajax_referer( 'kadence-ajax-verification', 'security' );
396
+
397
+ // Check if user has the WP capability to import data.
398
+ if ( ! current_user_can( 'import' ) ) {
399
+ wp_die(
400
+ sprintf(
401
+ __( '%sYour user role isn\'t high enough. You don\'t have permission to import demo data.%s', 'kadence-starter-templates' ),
402
+ '<div class="notice notice-error"><p>',
403
+ '</p></div>'
404
+ )
405
+ );
406
+ }
407
+ }
408
+
409
+
410
+ /**
411
+ * Process uploaded files and return the paths to these files.
412
+ *
413
+ * @param array $uploaded_files $_FILES array form an AJAX request.
414
+ * @param string $log_file_path path to the log file.
415
+ * @return array of paths to the content import and widget import files.
416
+ */
417
+ public static function process_uploaded_files( $uploaded_files, $log_file_path ) {
418
+ // Variable holding the paths to the uploaded files.
419
+ $selected_import_files = array(
420
+ 'content' => '',
421
+ 'widgets' => '',
422
+ 'customizer' => '',
423
+ 'redux' => '',
424
+ );
425
+
426
+ // Upload settings to disable form and type testing for AJAX uploads.
427
+ $upload_overrides = array(
428
+ 'test_form' => false,
429
+ 'test_type' => false,
430
+ );
431
+
432
+ // Error data if the demo file was not provided.
433
+ $file_not_provided_error = array(
434
+ 'error' => esc_html__( 'No file provided.', 'kadence-starter-templates' )
435
+ );
436
+
437
+ // Handle demo file uploads.
438
+ $content_file_info = isset( $_FILES['content_file'] ) ?
439
+ wp_handle_upload( $_FILES['content_file'], $upload_overrides ) :
440
+ $file_not_provided_error;
441
+
442
+ $widget_file_info = isset( $_FILES['widget_file'] ) ?
443
+ wp_handle_upload( $_FILES['widget_file'], $upload_overrides ) :
444
+ $file_not_provided_error;
445
+
446
+ $customizer_file_info = isset( $_FILES['customizer_file'] ) ?
447
+ wp_handle_upload( $_FILES['customizer_file'], $upload_overrides ) :
448
+ $file_not_provided_error;
449
+
450
+ $redux_file_info = isset( $_FILES['customizer_file'] ) ?
451
+ wp_handle_upload( $_FILES['redux_file'], $upload_overrides ) :
452
+ $file_not_provided_error;
453
+
454
+ // Process content import file.
455
+ if ( $content_file_info && ! isset( $content_file_info['error'] ) ) {
456
+ // Set uploaded content file.
457
+ $selected_import_files['content'] = $content_file_info['file'];
458
+ } else {
459
+ // Add this error to log file.
460
+ $log_added = self::append_to_file(
461
+ sprintf(
462
+ __( 'Content file was not uploaded. Error: %s', 'kadence-starter-templates' ),
463
+ $content_file_info['error']
464
+ ),
465
+ $log_file_path,
466
+ esc_html__( 'Upload files' , 'kadence-starter-templates' )
467
+ );
468
+ }
469
+
470
+ // Process widget import file.
471
+ if ( $widget_file_info && ! isset( $widget_file_info['error'] ) ) {
472
+ // Set uploaded widget file.
473
+ $selected_import_files['widgets'] = $widget_file_info['file'];
474
+ } else {
475
+ // Add this error to log file.
476
+ $log_added = self::append_to_file(
477
+ sprintf(
478
+ __( 'Widget file was not uploaded. Error: %s', 'kadence-starter-templates' ),
479
+ $widget_file_info['error']
480
+ ),
481
+ $log_file_path,
482
+ esc_html__( 'Upload files' , 'kadence-starter-templates' )
483
+ );
484
+ }
485
+
486
+ // Process Customizer import file.
487
+ if ( $customizer_file_info && ! isset( $customizer_file_info['error'] ) ) {
488
+ // Set uploaded customizer file.
489
+ $selected_import_files['customizer'] = $customizer_file_info['file'];
490
+ } else {
491
+ // Add this error to log file.
492
+ $log_added = self::append_to_file(
493
+ sprintf(
494
+ __( 'Customizer file was not uploaded. Error: %s', 'kadence-starter-templates' ),
495
+ $customizer_file_info['error']
496
+ ),
497
+ $log_file_path,
498
+ esc_html__( 'Upload files' , 'kadence-starter-templates' )
499
+ );
500
+ }
501
+
502
+ // Process Redux import file.
503
+ if ( $redux_file_info && ! isset( $redux_file_info['error'] ) ) {
504
+ if ( isset( $_POST['redux_option_name'] ) && empty( $_POST['redux_option_name'] ) ) {
505
+ // Write error to log file and send an AJAX response with the error.
506
+ self::log_error_and_send_ajax_response(
507
+ esc_html__( 'Missing Redux option name! Please also enter the Redux option name!', 'kadence-starter-templates' ),
508
+ $log_file_path,
509
+ esc_html__( 'Upload files', 'kadence-starter-templates' )
510
+ );
511
+ }
512
+
513
+ // Set uploaded Redux file.
514
+ $selected_import_files['redux'] = array(
515
+ array(
516
+ 'option_name' => sanitize_text_field( $_POST['redux_option_name'] ),
517
+ 'file_path' => $redux_file_info['file'],
518
+ ),
519
+ );
520
+ } else {
521
+ // Add this error to log file.
522
+ $log_added = self::append_to_file(
523
+ sprintf(
524
+ __( 'Redux file was not uploaded. Error: %s', 'kadence-starter-templates' ),
525
+ $redux_file_info['error']
526
+ ),
527
+ $log_file_path,
528
+ esc_html__( 'Upload files' , 'kadence-starter-templates' )
529
+ );
530
+ }
531
+
532
+ // Add this message to log file.
533
+ $log_added = self::append_to_file(
534
+ __( 'The import files were successfully uploaded!', 'kadence-starter-templates' ) . self::import_file_info( $selected_import_files ),
535
+ $log_file_path,
536
+ esc_html__( 'Upload files' , 'kadence-starter-templates' )
537
+ );
538
+
539
+ // Return array with paths of uploaded files.
540
+ return $selected_import_files;
541
+ }
542
+
543
+
544
+ /**
545
+ * Get import file information and max execution time.
546
+ *
547
+ * @param array $selected_import_files array of selected import files.
548
+ */
549
+ public static function import_file_info( $selected_import_files ) {
550
+ $redux_file_string = '';
551
+
552
+ if ( ! empty( $selected_import_files['redux'] ) ) {
553
+ $redux_file_string = array_reduce( $selected_import_files['redux'], function( $string, $item ) {
554
+ return sprintf( '%1$s%2$s -> %3$s %4$s', $string, $item['option_name'], $item['file_path'], PHP_EOL );
555
+ }, '' );
556
+ }
557
+
558
+ return PHP_EOL .
559
+ sprintf(
560
+ __( 'Initial max execution time = %s', 'kadence-starter-templates' ),
561
+ ini_get( 'max_execution_time' )
562
+ ) . PHP_EOL .
563
+ sprintf(
564
+ __( 'Files info:%1$sSite URL = %2$s%1$sData file = %3$s%1$sWidget file = %4$s%1$sCustomizer file = %5$s%1$sRedux files:%1$s%6$s', 'kadence-starter-templates' ),
565
+ PHP_EOL,
566
+ get_site_url(),
567
+ empty( $selected_import_files['content'] ) ? esc_html__( 'not defined!', 'kadence-starter-templates' ) : $selected_import_files['content'],
568
+ empty( $selected_import_files['widgets'] ) ? esc_html__( 'not defined!', 'kadence-starter-templates' ) : $selected_import_files['widgets'],
569
+ empty( $selected_import_files['customizer'] ) ? esc_html__( 'not defined!', 'kadence-starter-templates' ) : $selected_import_files['customizer'],
570
+ empty( $redux_file_string ) ? esc_html__( 'not defined!', 'kadence-starter-templates' ) : $redux_file_string
571
+ );
572
+ }
573
+
574
+
575
+ /**
576
+ * Write the error to the log file and send the AJAX response.
577
+ *
578
+ * @param string $error_text text to display in the log file and in the AJAX response.
579
+ * @param string $log_file_path path to the log file.
580
+ * @param string $separator title separating the old and new content.
581
+ */
582
+ public static function log_error_and_send_ajax_response( $error_text, $log_file_path, $separator = '' ) {
583
+ // Add this error to log file.
584
+ $log_added = self::append_to_file(
585
+ $error_text,
586
+ $log_file_path,
587
+ $separator
588
+ );
589
+
590
+ // Send JSON Error response to the AJAX call.
591
+ wp_send_json( $error_text );
592
+ }
593
+
594
+
595
+ /**
596
+ * Set the $demo_import_start_time class variable with the current date and time string.
597
+ */
598
+ public static function set_demo_import_start_time() {
599
+ self::$demo_import_start_time = date( apply_filters( 'kaence-starter-templates/date_format_for_file_names', 'Y-m-d__H-i-s' ) );
600
+ }
601
+
602
+
603
+ /**
604
+ * Get the category list of all categories used in the predefined demo imports array.
605
+ *
606
+ * @param array $demo_imports Array of demo import items (arrays).
607
+ * @return array|boolean List of all the categories or false if there aren't any.
608
+ */
609
+ public static function get_all_demo_import_categories( $demo_imports ) {
610
+ $categories = array();
611
+
612
+ foreach ( $demo_imports as $item ) {
613
+ if ( ! empty( $item['categories'] ) && is_array( $item['categories'] ) ) {
614
+ foreach ( $item['categories'] as $category ) {
615
+ $categories[ sanitize_key( $category ) ] = $category;
616
+ }
617
+ }
618
+ }
619
+
620
+ if ( empty( $categories ) ) {
621
+ return false;
622
+ }
623
+
624
+ return $categories;
625
+ }
626
+
627
+
628
+ /**
629
+ * Return the concatenated string of demo import item categories.
630
+ * These should be separated by comma and sanitized properly.
631
+ *
632
+ * @param array $item The predefined demo import item data.
633
+ * @return string The concatenated string of categories.
634
+ */
635
+ public static function get_demo_import_item_categories( $item ) {
636
+ $sanitized_categories = array();
637
+
638
+ if ( isset( $item['categories'] ) ) {
639
+ foreach ( $item['categories'] as $category ) {
640
+ $sanitized_categories[] = sanitize_key( $category );
641
+ }
642
+ }
643
+
644
+ if ( ! empty( $sanitized_categories ) ) {
645
+ return implode( ',', $sanitized_categories );
646
+ }
647
+
648
+ return false;
649
+ }
650
+
651
+
652
+ /**
653
+ * Set the Kadence transient with the current importer data.
654
+ *
655
+ * @param array $data Data to be saved to the transient.
656
+ */
657
+ public static function set_import_data_transient( $data ) {
658
+ set_transient( 'kadence_importer_data', $data, 0.1 * HOUR_IN_SECONDS );
659
+ }
660
+ }
inc/class-import-actions.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for the import actions used in the One Click Demo Import plugin.
4
+ * Register default WP actions for OCDI plugin.
5
+ *
6
+ * @package Kadence Starter Templates
7
+ */
8
+
9
+ namespace Kadence_Starter_Templates;
10
+
11
+ class ImportActions {
12
+ /**
13
+ * Register all action hooks for this class.
14
+ */
15
+ public function register_hooks() {
16
+ // Before content import.
17
+ add_action( 'kadence-starter-templates/before_content_import_execution', array( $this, 'before_content_import_action' ), 10, 4 );
18
+
19
+ // After content import.
20
+ add_action( 'kadence-starter-templates/after_content_import_execution', array( $this, 'before_widget_import_action' ), 10, 4 );
21
+ add_action( 'kadence-starter-templates/after_content_import_execution', array( $this, 'widgets_import' ), 20, 4 );
22
+
23
+ // Customizer import.
24
+ add_action( 'kadence-starter-templates/customizer_import_execution', array( $this, 'customizer_import' ), 10, 1 );
25
+
26
+ // After full import action.
27
+ add_action( 'kadence-starter-templates/after_all_import_execution', array( $this, 'after_import_action' ), 10, 4 );
28
+
29
+ // Special widget import cases.
30
+ add_action( 'kadence-starter-templates/widget_settings_array', array( $this, 'fix_custom_menu_widget_ids' ) );
31
+ }
32
+
33
+
34
+ /**
35
+ * Change the menu IDs in the custom menu widgets in the widget import data.
36
+ * This solves the issue with custom menu widgets not having the correct (new) menu ID, because they
37
+ * have the old menu ID from the export site.
38
+ *
39
+ * @param array $widget The widget settings array.
40
+ */
41
+ public function fix_custom_menu_widget_ids( $widget ) {
42
+ // Skip (no changes needed), if this is not a custom menu widget.
43
+ if ( ! array_key_exists( 'nav_menu', $widget ) || empty( $widget['nav_menu'] ) || ! is_int( $widget['nav_menu'] ) ) {
44
+ return $widget;
45
+ }
46
+
47
+ // Get import data, with new menu IDs.
48
+ $ocdi = Starter_Templates::get_instance();
49
+ $content_import_data = $ocdi->importer->get_importer_data();
50
+ $term_ids = $content_import_data['mapping']['term_id'];
51
+
52
+ // Set the new menu ID for the widget.
53
+ $widget['nav_menu'] = $term_ids[ $widget['nav_menu'] ];
54
+
55
+ return $widget;
56
+ }
57
+
58
+
59
+ /**
60
+ * Execute the widgets import.
61
+ *
62
+ * @param array $selected_import_files Actual selected import files (content, widgets, customizer, redux).
63
+ * @param array $import_files The filtered import files defined in `kadence-starter-templates/import_files` filter.
64
+ * @param int $selected_index Selected index of import.
65
+ */
66
+ public function widgets_import( $selected_import_files, $import_files, $selected_index, $selected_palette ) {
67
+ if ( ! empty( $selected_import_files['widgets'] ) ) {
68
+ WidgetImporter::import( $selected_import_files['widgets'] );
69
+ }
70
+ }
71
+
72
+
73
+ /**
74
+ * Execute the customizer import.
75
+ *
76
+ * @param array $selected_import_files Actual selected import files (content, widgets, customizer, redux).
77
+ * @param array $import_files The filtered import files defined in `kadence-starter-templates/import_files` filter.
78
+ * @param int $selected_index Selected index of import.
79
+ */
80
+ public function customizer_import( $selected_import_files ) {
81
+ if ( ! empty( $selected_import_files['customizer'] ) ) {
82
+ CustomizerImporter::import( $selected_import_files['customizer'] );
83
+ }
84
+ }
85
+
86
+
87
+ /**
88
+ * Execute the action: 'kadence-starter-templates/before_content_import'.
89
+ *
90
+ * @param array $selected_import_files Actual selected import files (content, widgets, customizer, redux).
91
+ * @param array $import_files The filtered import files defined in `kadence-starter-templates/import_files` filter.
92
+ * @param int $selected_index Selected index of import.
93
+ */
94
+ public function before_content_import_action( $selected_import_files, $import_files, $selected_index, $selected_palette ) {
95
+ $this->do_import_action( 'kadence-starter-templates/before_content_import', $import_files[ $selected_index ], $selected_palette );
96
+ }
97
+
98
+
99
+ /**
100
+ * Execute the action: 'kadence-starter-templates/before_widgets_import'.
101
+ *
102
+ * @param array $selected_import_files Actual selected import files (content, widgets, customizer, redux).
103
+ * @param array $import_files The filtered import files defined in `kadence-starter-templates/import_files` filter.
104
+ * @param int $selected_index Selected index of import.
105
+ */
106
+ public function before_widget_import_action( $selected_import_files, $import_files, $selected_index, $selected_palette ) {
107
+ $this->do_import_action( 'kadence-starter-templates/before_widgets_import', $import_files[ $selected_index ], $selected_palette );
108
+ }
109
+
110
+
111
+ /**
112
+ * Execute the action: 'kadence-starter-templates/after_import'.
113
+ *
114
+ * @param array $selected_import_files Actual selected import files (content, widgets, customizer, redux).
115
+ * @param array $import_files The filtered import files defined in `kadence-starter-templates/import_files` filter.
116
+ * @param int $selected_index Selected index of import.
117
+ */
118
+ public function after_import_action( $selected_import_files, $import_files, $selected_index, $selected_palette ) {
119
+ $this->do_import_action( 'kadence-starter-templates/after_import', $import_files[ $selected_index ], $selected_palette );
120
+ }
121
+
122
+
123
+ /**
124
+ * Register the do_action hook, so users can hook to these during import.
125
+ *
126
+ * @param string $action The action name to be executed.
127
+ * @param array $selected_import The data of selected import from `kadence-starter-templates/import_files` filter.
128
+ */
129
+ private function do_import_action( $action, $selected_import, $selected_palette ) {
130
+ if ( false !== has_action( $action ) ) {
131
+ $kadence_starter_templates = Starter_Templates::get_instance();
132
+ $log_file_path = $kadence_starter_templates->get_log_file_path();
133
+
134
+ ob_start();
135
+ do_action( $action, $selected_import, $selected_palette );
136
+ $message = ob_get_clean();
137
+
138
+ // Add this message to log file.
139
+ $log_added = Helpers::append_to_file(
140
+ $message,
141
+ $log_file_path,
142
+ $action
143
+ );
144
+ }
145
+ }
146
+ }
inc/class-importer.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for declaring the content importer used in the One Click Demo Import plugin
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ class Importer {
11
+ /**
12
+ * The importer class object used for importing content.
13
+ *
14
+ * @var object
15
+ */
16
+ private $importer;
17
+
18
+ /**
19
+ * Time in milliseconds, marking the beginning of the import.
20
+ *
21
+ * @var float
22
+ */
23
+ private $microtime;
24
+
25
+ /**
26
+ * The instance of the Kadence_Starter_Templates\Logger class.
27
+ *
28
+ * @var object
29
+ */
30
+ public $logger;
31
+
32
+ /**
33
+ * The instance of the Kadence_Starter_Templates class.
34
+ *
35
+ * @var object
36
+ */
37
+ private $kadence_starter_templates;
38
+
39
+ /**
40
+ * Constructor method.
41
+ *
42
+ * @param array $importer_options Importer options.
43
+ * @param object $logger Logger object used in the importer.
44
+ */
45
+ public function __construct( $importer_options = array(), $logger = null ) {
46
+ // Include files that are needed for WordPress Importer v2.
47
+ $this->include_required_files();
48
+
49
+ // Set the WordPress Importer v2 as the importer used in this plugin.
50
+ // More: https://github.com/humanmade/WordPress-Importer.
51
+ $this->importer = new WXRImporter( $importer_options );
52
+
53
+ // Set logger to the importer.
54
+ $this->logger = $logger;
55
+ if ( ! empty( $this->logger ) ) {
56
+ $this->set_logger( $this->logger );
57
+ }
58
+
59
+ // Get the kadence_starter_templates (main plugin class) instance.
60
+ $this->kadence_starter_templates = Starter_Templates::get_instance();
61
+ }
62
+
63
+
64
+ /**
65
+ * Include required files.
66
+ */
67
+ private function include_required_files() {
68
+ if ( ! class_exists( '\WP_Importer' ) ) {
69
+ require ABSPATH . '/wp-admin/includes/class-wp-importer.php';
70
+ }
71
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'vendor/wxr-importer/WXRImporter.php';
72
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'vendor/wxr-importer/WXRImportInfo.php';
73
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'vendor/wxr-importer/Importer.php';
74
+ require_once KADENCE_STARTER_TEMPLATES_PATH . 'inc/class-wxr-importer.php';
75
+ }
76
+
77
+
78
+ /**
79
+ * Imports content from a WordPress export file.
80
+ *
81
+ * @param string $data_file path to xml file, file with WordPress export data.
82
+ */
83
+ public function import( $data_file ) {
84
+ $this->importer->import( $data_file );
85
+ }
86
+
87
+
88
+ /**
89
+ * Set the logger used in the import
90
+ *
91
+ * @param object $logger logger instance.
92
+ */
93
+ public function set_logger( $logger ) {
94
+ $this->importer->set_logger( $logger );
95
+ }
96
+
97
+
98
+ /**
99
+ * Get all protected variables from the WXR_Importer needed for continuing the import.
100
+ */
101
+ public function get_importer_data() {
102
+ return $this->importer->get_importer_data();
103
+ }
104
+
105
+
106
+ /**
107
+ * Sets all protected variables from the WXR_Importer needed for continuing the import.
108
+ *
109
+ * @param array $data with set variables.
110
+ */
111
+ public function set_importer_data( $data ) {
112
+ $this->importer->set_importer_data( $data );
113
+ }
114
+
115
+
116
+ /**
117
+ * Import content from an WP XML file.
118
+ *
119
+ * @param string $import_file_path Path to the import file.
120
+ */
121
+ public function import_content( $import_file_path ) {
122
+ $this->microtime = microtime( true );
123
+
124
+ // Increase PHP max execution time. Just in case, even though the AJAX calls are only 25 sec long.
125
+ if ( strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) === false ) {
126
+ set_time_limit( apply_filters( 'kadence-starter-templates/set_time_limit_for_demo_data_import', 300 ) );
127
+ }
128
+
129
+ // Disable import of authors.
130
+ add_filter( 'wxr_importer.pre_process.user', '__return_false' );
131
+
132
+ // Check, if we need to send another AJAX request and set the importing author to the current user.
133
+ add_filter( 'wxr_importer.pre_process.post', array( $this, 'new_ajax_request_maybe' ) );
134
+
135
+ // Disables generation of multiple image sizes (thumbnails) in the content import step.
136
+ if ( ! apply_filters( 'kadence-starter-templates/regenerate_thumbnails_in_content_import', true ) ) {
137
+ add_filter( 'intermediate_image_sizes_advanced', '__return_null' );
138
+ }
139
+
140
+ // Import content.
141
+ if ( ! empty( $import_file_path ) ) {
142
+ ob_start();
143
+ $this->import( $import_file_path );
144
+ $message = ob_get_clean();
145
+ }
146
+
147
+ // Return any error messages for the front page output (errors, critical, alert and emergency level messages only).
148
+ return $this->logger->error_output;
149
+ }
150
+
151
+
152
+ /**
153
+ * Check if we need to create a new AJAX request, so that server does not timeout.
154
+ *
155
+ * @param array $data current post data.
156
+ * @return array
157
+ */
158
+ public function new_ajax_request_maybe( $data ) {
159
+ $time = microtime( true ) - $this->microtime;
160
+
161
+ // We should make a new ajax call, if the time is right.
162
+ if ( $time > apply_filters( 'kadence-starter-templates/time_for_one_ajax_call', 25 ) ) {
163
+ $response = array(
164
+ 'status' => 'newAJAX',
165
+ 'message' => 'Time for new AJAX request!: ' . $time,
166
+ );
167
+
168
+ // Add any output to the log file and clear the buffers.
169
+ $message = ob_get_clean();
170
+
171
+ // Add any error messages to the frontend_error_messages variable in OCDI main class.
172
+ if ( ! empty( $message ) ) {
173
+ $this->kadence_starter_templates->append_to_frontend_error_messages( $message );
174
+ }
175
+
176
+ // Add message to log file.
177
+ $log_added = Helpers::append_to_file(
178
+ __( 'New AJAX call!' , 'kadence-starter-templates' ) . PHP_EOL . $message,
179
+ $this->kadence_starter_templates->get_log_file_path(),
180
+ ''
181
+ );
182
+
183
+ // Set the current importer stat, so it can be continued on the next AJAX call.
184
+ $this->set_current_importer_data();
185
+
186
+ // Send the request for a new AJAX call.
187
+ wp_send_json( $response );
188
+ }
189
+
190
+ // Set importing author to the current user.
191
+ // Fixes the [WARNING] Could not find the author for ... log warning messages.
192
+ $current_user_obj = wp_get_current_user();
193
+ $data['post_author'] = $current_user_obj->user_login;
194
+
195
+ return $data;
196
+ }
197
+
198
+
199
+ /**
200
+ * Set current state of the content importer, so we can continue the import with new AJAX request.
201
+ */
202
+ private function set_current_importer_data() {
203
+ $data = array_merge( $this->kadence_starter_templates->get_current_importer_data(), $this->get_importer_data() );
204
+
205
+ Helpers::set_import_data_transient( $data );
206
+ }
207
+ }
inc/class-logger-cli.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Logger class
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
+ * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-logger.php
21
+ * @package Kadence Starter Templates
22
+ */
23
+
24
+ namespace Kadence_Starter_Templates;
25
+
26
+ class Logger_CLI extends Logger {
27
+
28
+ public $min_level = 'notice';
29
+ /**
30
+ * Variable for front-end error display.
31
+ *
32
+ * @var string
33
+ */
34
+ public $error_output = '';
35
+
36
+ /**
37
+ * Overwritten log function from WP_Importer_Logger_CLI.
38
+ *
39
+ * Logs with an arbitrary level.
40
+ *
41
+ * @param mixed $level level of reporting.
42
+ * @param string $message log message.
43
+ * @param array $context context to the log message.
44
+ */
45
+ public function log( $level, $message, array $context = array() ) {
46
+ // Save error messages for front-end display.
47
+ $this->error_output( $level, $message, $context = array() );
48
+
49
+ if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( $this->min_level ) ) {
50
+ return;
51
+ }
52
+
53
+ printf(
54
+ '[%s] %s' . PHP_EOL,
55
+ strtoupper( $level ),
56
+ $message
57
+ );
58
+ }
59
+
60
+
61
+ /**
62
+ * Save messages for error output.
63
+ * Only the messages greater then Error.
64
+ *
65
+ * @param mixed $level level of reporting.
66
+ * @param string $message log message.
67
+ * @param array $context context to the log message.
68
+ */
69
+ public function error_output( $level, $message, array $context = array() ) {
70
+ if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( 'error' ) ) {
71
+ return;
72
+ }
73
+
74
+ $this->error_output .= sprintf(
75
+ '[%s] %s<br>',
76
+ strtoupper( $level ),
77
+ $message
78
+ );
79
+ }
80
+
81
+ public static function level_to_numeric( $level ) {
82
+ $levels = array(
83
+ 'emergency' => 8,
84
+ 'alert' => 7,
85
+ 'critical' => 6,
86
+ 'error' => 5,
87
+ 'warning' => 4,
88
+ 'notice' => 3,
89
+ 'info' => 2,
90
+ 'debug' => 1,
91
+ );
92
+ if ( ! isset( $levels[ $level ] ) ) {
93
+ return 0;
94
+ }
95
+
96
+ return $levels[ $level ];
97
+ }
98
+ }
inc/class-logger.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Logger class
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
+ * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-logger.php
21
+ * @package Kadence Starter Templates
22
+ */
23
+
24
+ namespace Kadence_Starter_Templates;
25
+
26
+ class Logger {
27
+ /**
28
+ * System is unusable.
29
+ *
30
+ * @param string $message
31
+ * @param array $context
32
+ * @return null
33
+ */
34
+ public function emergency( $message, array $context = array() ) {
35
+ return $this->log( 'emergency', $message, $context );
36
+ }
37
+
38
+ /**
39
+ * Action must be taken immediately.
40
+ *
41
+ * Example: Entire website down, database unavailable, etc. This should
42
+ * trigger the SMS alerts and wake you up.
43
+ *
44
+ * @param string $message
45
+ * @param array $context
46
+ * @return null
47
+ */
48
+ public function alert( $message, array $context = array() ) {
49
+ return $this->log( 'alert', $message, $context );
50
+ }
51
+
52
+ /**
53
+ * Critical conditions.
54
+ *
55
+ * Example: Application component unavailable, unexpected exception.
56
+ *
57
+ * @param string $message
58
+ * @param array $context
59
+ * @return null
60
+ */
61
+ public function critical( $message, array $context = array() ) {
62
+ return $this->log( 'critical', $message, $context );
63
+ }
64
+
65
+ /**
66
+ * Runtime errors that do not require immediate action but should typically
67
+ * be logged and monitored.
68
+ *
69
+ * @param string $message
70
+ * @param array $context
71
+ * @return null
72
+ */
73
+ public function error( $message, array $context = array()) {
74
+ return $this->log( 'error', $message, $context );
75
+ }
76
+
77
+ /**
78
+ * Exceptional occurrences that are not errors.
79
+ *
80
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
81
+ * that are not necessarily wrong.
82
+ *
83
+ * @param string $message
84
+ * @param array $context
85
+ * @return null
86
+ */
87
+ public function warning( $message, array $context = array() ) {
88
+ return $this->log( 'warning', $message, $context );
89
+ }
90
+
91
+ /**
92
+ * Normal but significant events.
93
+ *
94
+ * @param string $message
95
+ * @param array $context
96
+ * @return null
97
+ */
98
+ public function notice( $message, array $context = array() ) {
99
+ return $this->log( 'notice', $message, $context );
100
+ }
101
+
102
+ /**
103
+ * Interesting events.
104
+ *
105
+ * Example: User logs in, SQL logs.
106
+ *
107
+ * @param string $message
108
+ * @param array $context
109
+ * @return null
110
+ */
111
+ public function info( $message, array $context = array() ) {
112
+ return $this->log( 'info', $message, $context );
113
+ }
114
+
115
+ /**
116
+ * Detailed debug information.
117
+ *
118
+ * @param string $message
119
+ * @param array $context
120
+ * @return null
121
+ */
122
+ public function debug( $message, array $context = array() ) {
123
+ return $this->log( 'debug', $message, $context );
124
+ }
125
+
126
+ /**
127
+ * Logs with an arbitrary level.
128
+ *
129
+ * @param mixed $level
130
+ * @param string $message
131
+ * @param array $context
132
+ * @return null
133
+ */
134
+ public function log( $level, $message, array $context = array() ) {
135
+ $this->messages[] = array(
136
+ 'timestamp' => time(),
137
+ 'level' => $level,
138
+ 'message' => $message,
139
+ 'context' => $context,
140
+ );
141
+ }
142
+ }
inc/class-plugin-check.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Static functions used in the Kadence Starter Templates plugin.
4
+ *
5
+ * @package Kadence Starter Templates
6
+ */
7
+
8
+ namespace Kadence_Starter_Templates;
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit; // Exit if accessed directly.
12
+ }
13
+
14
+
15
+ /**
16
+ * Class with static helper functions.
17
+ */
18
+ class Plugin_Check {
19
+
20
+ /**
21
+ * Static var active plugins
22
+ *
23
+ * @var $active_plugins
24
+ */
25
+ private static $active_plugins;
26
+
27
+ /**
28
+ * Static var installed plugins
29
+ *
30
+ * @var $installed_plugins
31
+ */
32
+ private static $installed_plugins;
33
+ /**
34
+ * Initialize
35
+ */
36
+ public static function init() {
37
+ if ( ! function_exists( 'get_plugins' ) ) {
38
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
39
+ }
40
+ self::$installed_plugins = (array) get_plugins();
41
+
42
+ self::$active_plugins = (array) get_option( 'active_plugins', array() );
43
+
44
+ if ( is_multisite() ) {
45
+ self::$active_plugins = array_merge( self::$active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Active Check
51
+ *
52
+ * @param string $plugin_base_name is plugin folder/filename.php.
53
+ */
54
+ public static function active_check( $plugin_base_name ) {
55
+ if ( ! self::$active_plugins || ! self::$installed_plugins ) {
56
+ self::init();
57
+ }
58
+ if ( in_array( $plugin_base_name, self::$active_plugins, true ) || array_key_exists( $plugin_base_name, self::$active_plugins ) ) {
59
+ return 'active';
60
+ } elseif ( in_array( $plugin_base_name, self::$installed_plugins, true ) || array_key_exists( $plugin_base_name, self::$installed_plugins ) ) {
61
+ return 'installed';
62
+ } else {
63
+ return 'notactive';
64
+ }
65
+ }
66
+ }
inc/class-widget-importer.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for the widget importer used in the One Click Demo Import plugin.
4
+ *
5
+ * Code is mostly from the Widget Importer & Exporter plugin.
6
+ *
7
+ * @see https://wordpress.org/plugins/widget-importer-exporter/
8
+ * @package Kadence Starter Templates
9
+ */
10
+
11
+ namespace Kadence_Starter_Templates;
12
+
13
+ class WidgetImporter {
14
+ /**
15
+ * Import widgets from WIE or JSON file.
16
+ *
17
+ * @param string $widget_import_file_path path to the widget import file.
18
+ */
19
+ public static function import( $widget_import_file_path ) {
20
+ $results = array();
21
+ $kadence_starter_templates = Starter_Templates::get_instance();
22
+ $log_file_path = $kadence_starter_templates->get_log_file_path();
23
+
24
+ // Import widgets and return result.
25
+ if ( ! empty( $widget_import_file_path ) ) {
26
+ $results = self::import_widgets( $widget_import_file_path );
27
+ }
28
+
29
+ // Check for errors, else write the results to the log file.
30
+ if ( is_wp_error( $results ) ) {
31
+ $error_message = $results->get_error_message();
32
+
33
+ // Add any error messages to the frontend_error_messages variable in OCDI main class.
34
+ $kadence_starter_templates->append_to_frontend_error_messages( $error_message );
35
+
36
+ // Write error to log file.
37
+ Helpers::append_to_file(
38
+ $error_message,
39
+ $log_file_path,
40
+ esc_html__( 'Importing widgets', 'kadence-starter-templates' )
41
+ );
42
+ } else {
43
+ ob_start();
44
+ self::format_results_for_log( $results );
45
+ $message = ob_get_clean();
46
+
47
+ // Add this message to log file.
48
+ $log_added = Helpers::append_to_file(
49
+ $message,
50
+ $log_file_path,
51
+ esc_html__( 'Importing widgets' , 'kadence-starter-templates' )
52
+ );
53
+ }
54
+
55
+ }
56
+
57
+
58
+ /**
59
+ * Imports widgets from a json file.
60
+ *
61
+ * @param string $data_file path to json file with WordPress widget export data.
62
+ */
63
+ private static function import_widgets( $data_file ) {
64
+ // Get widgets data from file.
65
+ $data = self::process_import_file( $data_file );
66
+
67
+ // Return from this function if there was an error.
68
+ if ( is_wp_error( $data ) ) {
69
+ return $data;
70
+ }
71
+
72
+ // Import the widget data and save the results.
73
+ return self::import_data( $data );
74
+ }
75
+
76
+ /**
77
+ * Process import file - this parses the widget data and returns it.
78
+ *
79
+ * @param string $file path to json file.
80
+ * @return object $data decoded JSON string
81
+ */
82
+ private static function process_import_file( $file ) {
83
+ // File exists?
84
+ if ( ! file_exists( $file ) ) {
85
+ return new \WP_Error(
86
+ 'widget_import_file_not_found',
87
+ __( 'Error: Widget import file could not be found.', 'kadence-starter-templates' )
88
+ );
89
+ }
90
+
91
+ // Get file contents and decode.
92
+ $data = Helpers::data_from_file( $file );
93
+
94
+ // Return from this function if there was an error.
95
+ if ( is_wp_error( $data ) ) {
96
+ return $data;
97
+ }
98
+
99
+ return json_decode( $data );
100
+ }
101
+
102
+
103
+ /**
104
+ * Import widget JSON data
105
+ *
106
+ * @global array $wp_registered_sidebars
107
+ * @param object $data JSON widget data.
108
+ * @return array $results
109
+ */
110
+ private static function import_data( $data ) {
111
+ global $wp_registered_sidebars;
112
+
113
+ // Have valid data? If no data or could not decode.
114
+ if ( empty( $data ) || ! is_object( $data ) ) {
115
+ return new \WP_Error(
116
+ 'corrupted_widget_import_data',
117
+ __( 'Error: Widget import data could not be read. Please try a different file.', 'kadence-starter-templates' )
118
+ );
119
+ }
120
+
121
+ // Hook before import.
122
+ do_action( 'kadence-starter-templates/widget_importer_before_widgets_import' );
123
+ $data = apply_filters( 'kadence-starter-templates/before_widgets_import_data', $data );
124
+
125
+ // Get all available widgets site supports.
126
+ $available_widgets = self::available_widgets();
127
+
128
+ // Get all existing widget instances.
129
+ $widget_instances = array();
130
+
131
+ foreach ( $available_widgets as $widget_data ) {
132
+ $widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] );
133
+ }
134
+
135
+ // Begin results.
136
+ $results = array();
137
+
138
+ // Loop import data's sidebars.
139
+ foreach ( $data as $sidebar_id => $widgets ) {
140
+ // Skip inactive widgets (should not be in export file).
141
+ if ( 'wp_inactive_widgets' == $sidebar_id ) {
142
+ continue;
143
+ }
144
+
145
+ // Check if sidebar is available on this site. Otherwise add widgets to inactive, and say so.
146
+ if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
147
+ $sidebar_available = true;
148
+ $use_sidebar_id = $sidebar_id;
149
+ $sidebar_message_type = 'success';
150
+ $sidebar_message = '';
151
+ }
152
+ else {
153
+ $sidebar_available = false;
154
+ $use_sidebar_id = 'wp_inactive_widgets'; // Add to inactive if sidebar does not exist in theme.
155
+ $sidebar_message_type = 'error';
156
+ $sidebar_message = __( 'Sidebar does not exist in theme (moving widget to Inactive)', 'kadence-starter-templates' );
157
+ }
158
+
159
+ // Result for sidebar.
160
+ $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.
161
+ $results[ $sidebar_id ]['message_type'] = $sidebar_message_type;
162
+ $results[ $sidebar_id ]['message'] = $sidebar_message;
163
+ $results[ $sidebar_id ]['widgets'] = array();
164
+
165
+ // Loop widgets.
166
+ foreach ( $widgets as $widget_instance_id => $widget ) {
167
+ $fail = false;
168
+
169
+ // Get id_base (remove -# from end) and instance ID number.
170
+ $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
171
+ $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
172
+
173
+ // Does site support this widget?
174
+ if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) {
175
+ $fail = true;
176
+ $widget_message_type = 'error';
177
+ $widget_message = __( 'Site does not support widget', 'kadence-starter-templates' ); // Explain why widget not imported.
178
+ }
179
+
180
+ // Filter to modify settings object before conversion to array and import.
181
+ // Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below).
182
+ // Ideally the newer wie_widget_settings_array below will be used instead of this.
183
+ $widget = apply_filters( 'kadence-starter-templates/widget_settings', $widget ); // Object.
184
+
185
+ // Convert multidimensional objects to multidimensional arrays.
186
+ // Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays.
187
+ // Without this, they are imported as objects and cause fatal error on Widgets page.
188
+ // 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.
189
+ // It is probably much more likely that arrays are used than objects, however.
190
+ $widget = json_decode( json_encode( $widget ), true );
191
+
192
+ // Filter to modify settings array.
193
+ // This is preferred over the older wie_widget_settings filter above.
194
+ // Do before identical check because changes may make it identical to end result (such as URL replacements).
195
+ $widget = apply_filters( 'kadence-starter-templates/widget_settings_array', $widget );
196
+
197
+ // Does widget with identical settings already exist in same sidebar?
198
+ if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) {
199
+ // Get existing widgets in this sidebar.
200
+ $sidebars_widgets = get_option( 'sidebars_widgets' );
201
+ $sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // Check Inactive if that's where will go.
202
+
203
+ // Loop widgets with ID base.
204
+ $single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array();
205
+ foreach ( $single_widget_instances as $check_id => $check_widget ) {
206
+ // Is widget in same sidebar and has identical settings?
207
+ if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
208
+ $fail = true;
209
+ $widget_message_type = 'warning';
210
+ $widget_message = __( 'Widget already exists', 'kadence-starter-templates' ); // Explain why widget not imported.
211
+
212
+ break;
213
+ }
214
+ }
215
+ }
216
+
217
+ // No failure.
218
+ if ( ! $fail ) {
219
+ // Add widget instance.
220
+ $single_widget_instances = get_option( 'widget_' . $id_base ); // All instances for that widget ID base, get fresh every time.
221
+ $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // Start fresh if have to.
222
+ $single_widget_instances[] = $widget; // Add it.
223
+
224
+ // Get the key it was given.
225
+ end( $single_widget_instances );
226
+ $new_instance_id_number = key( $single_widget_instances );
227
+
228
+ // If key is 0, make it 1.
229
+ // 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).
230
+ if ( '0' === strval( $new_instance_id_number ) ) {
231
+ $new_instance_id_number = 1;
232
+ $single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0];
233
+ unset( $single_widget_instances[0] );
234
+ }
235
+
236
+ // Move _multiwidget to end of array for uniformity.
237
+ if ( isset( $single_widget_instances['_multiwidget'] ) ) {
238
+ $multiwidget = $single_widget_instances['_multiwidget'];
239
+ unset( $single_widget_instances['_multiwidget'] );
240
+ $single_widget_instances['_multiwidget'] = $multiwidget;
241
+ }
242
+
243
+ // Update option with new widget.
244
+ update_option( 'widget_' . $id_base, $single_widget_instances );
245
+
246
+ // Assign widget instance to sidebar.
247
+ $sidebars_widgets = get_option( 'sidebars_widgets' ); // Which sidebars have which widgets, get fresh every time.
248
+
249
+ // Avoid rarely fatal error when the option is an empty string
250
+ // https://github.com/churchthemes/widget-importer-exporter/pull/11.
251
+ if ( ! $sidebars_widgets ) {
252
+ $sidebars_widgets = array();
253
+ }
254
+
255
+ $new_instance_id = $id_base . '-' . $new_instance_id_number; // Use ID number from new widget instance.
256
+ $sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // Add new instance to sidebar.
257
+ update_option( 'sidebars_widgets', $sidebars_widgets ); // Save the amended data.
258
+
259
+ // After widget import action.
260
+ $after_widget_import = array(
261
+ 'sidebar' => $use_sidebar_id,
262
+ 'sidebar_old' => $sidebar_id,
263
+ 'widget' => $widget,
264
+ 'widget_type' => $id_base,
265
+ 'widget_id' => $new_instance_id,
266
+ 'widget_id_old' => $widget_instance_id,
267
+ 'widget_id_num' => $new_instance_id_number,
268
+ 'widget_id_num_old' => $instance_id_number,
269
+ );
270
+ do_action( 'kadence-starter-templates/widget_importer_after_single_widget_import', $after_widget_import );
271
+
272
+ // Success message.
273
+ if ( $sidebar_available ) {
274
+ $widget_message_type = 'success';
275
+ $widget_message = __( 'Imported', 'kadence-starter-templates' );
276
+ }
277
+ else {
278
+ $widget_message_type = 'warning';
279
+ $widget_message = __( 'Imported to Inactive', 'kadence-starter-templates' );
280
+ }
281
+ }
282
+
283
+ // Result for widget instance.
284
+ $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).
285
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : __( 'No Title', 'kadence-starter-templates' ); // Show "No Title" if widget instance is untitled.
286
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type;
287
+ $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message;
288
+
289
+ }
290
+ }
291
+
292
+ // Hook after import.
293
+ do_action( 'kadence-starter-templates/widget_importer_after_widgets_import' );
294
+
295
+ // Return results.
296
+ return apply_filters( 'kadence-starter-templates/widget_import_results', $results );
297
+ }
298
+
299
+
300
+ /**
301
+ * Available widgets.
302
+ *
303
+ * Gather site's widgets into array with ID base, name, etc.
304
+ *
305
+ * @global array $wp_registered_widget_controls
306
+ * @return array $available_widgets, Widget information
307
+ */
308
+ private static function available_widgets() {
309
+ global $wp_registered_widget_controls;
310
+
311
+ $widget_controls = $wp_registered_widget_controls;
312
+ $available_widgets = array();
313
+
314
+ foreach ( $widget_controls as $widget ) {
315
+ if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) {
316
+ $available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base'];
317
+ $available_widgets[ $widget['id_base'] ]['name'] = $widget['name'];
318
+ }
319
+ }
320
+
321
+ return apply_filters( 'kadence-starter-templates/available_widgets', $available_widgets );
322
+ }
323
+
324
+
325
+ /**
326
+ * Format results for log file
327
+ *
328
+ * @param array $results widget import results.
329
+ */
330
+ private static function format_results_for_log( $results ) {
331
+ if ( empty( $results ) ) {
332
+ esc_html_e( 'No results for widget import!', 'kadence-starter-templates' );
333
+ }
334
+
335
+ // Loop sidebars.
336
+ foreach ( $results as $sidebar ) {
337
+ echo esc_html( $sidebar['name'] ) . ' : ' . esc_html( $sidebar['message'] ) . PHP_EOL . PHP_EOL;
338
+ // Loop widgets.
339
+ foreach ( $sidebar['widgets'] as $widget ) {
340
+ echo esc_html( $widget['name'] ) . ' - ' . esc_html( $widget['title'] ) . ' - ' . esc_html( $widget['message'] ) . PHP_EOL;
341
+ }
342
+ echo PHP_EOL;
343
+ }
344
+ }
345
+ }
inc/class-wxr-importer.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WXR importer class used in the One Click Demo Import plugin.
4
+ * Needed to extend the WXR_Importer class to get/set the importer protected variables,
5
+ * for use in the multiple AJAX calls.
6
+ *
7
+ * @package Kadence Starter Templates
8
+ */
9
+
10
+ namespace Kadence_Starter_Templates;
11
+
12
+ class WXRImporter extends \AwesomeMotive\WPContentImporter2\WXRImporter {
13
+ /**
14
+ * Constructor method.
15
+ *
16
+ * @param array $options Importer options.
17
+ */
18
+ public function __construct( $options = array() ) {
19
+ parent::__construct( $options );
20
+
21
+ // Set current user to $mapping variable.
22
+ // Fixes the [WARNING] Could not find the author for ... log warning messages.
23
+ $current_user_obj = wp_get_current_user();
24
+ $this->mapping['user_slug'][ $current_user_obj->user_login ] = $current_user_obj->ID;
25
+
26
+ // WooCommerce product attributes registration.
27
+ if ( class_exists( 'WooCommerce' ) ) {
28
+ add_filter( 'wxr_importer.pre_process.term', array( $this, 'woocommerce_product_attributes_registration' ), 10, 1 );
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Get all protected variables from the WXR_Importer needed for continuing the import.
34
+ */
35
+ public function get_importer_data() {
36
+ return array(
37
+ 'mapping' => $this->mapping,
38
+ 'requires_remapping' => $this->requires_remapping,
39
+ 'exists' => $this->exists,
40
+ 'user_slug_override' => $this->user_slug_override,
41
+ 'url_remap' => $this->url_remap,
42
+ 'featured_images' => $this->featured_images,
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Sets all protected variables from the WXR_Importer needed for continuing the import.
48
+ *
49
+ * @param array $data with set variables.
50
+ */
51
+ public function set_importer_data( $data ) {
52
+ $this->mapping = empty( $data['mapping'] ) ? array() : $data['mapping'];
53
+ $this->requires_remapping = empty( $data['requires_remapping'] ) ? array() : $data['requires_remapping'];
54
+ $this->exists = empty( $data['exists'] ) ? array() : $data['exists'];
55
+ $this->user_slug_override = empty( $data['user_slug_override'] ) ? array() : $data['user_slug_override'];
56
+ $this->url_remap = empty( $data['url_remap'] ) ? array() : $data['url_remap'];
57
+ $this->featured_images = empty( $data['featured_images'] ) ? array() : $data['featured_images'];
58
+ }
59
+
60
+ /**
61
+ * Hook into the pre-process term filter of the content import and register the
62
+ * custom WooCommerce product attributes, so that the terms can then be imported normally.
63
+ *
64
+ * This should probably be removed once the WP importer 2.0 support is added in WooCommerce.
65
+ *
66
+ * Fixes: [WARNING] Failed to import pa_size L warnings in content import.
67
+ * Code from: woocommerce/includes/admin/class-wc-admin-importers.php (ver 2.6.9).
68
+ *
69
+ * Github issue: https://github.com/proteusthemes/one-click-demo-import/issues/71
70
+ *
71
+ * @param array $date The term data to import.
72
+ * @return array The unchanged term data.
73
+ */
74
+ public function woocommerce_product_attributes_registration( $data ) {
75
+ global $wpdb;
76
+
77
+ if ( strstr( $data['taxonomy'], 'pa_' ) ) {
78
+ if ( ! taxonomy_exists( $data['taxonomy'] ) ) {
79
+ $attribute_name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $data['taxonomy'] ) );
80
+
81
+ // Create the taxonomy
82
+ if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) {
83
+ $attribute = array(
84
+ 'attribute_label' => $attribute_name,
85
+ 'attribute_name' => $attribute_name,
86
+ 'attribute_type' => 'select',
87
+ 'attribute_orderby' => 'menu_order',
88
+ 'attribute_public' => 0
89
+ );
90
+ $wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
91
+ delete_transient( 'wc_attribute_taxonomies' );
92
+ }
93
+
94
+ // Register the taxonomy now so that the import works!
95
+ register_taxonomy(
96
+ $data['taxonomy'],
97
+ apply_filters( 'woocommerce_taxonomy_objects_' . $data['taxonomy'], array( 'product' ) ),
98
+ apply_filters( 'woocommerce_taxonomy_args_' . $data['taxonomy'], array(
99
+ 'hierarchical' => true,
100
+ 'show_ui' => false,
101
+ 'query_var' => true,
102
+ 'rewrite' => false,
103
+ ) )
104
+ );
105
+ }
106
+ }
107
+
108
+ return $data;
109
+ }
110
+ }
inc/react/src/index.js ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ // import HelpTab from './help';
5
+ // import ProSettings from './pro-extension';
6
+ // import RecommendedTab from './recomended';
7
+ // import StarterTab from './starter';
8
+ // import Sidebar from './sidebar';
9
+ // import CustomizerLinks from './customizer';
10
+ // import Notices from './notices';
11
+ import map from 'lodash/map';
12
+ import LazyLoad from 'react-lazy-load';
13
+ /**
14
+ * WordPress dependencies
15
+ */
16
+ const { __, sprintf } = wp.i18n;
17
+ const { Fragment, Component, render } = wp.element;
18
+ const { Modal, Spinner, ButtonGroup, Button, ExternalLink } = wp.components;
19
+ const { apiFetch } = wp;
20
+
21
+ class KadenceImporter extends Component {
22
+ constructor() {
23
+ super( ...arguments );
24
+ this.runAjax = this.runAjax.bind( this );
25
+ this.runPluginInstall = this.runPluginInstall.bind( this );
26
+ this.focusMode = this.focusMode.bind( this );
27
+ this.state = {
28
+ category: 'all',
29
+ activeTemplate: '',
30
+ colorPalette: '',
31
+ search: null,
32
+ isFetching: false,
33
+ isImporting: false,
34
+ progress: '',
35
+ focusMode: false,
36
+ finished: false,
37
+ templates: ( kadenceStarterParams.templates ? kadenceStarterParams.templates : [] ),
38
+ palettes: ( kadenceStarterParams.palettes ? kadenceStarterParams.palettes : [] ),
39
+ };
40
+ apiFetch.setFetchHandler( ( options ) => {
41
+ const { url, path, data, method } = options;
42
+
43
+ return axios( {
44
+ url: url || path,
45
+ method,
46
+ data,
47
+ } );
48
+ } );
49
+ }
50
+ capitalizeFirstLetter( string ) {
51
+ return string.charAt( 0 ).toUpperCase() + string.slice( 1 );
52
+ }
53
+ focusMode( template_id ) {
54
+ this.setState( { activeTemplate: template_id, focusMode: true } )
55
+ }
56
+ runPluginInstall( selected ) {
57
+ this.setState( { progress: 'plugins', isFetching: true } );
58
+ var data = new FormData();
59
+ data.append( 'action', 'kadence_import_install_plugins' );
60
+ data.append( 'security', kadenceStarterParams.ajax_nonce );
61
+ data.append( 'selected', selected );
62
+ this.runAjax( data );
63
+ }
64
+ runAjax( data ) {
65
+ var control = this;
66
+ jQuery.ajax({
67
+ method: 'POST',
68
+ url: kadenceStarterParams.ajax_url,
69
+ data: data,
70
+ contentType: false,
71
+ processData: false,
72
+ })
73
+ .done( function( response, status, stately ) {
74
+ if ( 'undefined' !== typeof response.status && 'newAJAX' === response.status ) {
75
+ control.state.progress = 'contentNew';
76
+ control.runAjax( data );
77
+ } else if ( 'undefined' !== typeof response.status && 'customizerAJAX' === response.status ) {
78
+ control.setState( { progress: 'customizer' } );
79
+ // Fix for data.set and data.delete, which they are not supported in some browsers.
80
+ var newData = new FormData();
81
+ newData.append( 'action', 'kadence_import_customizer_data' );
82
+ newData.append( 'security', kadenceStarterParams.ajax_nonce );
83
+ newData.append( 'wp_customize', 'on' );
84
+
85
+ control.runAjax( newData );
86
+ } else if ( 'undefined' !== typeof response.status && 'afterAllImportAJAX' === response.status ) {
87
+ control.setState( { progress: 'widgets' } );
88
+ // Fix for data.set and data.delete, which they are not supported in some browsers.
89
+ var newData = new FormData();
90
+ newData.append( 'action', 'kadence_after_import_data' );
91
+ newData.append( 'security', kadenceStarterParams.ajax_nonce );
92
+ control.runAjax( newData );
93
+ } else if ( 'undefined' !== typeof response.status && 'pluginSuccess' === response.status ) {
94
+ control.setState( { progress: 'content' } );
95
+ var newData = new FormData();
96
+ newData.append( 'action', 'kadence_import_demo_data' );
97
+ newData.append( 'security', kadenceStarterParams.ajax_nonce );
98
+ newData.append( 'selected', control.state.activeTemplate );
99
+ newData.append( 'palette', control.state.colorPalette );
100
+ control.runAjax( newData );
101
+ } else if ( 'undefined' !== typeof response.message ) {
102
+ jQuery( '.kadence_starter_templates_finished' ).append( '<p>' + response.message + '</p>' );
103
+ control.setState( { finished: true, isFetching: false, activeTemplate: '', focusMode: false, isImporting: false, progress: '' } );
104
+ } else {
105
+ jQuery( '.kadence_starter_templates_error' ).append( '<div class="notice kadence_starter_templates_response notice-error"><p>' + response + '</p></div>' );
106
+ control.setState( { finished: true, isFetching: false, activeTemplate: '', focusMode: false, isImporting: false, progress: '' } );
107
+ }
108
+ })
109
+ .fail( function( error ) {
110
+ jQuery( '.kadence_starter_templates_error' ).append( '<div class="notice kadence_starter_templates_response notice-error"><p>Error: ' + error.statusText + ' (' + error.status + ')' + '</p></div>' );
111
+ control.setState( { finished: true, isFetching: false, activeTemplate: '', focusMode: false, isImporting: false, progress: '' } );
112
+ });
113
+ }
114
+ render() {
115
+ const cats = [ 'all' ];
116
+ for ( let i = 0; i < this.state.templates.length; i++ ) {
117
+ for ( let c = 0; c < this.state.templates[ i ].categories.length; c++ ) {
118
+ if ( ! cats.includes( this.state.templates[ i ].categories[ c ] ) ) {
119
+ cats.push( this.state.templates[ i ].categories[ c ] );
120
+ }
121
+ }
122
+ }
123
+ const catOptions = cats.map( ( item ) => {
124
+ return { value: item, label: this.capitalizeFirstLetter( item ) }
125
+ } );
126
+ const KadenceFocusMode = () => {
127
+ const itemArray = this.state.templates.filter( ( { key } ) => key === this.state.activeTemplate );
128
+ const item = itemArray[0];
129
+ let pluginsActive = true;
130
+ const url = ( this.state.colorPalette ? item.url + '?previewcolor=' + this.state.colorPalette : item.url );
131
+ return (
132
+ <div className="kadence-starter-templates-preview theme-install-overlay wp-full-overlay expanded" style={{ display:'block'}}>
133
+ <div className="wp-full-overlay-sidebar">
134
+ <div className="wp-full-overlay-header">
135
+ <button
136
+ className="kst-close-focus-btn close-full-overlay"
137
+ onClick={ () => this.setState( { activeTemplate: '', colorPalette: '', focusMode: false } ) }
138
+ >
139
+ </button>
140
+ </div>
141
+ <div className="wp-full-overlay-sidebar-content">
142
+ <div className="install-theme-info">
143
+ <h3 className="theme-name">{ item.name }</h3>
144
+ <div className="theme-by">{ item.categories.map( category => this.capitalizeFirstLetter( category ) ).join( ', ' ) }</div>
145
+ <img className="theme-screenshot" src={ item.image } alt={ item.name } />
146
+ <div className="palette-title-wrap">
147
+ <h2 className="palette-title">{__( 'Optional: Choose Color Scheme', 'kadence-starter-templates' ) }</h2>
148
+ <Button
149
+ label={ __( 'clear' ) }
150
+ className="kst-clear-palette"
151
+ disabled={ this.state.colorPalette ? false : true }
152
+ icon="image-rotate"
153
+ iconSize={ 10 }
154
+ onClick={ () => this.setState( { colorPalette: '' } ) }
155
+ />
156
+ </div>
157
+ <ButtonGroup className="kst-palette-group" aria-label={ __( 'Select a Palette', 'kadence-starter-templates' ) }>
158
+ { map( this.state.palettes, ( { palette, colors } ) => {
159
+ return (
160
+ <Button
161
+ className="kst-palette-btn"
162
+ isPrimary={ palette === this.state.colorPalette }
163
+ aria-pressed={ palette === this.state.colorPalette }
164
+ onClick={ () => this.setState( { colorPalette: palette } ) }
165
+ >
166
+ { map( colors, ( color, index ) => {
167
+ return (
168
+ <div key={ index } style={ {
169
+ width: 30,
170
+ height: 30,
171
+ marginBottom: 0,
172
+ marginRight:'3px',
173
+ transform: 'scale(1)',
174
+ transition: '100ms transform ease',
175
+ } } className="kadence-swatche-item-wrap">
176
+ <span
177
+ className={ 'kadence-swatch-item' }
178
+ style={ {
179
+ height: '100%',
180
+ display: 'block',
181
+ width: '100%',
182
+ border: '1px solid rgb(218, 218, 218)',
183
+ borderRadius: '50%',
184
+ color: `${ color }`,
185
+ boxShadow: `inset 0 0 0 ${ 30 / 2 }px`,
186
+ transition: '100ms box-shadow ease',
187
+ } }
188
+ >
189
+ </span>
190
+ </div>
191
+ )
192
+ } ) }
193
+ </Button>
194
+ )
195
+ } ) }
196
+ </ButtonGroup>
197
+ <p className="desc-small">{__( '*You can change this after import.', 'kadence-starter-templates' ) }</p>
198
+ </div>
199
+ <div className="kadence-starter-required-plugins">
200
+ <h3>{ __( 'Required Plugins', 'kadence-starter-templates' ) }</h3>
201
+ <ul className="kadence-required-wrap">
202
+ { map( item.plugins, ( { state, title } ) => {
203
+ if ( 'active' !== state ) {
204
+ pluginsActive = false;
205
+ }
206
+ return (
207
+ <li className="plugin-required">
208
+ { title } - <span class="plugin-status">{ ( 'notactive' === state ? __( 'Not Installed', 'kadence-starter-templates' ) : state ) }</span>
209
+ </li>
210
+ );
211
+ } ) }
212
+ </ul>
213
+ { ! pluginsActive && (
214
+ <p className="desc-small">{__( '*Missing/Inactive plugins will be installed on import.', 'kadence-starter-templates' ) }</p>
215
+ ) }
216
+ </div>
217
+ </div>
218
+
219
+ <div class="wp-full-overlay-footer">
220
+ <div class="kadence-starter-templates-preview-actions">
221
+ <button
222
+ className="kst-import-btn button-hero button button-primary"
223
+ isDisabled={ undefined !== item.pro && item.pro && 'true' !== kadenceStarterParams.pro }
224
+ onClick={ () => this.setState( { isImporting: true } ) }
225
+ >
226
+ { __( 'Import', 'kadence-starter-templates' ) }
227
+ </button>
228
+ </div>
229
+ </div>
230
+ </div>
231
+
232
+ <div class="wp-full-overlay-main">
233
+ <iframe id="kadence-starter-preview" src={ url } ></iframe>
234
+ </div>
235
+ </div>
236
+ );
237
+ }
238
+ const KadenceImportMode = () => {
239
+ const itemArray = this.state.templates.filter( ( { key } ) => key === this.state.activeTemplate );
240
+ const item = itemArray[0];
241
+ return (
242
+ <Fragment>
243
+ <div className="kst-grid-single-item">
244
+ <div className="kst-template-item">
245
+ <div className="kst-import-btn">
246
+ <img src={ item.image } alt={ item.name } />
247
+ <div className="demo-title">
248
+ <h4>{ item.name }</h4>
249
+ </div>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ <Modal
254
+ className="kst-import-modal"
255
+ title={ __( 'Import Starter Template' ) }
256
+ onRequestClose={ () => this.state.isFetching ? false : this.setState( { activeTemplate: '', colorPalette: '', focusMode: false, isImporting: false, progress: '' } ) }>
257
+ <div className="kadence_starter_templates_notice">
258
+ { kadenceStarterParams.notice }
259
+ </div>
260
+ <h3>{ __( 'Starter Template Plugins', 'kadence-starter-templates' ) }</h3>
261
+ <ul className="kadence-required-wrap">
262
+ { map( item.plugins, ( { state, title } ) => {
263
+ return (
264
+ <li className="plugin-required">
265
+ { title }
266
+ </li>
267
+ );
268
+ } ) }
269
+ </ul>
270
+ { this.state.colorPalette && (
271
+ <Fragment>
272
+ <h3>{ __( 'Selected Color Palette', 'kadence-starter-templates' ) }</h3>
273
+ { map( this.state.palettes, ( { palette, colors } ) => {
274
+ if ( palette !== this.state.colorPalette ) {
275
+ return;
276
+ }
277
+ return (
278
+ <div className="kst-palette-btn kst-selected-color-palette">
279
+ { map( colors, ( color, index ) => {
280
+ return (
281
+ <div key={ index } style={ {
282
+ width: 22,
283
+ height: 22,
284
+ marginBottom: 0,
285
+ marginRight:'3px',
286
+ transform: 'scale(1)',
287
+ transition: '100ms transform ease',
288
+ } } className="kadence-swatche-item-wrap">
289
+ <span
290
+ className={ 'kadence-swatch-item' }
291
+ style={ {
292
+ height: '100%',
293
+ display: 'block',
294
+ width: '100%',
295
+ border: '1px solid rgb(218, 218, 218)',
296
+ borderRadius: '50%',
297
+ color: `${ color }`,
298
+ boxShadow: `inset 0 0 0 ${ 30 / 2 }px`,
299
+ transition: '100ms box-shadow ease',
300
+ } }
301
+ >
302
+ </span>
303
+ </div>
304
+ )
305
+ } ) }
306
+ </div>
307
+ )
308
+ } ) }
309
+ </Fragment>
310
+ ) }
311
+ { this.state.progress === 'plugins' && (
312
+ <div class="kadence_starter_templates_response">{ kadenceStarterParams.plugin_progress }</div>
313
+ ) }
314
+ { this.state.progress === 'content' && (
315
+ <div class="kadence_starter_templates_response">{ kadenceStarterParams.content_progress }</div>
316
+ ) }
317
+ { this.state.progress === 'contentNew' && (
318
+ <div class="kadence_starter_templates_response">{ kadenceStarterParams.content_new_progress }</div>
319
+ ) }
320
+ { this.state.progress === 'customizer' && (
321
+ <div class="kadence_starter_templates_response">{ kadenceStarterParams.customizer_progress }</div>
322
+ ) }
323
+ { this.state.progress === 'widgets' && (
324
+ <div class="kadence_starter_templates_response">{ kadenceStarterParams.widgets_progress }</div>
325
+ ) }
326
+ { this.state.isFetching && (
327
+ <Spinner />
328
+ ) }
329
+ { ! kadenceStarterParams.isKadence && (
330
+ <div class="kadence_starter_templates_response">
331
+ <h2>{ __( 'This Starter Template Requires the Kadence Theme', 'kadence-starter-templates' ) }</h2>
332
+ <ExternalLink href={ 'https://www.kadencewp.com/kadence-theme/' }>{ __( 'Get Free Theme', 'kadence-blocks' ) }</ExternalLink>
333
+ </div>
334
+ ) }
335
+ { kadenceStarterParams.isKadence && (
336
+ <Button className="kt-defaults-save" isPrimary disabled={ this.state.isFetching } onClick={ () => {
337
+ this.runPluginInstall( item.key );
338
+ } }>
339
+ { __( 'Start Importing' ) }
340
+ </Button>
341
+ ) }
342
+ </Modal>
343
+ </Fragment>
344
+ );
345
+ }
346
+ const KadenceSitesGrid = () => (
347
+ <div className="templates-grid">
348
+ { map( this.state.templates, ( { name, key, image, content, categories, keywords, pro } ) => {
349
+ if ( ( 'all' === this.state.category || categories.includes( this.state.category ) ) && ( ! this.state.search || ( keywords && keywords.some( x => x.toLowerCase().includes( this.state.search.toLowerCase() ) ) ) ) ) {
350
+ return (
351
+ <div className="kst-template-item">
352
+ <Button
353
+ key={ key }
354
+ className="kst-import-btn"
355
+ isSmall
356
+ isDisabled={ undefined !== pro && pro && 'true' !== kadenceStarterParams.pro }
357
+ onClick={ () => this.focusMode( key ) }
358
+ >
359
+ <LazyLoad>
360
+ <img src={ image } alt={ name } />
361
+ </LazyLoad>
362
+ <div className="demo-title">
363
+ <h4>{ name }</h4>
364
+ </div>
365
+ </Button>
366
+ { undefined !== pro && pro && (
367
+ <Fragment>
368
+ <span className="kb-pro-template">{ __( 'Pro', 'kadence-blocks' ) }</span>
369
+ { 'true' !== kadenceStarterParams.pro && (
370
+ <div className="kt-popover-pro-notice">
371
+ <h2>{ __( 'Kadence Pro required for this item', 'kadence-starter-sites' ) } </h2>
372
+ <ExternalLink href={ 'https://www.kadencewp.com/pro/' }>{ __( 'Upgrade to Pro', 'kadence-blocks' ) }</ExternalLink>
373
+ </div>
374
+ ) }
375
+ </Fragment>
376
+ ) }
377
+ </div>
378
+ );
379
+ }
380
+ } ) }
381
+ </div>
382
+ );
383
+
384
+ const MainPanel = () => (
385
+ <div className="main-panel">
386
+ { this.state.focusMode && (
387
+ <Fragment>
388
+ { this.state.isImporting && (
389
+ <KadenceImportMode />
390
+ ) }
391
+ { ! this.state.isImporting && (
392
+ <KadenceFocusMode />
393
+ ) }
394
+ </Fragment>
395
+ ) }
396
+ { ! this.state.focusMode && ! this.state.finished && (
397
+ <KadenceSitesGrid />
398
+ ) }
399
+ </div>
400
+ );
401
+
402
+ return (
403
+ <Fragment>
404
+ <MainPanel />
405
+ </Fragment>
406
+ );
407
+ }
408
+ }
409
+
410
+ wp.domReady( () => {
411
+ render(
412
+ <KadenceImporter />,
413
+ document.querySelector( '.kadence_starter_dashboard_main' )
414
+ );
415
+ } );
inc/react/src/starter-templates.scss ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ $color-primary: #007cba;
2
+ $color-gray-200: #EDF2F7;
3
+ $color-gray-300: #E2E8F0;
4
+ $color-gray-400: #CBD5E0;
5
+ $color-gray-500: #A0AEC0;
6
+ $color-gray-600: #718096;
7
+ $color-gray-700: #4A5568;
8
+ $color-gray-800: #2D3748;
9
+ .appearance_page_kadence-starter-templates #wpcontent {
10
+ padding: 0;
11
+ }
12
+ .kadence_theme_dash_head {
13
+ background: white;
14
+ padding: 10px 20px;
15
+ height: 50px;
16
+ h1 {
17
+ color: $color-gray-800;
18
+ line-height: 50px;
19
+ padding:0;
20
+ height: 50px;
21
+ margin:0;
22
+ display: flex;
23
+ align-items: center;
24
+ }
25
+ .subtext {
26
+ font-size: 16px;
27
+ color: $color-gray-600;
28
+ display: inline-block;
29
+ padding-left: 10px;
30
+ }
31
+ .kadence_theme_dash_head_container {
32
+ margin: 0 auto;
33
+ display: flex;
34
+ align-items: center;
35
+ }
36
+ .kadence_theme_dash_logo{
37
+ width: 50px;
38
+ height: 50px;
39
+ padding-right: 10px;
40
+ img {
41
+ width: 50px;
42
+ }
43
+ }
44
+ .kadence_theme_dash_version {
45
+ flex-grow: 1;
46
+ text-align: right;
47
+ }
48
+ .kadence_theme_dash_version span {
49
+ padding: 5px;
50
+ background: $color-gray-700;
51
+ color: white;
52
+ }
53
+ }
54
+ .wrap.kadence_theme_dash {
55
+ margin: 20px 20px 0;
56
+ }
57
+ .kadence_theme_dashboard {
58
+ margin: 0 auto;
59
+ }
60
+ .templates-grid {
61
+ display: grid;
62
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
63
+ grid-gap: 2em;
64
+ }
65
+ .kst-template-item .components-button.kst-import-btn {
66
+ height: 100%;
67
+ width: 100%;
68
+ padding: 0;
69
+ display: block;
70
+ border: 0;
71
+ background: transparent;
72
+ position: absolute;
73
+ box-shadow: 0 0px 0px 0 rgba(0,0,0,0);
74
+ }
75
+ .kst-template-item .components-button.kst-import-btn img {
76
+ max-width:100%;
77
+ height:auto;
78
+ }
79
+ .kst-grid-single-item {
80
+ max-width: 800px;
81
+ .kst-template-item {
82
+ overflow: hidden;
83
+ }
84
+ .kst-import-btn {
85
+ height: 100%;
86
+ width: 100%;
87
+ padding: 0;
88
+ display: block;
89
+ border: 0;
90
+ background: transparent;
91
+ position: absolute;
92
+ box-shadow: 0 0px 0px 0 rgba(0,0,0,0);
93
+ img {
94
+ max-width:100%;
95
+ height:auto;
96
+ }
97
+ }
98
+ }
99
+ .kadence_starter_templates_notice {
100
+ background: #ebf8ff;
101
+ margin-bottom: 20px;
102
+ border: 1px solid #4299e1;
103
+ padding: 10px;
104
+ color: #2b6cb0;
105
+ font-weight: bold;
106
+ }
107
+ .kst-import-modal ul.kadence-required-wrap {
108
+ margin-bottom: 20px;
109
+ }
110
+ .components-modal__frame.kst-import-modal {
111
+ animation: none !important;
112
+ }
113
+ .kst-import-modal h3 {
114
+ font-size: 16px;
115
+ }
116
+ .kadence_starter_templates_response {
117
+ background: #fffaf0;
118
+ margin-bottom: 20px;
119
+ border: 1px solid #ed8936;
120
+ padding: 10px;
121
+ color: #c05621;
122
+ font-weight: bold;
123
+ }
124
+ .kst-template-item {
125
+ position: relative;
126
+ height: 0;
127
+ padding-bottom: 65%;
128
+ }
129
+ .sidebar-section .components-panel__body.is-opened {
130
+ padding: 20px;
131
+ }
132
+ .sidebar-section h2:first-child, .tab-section h2:first-child {
133
+ margin-top: 0;
134
+ }
135
+ .side-panel .components-panel+.components-panel {
136
+ margin-top: 1rem;
137
+ }
138
+ .tab-section .components-panel__body.is-opened {
139
+ padding: 25px;
140
+ }
141
+ .kadence-dashboard-tab-panel {
142
+ .components-tab-panel__tabs {
143
+ .components-button {
144
+ border: 1px solid transparent;
145
+ background: transparent;
146
+ border: none;
147
+ box-shadow: none;
148
+ border-radius: 0;
149
+ cursor: pointer;
150
+ height: 48px;
151
+ padding: 3px 16px;
152
+ margin-left: 0;
153
+ font-weight: 500;
154
+ &:hover {
155
+ box-shadow: none !important;
156
+ }
157
+ &:not(.active-tab):hover {
158
+ color: $color-primary !important;
159
+ background: transparent !important;;
160
+ }
161
+ }
162
+ .components-button.active-tab {
163
+ background: white;
164
+ border: 1px solid #e2e4e7;
165
+ border-bottom-color: transparent;
166
+ }
167
+ }
168
+ .components-tab-panel__tabs {
169
+ margin-bottom: -1px;
170
+ }
171
+ }
172
+ .two-col-grid {
173
+ display: grid;
174
+ grid-gap: 1rem;
175
+ grid-template-columns: 1fr 1fr 1fr;
176
+ }
177
+ h3.section-sub-head {
178
+ background: $color-gray-200;
179
+ padding: 10px;
180
+ color: $color-gray-700;
181
+ font-size: 14px;
182
+ text-transform: uppercase;
183
+ margin-bottom:1rem;
184
+ margin-top: 2rem;
185
+ }
186
+ .link-item {
187
+ border: 1px solid $color-gray-300;
188
+ padding: 20px;
189
+ border-radius: 4px;
190
+ display: flex;
191
+ flex-flow: column nowrap;
192
+ }
193
+ .link-item h4 {
194
+ margin: 0;
195
+ }
196
+ .dashboard-pro-settings {
197
+ margin-top: 2rem;
198
+ }
199
+ .link-item .link-item-foot {
200
+ margin-top: auto;
201
+ display: flex;
202
+ align-items: center;
203
+ .components-spinner {
204
+ margin-top: 0;
205
+ }
206
+ .components-toggle-control .components-base-control__field {
207
+ margin-bottom: 0;
208
+ .components-form-toggle {
209
+ margin-right: 0;
210
+ }
211
+ }
212
+ }
213
+ .link-item .link-item-foot > *:first-child {
214
+ flex-grow: 2;
215
+ }
216
+ .link-item a {
217
+ display: block;
218
+ background: transparent;
219
+ color: $color-gray-700;
220
+ &:hover {
221
+ color:$color-primary;
222
+ }
223
+ }
224
+ span.kt-license-status {
225
+ padding: 4px;
226
+ margin-left: 10px;
227
+ font-size: 14px;
228
+ text-transform: uppercase;
229
+ }
230
+ span.kt-license-status.k-inactive {
231
+ color: #c05621!important;
232
+ background: #fffaf0!important;
233
+ }
234
+ span.kt-license-status.k-active {
235
+ color: #2b6cb0!important;
236
+ background: #ebf8ff!important;
237
+ }
238
+ .license-section h2 {
239
+ display: flex;
240
+ margin-top: 0;
241
+ align-items: center;
242
+ justify-content: space-between;
243
+ }
244
+ .license-section table.form-table {
245
+ display: block;
246
+ }
247
+ .license-section table.form-table tbody {
248
+ display: block;
249
+ }
250
+ .license-section table.form-table td, .license-section table.form-table tr {
251
+ display: block;
252
+ padding: 0;
253
+ width: 100%;
254
+ }
255
+ .license-section .form-table th {
256
+ padding:0;
257
+ width: 100%;
258
+ margin-bottom: 4px;
259
+ display: block;
260
+ color: $color-gray-700;
261
+ }
262
+ .license-section p.submit {
263
+ padding: 0;
264
+ margin-top: 10px;
265
+ }
266
+ .license-section table.form-table input[type="text"] {
267
+ width:100%;
268
+ }
269
+ // Preview
270
+ .kadence-starter-templates-preview .install-theme-info .theme-name {
271
+ font-size: 20px;
272
+ }
273
+ .appearance_page_kadence-starter-templates .kadence-starter-templates-preview-actions .button {
274
+ width: 100%;
275
+ line-height: 1;
276
+ min-height: 35px;
277
+ }
278
+ .appearance_page_kadence-starter-templates .kadence-starter-templates-preview-actions {
279
+ padding:5px
280
+ }
281
+ .kadence-starter-templates-preview button.components-button.kst-palette-btn {
282
+ border-radius: 4px !important;
283
+ padding: 5px 5px 5px 7px;
284
+ height: auto;
285
+ display: inline-flex;
286
+ align-items: flex-end;
287
+ justify-content: center;
288
+ border:1px solid transparent;
289
+ box-shadow: none !important;
290
+ }
291
+ .kst-template-item .components-button.kst-import-btn:hover, .kst-template-item .components-button.kst-import-btn:focus {
292
+ box-shadow: 0 7px 15px 0 rgba(0,0,0,.15) !important;
293
+ }
294
+ .kadence-starter-templates-preview button.components-button.kst-palette-btn:hover, .kadence-starter-templates-preview button.components-button.kst-palette-btn:focus {
295
+ border:1px solid #444;
296
+ box-shadow:none !important;
297
+ }
298
+ .kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary, .kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary:hover, .kadence-starter-templates-preview button.components-button.kst-palette-btn.is-primary:focus {
299
+ background: #eee;
300
+ border:1px solid #aaa;
301
+ box-shadow:none;
302
+ }
303
+ .demo-title {
304
+ height: 30px;
305
+ margin: 0;
306
+ padding: 10px 0;
307
+ box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
308
+ background: rgba(255,255,255,0.95);
309
+ position: absolute;
310
+ bottom: 0;
311
+ left: 0;
312
+ width: 100%;
313
+ }
314
+
315
+ .demo-title h4 {
316
+ margin: 0;
317
+ font-size: 18px;
318
+ text-align: left;
319
+ line-height: 30px;
320
+ padding: 0 20px;
321
+ }
322
+ .kadence-starter-templates-preview .components-button-group.kst-palette-group {
323
+ display: grid;
324
+ grid-template-columns: 1fr 1fr;
325
+ grid-gap: 5px;
326
+ }
327
+ .palette-title-wrap {
328
+ display: flex;
329
+ align-items: center;
330
+ justify-content: space-between;
331
+ }
332
+ @media ( max-width: 1760px ) {
333
+ .kadence-starter-templates-preview .kadence-swatche-item-wrap {
334
+ width: 22px !important;
335
+ height: 22px !important;
336
+ }
337
+ h2.palette-title {
338
+ font-size: 15px;
339
+ }
340
+ }
341
+ @media ( max-width: 1460px ) {
342
+ .templates-grid {
343
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
344
+ }
345
+ }
346
+ .kadence-starter-required-plugins {
347
+ padding: 10px 20px 10px;
348
+ }
349
+ .kadence-required-wrap {
350
+ font-weight: bold;
351
+ list-style: disc;
352
+ padding-left: 1.4em;
353
+ }
354
+ span.plugin-status {
355
+ text-transform: uppercase;
356
+ color: #777;
357
+ }
358
+ .kst-palette-btn.kst-selected-color-palette {
359
+ display: flex;
360
+ margin-bottom: 20px;
361
+ }
362
+ .finshed-notice-success .button-primary.button.kadence-starter-templates-finish-button {
363
+ padding: 10px 16px;
364
+ font-size: 18px;
365
+ }
kadence-starter-templates.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: Kadence Starter Templates
4
+ * Description: Choose the prebuilt website and click to import.
5
+ * Version: 1.0.1
6
+ * Author: Kadence WP
7
+ * Author URI: https://kadencewp.com/
8
+ * License: GPLv2 or later
9
+ * Text Domain: kadence-starter-templates
10
+ *
11
+ * @package Kadence Starter Templates
12
+ */
13
+
14
+ // Block direct access to the main plugin file.
15
+ if ( ! defined( 'ABSPATH' ) ) {
16
+ exit;
17
+ }
18
+
19
+ if ( ! version_compare( PHP_VERSION, '7.0', '>=' ) ) {
20
+ add_action( 'admin_notices', 'kadence_starter_old_php_admin_error_notice' );
21
+ } else {
22
+ require_once 'class-kadence-starter-templates.php';
23
+ }
24
+ /**
25
+ * Display an admin error notice when PHP is older the version 5.3.2.
26
+ * Hook it to the 'admin_notices' action.
27
+ */
28
+ function kadence_starter_old_php_admin_error_notice() {
29
+ $message = __( 'The Kadence Starter templates plugin requires at least PHP 7.0 to run properly. Please contact your hosting company and ask them to update the PHP version of your site to at least PHP 7.0. We strongly encourage you to update to 7.3+', 'kadence-starter-templates' );
30
+
31
+ printf( '<div class="notice notice-error"><p>%1$s</p></div>', wp_kses_post( $message ) );
32
+ }
languages/kadence-starter-templates.pot ADDED
@@ -0,0 +1,616 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2017 ProteusThemes
2
+ # This file is distributed under the GPL 2.0.
3
+ #, fuzzy
4
+ msgid ""
5
+ msgstr ""
6
+ "Project-Id-Version: Kadence Starter Templates\n"
7
+ "Report-Msgid-Bugs-To: http://support.proteusthemes.com/\n"
8
+ "POT-Creation-Date: 2020-08-05 17:23-0600\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: \n"
15
+ "X-Generator: Poedit 2.3.1\n"
16
+ "X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;"
17
+ "_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;"
18
+ "esc_html_x:1,2c\n"
19
+ "Language: en\n"
20
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
21
+ "X-Poedit-SourceCharset: UTF-8\n"
22
+ "X-Poedit-Basepath: ..\n"
23
+ "X-Textdomain-Support: yes\n"
24
+ "X-Poedit-SearchPath-0: .\n"
25
+ "X-Poedit-SearchPathExcluded-0: node_modules\n"
26
+
27
+ #: class-kadence-starter-templates.php:516
28
+ msgid "View Template Library"
29
+ msgstr ""
30
+
31
+ #: class-kadence-starter-templates.php:526
32
+ msgid "Starter Templates by Kadence WP"
33
+ msgstr ""
34
+
35
+ #: class-kadence-starter-templates.php:527
36
+ msgid "Starter Templates"
37
+ msgstr ""
38
+
39
+ #: class-kadence-starter-templates.php:596
40
+ #: class-kadence-starter-templates.php:615
41
+ msgid "Agency"
42
+ msgstr ""
43
+
44
+ #: class-kadence-starter-templates.php:598
45
+ #: class-kadence-starter-templates.php:617
46
+ msgid "portfolio"
47
+ msgstr ""
48
+
49
+ #: class-kadence-starter-templates.php:599
50
+ #: class-kadence-starter-templates.php:618
51
+ msgid "services"
52
+ msgstr ""
53
+
54
+ #: class-kadence-starter-templates.php:600
55
+ #: class-kadence-starter-templates.php:619
56
+ #: class-kadence-starter-templates.php:656
57
+ #: class-kadence-starter-templates.php:673
58
+ #: class-kadence-starter-templates.php:689
59
+ msgid "business"
60
+ msgstr ""
61
+
62
+ #: class-kadence-starter-templates.php:601
63
+ #: class-kadence-starter-templates.php:620
64
+ msgid "transparent"
65
+ msgstr ""
66
+
67
+ #: class-kadence-starter-templates.php:635
68
+ msgid "Recipe Blog"
69
+ msgstr ""
70
+
71
+ #: class-kadence-starter-templates.php:637
72
+ msgid "blog"
73
+ msgstr ""
74
+
75
+ #: class-kadence-starter-templates.php:638
76
+ msgid "food"
77
+ msgstr ""
78
+
79
+ #: class-kadence-starter-templates.php:639
80
+ msgid "recipe"
81
+ msgstr ""
82
+
83
+ #: class-kadence-starter-templates.php:652
84
+ msgid "Shopping"
85
+ msgstr ""
86
+
87
+ #: class-kadence-starter-templates.php:654
88
+ msgid "ecommerce"
89
+ msgstr ""
90
+
91
+ #: class-kadence-starter-templates.php:655
92
+ msgid "shopping"
93
+ msgstr ""
94
+
95
+ #: class-kadence-starter-templates.php:669
96
+ msgid "Yoga Studio"
97
+ msgstr ""
98
+
99
+ #: class-kadence-starter-templates.php:671
100
+ msgid "yoga"
101
+ msgstr ""
102
+
103
+ #: class-kadence-starter-templates.php:672
104
+ msgid "gym"
105
+ msgstr ""
106
+
107
+ #: class-kadence-starter-templates.php:685
108
+ msgid "Sass"
109
+ msgstr ""
110
+
111
+ #: class-kadence-starter-templates.php:687
112
+ msgid "sass"
113
+ msgstr ""
114
+
115
+ #: class-kadence-starter-templates.php:688
116
+ msgid "pricing"
117
+ msgstr ""
118
+
119
+ #: class-kadence-starter-templates.php:785
120
+ msgid ""
121
+ "Please Note: This importer is designed for new/empty sites with no content."
122
+ msgstr ""
123
+
124
+ #: class-kadence-starter-templates.php:786
125
+ msgid "Checking/Installing/Activating Required Plugins"
126
+ msgstr ""
127
+
128
+ #: class-kadence-starter-templates.php:787
129
+ msgid "Importing Demo Content..."
130
+ msgstr ""
131
+
132
+ #: class-kadence-starter-templates.php:788
133
+ msgid "Importing Demo Content... Still Importing."
134
+ msgstr ""
135
+
136
+ #: class-kadence-starter-templates.php:789
137
+ msgid "Importing Menus/Widgets..."
138
+ msgstr ""
139
+
140
+ #: class-kadence-starter-templates.php:790
141
+ msgid "Importing Customizer Settings..."
142
+ msgstr ""
143
+
144
+ #: class-kadence-starter-templates.php:915
145
+ #: class-kadence-starter-templates.php:926
146
+ msgid "Downloaded files"
147
+ msgstr ""
148
+
149
+ #: class-kadence-starter-templates.php:922
150
+ #, php-format
151
+ msgid "The import files for: %s were successfully downloaded!"
152
+ msgstr ""
153
+
154
+ #: class-kadence-starter-templates.php:930
155
+ msgid "No import files specified!"
156
+ msgstr ""
157
+
158
+ #: class-kadence-starter-templates.php:1051
159
+ #, php-format
160
+ msgid "%1$sFinished! View your site%2$s"
161
+ msgstr ""
162
+
163
+ #: class-kadence-starter-templates.php:1058
164
+ #, php-format
165
+ msgid ""
166
+ "%1$sThe demo import has finished, but there were some import errors.%2$sMore "
167
+ "details about the errors can be found in this %3$s%5$slog file%6$s%4$s%7$s"
168
+ msgstr ""
169
+
170
+ #: inc/class-customizer-importer.php:34 inc/class-customizer-importer.php:41
171
+ msgid "Importing customizer settings"
172
+ msgstr ""
173
+
174
+ #: inc/class-customizer-importer.php:39
175
+ msgid "Customizer settings import finished!"
176
+ msgstr ""
177
+
178
+ #: inc/class-customizer-importer.php:69
179
+ #, php-format
180
+ msgid "Error: The customizer import file is missing! File path: %s"
181
+ msgstr ""
182
+
183
+ #: inc/class-customizer-importer.php:88
184
+ msgid ""
185
+ "Error: The customizer import file is not in a correct format. Please make sure "
186
+ "to use the correct customizer import file."
187
+ msgstr ""
188
+
189
+ #: inc/class-customizer-importer.php:94
190
+ msgid ""
191
+ "Error: The customizer import file is not suitable for current theme. You can "
192
+ "only import customizer settings for the same theme or a child theme."
193
+ msgstr ""
194
+
195
+ #: inc/class-downloader.php:58
196
+ msgid "Missing URL for downloading a file!"
197
+ msgstr ""
198
+
199
+ #: inc/class-downloader.php:76
200
+ #, php-format
201
+ msgid ""
202
+ "An error occurred while fetching file from: %1$s%2$s%3$s!%4$sReason: %5$s - "
203
+ "%6$s."
204
+ msgstr ""
205
+
206
+ #: inc/class-helpers.php:195 inc/class-helpers.php:238
207
+ #, php-format
208
+ msgid ""
209
+ "An error occurred while writing file to your server! Tried to write a file to: "
210
+ "%s%s."
211
+ msgstr ""
212
+
213
+ #: inc/class-helpers.php:272
214
+ #, php-format
215
+ msgid ""
216
+ "An error occurred while reading a file from your server! Tried reading file "
217
+ "from path: %s%s."
218
+ msgstr ""
219
+
220
+ #: inc/class-helpers.php:295
221
+ #, php-format
222
+ msgid ""
223
+ "This WordPress page does not have %sdirect%s write file access. This plugin "
224
+ "needs it in order to save the demo import xml file to the upload directory of "
225
+ "your site. You can change this setting with these instructions: %s."
226
+ msgstr ""
227
+
228
+ #: inc/class-helpers.php:306
229
+ msgid "One Click Demo Import"
230
+ msgstr ""
231
+
232
+ #: inc/class-helpers.php:307
233
+ msgid "Import Demo Data"
234
+ msgstr ""
235
+
236
+ #: inc/class-helpers.php:319
237
+ msgid ""
238
+ "An error occurred while retrieving reading/writing permissions to your server "
239
+ "(could not retrieve WP filesystem credentials)!"
240
+ msgstr ""
241
+
242
+ #: inc/class-helpers.php:327
243
+ msgid "Your WordPress login credentials don't allow to use WP_Filesystem!"
244
+ msgstr ""
245
+
246
+ #: inc/class-helpers.php:367
247
+ msgid "One Click Demo Import - "
248
+ msgstr ""
249
+
250
+ #: inc/class-helpers.php:401
251
+ #, php-format
252
+ msgid ""
253
+ "%sYour user role isn't high enough. You don't have permission to import demo "
254
+ "data.%s"
255
+ msgstr ""
256
+
257
+ #: inc/class-helpers.php:434
258
+ msgid "No file provided."
259
+ msgstr ""
260
+
261
+ #: inc/class-helpers.php:462
262
+ #, php-format
263
+ msgid "Content file was not uploaded. Error: %s"
264
+ msgstr ""
265
+
266
+ #: inc/class-helpers.php:466 inc/class-helpers.php:482 inc/class-helpers.php:498
267
+ #: inc/class-helpers.php:509 inc/class-helpers.php:528 inc/class-helpers.php:536
268
+ msgid "Upload files"
269
+ msgstr ""
270
+
271
+ #: inc/class-helpers.php:478
272
+ #, php-format
273
+ msgid "Widget file was not uploaded. Error: %s"
274
+ msgstr ""
275
+
276
+ #: inc/class-helpers.php:494
277
+ #, php-format
278
+ msgid "Customizer file was not uploaded. Error: %s"
279
+ msgstr ""
280
+
281
+ #: inc/class-helpers.php:507
282
+ msgid "Missing Redux option name! Please also enter the Redux option name!"
283
+ msgstr ""
284
+
285
+ #: inc/class-helpers.php:524
286
+ #, php-format
287
+ msgid "Redux file was not uploaded. Error: %s"
288
+ msgstr ""
289
+
290
+ #: inc/class-helpers.php:534
291
+ msgid "The import files were successfully uploaded!"
292
+ msgstr ""
293
+
294
+ #: inc/class-helpers.php:560
295
+ #, php-format
296
+ msgid "Initial max execution time = %s"
297
+ msgstr ""
298
+
299
+ #: inc/class-helpers.php:564
300
+ #, php-format
301
+ msgid ""
302
+ "Files info:%1$sSite URL = %2$s%1$sData file = %3$s%1$sWidget file = %4$s"
303
+ "%1$sCustomizer file = %5$s%1$sRedux files:%1$s%6$s"
304
+ msgstr ""
305
+
306
+ #: inc/class-helpers.php:567 inc/class-helpers.php:568 inc/class-helpers.php:569
307
+ #: inc/class-helpers.php:570
308
+ msgid "not defined!"
309
+ msgstr ""
310
+
311
+ #: inc/class-importer.php:178 vendor/wxr-importer/Importer.php:504
312
+ msgid "New AJAX call!"
313
+ msgstr ""
314
+
315
+ #: inc/class-widget-importer.php:40 inc/class-widget-importer.php:51
316
+ msgid "Importing widgets"
317
+ msgstr ""
318
+
319
+ #: inc/class-widget-importer.php:87
320
+ msgid "Error: Widget import file could not be found."
321
+ msgstr ""
322
+
323
+ #: inc/class-widget-importer.php:117
324
+ msgid "Error: Widget import data could not be read. Please try a different file."
325
+ msgstr ""
326
+
327
+ #: inc/class-widget-importer.php:156
328
+ msgid "Sidebar does not exist in theme (moving widget to Inactive)"
329
+ msgstr ""
330
+
331
+ #: inc/class-widget-importer.php:177
332
+ msgid "Site does not support widget"
333
+ msgstr ""
334
+
335
+ #: inc/class-widget-importer.php:210
336
+ msgid "Widget already exists"
337
+ msgstr ""
338
+
339
+ #: inc/class-widget-importer.php:275
340
+ msgid "Imported"
341
+ msgstr ""
342
+
343
+ #: inc/class-widget-importer.php:279
344
+ msgid "Imported to Inactive"
345
+ msgstr ""
346
+
347
+ #: inc/class-widget-importer.php:285
348
+ msgid "No Title"
349
+ msgstr ""
350
+
351
+ #: inc/class-widget-importer.php:332
352
+ msgid "No results for widget import!"
353
+ msgstr ""
354
+
355
+ #: inc/react/src/index.js:147
356
+ msgid "Optional: Choose Color Scheme"
357
+ msgstr ""
358
+
359
+ #: inc/react/src/index.js:149
360
+ msgid "clear"
361
+ msgstr ""
362
+
363
+ #: inc/react/src/index.js:157
364
+ msgid "Select a Palette"
365
+ msgstr ""
366
+
367
+ #: kadence-starter-templates.php:29
368
+ msgid ""
369
+ "The Kadence Starter templates plugin requires at least PHP 7.0 to run "
370
+ "properly. Please contact your hosting company and ask them to update the PHP "
371
+ "version of your site to at least PHP 7.0. We strongly encourage you to update "
372
+ "to 7.3+"
373
+ msgstr ""
374
+
375
+ #: vendor/wxr-importer/Importer.php:55
376
+ msgid ""
377
+ "The XMLReader class is missing! Please install the XMLReader PHP extension on "
378
+ "your server"
379
+ msgstr ""
380
+
381
+ #: vendor/wxr-importer/Importer.php:68
382
+ msgid "Could not open the XML file for parsing!"
383
+ msgstr ""
384
+
385
+ #: vendor/wxr-importer/Importer.php:248
386
+ msgid "Content import start error: "
387
+ msgstr ""
388
+
389
+ #: vendor/wxr-importer/Importer.php:280 vendor/wxr-importer/WXRImporter.php:173
390
+ #: vendor/wxr-importer/WXRImporter.php:291
391
+ #: vendor/wxr-importer/WXRImporter.php:375
392
+ #, php-format
393
+ msgid ""
394
+ "This WXR file (version %s) is newer than the importer (version %s) and may not "
395
+ "be supported. Please consider updating."
396
+ msgstr ""
397
+
398
+ #: vendor/wxr-importer/WXRImporter.php:135
399
+ msgid "Could not open the file for parsing"
400
+ msgstr ""
401
+
402
+ #: vendor/wxr-importer/WXRImporter.php:525
403
+ msgid "The file does not exist, please try again."
404
+ msgstr ""
405
+
406
+ #: vendor/wxr-importer/WXRImporter.php:588
407
+ msgid "Invalid author mapping"
408
+ msgstr ""
409
+
410
+ #: vendor/wxr-importer/WXRImporter.php:689
411
+ msgid "Cannot import auto-draft posts"
412
+ msgstr ""
413
+
414
+ #: vendor/wxr-importer/WXRImporter.php:781
415
+ #, php-format
416
+ msgid "Failed to import \"%s\": Invalid post type %s"
417
+ msgstr ""
418
+
419
+ #: vendor/wxr-importer/WXRImporter.php:791
420
+ #, php-format
421
+ msgid "%s \"%s\" already exists."
422
+ msgstr ""
423
+
424
+ #: vendor/wxr-importer/WXRImporter.php:869
425
+ #, php-format
426
+ msgid "Skipping attachment \"%s\", fetching attachments disabled"
427
+ msgstr ""
428
+
429
+ #: vendor/wxr-importer/WXRImporter.php:883
430
+ #, php-format
431
+ msgid "Failed to import \"%s\" (%s)"
432
+ msgstr ""
433
+
434
+ #: vendor/wxr-importer/WXRImporter.php:915
435
+ #: vendor/wxr-importer/WXRImporter.php:1833
436
+ #, php-format
437
+ msgid "Imported \"%s\" (%s)"
438
+ msgstr ""
439
+
440
+ #: vendor/wxr-importer/WXRImporter.php:920
441
+ #, php-format
442
+ msgid "Post %d remapped to %d"
443
+ msgstr ""
444
+
445
+ #: vendor/wxr-importer/WXRImporter.php:960
446
+ #, php-format
447
+ msgid "Failed to import term: %s - %s"
448
+ msgstr ""
449
+
450
+ #: vendor/wxr-importer/WXRImporter.php:1105
451
+ msgid "Invalid file type"
452
+ msgstr ""
453
+
454
+ #: vendor/wxr-importer/WXRImporter.php:1604
455
+ #, php-format
456
+ msgid "Failed to import user \"%s\""
457
+ msgstr ""
458
+
459
+ #: vendor/wxr-importer/WXRImporter.php:1625
460
+ #, php-format
461
+ msgid "Imported user \"%s\""
462
+ msgstr ""
463
+
464
+ #: vendor/wxr-importer/WXRImporter.php:1629
465
+ #, php-format
466
+ msgid "User %d remapped to %d"
467
+ msgstr ""
468
+
469
+ #: vendor/wxr-importer/WXRImporter.php:1794
470
+ #, php-format
471
+ msgid "Failed to import %s %s"
472
+ msgstr ""
473
+
474
+ #: vendor/wxr-importer/WXRImporter.php:1838
475
+ #, php-format
476
+ msgid "Term %d remapped to %d"
477
+ msgstr ""
478
+
479
+ #: vendor/wxr-importer/WXRImporter.php:1897
480
+ #, php-format
481
+ msgid "Failed to add metakey: %s, metavalue: %s to term_id: %d"
482
+ msgstr ""
483
+
484
+ #: vendor/wxr-importer/WXRImporter.php:1906
485
+ #, php-format
486
+ msgid "Meta for term_id %d : %s => %s ; successfully added!"
487
+ msgstr ""
488
+
489
+ #: vendor/wxr-importer/WXRImporter.php:1958
490
+ #, php-format
491
+ msgid "Remote server returned %1$d %2$s for %3$s"
492
+ msgstr ""
493
+
494
+ #: vendor/wxr-importer/WXRImporter.php:1980
495
+ msgid "Zero size file downloaded"
496
+ msgstr ""
497
+
498
+ #: vendor/wxr-importer/WXRImporter.php:1986
499
+ #, php-format
500
+ msgid "Remote file is too large, limit is %s"
501
+ msgstr ""
502
+
503
+ #: vendor/wxr-importer/WXRImporter.php:2011
504
+ #, php-format
505
+ msgid "Running post-processing for post %d"
506
+ msgstr ""
507
+
508
+ #: vendor/wxr-importer/WXRImporter.php:2024
509
+ #, php-format
510
+ msgid "Could not find the post parent for \"%s\" (post #%d)"
511
+ msgstr ""
512
+
513
+ #: vendor/wxr-importer/WXRImporter.php:2029
514
+ #, php-format
515
+ msgid "Post %d was imported with parent %d, but could not be found"
516
+ msgstr ""
517
+
518
+ #: vendor/wxr-importer/WXRImporter.php:2043
519
+ #, php-format
520
+ msgid "Could not find the author for \"%s\" (post #%d)"
521
+ msgstr ""
522
+
523
+ #: vendor/wxr-importer/WXRImporter.php:2048
524
+ #, php-format
525
+ msgid "Post %d was imported with author \"%s\", but could not be found"
526
+ msgstr ""
527
+
528
+ #: vendor/wxr-importer/WXRImporter.php:2074
529
+ #, php-format
530
+ msgid "Post %d was marked for post-processing, but none was required."
531
+ msgstr ""
532
+
533
+ #: vendor/wxr-importer/WXRImporter.php:2085
534
+ #, php-format
535
+ msgid "Could not update \"%s\" (post #%d) with mapped data"
536
+ msgstr ""
537
+
538
+ #: vendor/wxr-importer/WXRImporter.php:2130
539
+ #, php-format
540
+ msgid "Could not find the menu object for \"%s\" (post #%d)"
541
+ msgstr ""
542
+
543
+ #: vendor/wxr-importer/WXRImporter.php:2135
544
+ #, php-format
545
+ msgid ""
546
+ "Post %d was imported with object \"%d\" of type \"%s\", but could not be found"
547
+ msgstr ""
548
+
549
+ #: vendor/wxr-importer/WXRImporter.php:2157
550
+ #, php-format
551
+ msgid "Could not find the comment parent for comment #%d"
552
+ msgstr ""
553
+
554
+ #: vendor/wxr-importer/WXRImporter.php:2161
555
+ #, php-format
556
+ msgid "Comment %d was imported with parent %d, but could not be found"
557
+ msgstr ""
558
+
559
+ #: vendor/wxr-importer/WXRImporter.php:2175
560
+ #, php-format
561
+ msgid "Could not find the author for comment #%d"
562
+ msgstr ""
563
+
564
+ #: vendor/wxr-importer/WXRImporter.php:2179
565
+ #, php-format
566
+ msgid "Comment %d was imported with author %d, but could not be found"
567
+ msgstr ""
568
+
569
+ #: vendor/wxr-importer/WXRImporter.php:2196
570
+ #, php-format
571
+ msgid "Could not update comment #%d with mapped data"
572
+ msgstr ""
573
+
574
+ #: vendor/wxr-importer/WXRImporter.php:2227
575
+ #, php-format
576
+ msgid "Faulty term_id provided in terms-to-be-remapped array %s"
577
+ msgstr ""
578
+
579
+ #: vendor/wxr-importer/WXRImporter.php:2237
580
+ #, php-format
581
+ msgid "No taxonomy provided in terms-to-be-remapped array for term #%d"
582
+ msgstr ""
583
+
584
+ #: vendor/wxr-importer/WXRImporter.php:2247
585
+ #, php-format
586
+ msgid "No parent_slug identified in remapping-array for term: %d"
587
+ msgstr ""
588
+
589
+ #: vendor/wxr-importer/WXRImporter.php:2255
590
+ #, php-format
591
+ msgid "The term(%d)\"s parent_slug (%s) is not found in the remapping-array."
592
+ msgstr ""
593
+
594
+ #: vendor/wxr-importer/WXRImporter.php:2269
595
+ #, php-format
596
+ msgid "No data returned by get_term_by for term_id #%d"
597
+ msgstr ""
598
+
599
+ #: vendor/wxr-importer/WXRImporter.php:2288
600
+ #, php-format
601
+ msgid "Could not update \"%s\" (term #%d) with mapped data"
602
+ msgstr ""
603
+
604
+ #: vendor/wxr-importer/WXRImporter.php:2298
605
+ #, php-format
606
+ msgid "Term %d was successfully updated with parent %d"
607
+ msgstr ""
608
+
609
+ #: vendor/wxr-importer/WXRImporter.php:2333
610
+ msgid "Starting remapping of featured images"
611
+ msgstr ""
612
+
613
+ #: vendor/wxr-importer/WXRImporter.php:2342
614
+ #, php-format
615
+ msgid "Remapping featured image ID %d to new ID %d for post ID %d"
616
+ msgstr ""
readme.txt ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Starter Templates by Kadence WP ===
2
+ Contributors: britner
3
+ Tags: templates, gutenberg
4
+ Requires at least: 5.0
5
+ Tested up to: 5.5.0
6
+ Stable tag: 1.0.1
7
+ Requires PHP: 7.0
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Choose the prebuilt website and click to import.
12
+
13
+ == Description ==
14
+
15
+ = BUILD AN BEAUTIFUL "PERFORMANCE OPTIMIZED" WEBSITE WITH 3 MOUSE CLICKS =
16
+
17
+ Create and customize professionally designed websites in minutes.
18
+
19
+ Kadence Starter Templates give you full access to pre-made website designs using the WordPress block builder.
20
+
21
+ Unlike other website templates that are difficult to change fonts and colors, our template are built on a globally linked color palette and font selector. This allows you to change the color scheme or font on your entire website with a few mouse clicks.
22
+
23
+ Simply choose your template, choose your colors, and import. Done!
24
+
25
+ = KADENCE STARTER TEMPLATES PUTS THE E BACK IN EASY =
26
+
27
+ Kadence Starter Templates using the Kadence Theme for all the designs.
28
+
29
+ Why the Kadence Theme?
30
+
31
+ **Header/Footer Builder** - Unlike other themes, the Kadence Theme comes with a drag and drop header/footer builder. This allows unlimited design possibilities.
32
+
33
+ **Globally Linked Color Palette** - The color of element on your website is linked to a global color palette. Want to change a color, change it in one place and the color changes across your entire website.
34
+
35
+ **Globally Linked Fonts** - Like with the color system, the same goes for your font choices. Want to change a font, change it in one place and the font changes across your entire website.
36
+
37
+ **WooCommerce Integration** - The Kadence Theme features the most advanced WooCommerce integration to allow you to: customize your WooCommerce product pages, has a flyout cart, cart icon in header, product page bullet points, my account page customizations, and more.
38
+
39
+ **Nothing Held Back** - Where other free themes stop, the Kadence Theme holds nothing back.
40
+
41
+ = KADENCE STARTER TEMPLATES PUTS THE P BACK IN PERFORMANCE =
42
+
43
+ Are you concerned with how fast your website loads? If not you should be because it impacts wether a website visitor stays on your website or leaves. And Google doesn't like it when people leave your website.
44
+
45
+ Because of this we obsess over website performance so your don't have to.
46
+
47
+ All of our starter templates are performance optimized and will load faster the other website templates.
48
+
49
+ = GET YOUR NEXT WEBSITE INSTALLED AND CUSTOMIZED IN 5 STEPS! =
50
+
51
+ 1. Install and activate Kadence Starter Templates
52
+ 2. Choose from one of our professionally designed templates
53
+ 3. Choose your color palette, there are many to choose from
54
+ 4. Click on import
55
+ 5. You're Done!
56
+
57
+ = JOIN THE KADENCE WEB CREATORS COMMUNITY =
58
+
59
+ **<a href="https://www.facebook.com/groups/webcreatorcommunity" target="_blank">JOIN OUR FACEBOOK GROUP COMMUNITY</a>**: Want to be a part of a thriving community of web creators like you? Join our thriving web creator community.
60
+
61
+ = Support =
62
+
63
+ We are happy to help as best we can with questions! Please use the support forums.
64
+
65
+ = Credits =
66
+
67
+ Thanks to proteusthemes, we heavily pulled code and structure for this plugin from [One Click Demo Import](https://wordpress.org/plugins/one-click-demo-import/).
68
+
69
+ == Installation ==
70
+
71
+ Install the plugin into the `/wp-content/plugins/` folder, and activate it.
72
+
73
+ == Frequently Asked Questions ==
74
+
75
+ = Will this work work with my theme? =
76
+
77
+ All of our starter templates are designed to work with the free Kadence Theme.
78
+
79
+ = Will this work with my page builder? =
80
+
81
+ Currently all the starter templates are built using the default WordPress block builder and Kadence Blocks which adds page builder like features to your website. We will be adding Elementor site templates in the future and will be keeping tabs on what our users request.
82
+
83
+ = Can I import a starter template on an existing website? =
84
+
85
+ It's best that you install one of our starter templates on a fresh WordPress installation.
86
+
87
+ = Do I still need the plugin after I install a starter template? =
88
+
89
+ You can disable the plugin however you would lose out on the additional features we are adding to make it easier to having and customizing your website.
90
+
91
+ = How often will you be adding new starter templates? =
92
+
93
+ We will be adding 1 - 2 new site templates each month.
94
+
95
+ = Will this slow down my website? =
96
+
97
+ Absolutely not.
98
+
99
+ == Screenshots ==
100
+
101
+ 1. Choose a starter template.
102
+ 2. Choose a color palette while previewing.
103
+ 3. Choose a color palette while previewing.
104
+ 4. Choose a color palette while previewing.
105
+ 5. Choose a color palette while previewing.
106
+ 6. Choose a color palette while previewing.
107
+
108
+
109
+ == Changelog ==
110
+
111
+ = 1.0.1 =
112
+ * Fix: Post Sanitize
113
+
114
+ = 1.0.0 =
115
+ * release
vendor/wxr-importer/Importer.php ADDED
@@ -0,0 +1,629 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The main importer class, extending the slightly modified WP importer 2.0 class WXRImporter
4
+ */
5
+
6
+ namespace AwesomeMotive\WPContentImporter2;
7
+
8
+ use XMLReader;
9
+
10
+ class Importer extends WXRImporter {
11
+
12
+ /**
13
+ * Time in milliseconds, marking the beginning of the import.
14
+ *
15
+ * @var float
16
+ */
17
+ private $start_time;
18
+
19
+ /**
20
+ * Importer constructor.
21
+ * Look at the parent constructor for the options parameters.
22
+ *
23
+ * @param array $options The importer options.
24
+ * @param object $logger The logger object.
25
+ */
26
+ public function __construct( $options = array(), $logger = null ) {
27
+ parent::__construct( $options );
28
+
29
+ $this->set_logger( $logger );
30
+
31
+ // Check, if a new AJAX request is required.
32
+ add_filter( 'wxr_importer.pre_process.post', array( $this, 'new_ajax_request_maybe' ) );
33
+
34
+ // WooCommerce product attributes registration.
35
+ if ( class_exists( 'WooCommerce' ) ) {
36
+ add_filter( 'wxr_importer.pre_process.term', array( $this, 'woocommerce_product_attributes_registration' ), 10, 1 );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get the XML reader for the file.
42
+ *
43
+ * @param string $file Path to the XML file.
44
+ *
45
+ * @return XMLReader|boolean Reader instance on success, false otherwise.
46
+ */
47
+ protected function get_reader( $file ) {
48
+ // Avoid loading external entities for security
49
+ $old_value = null;
50
+ if ( function_exists( 'libxml_disable_entity_loader' ) ) {
51
+ // $old_value = libxml_disable_entity_loader( true );
52
+ }
53
+
54
+ if ( ! class_exists( 'XMLReader' ) ) {
55
+ $this->logger->critical( __( 'The XMLReader class is missing! Please install the XMLReader PHP extension on your server', 'wordpress-importer' ) );
56
+
57
+ return false;
58
+ }
59
+
60
+ $reader = new XMLReader();
61
+ $status = $reader->open( $file );
62
+
63
+ if ( ! is_null( $old_value ) ) {
64
+ // libxml_disable_entity_loader( $old_value );
65
+ }
66
+
67
+ if ( ! $status ) {
68
+ $this->logger->error( __( 'Could not open the XML file for parsing!', 'wordpress-importer' ) );
69
+
70
+ return false;
71
+ }
72
+
73
+ return $reader;
74
+ }
75
+
76
+ /**
77
+ * Get the basic import content data.
78
+ * Which elements are present in this import file (check possible elements in the $data variable)?
79
+ *
80
+ * @param $file
81
+ *
82
+ * @return array|bool
83
+ */
84
+ public function get_basic_import_content_data( $file ) {
85
+ $data = array(
86
+ 'users' => false,
87
+ 'categories' => false,
88
+ 'tags' => false,
89
+ 'terms' => false,
90
+ 'posts' => false,
91
+ );
92
+
93
+ // Get the XML reader and open the file.
94
+ $reader = $this->get_reader( $file );
95
+
96
+ if ( empty( $reader ) ) {
97
+ return false;
98
+ }
99
+
100
+ // Start parsing!
101
+ while ( $reader->read() ) {
102
+ // Only deal with element opens.
103
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
104
+ continue;
105
+ }
106
+
107
+ switch ( $reader->name ) {
108
+ case 'wp:author':
109
+ // Skip, if the users were already detected.
110
+ if ( $data['users'] ) {
111
+ $reader->next();
112
+ break;
113
+ }
114
+
115
+ $node = $reader->expand();
116
+ $parsed = $this->parse_author_node( $node );
117
+
118
+ // Skip, if there was an error in parsing the author node.
119
+ if ( is_wp_error( $parsed ) ) {
120
+ $reader->next();
121
+ break;
122
+ }
123
+
124
+ $data['users'] = true;
125
+
126
+ // Handled everything in this node, move on to the next.
127
+ $reader->next();
128
+ break;
129
+
130
+ case 'item':
131
+ // Skip, if the posts were already detected.
132
+ if ( $data['posts'] ) {
133
+ $reader->next();
134
+ break;
135
+ }
136
+
137
+ $node = $reader->expand();
138
+ $parsed = $this->parse_post_node( $node );
139
+
140
+ // Skip, if there was an error in parsing the item node.
141
+ if ( is_wp_error( $parsed ) ) {
142
+ $reader->next();
143
+ break;
144
+ }
145
+
146
+ $data['posts'] = true;
147
+
148
+ // Handled everything in this node, move on to the next
149
+ $reader->next();
150
+ break;
151
+
152
+ case 'wp:category':
153
+ $data['categories'] = true;
154
+
155
+ // Handled everything in this node, move on to the next
156
+ $reader->next();
157
+ break;
158
+ case 'wp:tag':
159
+ $data['tags'] = true;
160
+
161
+ // Handled everything in this node, move on to the next
162
+ $reader->next();
163
+ break;
164
+ case 'wp:term':
165
+ $data['terms'] = true;
166
+
167
+ // Handled everything in this node, move on to the next
168
+ $reader->next();
169
+ break;
170
+ }
171
+ }
172
+
173
+ return $data;
174
+ }
175
+
176
+
177
+ /**
178
+ * Get the number of posts (posts, pages, CPT, attachments), that the import file has.
179
+ *
180
+ * @param $file
181
+ *
182
+ * @return int
183
+ */
184
+ public function get_number_of_posts_to_import( $file ) {
185
+ $reader = $this->get_reader( $file );
186
+ $counter = 0;
187
+
188
+ if ( empty( $reader ) ) {
189
+ return $counter;
190
+ }
191
+
192
+ // Start parsing!
193
+ while ( $reader->read() ) {
194
+ // Only deal with element opens.
195
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
196
+ continue;
197
+ }
198
+
199
+ if ( 'item' == $reader->name ) {
200
+ $node = $reader->expand();
201
+ $parsed = $this->parse_post_node( $node );
202
+
203
+ // Skip, if there was an error in parsing the item node.
204
+ if ( is_wp_error( $parsed ) ) {
205
+ $reader->next();
206
+ continue;
207
+ }
208
+
209
+ $counter++;
210
+ }
211
+ }
212
+
213
+ return $counter;
214
+ }
215
+
216
+ /**
217
+ * The main controller for the actual import stage.
218
+ *
219
+ * @param string $file Path to the WXR file for importing.
220
+ * @param array $options Import options (which parts to import).
221
+ *
222
+ * @return boolean
223
+ */
224
+ public function import( $file, $options = array() ) {
225
+ add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
226
+ add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
227
+
228
+ // Start the import timer.
229
+ $this->start_time = microtime( true );
230
+
231
+ // Set the existing import data, from previous AJAX call, if any.
232
+ $this->restore_import_data_transient();
233
+
234
+ // Set the import options defaults.
235
+ if ( empty( $options ) ) {
236
+ $options = array(
237
+ 'users' => false,
238
+ 'categories' => true,
239
+ 'tags' => true,
240
+ 'terms' => true,
241
+ 'posts' => true,
242
+ );
243
+ }
244
+
245
+ $result = $this->import_start( $file );
246
+
247
+ if ( is_wp_error( $result ) ) {
248
+ $this->logger->error( __( 'Content import start error: ', 'wordpress-importer' ) . $result->get_error_message() );
249
+
250
+ return false;
251
+ }
252
+
253
+ // Get the actual XML reader.
254
+ $reader = $this->get_reader( $file );
255
+
256
+ if ( empty( $reader ) ) {
257
+ return false;
258
+ }
259
+
260
+ // Set the version to compatibility mode first
261
+ $this->version = '1.0';
262
+
263
+ // Reset other variables
264
+ $this->base_url = '';
265
+
266
+ // Start parsing!
267
+ while ( $reader->read() ) {
268
+ // Only deal with element opens.
269
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
270
+ continue;
271
+ }
272
+
273
+ switch ( $reader->name ) {
274
+ case 'wp:wxr_version':
275
+ // Upgrade to the correct version
276
+ $this->version = $reader->readString();
277
+
278
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
279
+ $this->logger->warning( sprintf(
280
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
281
+ $this->version,
282
+ self::MAX_WXR_VERSION
283
+ ) );
284
+ }
285
+
286
+ // Handled everything in this node, move on to the next
287
+ $reader->next();
288
+ break;
289
+
290
+ case 'wp:base_site_url':
291
+ $this->base_url = $reader->readString();
292
+
293
+ // Handled everything in this node, move on to the next
294
+ $reader->next();
295
+ break;
296
+
297
+ case 'item':
298
+ if ( empty( $options['posts'] ) ) {
299
+ $reader->next();
300
+ break;
301
+ }
302
+
303
+ $node = $reader->expand();
304
+ $parsed = $this->parse_post_node( $node );
305
+
306
+ if ( is_wp_error( $parsed ) ) {
307
+ $this->log_error( $parsed );
308
+
309
+ // Skip the rest of this post
310
+ $reader->next();
311
+ break;
312
+ }
313
+
314
+ $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
315
+
316
+ // Handled everything in this node, move on to the next
317
+ $reader->next();
318
+ break;
319
+
320
+ case 'wp:author':
321
+ if ( empty( $options['users'] ) ) {
322
+ $reader->next();
323
+ break;
324
+ }
325
+
326
+ $node = $reader->expand();
327
+ $parsed = $this->parse_author_node( $node );
328
+
329
+ if ( is_wp_error( $parsed ) ) {
330
+ $this->log_error( $parsed );
331
+
332
+ // Skip the rest of this post
333
+ $reader->next();
334
+ break;
335
+ }
336
+
337
+ $status = $this->process_author( $parsed['data'], $parsed['meta'] );
338
+
339
+ if ( is_wp_error( $status ) ) {
340
+ $this->log_error( $status );
341
+ }
342
+
343
+ // Handled everything in this node, move on to the next
344
+ $reader->next();
345
+ break;
346
+
347
+ case 'wp:category':
348
+ if ( empty( $options['categories'] ) ) {
349
+ $reader->next();
350
+ break;
351
+ }
352
+
353
+ $node = $reader->expand();
354
+ $parsed = $this->parse_term_node( $node, 'category' );
355
+
356
+ if ( is_wp_error( $parsed ) ) {
357
+ $this->log_error( $parsed );
358
+
359
+ // Skip the rest of this post
360
+ $reader->next();
361
+ break;
362
+ }
363
+
364
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
365
+
366
+ // Handled everything in this node, move on to the next
367
+ $reader->next();
368
+ break;
369
+
370
+ case 'wp:tag':
371
+ if ( empty( $options['tags'] ) ) {
372
+ $reader->next();
373
+ break;
374
+ }
375
+
376
+ $node = $reader->expand();
377
+ $parsed = $this->parse_term_node( $node, 'tag' );
378
+
379
+ if ( is_wp_error( $parsed ) ) {
380
+ $this->log_error( $parsed );
381
+
382
+ // Skip the rest of this post
383
+ $reader->next();
384
+ break;
385
+ }
386
+
387
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
388
+
389
+ // Handled everything in this node, move on to the next
390
+ $reader->next();
391
+ break;
392
+
393
+ case 'wp:term':
394
+ if ( empty( $options['terms'] ) ) {
395
+ $reader->next();
396
+ break;
397
+ }
398
+
399
+ $node = $reader->expand();
400
+ $parsed = $this->parse_term_node( $node );
401
+
402
+ if ( is_wp_error( $parsed ) ) {
403
+ $this->log_error( $parsed );
404
+
405
+ // Skip the rest of this post
406
+ $reader->next();
407
+ break;
408
+ }
409
+
410
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
411
+
412
+ // Handled everything in this node, move on to the next
413
+ $reader->next();
414
+ break;
415
+
416
+ default:
417
+ // Skip this node, probably handled by something already
418
+ break;
419
+ }
420
+ }
421
+
422
+ // Now that we've done the main processing, do any required
423
+ // post-processing and remapping.
424
+ $this->post_process();
425
+
426
+ if ( $this->options['aggressive_url_search'] ) {
427
+ $this->replace_attachment_urls_in_content();
428
+ }
429
+
430
+ $this->remap_featured_images();
431
+
432
+ $this->import_end();
433
+
434
+ // Set the current importer state, so the data can be used on the next AJAX call.
435
+ $this->set_current_importer_data();
436
+
437
+ return true;
438
+ }
439
+
440
+ /**
441
+ * Import users only.
442
+ *
443
+ * @param string $file Path to the import file.
444
+ */
445
+ public function import_users( $file ) {
446
+ return $this->import( $file, array( 'users' => true ) );
447
+ }
448
+
449
+ /**
450
+ * Import categories only.
451
+ *
452
+ * @param string $file Path to the import file.
453
+ */
454
+ public function import_categories( $file ) {
455
+ return $this->import( $file, array( 'categories' => true ) );
456
+ }
457
+
458
+ /**
459
+ * Import tags only.
460
+ *
461
+ * @param string $file Path to the import file.
462
+ */
463
+ public function import_tags( $file ) {
464
+ return $this->import( $file, array( 'tags' => true ) );
465
+ }
466
+
467
+ /**
468
+ * Import terms only.
469
+ *
470
+ * @param string $file Path to the import file.
471
+ */
472
+ public function import_terms( $file ) {
473
+ return $this->import( $file, array( 'terms' => true ) );
474
+ }
475
+
476
+ /**
477
+ * Import posts only.
478
+ *
479
+ * @param string $file Path to the import file.
480
+ */
481
+ public function import_posts( $file ) {
482
+ return $this->import( $file, array( 'posts' => true ) );
483
+ }
484
+
485
+ /**
486
+ * Check if we need to create a new AJAX request, so that server does not timeout.
487
+ * And fix the import warning for missing post author.
488
+ *
489
+ * @param array $data current post data.
490
+ * @return array
491
+ */
492
+ public function new_ajax_request_maybe( $data ) {
493
+ $time = microtime( true ) - $this->start_time;
494
+
495
+ // We should make a new ajax call, if the time is right.
496
+ if ( $time > apply_filters( 'pt-importer/time_for_one_ajax_call', 20 ) ) {
497
+ $response = apply_filters( 'pt-importer/new_ajax_request_response_data', array(
498
+ 'status' => 'newAJAX',
499
+ 'log' => 'Time for new AJAX request!: ' . $time,
500
+ 'num_of_imported_posts' => count( $this->mapping['post'] ),
501
+ ) );
502
+
503
+ // Add message to log file.
504
+ $this->logger->info( __( 'New AJAX call!', 'wordpress-importer' ) );
505
+
506
+ // Set the current importer state, so it can be continued on the next AJAX call.
507
+ $this->set_current_importer_data();
508
+
509
+ // Send the request for a new AJAX call.
510
+ wp_send_json( $response );
511
+ }
512
+
513
+ // Set importing author to the current user.
514
+ // Fixes the [WARNING] Could not find the author for ... log warning messages.
515
+ $current_user_obj = wp_get_current_user();
516
+ $data['post_author'] = $current_user_obj->user_login;
517
+
518
+ return $data;
519
+ }
520
+
521
+ /**
522
+ * Save current importer data to the DB, for later use.
523
+ */
524
+ public function set_current_importer_data() {
525
+ $data = apply_filters( 'pt-importer/set_current_importer_data', array(
526
+ 'options' => $this->options,
527
+ 'mapping' => $this->mapping,
528
+ 'requires_remapping' => $this->requires_remapping,
529
+ 'exists' => $this->exists,
530
+ 'user_slug_override' => $this->user_slug_override,
531
+ 'url_remap' => $this->url_remap,
532
+ 'featured_images' => $this->featured_images,
533
+ ) );
534
+
535
+ $this->save_current_import_data_transient( $data );
536
+ }
537
+
538
+ /**
539
+ * Set the importer data to the transient.
540
+ *
541
+ * @param array $data Data to be saved to the transient.
542
+ */
543
+ public function save_current_import_data_transient( $data ) {
544
+ set_transient( 'pt_importer_data', $data, MINUTE_IN_SECONDS );
545
+ }
546
+
547
+ /**
548
+ * Restore the importer data from the transient.
549
+ *
550
+ * @return boolean
551
+ */
552
+ public function restore_import_data_transient() {
553
+ if ( $data = get_transient( 'pt_importer_data' ) ) {
554
+ $this->options = empty( $data['options'] ) ? array() : $data['options'];
555
+ $this->mapping = empty( $data['mapping'] ) ? array() : $data['mapping'];
556
+ $this->requires_remapping = empty( $data['requires_remapping'] ) ? array() : $data['requires_remapping'];
557
+ $this->exists = empty( $data['exists'] ) ? array() : $data['exists'];
558
+ $this->user_slug_override = empty( $data['user_slug_override'] ) ? array() : $data['user_slug_override'];
559
+ $this->url_remap = empty( $data['url_remap'] ) ? array() : $data['url_remap'];
560
+ $this->featured_images = empty( $data['featured_images'] ) ? array() : $data['featured_images'];
561
+
562
+ do_action( 'pt-importer/restore_import_data_transient' );
563
+
564
+ return true;
565
+ }
566
+
567
+ return false;
568
+ }
569
+
570
+ /**
571
+ * Get the importer mapping data.
572
+ *
573
+ * @return array An empty array or an array of mapping data.
574
+ */
575
+ public function get_mapping() {
576
+ return $this->mapping;
577
+ }
578
+
579
+ /**
580
+ * Hook into the pre-process term filter of the content import and register the
581
+ * custom WooCommerce product attributes, so that the terms can then be imported normally.
582
+ *
583
+ * This should probably be removed once the WP importer 2.0 support is added in WooCommerce.
584
+ *
585
+ * Fixes: [WARNING] Failed to import pa_size L warnings in content import.
586
+ * Code from: woocommerce/includes/admin/class-wc-admin-importers.php (ver 2.6.9).
587
+ *
588
+ * Github issue: https://github.com/awesomemotive/one-click-demo-import/issues/71
589
+ *
590
+ * @param array $date The term data to import.
591
+ * @return array The unchanged term data.
592
+ */
593
+ public function woocommerce_product_attributes_registration( $data ) {
594
+ global $wpdb;
595
+
596
+ if ( strstr( $data['taxonomy'], 'pa_' ) ) {
597
+ if ( ! taxonomy_exists( $data['taxonomy'] ) ) {
598
+ $attribute_name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $data['taxonomy'] ) );
599
+
600
+ // Create the taxonomy
601
+ if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) {
602
+ $attribute = array(
603
+ 'attribute_label' => $attribute_name,
604
+ 'attribute_name' => $attribute_name,
605
+ 'attribute_type' => 'select',
606
+ 'attribute_orderby' => 'menu_order',
607
+ 'attribute_public' => 0
608
+ );
609
+ $wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
610
+ delete_transient( 'wc_attribute_taxonomies' );
611
+ }
612
+
613
+ // Register the taxonomy now so that the import works!
614
+ register_taxonomy(
615
+ $data['taxonomy'],
616
+ apply_filters( 'woocommerce_taxonomy_objects_' . $data['taxonomy'], array( 'product' ) ),
617
+ apply_filters( 'woocommerce_taxonomy_args_' . $data['taxonomy'], array(
618
+ 'hierarchical' => true,
619
+ 'show_ui' => false,
620
+ 'query_var' => true,
621
+ 'rewrite' => false,
622
+ ) )
623
+ );
624
+ }
625
+ }
626
+
627
+ return $data;
628
+ }
629
+ }
vendor/wxr-importer/WPImporterLogger.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AwesomeMotive\WPContentImporter2;
3
+
4
+ /**
5
+ * Describes a logger instance
6
+ *
7
+ * Based on PSR-3: http://www.php-fig.org/psr/psr-3/
8
+ *
9
+ * The message MUST be a string or object implementing __toString().
10
+ *
11
+ * The message MAY contain placeholders in the form: {foo} where foo
12
+ * will be replaced by the context data in key "foo".
13
+ *
14
+ * The context array can contain arbitrary data, the only assumption that
15
+ * can be made by implementors is that if an Exception instance is given
16
+ * to produce a stack trace, it MUST be in a key named "exception".
17
+ *
18
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
19
+ * for the full interface specification.
20
+ */
21
+ class WPImporterLogger {
22
+ /**
23
+ * System is unusable.
24
+ *
25
+ * @param string $message
26
+ * @param array $context
27
+ * @return null
28
+ */
29
+ public function emergency( $message, array $context = array() ) {
30
+ return $this->log( 'emergency', $message, $context );
31
+ }
32
+
33
+ /**
34
+ * Action must be taken immediately.
35
+ *
36
+ * Example: Entire website down, database unavailable, etc. This should
37
+ * trigger the SMS alerts and wake you up.
38
+ *
39
+ * @param string $message
40
+ * @param array $context
41
+ * @return null
42
+ */
43
+ public function alert( $message, array $context = array() ) {
44
+ return $this->log( 'alert', $message, $context );
45
+ }
46
+
47
+ /**
48
+ * Critical conditions.
49
+ *
50
+ * Example: Application component unavailable, unexpected exception.
51
+ *
52
+ * @param string $message
53
+ * @param array $context
54
+ * @return null
55
+ */
56
+ public function critical( $message, array $context = array() ) {
57
+ return $this->log( 'critical', $message, $context );
58
+ }
59
+
60
+ /**
61
+ * Runtime errors that do not require immediate action but should typically
62
+ * be logged and monitored.
63
+ *
64
+ * @param string $message
65
+ * @param array $context
66
+ * @return null
67
+ */
68
+ public function error( $message, array $context = array()) {
69
+ return $this->log( 'error', $message, $context );
70
+ }
71
+
72
+ /**
73
+ * Exceptional occurrences that are not errors.
74
+ *
75
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
76
+ * that are not necessarily wrong.
77
+ *
78
+ * @param string $message
79
+ * @param array $context
80
+ * @return null
81
+ */
82
+ public function warning( $message, array $context = array() ) {
83
+ return $this->log( 'warning', $message, $context );
84
+ }
85
+
86
+ /**
87
+ * Normal but significant events.
88
+ *
89
+ * @param string $message
90
+ * @param array $context
91
+ * @return null
92
+ */
93
+ public function notice( $message, array $context = array() ) {
94
+ return $this->log( 'notice', $message, $context );
95
+ }
96
+
97
+ /**
98
+ * Interesting events.
99
+ *
100
+ * Example: User logs in, SQL logs.
101
+ *
102
+ * @param string $message
103
+ * @param array $context
104
+ * @return null
105
+ */
106
+ public function info( $message, array $context = array() ) {
107
+ return $this->log( 'info', $message, $context );
108
+ }
109
+
110
+ /**
111
+ * Detailed debug information.
112
+ *
113
+ * @param string $message
114
+ * @param array $context
115
+ * @return null
116
+ */
117
+ public function debug( $message, array $context = array() ) {
118
+ return $this->log( 'debug', $message, $context );
119
+ }
120
+
121
+ /**
122
+ * Logs with an arbitrary level.
123
+ *
124
+ * @param mixed $level
125
+ * @param string $message
126
+ * @param array $context
127
+ * @return null
128
+ */
129
+ public function log( $level, $message, array $context = array() ) {
130
+ $this->messages[] = array(
131
+ 'timestamp' => time(),
132
+ 'level' => $level,
133
+ 'message' => $message,
134
+ 'context' => $context,
135
+ );
136
+ }
137
+ }
vendor/wxr-importer/WPImporterLoggerCLI.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AwesomeMotive\WPContentImporter2;
3
+
4
+ class WPImporterLoggerCLI extends WPImporterLogger {
5
+ public $min_level = 'notice';
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
+ if ( $this->level_to_numeric( $level ) < $this->level_to_numeric( $this->min_level ) ) {
17
+ return;
18
+ }
19
+
20
+ printf(
21
+ '[%s] %s' . PHP_EOL,
22
+ strtoupper( $level ),
23
+ $message
24
+ );
25
+ }
26
+
27
+ public static function level_to_numeric( $level ) {
28
+ $levels = array(
29
+ 'emergency' => 8,
30
+ 'alert' => 7,
31
+ 'critical' => 6,
32
+ 'error' => 5,
33
+ 'warning' => 4,
34
+ 'notice' => 3,
35
+ 'info' => 2,
36
+ 'debug' => 1,
37
+ );
38
+ if ( ! isset( $levels[ $level ] ) ) {
39
+ return 0;
40
+ }
41
+
42
+ return $levels[ $level ];
43
+ }
44
+ }
vendor/wxr-importer/WXRImportInfo.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace AwesomeMotive\WPContentImporter2;
4
+
5
+ class WXRImportInfo {
6
+ public $home;
7
+ public $siteurl;
8
+ public $title;
9
+ public $users = array();
10
+ public $post_count = 0;
11
+ public $media_count = 0;
12
+ public $comment_count = 0;
13
+ public $term_count = 0;
14
+ public $generator = '';
15
+ public $version;
16
+ }
vendor/wxr-importer/WXRImporter.php ADDED
@@ -0,0 +1,2561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AwesomeMotive\WPContentImporter2;
3
+
4
+ use WP_Error;
5
+ use XMLReader;
6
+
7
+ class WXRImporter extends \WP_Importer {
8
+ /**
9
+ * Maximum supported WXR version
10
+ */
11
+ const MAX_WXR_VERSION = 1.2;
12
+
13
+ /**
14
+ * Regular expression for checking if a post references an attachment
15
+ *
16
+ * Note: This is a quick, weak check just to exclude text-only posts. More
17
+ * vigorous checking is done later to verify.
18
+ */
19
+ const REGEX_HAS_ATTACHMENT_REFS = '!
20
+ (
21
+ # Match anything with an image or attachment class
22
+ class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
23
+ |
24
+ # Match anything that looks like an upload URL
25
+ src=[\'"][^\'"]*(
26
+ [0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
27
+ |
28
+ content/uploads[^\'"]+
29
+ )[\'"]
30
+ )!ix';
31
+
32
+ /**
33
+ * Version of WXR we're importing.
34
+ *
35
+ * Defaults to 1.0 for compatibility. Typically overridden by a
36
+ * `<wp:wxr_version>` tag at the start of the file.
37
+ *
38
+ * @var string
39
+ */
40
+ protected $version = '1.0';
41
+
42
+ // information to import from WXR file
43
+ protected $categories = array();
44
+ protected $tags = array();
45
+ protected $base_url = '';
46
+
47
+ // TODO: REMOVE THESE
48
+ protected $processed_terms = array();
49
+ protected $processed_posts = array();
50
+ protected $processed_menu_items = array();
51
+ protected $menu_item_orphans = array();
52
+ protected $missing_menu_items = array();
53
+
54
+ // NEW STYLE
55
+ public $options = array();
56
+ protected $mapping = array();
57
+ protected $requires_remapping = array();
58
+ protected $exists = array();
59
+ protected $user_slug_override = array();
60
+
61
+ protected $url_remap = array();
62
+ protected $featured_images = array();
63
+
64
+ /**
65
+ * Logger instance.
66
+ *
67
+ * @var WPImporterLogger
68
+ */
69
+ protected $logger;
70
+
71
+ /**
72
+ * Constructor
73
+ *
74
+ * @param array $options {
75
+ * @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.)
76
+ * @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.)
77
+ * @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.)
78
+ * @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 duplication and reimporting. Default is false.)
79
+ * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
80
+ * @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.)
81
+ * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
82
+ * }
83
+ */
84
+ public function __construct( $options = array() ) {
85
+ // Initialize some important variables
86
+ $empty_types = array(
87
+ 'post' => array(),
88
+ 'comment' => array(),
89
+ 'term' => array(),
90
+ 'user' => array(),
91
+ );
92
+
93
+ $this->mapping = $empty_types;
94
+ $this->mapping['user_slug'] = array();
95
+ $this->mapping['term_id'] = array();
96
+ $this->requires_remapping = $empty_types;
97
+ $this->exists = $empty_types;
98
+
99
+ $this->options = wp_parse_args( $options, array(
100
+ 'prefill_existing_posts' => true,
101
+ 'prefill_existing_comments' => true,
102
+ 'prefill_existing_terms' => true,
103
+ 'update_attachment_guids' => false,
104
+ 'fetch_attachments' => false,
105
+ 'aggressive_url_search' => false,
106
+ 'default_author' => null,
107
+ ) );
108
+ }
109
+
110
+ public function set_logger( $logger ) {
111
+ $this->logger = $logger;
112
+ }
113
+
114
+ /**
115
+ * Get a stream reader for the file.
116
+ *
117
+ * @param string $file Path to the XML file.
118
+ * @return XMLReader|WP_Error Reader instance on success, error otherwise.
119
+ */
120
+ protected function get_reader( $file ) {
121
+ // Avoid loading external entities for security
122
+ $old_value = null;
123
+ if ( function_exists( 'libxml_disable_entity_loader' ) ) {
124
+ // $old_value = libxml_disable_entity_loader( true );
125
+ }
126
+
127
+ $reader = new XMLReader();
128
+ $status = $reader->open( $file );
129
+
130
+ if ( ! is_null( $old_value ) ) {
131
+ // libxml_disable_entity_loader( $old_value );
132
+ }
133
+
134
+ if ( ! $status ) {
135
+ return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) );
136
+ }
137
+
138
+ return $reader;
139
+ }
140
+
141
+ /**
142
+ * The main controller for the actual import stage.
143
+ *
144
+ * @param string $file Path to the WXR file for importing
145
+ *
146
+ * @return WXRImportInfo|WP_Error
147
+ */
148
+ public function get_preliminary_information( $file ) {
149
+ // Let's run the actual importer now, woot
150
+ $reader = $this->get_reader( $file );
151
+ if ( is_wp_error( $reader ) ) {
152
+ return $reader;
153
+ }
154
+
155
+ // Set the version to compatibility mode first
156
+ $this->version = '1.0';
157
+
158
+ // Start parsing!
159
+ $data = new WXRImportInfo();
160
+ while ( $reader->read() ) {
161
+ // Only deal with element opens
162
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
163
+ continue;
164
+ }
165
+
166
+ switch ( $reader->name ) {
167
+ case 'wp:wxr_version':
168
+ // Upgrade to the correct version
169
+ $this->version = $reader->readString();
170
+
171
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
172
+ $this->logger->warning( sprintf(
173
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
174
+ $this->version,
175
+ self::MAX_WXR_VERSION
176
+ ) );
177
+ }
178
+
179
+ // Handled everything in this node, move on to the next
180
+ $reader->next();
181
+ break;
182
+
183
+ case 'generator':
184
+ $data->generator = $reader->readString();
185
+ $reader->next();
186
+ break;
187
+
188
+ case 'title':
189
+ $data->title = $reader->readString();
190
+ $reader->next();
191
+ break;
192
+
193
+ case 'wp:base_site_url':
194
+ $data->siteurl = $reader->readString();
195
+ $reader->next();
196
+ break;
197
+
198
+ case 'wp:base_blog_url':
199
+ $data->home = $reader->readString();
200
+ $reader->next();
201
+ break;
202
+
203
+ case 'wp:author':
204
+ $node = $reader->expand();
205
+
206
+ $parsed = $this->parse_author_node( $node );
207
+ if ( is_wp_error( $parsed ) ) {
208
+ $this->log_error( $parsed );
209
+
210
+ // Skip the rest of this post
211
+ $reader->next();
212
+ break;
213
+ }
214
+
215
+ $data->users[] = $parsed;
216
+
217
+ // Handled everything in this node, move on to the next
218
+ $reader->next();
219
+ break;
220
+
221
+ case 'item':
222
+ $node = $reader->expand();
223
+ $parsed = $this->parse_post_node( $node );
224
+ if ( is_wp_error( $parsed ) ) {
225
+ $this->log_error( $parsed );
226
+
227
+ // Skip the rest of this post
228
+ $reader->next();
229
+ break;
230
+ }
231
+
232
+ if ( $parsed['data']['post_type'] === 'attachment' ) {
233
+ $data->media_count++;
234
+ } else {
235
+ $data->post_count++;
236
+ }
237
+ $data->comment_count += count( $parsed['comments'] );
238
+
239
+ // Handled everything in this node, move on to the next
240
+ $reader->next();
241
+ break;
242
+
243
+ case 'wp:category':
244
+ case 'wp:tag':
245
+ case 'wp:term':
246
+ $data->term_count++;
247
+
248
+ // Handled everything in this node, move on to the next
249
+ $reader->next();
250
+ break;
251
+ }
252
+ }
253
+
254
+ $data->version = $this->version;
255
+
256
+ return $data;
257
+ }
258
+
259
+ /**
260
+ * The main controller for the actual import stage.
261
+ *
262
+ * @param string $file Path to the WXR file for importing
263
+ *
264
+ * @return array|WP_Error
265
+ */
266
+ public function parse_authors( $file ) {
267
+ // Let's run the actual importer now, woot
268
+ $reader = $this->get_reader( $file );
269
+ if ( is_wp_error( $reader ) ) {
270
+ return $reader;
271
+ }
272
+
273
+ // Set the version to compatibility mode first
274
+ $this->version = '1.0';
275
+
276
+ // Start parsing!
277
+ $authors = array();
278
+ while ( $reader->read() ) {
279
+ // Only deal with element opens
280
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
281
+ continue;
282
+ }
283
+
284
+ switch ( $reader->name ) {
285
+ case 'wp:wxr_version':
286
+ // Upgrade to the correct version
287
+ $this->version = $reader->readString();
288
+
289
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
290
+ $this->logger->warning( sprintf(
291
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
292
+ $this->version,
293
+ self::MAX_WXR_VERSION
294
+ ) );
295
+ }
296
+
297
+ // Handled everything in this node, move on to the next
298
+ $reader->next();
299
+ break;
300
+
301
+ case 'wp:author':
302
+ $node = $reader->expand();
303
+
304
+ $parsed = $this->parse_author_node( $node );
305
+ if ( is_wp_error( $parsed ) ) {
306
+ $this->log_error( $parsed );
307
+
308
+ // Skip the rest of this post
309
+ $reader->next();
310
+ break;
311
+ }
312
+
313
+ $authors[] = $parsed;
314
+
315
+ // Handled everything in this node, move on to the next
316
+ $reader->next();
317
+ break;
318
+ }
319
+ }
320
+
321
+ return $authors;
322
+ }
323
+
324
+ /**
325
+ * The main controller for the actual import stage.
326
+ *
327
+ * @param string $file Path to the WXR file for importing.
328
+ */
329
+ public function import( $file ) {
330
+ add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
331
+ add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
332
+
333
+ /*
334
+ * Elementor fix for excessive use of `wp_slash` after our update v3.0.2.
335
+ * Method in Elementor: \Elementor\Compatibility::register_actions
336
+ * https://wordpress.org/support/topic/version-2-6-0-breaks-every-elementor-theme/
337
+ *
338
+ * This can be removed after Elementor skips the functionality in above method if our plugin is in use.
339
+ */
340
+ if ( method_exists( '\Elementor\Compatibility', 'on_wxr_importer_pre_process_post_meta' ) ) {
341
+ remove_action( 'wxr_importer.pre_process.post_meta', array( 'Elementor\Compatibility', 'on_wxr_importer_pre_process_post_meta' ) );
342
+ }
343
+
344
+ $result = $this->import_start( $file );
345
+ if ( is_wp_error( $result ) ) {
346
+ return $result;
347
+ }
348
+
349
+ // Let's run the actual importer now, woot
350
+ $reader = $this->get_reader( $file );
351
+ if ( is_wp_error( $reader ) ) {
352
+ return $reader;
353
+ }
354
+
355
+ // Set the version to compatibility mode first
356
+ $this->version = '1.0';
357
+
358
+ // Reset other variables
359
+ $this->base_url = '';
360
+
361
+ // Start parsing!
362
+ while ( $reader->read() ) {
363
+ // Only deal with element opens
364
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
365
+ continue;
366
+ }
367
+
368
+ switch ( $reader->name ) {
369
+ case 'wp:wxr_version':
370
+ // Upgrade to the correct version
371
+ $this->version = $reader->readString();
372
+
373
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
374
+ $this->logger->warning( sprintf(
375
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'wordpress-importer' ),
376
+ $this->version,
377
+ self::MAX_WXR_VERSION
378
+ ) );
379
+ }
380
+
381
+ // Handled everything in this node, move on to the next
382
+ $reader->next();
383
+ break;
384
+
385
+ case 'wp:base_site_url':
386
+ $this->base_url = $reader->readString();
387
+
388
+ // Handled everything in this node, move on to the next
389
+ $reader->next();
390
+ break;
391
+
392
+ case 'item':
393
+ $node = $reader->expand();
394
+ $parsed = $this->parse_post_node( $node );
395
+ if ( is_wp_error( $parsed ) ) {
396
+ $this->log_error( $parsed );
397
+
398
+ // Skip the rest of this post
399
+ $reader->next();
400
+ break;
401
+ }
402
+
403
+ $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
404
+
405
+ // Handled everything in this node, move on to the next
406
+ $reader->next();
407
+ break;
408
+
409
+ case 'wp:author':
410
+ $node = $reader->expand();
411
+
412
+ $parsed = $this->parse_author_node( $node );
413
+ if ( is_wp_error( $parsed ) ) {
414
+ $this->log_error( $parsed );
415
+
416
+ // Skip the rest of this post
417
+ $reader->next();
418
+ break;
419
+ }
420
+
421
+ $status = $this->process_author( $parsed['data'], $parsed['meta'] );
422
+ if ( is_wp_error( $status ) ) {
423
+ $this->log_error( $status );
424
+ }
425
+
426
+ // Handled everything in this node, move on to the next
427
+ $reader->next();
428
+ break;
429
+
430
+ case 'wp:category':
431
+ $node = $reader->expand();
432
+
433
+ $parsed = $this->parse_term_node( $node, 'category' );
434
+ if ( is_wp_error( $parsed ) ) {
435
+ $this->log_error( $parsed );
436
+
437
+ // Skip the rest of this post
438
+ $reader->next();
439
+ break;
440
+ }
441
+
442
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
443
+
444
+ // Handled everything in this node, move on to the next
445
+ $reader->next();
446
+ break;
447
+
448
+ case 'wp:tag':
449
+ $node = $reader->expand();
450
+
451
+ $parsed = $this->parse_term_node( $node, 'tag' );
452
+ if ( is_wp_error( $parsed ) ) {
453
+ $this->log_error( $parsed );
454
+
455
+ // Skip the rest of this post
456
+ $reader->next();
457
+ break;
458
+ }
459
+
460
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
461
+
462
+ // Handled everything in this node, move on to the next
463
+ $reader->next();
464
+ break;
465
+
466
+ case 'wp:term':
467
+ $node = $reader->expand();
468
+
469
+ $parsed = $this->parse_term_node( $node );
470
+ if ( is_wp_error( $parsed ) ) {
471
+ $this->log_error( $parsed );
472
+
473
+ // Skip the rest of this post
474
+ $reader->next();
475
+ break;
476
+ }
477
+
478
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
479
+
480
+ // Handled everything in this node, move on to the next
481
+ $reader->next();
482
+ break;
483
+
484
+ default:
485
+ // Skip this node, probably handled by something already
486
+ break;
487
+ }
488
+ }
489
+
490
+ // Now that we've done the main processing, do any required
491
+ // post-processing and remapping.
492
+ $this->post_process();
493
+
494
+ if ( $this->options['aggressive_url_search'] ) {
495
+ $this->replace_attachment_urls_in_content();
496
+ }
497
+
498
+ $this->remap_featured_images();
499
+
500
+ $this->import_end();
501
+ }
502
+
503
+ /**
504
+ * Log an error instance to the logger.
505
+ *
506
+ * @param WP_Error $error Error instance to log.
507
+ */
508
+ protected function log_error( WP_Error $error ) {
509
+ $this->logger->warning( $error->get_error_message() );
510
+
511
+ // Log the data as debug info too
512
+ $data = $error->get_error_data();
513
+ if ( ! empty( $data ) ) {
514
+ $this->logger->debug( var_export( $data, true ) );
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Parses the WXR file and prepares us for the task of processing parsed data
520
+ *
521
+ * @param string $file Path to the WXR file for importing
522
+ */
523
+ protected function import_start( $file ) {
524
+ if ( ! is_file( $file ) ) {
525
+ return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) );
526
+ }
527
+
528
+ // Suspend bunches of stuff in WP core
529
+ wp_defer_term_counting( true );
530
+ wp_defer_comment_counting( true );
531
+ wp_suspend_cache_invalidation( true );
532
+
533
+ // Prefill exists calls if told to
534
+ if ( $this->options['prefill_existing_posts'] ) {
535
+ $this->prefill_existing_posts();
536
+ }
537
+ if ( $this->options['prefill_existing_comments'] ) {
538
+ $this->prefill_existing_comments();
539
+ }
540
+ if ( $this->options['prefill_existing_terms'] ) {
541
+ $this->prefill_existing_terms();
542
+ }
543
+
544
+ /**
545
+ * Begin the import.
546
+ *
547
+ * Fires before the import process has begun. If you need to suspend
548
+ * caching or heavy processing on hooks, do so here.
549
+ */
550
+ do_action( 'import_start' );
551
+ }
552
+
553
+ /**
554
+ * Performs post-import cleanup of files and the cache
555
+ */
556
+ protected function import_end() {
557
+ // Re-enable stuff in core
558
+ wp_suspend_cache_invalidation( false );
559
+ wp_cache_flush();
560
+
561
+ foreach ( get_taxonomies() as $tax ) {
562
+ delete_option( "{$tax}_children" );
563
+ _get_term_hierarchy( $tax );
564
+ }
565
+
566
+ wp_defer_term_counting( false );
567
+ wp_defer_comment_counting( false );
568
+
569
+ flush_rewrite_rules();
570
+
571
+ /**
572
+ * Complete the import.
573
+ *
574
+ * Fires after the import process has finished. If you need to update
575
+ * your cache or re-enable processing, do so here.
576
+ */
577
+ do_action( 'import_end' );
578
+ }
579
+
580
+ /**
581
+ * Set the user mapping.
582
+ *
583
+ * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
584
+ */
585
+ public function set_user_mapping( $mapping ) {
586
+ foreach ( $mapping as $map ) {
587
+ if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
588
+ $this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) );
589
+ $this->logger->debug( var_export( $map, true ) );
590
+ continue;
591
+ }
592
+
593
+ $old_slug = $map['old_slug'];
594
+ $old_id = $map['old_id'];
595
+ $new_id = $map['new_id'];
596
+
597
+ $this->mapping['user'][ $old_id ] = $new_id;
598
+ $this->mapping['user_slug'][ $old_slug ] = $new_id;
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Set the user slug overrides.
604
+ *
605
+ * Allows overriding the slug in the import with a custom/renamed version.
606
+ *
607
+ * @param string[] $overrides Map of old slug to new slug.
608
+ */
609
+ public function set_user_slug_overrides( $overrides ) {
610
+ foreach ( $overrides as $original => $renamed ) {
611
+ $this->user_slug_override[ $original ] = $renamed;
612
+ }
613
+ }
614
+
615
+ /**
616
+ * Parse a post node into post data.
617
+ *
618
+ * @param \DOMNode $node Parent node of post data (typically `item`).
619
+ * @return array|WP_Error Post data array on success, error otherwise.
620
+ */
621
+ protected function parse_post_node( $node ) {
622
+ $data = array();
623
+ $meta = array();
624
+ $comments = array();
625
+ $terms = array();
626
+
627
+ foreach ( $node->childNodes as $child ) {
628
+ // We only care about child elements
629
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
630
+ continue;
631
+ }
632
+
633
+ switch ( $child->tagName ) {
634
+ case 'wp:post_type':
635
+ $data['post_type'] = $child->textContent;
636
+ break;
637
+
638
+ case 'title':
639
+ $data['post_title'] = $child->textContent;
640
+ break;
641
+
642
+ case 'guid':
643
+ $data['guid'] = $child->textContent;
644
+ break;
645
+
646
+ case 'dc:creator':
647
+ $data['post_author'] = $child->textContent;
648
+ break;
649
+
650
+ case 'content:encoded':
651
+ $data['post_content'] = $child->textContent;
652
+ break;
653
+
654
+ case 'excerpt:encoded':
655
+ $data['post_excerpt'] = $child->textContent;
656
+ break;
657
+
658
+ case 'wp:post_id':
659
+ $data['post_id'] = $child->textContent;
660
+ break;
661
+
662
+ case 'wp:post_date':
663
+ $data['post_date'] = $child->textContent;
664
+ break;
665
+
666
+ case 'wp:post_date_gmt':
667
+ $data['post_date_gmt'] = $child->textContent;
668
+ break;
669
+
670
+ case 'wp:comment_status':
671
+ $data['comment_status'] = $child->textContent;
672
+ break;
673
+
674
+ case 'wp:ping_status':
675
+ $data['ping_status'] = $child->textContent;
676
+ break;
677
+
678
+ case 'wp:post_name':
679
+ $data['post_name'] = $child->textContent;
680
+ break;
681
+
682
+ case 'wp:status':
683
+ $data['post_status'] = $child->textContent;
684
+
685
+ if ( $data['post_status'] === 'auto-draft' ) {
686
+ // Bail now
687
+ return new WP_Error(
688
+ 'wxr_importer.post.cannot_import_draft',
689
+ __( 'Cannot import auto-draft posts' ),
690
+ $data
691
+ );
692
+ }
693
+ break;
694
+
695
+ case 'wp:post_parent':
696
+ $data['post_parent'] = $child->textContent;
697
+ break;
698
+
699
+ case 'wp:menu_order':
700
+ $data['menu_order'] = $child->textContent;
701
+ break;
702
+
703
+ case 'wp:post_password':
704
+ $data['post_password'] = $child->textContent;
705
+ break;
706
+
707
+ case 'wp:is_sticky':
708
+ $data['is_sticky'] = $child->textContent;
709
+ break;
710
+
711
+ case 'wp:attachment_url':
712
+ $data['attachment_url'] = $child->textContent;
713
+ break;
714
+
715
+ case 'wp:postmeta':
716
+ $meta_item = $this->parse_meta_node( $child );
717
+ if ( ! empty( $meta_item ) ) {
718
+ $meta[] = $meta_item;
719
+ }
720
+ break;
721
+
722
+ case 'wp:comment':
723
+ $comment_item = $this->parse_comment_node( $child );
724
+ if ( ! empty( $comment_item ) ) {
725
+ $comments[] = $comment_item;
726
+ }
727
+ break;
728
+
729
+ case 'category':
730
+ $term_item = $this->parse_category_node( $child );
731
+ if ( ! empty( $term_item ) ) {
732
+ $terms[] = $term_item;
733
+ }
734
+ break;
735
+ }
736
+ }
737
+
738
+ return compact( 'data', 'meta', 'comments', 'terms' );
739
+ }
740
+
741
+ /**
742
+ * Create new posts based on import information
743
+ *
744
+ * Posts marked as having a parent which doesn't exist will become top level items.
745
+ * Doesn't create a new post if: the post type doesn't exist, the given post ID
746
+ * is already noted as imported or a post with the same title and date already exists.
747
+ * Note that new/updated terms, comments and meta are imported for the last of the above.
748
+ *
749
+ * @param array $data Post data.
750
+ * @param array $meta Meta data.
751
+ * @param array $comments Comments on the post.
752
+ * @param array $terms Terms on the post.
753
+ */
754
+ protected function process_post( $data, $meta, $comments, $terms ) {
755
+ /**
756
+ * Pre-process post data.
757
+ *
758
+ * @param array $data Post data. (Return empty to skip.)
759
+ * @param array $meta Meta data.
760
+ * @param array $comments Comments on the post.
761
+ * @param array $terms Terms on the post.
762
+ */
763
+ $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
764
+ if ( empty( $data ) ) {
765
+ return false;
766
+ }
767
+
768
+ $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
769
+ $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
770
+
771
+ // Have we already processed this?
772
+ if ( isset( $this->mapping['post'][ $original_id ] ) ) {
773
+ return false;
774
+ }
775
+
776
+ $post_type_object = get_post_type_object( $data['post_type'] );
777
+
778
+ // Is this type even valid?
779
+ if ( ! $post_type_object ) {
780
+ $this->logger->warning( sprintf(
781
+ __( 'Failed to import "%s": Invalid post type %s', 'wordpress-importer' ),
782
+ $data['post_title'],
783
+ $data['post_type']
784
+ ) );
785
+ return false;
786
+ }
787
+
788
+ $post_exists = $this->post_exists( $data );
789
+ if ( $post_exists ) {
790
+ $this->logger->info( sprintf(
791
+ __( '%s "%s" already exists.', 'wordpress-importer' ),
792
+ $post_type_object->labels->singular_name,
793
+ $data['post_title']
794
+ ) );
795
+
796
+ // Even though this post already exists, new comments might need importing
797
+ $this->process_comments( $comments, $original_id, $data, $post_exists );
798
+
799
+ return false;
800
+ }
801
+
802
+ // Map the parent post, or mark it as one we need to fix
803
+ $requires_remapping = false;
804
+ if ( $parent_id ) {
805
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
806
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
807
+ } else {
808
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
809
+ $requires_remapping = true;
810
+
811
+ $data['post_parent'] = 0;
812
+ }
813
+ }
814
+
815
+ // Map the author, or mark it as one we need to fix
816
+ $author = sanitize_user( $data['post_author'], true );
817
+ if ( empty( $author ) ) {
818
+ // Missing or invalid author, use default if available.
819
+ $data['post_author'] = $this->options['default_author'];
820
+ } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
821
+ $data['post_author'] = $this->mapping['user_slug'][ $author ];
822
+ } else {
823
+ $meta[] = array( 'key' => '_wxr_import_user_slug', 'value' => $author );
824
+ $requires_remapping = true;
825
+
826
+ $data['post_author'] = (int) get_current_user_id();
827
+ }
828
+
829
+ // Does the post look like it contains attachment images?
830
+ if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
831
+ $meta[] = array( 'key' => '_wxr_import_has_attachment_refs', 'value' => true );
832
+ $requires_remapping = true;
833
+ }
834
+
835
+ // Whitelist to just the keys we allow
836
+ $postdata = array(
837
+ 'import_id' => $data['post_id'],
838
+ );
839
+ $allowed = array(
840
+ 'post_author' => true,
841
+ 'post_date' => true,
842
+ 'post_date_gmt' => true,
843
+ 'post_content' => true,
844
+ 'post_excerpt' => true,
845
+ 'post_title' => true,
846
+ 'post_status' => true,
847
+ 'post_name' => true,
848
+ 'comment_status' => true,
849
+ 'ping_status' => true,
850
+ 'guid' => true,
851
+ 'post_parent' => true,
852
+ 'menu_order' => true,
853
+ 'post_type' => true,
854
+ 'post_password' => true,
855
+ );
856
+ foreach ( $data as $key => $value ) {
857
+ if ( ! isset( $allowed[ $key ] ) ) {
858
+ continue;
859
+ }
860
+
861
+ $postdata[ $key ] = $data[ $key ];
862
+ }
863
+
864
+ $postdata = apply_filters( 'wp_import_post_data_processed', wp_slash( $postdata ), $data );
865
+
866
+ if ( 'attachment' === $postdata['post_type'] ) {
867
+ if ( ! $this->options['fetch_attachments'] ) {
868
+ $this->logger->notice( sprintf(
869
+ __( 'Skipping attachment "%s", fetching attachments disabled' ),
870
+ $data['post_title']
871
+ ) );
872
+ return false;
873
+ }
874
+ $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
875
+ $post_id = $this->process_attachment( $postdata, $meta, $remote_url );
876
+ } else {
877
+ $post_id = wp_insert_post( $postdata, true );
878
+ do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
879
+ }
880
+
881
+ if ( is_wp_error( $post_id ) ) {
882
+ $this->logger->error( sprintf(
883
+ __( 'Failed to import "%s" (%s)', 'wordpress-importer' ),
884
+ $data['post_title'],
885
+ $post_type_object->labels->singular_name
886
+ ) );
887
+ $this->logger->debug( $post_id->get_error_message() );
888
+
889
+ /**
890
+ * Post processing failed.
891
+ *
892
+ * @param WP_Error $post_id Error object.
893
+ * @param array $data Raw data imported for the post.
894
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
895
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
896
+ * @param array $terms Raw term data, already processed.
897
+ */
898
+ do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
899
+ return false;
900
+ }
901
+
902
+ // Ensure stickiness is handled correctly too
903
+ if ( $data['is_sticky'] === '1' ) {
904
+ stick_post( $post_id );
905
+ }
906
+
907
+ // map pre-import ID to local ID
908
+ $this->mapping['post'][ $original_id ] = (int) $post_id;
909
+ if ( $requires_remapping ) {
910
+ $this->requires_remapping['post'][ $post_id ] = true;
911
+ }
912
+ $this->mark_post_exists( $data, $post_id );
913
+
914
+ $this->logger->info( sprintf(
915
+ __( 'Imported "%s" (%s)', 'wordpress-importer' ),
916
+ $data['post_title'],
917
+ $post_type_object->labels->singular_name
918
+ ) );
919
+ $this->logger->debug( sprintf(
920
+ __( 'Post %d remapped to %d', 'wordpress-importer' ),
921
+ $original_id,
922
+ $post_id
923
+ ) );
924
+
925
+ // Handle the terms too
926
+ $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
927
+
928
+ if ( ! empty( $terms ) ) {
929
+ $term_ids = array();
930
+ foreach ( $terms as $term ) {
931
+ $taxonomy = $term['taxonomy'];
932
+ $key = sha1( $taxonomy . ':' . $term['slug'] );
933
+
934
+ if ( isset( $this->mapping['term'][ $key ] ) ) {
935
+ $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
936
+ } else {
937
+
938
+ /**
939
+ * Fix for the post format "categories".
940
+ * The issue in this importer is, that these post formats are misused as categories in WP export
941
+ * (as the export data <category> item in the post export item), but they are not actually
942
+ * exported as wp:category items in the XML file, so they need to be inserted on the fly (here).
943
+ *
944
+ * Maybe something better can be done in the future?
945
+ *
946
+ * Original issue reported here: https://wordpress.org/support/topic/post-format-videoquotegallery-became-format-standard/#post-8447683
947
+ *
948
+ */
949
+ if ( 'post_format' === $taxonomy ) {
950
+ $term_exists = term_exists( $term['slug'], $taxonomy );
951
+ $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
952
+
953
+ if ( empty( $term_id ) ) {
954
+ $t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
955
+ if ( ! is_wp_error( $t ) ) {
956
+ $term_id = $t['term_id'];
957
+ $this->mapping['term'][ $key ] = $term_id;
958
+ } else {
959
+ $this->logger->warning( sprintf(
960
+ esc_html__( 'Failed to import term: %s - %s', 'wordpress-importer' ),
961
+ esc_html( $taxonomy ),
962
+ esc_html( $term['name'] )
963
+ ) );
964
+ continue;
965
+ }
966
+ }
967
+
968
+ if ( ! empty( $term_id ) ) {
969
+ $term_ids[ $taxonomy ][] = intval( $term_id );
970
+ }
971
+ } // End of fix.
972
+ else {
973
+ $meta[] = array( 'key' => '_wxr_import_term', 'value' => $term );
974
+ $requires_remapping = true;
975
+ }
976
+ }
977
+ }
978
+
979
+ foreach ( $term_ids as $tax => $ids ) {
980
+ $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
981
+ do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
982
+ }
983
+ }
984
+
985
+ $this->process_comments( $comments, $post_id, $data );
986
+ $this->process_post_meta( $meta, $post_id, $data );
987
+
988
+ if ( 'nav_menu_item' === $data['post_type'] ) {
989
+ $this->process_menu_item_meta( $post_id, $data, $meta );
990
+ }
991
+
992
+ /**
993
+ * Post processing completed.
994
+ *
995
+ * @param int $post_id New post ID.
996
+ * @param array $data Raw data imported for the post.
997
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
998
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
999
+ * @param array $terms Raw term data, already processed.
1000
+ */
1001
+ do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
1002
+ }
1003
+
1004
+ /**
1005
+ * Attempt to create a new menu item from import data
1006
+ *
1007
+ * Fails for draft, orphaned menu items and those without an associated nav_menu
1008
+ * or an invalid nav_menu term. If the post type or term object which the menu item
1009
+ * represents doesn't exist then the menu item will not be imported (waits until the
1010
+ * end of the import to retry again before discarding).
1011
+ *
1012
+ * @param int $post_id Menu item post ID.
1013
+ * @param array $data Menu item details from WXR file.
1014
+ * @param array $meta Menu item meta details.
1015
+ */
1016
+ protected function process_menu_item_meta( $post_id, $data, $meta ) {
1017
+
1018
+ $item_type = get_post_meta( $post_id, '_menu_item_type', true );
1019
+ $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
1020
+ $object_id = null;
1021
+
1022
+ $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
1023
+
1024
+ $requires_remapping = false;
1025
+ switch ( $item_type ) {
1026
+ case 'taxonomy':
1027
+ if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
1028
+ $object_id = $this->mapping['term_id'][ $original_object_id ];
1029
+ } else {
1030
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
1031
+ $requires_remapping = true;
1032
+ }
1033
+ break;
1034
+
1035
+ case 'post_type':
1036
+ if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
1037
+ $object_id = $this->mapping['post'][ $original_object_id ];
1038
+ } else {
1039
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
1040
+ $requires_remapping = true;
1041
+ }
1042
+ break;
1043
+
1044
+ case 'custom':
1045
+ // Custom refers to itself, wonderfully easy.
1046
+ $object_id = $post_id;
1047
+ break;
1048
+
1049
+ default:
1050
+ // associated object is missing or not imported yet, we'll retry later
1051
+ $this->missing_menu_items[] = $data;
1052
+ $this->logger->debug( 'Unknown menu item type' );
1053
+ break;
1054
+ }
1055
+
1056
+ if ( $requires_remapping ) {
1057
+ $this->requires_remapping['post'][ $post_id ] = true;
1058
+ }
1059
+
1060
+ if ( empty( $object_id ) ) {
1061
+ // Nothing needed here.
1062
+ return;
1063
+ }
1064
+
1065
+ $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
1066
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
1067
+ }
1068
+
1069
+ /**
1070
+ * If fetching attachments is enabled then attempt to create a new attachment
1071
+ *
1072
+ * @param array $post Attachment post details from WXR.
1073
+ * @param array $meta Attachment post meta details.
1074
+ * @param string $remote_url URL to fetch attachment from.
1075
+ *
1076
+ * @return int|WP_Error Post ID on success, WP_Error otherwise
1077
+ */
1078
+ protected function process_attachment( $post, $meta, $remote_url ) {
1079
+ // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
1080
+ // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
1081
+ $post['upload_date'] = $post['post_date'];
1082
+ foreach ( $meta as $meta_item ) {
1083
+ if ( $meta_item['key'] !== '_wp_attached_file' ) {
1084
+ continue;
1085
+ }
1086
+
1087
+ if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
1088
+ $post['upload_date'] = $matches[0];
1089
+ }
1090
+ break;
1091
+ }
1092
+
1093
+ // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
1094
+ if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
1095
+ $remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
1096
+ }
1097
+
1098
+ $upload = $this->fetch_remote_file( $remote_url, $post );
1099
+ if ( is_wp_error( $upload ) ) {
1100
+ return $upload;
1101
+ }
1102
+
1103
+ $info = wp_check_filetype( $upload['file'] );
1104
+ if ( ! $info ) {
1105
+ return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) );
1106
+ }
1107
+
1108
+ $post['post_mime_type'] = $info['type'];
1109
+
1110
+ // WP really likes using the GUID for display. Allow updating it.
1111
+ // See https://core.trac.wordpress.org/ticket/33386
1112
+ if ( $this->options['update_attachment_guids'] ) {
1113
+ $post['guid'] = $upload['url'];
1114
+ }
1115
+
1116
+ // as per wp-admin/includes/upload.php
1117
+ $post_id = wp_insert_attachment( $post, $upload['file'] );
1118
+ if ( is_wp_error( $post_id ) ) {
1119
+ return $post_id;
1120
+ }
1121
+
1122
+ $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
1123
+ wp_update_attachment_metadata( $post_id, $attachment_metadata );
1124
+
1125
+ // Map this image URL later if we need to
1126
+ $this->url_remap[ $remote_url ] = $upload['url'];
1127
+
1128
+ // If we have a HTTPS URL, ensure the HTTP URL gets replaced too
1129
+ if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
1130
+ $insecure_url = 'http' . substr( $remote_url, 5 );
1131
+ $this->url_remap[ $insecure_url ] = $upload['url'];
1132
+ }
1133
+
1134
+ if ( $this->options['aggressive_url_search'] ) {
1135
+ // remap resized image URLs, works by stripping the extension and remapping the URL stub.
1136
+ /*if ( preg_match( '!^image/!', $info['type'] ) ) {
1137
+ $parts = pathinfo( $remote_url );
1138
+ $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
1139
+
1140
+ $parts_new = pathinfo( $upload['url'] );
1141
+ $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
1142
+
1143
+ $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
1144
+ }*/
1145
+ }
1146
+
1147
+ return $post_id;
1148
+ }
1149
+
1150
+ /**
1151
+ * Parse a meta node into meta data.
1152
+ *
1153
+ * @param \DOMNode $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
1154
+ * @return array|null Meta data array on success, or null on error.
1155
+ */
1156
+ protected function parse_meta_node( $node ) {
1157
+ foreach ( $node->childNodes as $child ) {
1158
+ // We only care about child elements
1159
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1160
+ continue;
1161
+ }
1162
+
1163
+ switch ( $child->tagName ) {
1164
+ case 'wp:meta_key':
1165
+ $key = $child->textContent;
1166
+ break;
1167
+
1168
+ case 'wp:meta_value':
1169
+ $value = $child->textContent;
1170
+ break;
1171
+ }
1172
+ }
1173
+
1174
+ if ( empty( $key ) || ! isset( $value ) ) {
1175
+ return null;
1176
+ }
1177
+
1178
+ return compact( 'key', 'value' );
1179
+ }
1180
+
1181
+ /**
1182
+ * Process and import post meta items.
1183
+ *
1184
+ * @param array $meta List of meta data arrays
1185
+ * @param int $post_id Post to associate with
1186
+ * @param array $post Post data
1187
+ * @return int|WP_Error Number of meta items imported on success, error otherwise.
1188
+ */
1189
+ protected function process_post_meta( $meta, $post_id, $post ) {
1190
+ if ( empty( $meta ) ) {
1191
+ return true;
1192
+ }
1193
+
1194
+ foreach ( $meta as $meta_item ) {
1195
+ /**
1196
+ * Pre-process post meta data.
1197
+ *
1198
+ * @param array $meta_item Meta data. (Return empty to skip.)
1199
+ * @param int $post_id Post the meta is attached to.
1200
+ */
1201
+ $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
1202
+ if ( empty( $meta_item ) ) {
1203
+ return false;
1204
+ }
1205
+
1206
+ $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
1207
+ $value = false;
1208
+
1209
+ if ( '_edit_last' === $key ) {
1210
+ $value = intval( $meta_item['value'] );
1211
+ if ( ! isset( $this->mapping['user'][ $value ] ) ) {
1212
+ // Skip!
1213
+ continue;
1214
+ }
1215
+
1216
+ $value = $this->mapping['user'][ $value ];
1217
+ }
1218
+
1219
+ if ( $key ) {
1220
+ // export gets meta straight from the DB so could have a serialized string
1221
+ if ( ! $value ) {
1222
+ $value = maybe_unserialize( $meta_item['value'] );
1223
+ }
1224
+
1225
+ add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
1226
+ do_action( 'import_post_meta', $post_id, $key, $value );
1227
+
1228
+ // if the post has a featured image, take note of this in case of remap
1229
+ if ( '_thumbnail_id' === $key ) {
1230
+ $this->featured_images[ $post_id ] = (int) $value;
1231
+ }
1232
+ }
1233
+ }
1234
+
1235
+ return true;
1236
+ }
1237
+
1238
+ /**
1239
+ * Parse a comment node into comment data.
1240
+ *
1241
+ * @param \DOMNode $node Parent node of comment data (typically `wp:comment`).
1242
+ * @return array Comment data array.
1243
+ */
1244
+ protected function parse_comment_node( $node ) {
1245
+ $data = array(
1246
+ 'commentmeta' => array(),
1247
+ );
1248
+
1249
+ foreach ( $node->childNodes as $child ) {
1250
+ // We only care about child elements
1251
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1252
+ continue;
1253
+ }
1254
+
1255
+ switch ( $child->tagName ) {
1256
+ case 'wp:comment_id':
1257
+ $data['comment_id'] = $child->textContent;
1258
+ break;
1259
+ case 'wp:comment_author':
1260
+ $data['comment_author'] = $child->textContent;
1261
+ break;
1262
+
1263
+ case 'wp:comment_author_email':
1264
+ $data['comment_author_email'] = $child->textContent;
1265
+ break;
1266
+
1267
+ case 'wp:comment_author_IP':
1268
+ $data['comment_author_IP'] = $child->textContent;
1269
+ break;
1270
+
1271
+ case 'wp:comment_author_url':
1272
+ $data['comment_author_url'] = $child->textContent;
1273
+ break;
1274
+
1275
+ case 'wp:comment_user_id':
1276
+ $data['comment_user_id'] = $child->textContent;
1277
+ break;
1278
+
1279
+ case 'wp:comment_date':
1280
+ $data['comment_date'] = $child->textContent;
1281
+ break;
1282
+
1283
+ case 'wp:comment_date_gmt':
1284
+ $data['comment_date_gmt'] = $child->textContent;
1285
+ break;
1286
+
1287
+ case 'wp:comment_content':
1288
+ $data['comment_content'] = $child->textContent;
1289
+ break;
1290
+
1291
+ case 'wp:comment_approved':
1292
+ $data['comment_approved'] = $child->textContent;
1293
+ break;
1294
+
1295
+ case 'wp:comment_type':
1296
+ $data['comment_type'] = $child->textContent;
1297
+ break;
1298
+
1299
+ case 'wp:comment_parent':
1300
+ $data['comment_parent'] = $child->textContent;
1301
+ break;
1302
+
1303
+ case 'wp:commentmeta':
1304
+ $meta_item = $this->parse_meta_node( $child );
1305
+ if ( ! empty( $meta_item ) ) {
1306
+ $data['commentmeta'][] = $meta_item;
1307
+ }
1308
+ break;
1309
+ }
1310
+ }
1311
+
1312
+ return $data;
1313
+ }
1314
+
1315
+ /**
1316
+ * Process and import comment data.
1317
+ *
1318
+ * @param array $comments List of comment data arrays.
1319
+ * @param int $post_id Post to associate with.
1320
+ * @param array $post Post data.
1321
+ * @param boolean $post_exists Boolean if the post already exists.
1322
+ *
1323
+ * @return int|WP_Error Number of comments imported on success, error otherwise.
1324
+ */
1325
+ protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
1326
+
1327
+ $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
1328
+ if ( empty( $comments ) ) {
1329
+ return 0;
1330
+ }
1331
+
1332
+ $num_comments = 0;
1333
+
1334
+ // Sort by ID to avoid excessive remapping later
1335
+ usort( $comments, array( $this, 'sort_comments_by_id' ) );
1336
+
1337
+ foreach ( $comments as $key => $comment ) {
1338
+ /**
1339
+ * Pre-process comment data
1340
+ *
1341
+ * @param array $comment Comment data. (Return empty to skip.)
1342
+ * @param int $post_id Post the comment is attached to.
1343
+ */
1344
+ $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
1345
+ if ( empty( $comment ) ) {
1346
+ return false;
1347
+ }
1348
+
1349
+ $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
1350
+ $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
1351
+ $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
1352
+
1353
+ // if this is a new post we can skip the comment_exists() check
1354
+ // TODO: Check comment_exists for performance
1355
+ if ( $post_exists ) {
1356
+ $existing = $this->comment_exists( $comment );
1357
+ if ( $existing ) {
1358
+ $this->mapping['comment'][ $original_id ] = $existing;
1359
+ continue;
1360
+ }
1361
+ }
1362
+
1363
+ // Remove meta from the main array
1364
+ $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
1365
+ unset( $comment['commentmeta'] );
1366
+
1367
+ // Map the parent comment, or mark it as one we need to fix
1368
+ $requires_remapping = false;
1369
+ if ( $parent_id ) {
1370
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1371
+ $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1372
+ } else {
1373
+ // Prepare for remapping later
1374
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1375
+ $requires_remapping = true;
1376
+
1377
+ // Wipe the parent for now
1378
+ $comment['comment_parent'] = 0;
1379
+ }
1380
+ }
1381
+
1382
+ // Map the author, or mark it as one we need to fix
1383
+ if ( $author_id ) {
1384
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1385
+ $comment['user_id'] = $this->mapping['user'][ $author_id ];
1386
+ } else {
1387
+ // Prepare for remapping later
1388
+ $meta[] = array( 'key' => '_wxr_import_user', 'value' => $author_id );
1389
+ $requires_remapping = true;
1390
+
1391
+ // Wipe the user for now
1392
+ $comment['user_id'] = 0;
1393
+ }
1394
+ }
1395
+
1396
+ // Run standard core filters
1397
+ $comment['comment_post_ID'] = $post_id;
1398
+ $comment = wp_filter_comment( $comment );
1399
+
1400
+ // wp_insert_comment expects slashed data
1401
+ $comment_id = wp_insert_comment( wp_slash( $comment ) );
1402
+ $this->mapping['comment'][ $original_id ] = $comment_id;
1403
+ if ( $requires_remapping ) {
1404
+ $this->requires_remapping['comment'][ $comment_id ] = true;
1405
+ }
1406
+ $this->mark_comment_exists( $comment, $comment_id );
1407
+
1408
+ /**
1409
+ * Comment has been imported.
1410
+ *
1411
+ * @param int $comment_id New comment ID
1412
+ * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
1413
+ * @param int $post_id Post parent of the comment
1414
+ * @param array $post Post data
1415
+ */
1416
+ do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
1417
+
1418
+ // Process the meta items
1419
+ foreach ( $meta as $meta_item ) {
1420
+ $value = maybe_unserialize( $meta_item['value'] );
1421
+ add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
1422
+ }
1423
+
1424
+ /**
1425
+ * Post processing completed.
1426
+ *
1427
+ * @param int $post_id New post ID.
1428
+ * @param array $comment Raw data imported for the comment.
1429
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
1430
+ * @param array $post_id Parent post ID.
1431
+ */
1432
+ do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
1433
+
1434
+ $num_comments++;
1435
+ }
1436
+
1437
+ return $num_comments;
1438
+ }
1439
+
1440
+ /**
1441
+ * Parse the category node.
1442
+ *
1443
+ * @param \DOMNode $node The category node.
1444
+ *
1445
+ * @return array|null
1446
+ */
1447
+ protected function parse_category_node( $node ) {
1448
+ $data = array(
1449
+ // Default taxonomy to "category", since this is a `<category>` tag
1450
+ 'taxonomy' => 'category',
1451
+ );
1452
+ $meta = array();
1453
+
1454
+ if ( $node->hasAttribute( 'domain' ) ) {
1455
+ $data['taxonomy'] = $node->getAttribute( 'domain' );
1456
+ }
1457
+ if ( $node->hasAttribute( 'nicename' ) ) {
1458
+ $data['slug'] = $node->getAttribute( 'nicename' );
1459
+ }
1460
+
1461
+ $data['name'] = $node->textContent;
1462
+
1463
+ if ( empty( $data['slug'] ) ) {
1464
+ return null;
1465
+ }
1466
+
1467
+ // Just for extra compatibility
1468
+ if ( $data['taxonomy'] === 'tag' ) {
1469
+ $data['taxonomy'] = 'post_tag';
1470
+ }
1471
+
1472
+ return $data;
1473
+ }
1474
+
1475
+ /**
1476
+ * Callback for `usort` to sort comments by ID
1477
+ *
1478
+ * @param array $a Comment data for the first comment
1479
+ * @param array $b Comment data for the second comment
1480
+ *
1481
+ * @return int
1482
+ */
1483
+ public static function sort_comments_by_id( $a, $b ) {
1484
+ if ( empty( $a['comment_id'] ) ) {
1485
+ return 1;
1486
+ }
1487
+
1488
+ if ( empty( $b['comment_id'] ) ) {
1489
+ return -1;
1490
+ }
1491
+
1492
+ return $a['comment_id'] - $b['comment_id'];
1493
+ }
1494
+
1495
+ protected function parse_author_node( $node ) {
1496
+ $data = array();
1497
+ $meta = array();
1498
+ foreach ( $node->childNodes as $child ) {
1499
+ // We only care about child elements
1500
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1501
+ continue;
1502
+ }
1503
+
1504
+ switch ( $child->tagName ) {
1505
+ case 'wp:author_login':
1506
+ $data['user_login'] = $child->textContent;
1507
+ break;
1508
+
1509
+ case 'wp:author_id':
1510
+ $data['ID'] = $child->textContent;
1511
+ break;
1512
+
1513
+ case 'wp:author_email':
1514
+ $data['user_email'] = $child->textContent;
1515
+ break;
1516
+
1517
+ case 'wp:author_display_name':
1518
+ $data['display_name'] = $child->textContent;
1519
+ break;
1520
+
1521
+ case 'wp:author_first_name':
1522
+ $data['first_name'] = $child->textContent;
1523
+ break;
1524
+
1525
+ case 'wp:author_last_name':
1526
+ $data['last_name'] = $child->textContent;
1527
+ break;
1528
+ }
1529
+ }
1530
+
1531
+ return compact( 'data', 'meta' );
1532
+ }
1533
+
1534
+ /**
1535
+ * Process author.
1536
+ *
1537
+ * @param array $data The author data from WXR file.
1538
+ * @param array $meta The author meta data from WXR file.
1539
+ */
1540
+ protected function process_author( $data, $meta ) {
1541
+ /**
1542
+ * Pre-process user data.
1543
+ *
1544
+ * @param array $data User data. (Return empty to skip.)
1545
+ * @param array $meta Meta data.
1546
+ */
1547
+ $data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta );
1548
+ if ( empty( $data ) ) {
1549
+ return false;
1550
+ }
1551
+
1552
+ // Have we already handled this user?
1553
+ $original_id = isset( $data['ID'] ) ? $data['ID'] : 0;
1554
+ $original_slug = $data['user_login'];
1555
+
1556
+ if ( isset( $this->mapping['user'][ $original_id ] ) ) {
1557
+ $existing = $this->mapping['user'][ $original_id ];
1558
+
1559
+ // Note the slug mapping if we need to too
1560
+ if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1561
+ $this->mapping['user_slug'][ $original_slug ] = $existing;
1562
+ }
1563
+
1564
+ return false;
1565
+ }
1566
+
1567
+ if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) {
1568
+ $existing = $this->mapping['user_slug'][ $original_slug ];
1569
+
1570
+ // Ensure we note the mapping too
1571
+ $this->mapping['user'][ $original_id ] = $existing;
1572
+
1573
+ return false;
1574
+ }
1575
+
1576
+ // Allow overriding the user's slug
1577
+ $login = $original_slug;
1578
+ if ( isset( $this->user_slug_override[ $login ] ) ) {
1579
+ $login = $this->user_slug_override[ $login ];
1580
+ }
1581
+
1582
+ $userdata = array(
1583
+ 'user_login' => sanitize_user( $login, true ),
1584
+ 'user_pass' => wp_generate_password(),
1585
+ );
1586
+
1587
+ $allowed = array(
1588
+ 'user_email' => true,
1589
+ 'display_name' => true,
1590
+ 'first_name' => true,
1591
+ 'last_name' => true,
1592
+ );
1593
+ foreach ( $data as $key => $value ) {
1594
+ if ( ! isset( $allowed[ $key ] ) ) {
1595
+ continue;
1596
+ }
1597
+
1598
+ $userdata[ $key ] = $data[ $key ];
1599
+ }
1600
+
1601
+ $user_id = wp_insert_user( wp_slash( $userdata ) );
1602
+ if ( is_wp_error( $user_id ) ) {
1603
+ $this->logger->error( sprintf(
1604
+ __( 'Failed to import user "%s"', 'wordpress-importer' ),
1605
+ $userdata['user_login']
1606
+ ) );
1607
+ $this->logger->debug( $user_id->get_error_message() );
1608
+
1609
+ /**
1610
+ * User processing failed.
1611
+ *
1612
+ * @param WP_Error $user_id Error object.
1613
+ * @param array $userdata Raw data imported for the user.
1614
+ */
1615
+ do_action( 'wxr_importer.process_failed.user', $user_id, $userdata );
1616
+ return false;
1617
+ }
1618
+
1619
+ if ( $original_id ) {
1620
+ $this->mapping['user'][ $original_id ] = $user_id;
1621
+ }
1622
+ $this->mapping['user_slug'][ $original_slug ] = $user_id;
1623
+
1624
+ $this->logger->info( sprintf(
1625
+ __( 'Imported user "%s"', 'wordpress-importer' ),
1626
+ $userdata['user_login']
1627
+ ) );
1628
+ $this->logger->debug( sprintf(
1629
+ __( 'User %d remapped to %d', 'wordpress-importer' ),
1630
+ $original_id,
1631
+ $user_id
1632
+ ) );
1633
+
1634
+ // TODO: Implement meta handling once WXR includes it
1635
+ /**
1636
+ * User processing completed.
1637
+ *
1638
+ * @param int $user_id New user ID.
1639
+ * @param array $userdata Raw data imported for the user.
1640
+ */
1641
+ do_action( 'wxr_importer.processed.user', $user_id, $userdata );
1642
+ }
1643
+
1644
+
1645
+ /**
1646
+ * Parse term node.
1647
+ *
1648
+ * @param \DOMNode $node The term node from WXR file.
1649
+ * @param string $type The type of the term node.
1650
+ *
1651
+ * @return array|null
1652
+ */
1653
+ protected function parse_term_node( $node, $type = 'term' ) {
1654
+ $data = array();
1655
+ $meta = array();
1656
+
1657
+ $tag_name = array(
1658
+ 'id' => 'wp:term_id',
1659
+ 'taxonomy' => 'wp:term_taxonomy',
1660
+ 'slug' => 'wp:term_slug',
1661
+ 'parent' => 'wp:term_parent',
1662
+ 'name' => 'wp:term_name',
1663
+ 'description' => 'wp:term_description',
1664
+ );
1665
+ $taxonomy = null;
1666
+
1667
+ // Special casing!
1668
+ switch ( $type ) {
1669
+ case 'category':
1670
+ $tag_name['slug'] = 'wp:category_nicename';
1671
+ $tag_name['parent'] = 'wp:category_parent';
1672
+ $tag_name['name'] = 'wp:cat_name';
1673
+ $tag_name['description'] = 'wp:category_description';
1674
+ $tag_name['taxonomy'] = null;
1675
+
1676
+ $data['taxonomy'] = 'category';
1677
+ break;
1678
+
1679
+ case 'tag':
1680
+ $tag_name['slug'] = 'wp:tag_slug';
1681
+ $tag_name['parent'] = null;
1682
+ $tag_name['name'] = 'wp:tag_name';
1683
+ $tag_name['description'] = 'wp:tag_description';
1684
+ $tag_name['taxonomy'] = null;
1685
+
1686
+ $data['taxonomy'] = 'post_tag';
1687
+ break;
1688
+ }
1689
+
1690
+ foreach ( $node->childNodes as $child ) {
1691
+ // We only care about child elements
1692
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1693
+ continue;
1694
+ }
1695
+
1696
+ $key = array_search( $child->tagName, $tag_name );
1697
+ if ( $key ) {
1698
+ $data[ $key ] = $child->textContent;
1699
+ } else if ( $child->tagName == 'wp:termmeta' ) {
1700
+ $meta_item = $this->parse_meta_node( $child );
1701
+ if ( ! empty( $meta_item ) ) {
1702
+ $meta[] = $meta_item;
1703
+ }
1704
+ }
1705
+ }
1706
+
1707
+ if ( empty( $data['taxonomy'] ) ) {
1708
+ return null;
1709
+ }
1710
+
1711
+ // Compatibility with WXR 1.0
1712
+ if ( $data['taxonomy'] === 'tag' ) {
1713
+ $data['taxonomy'] = 'post_tag';
1714
+ }
1715
+
1716
+ return compact( 'data', 'meta' );
1717
+ }
1718
+
1719
+ /**
1720
+ * Process term.
1721
+ *
1722
+ * @param array $data The term data from WXR file.
1723
+ * @param array $meta The term meta data from WXR file.
1724
+ */
1725
+ protected function process_term( $data, $meta ) {
1726
+ /**
1727
+ * Pre-process term data.
1728
+ *
1729
+ * @param array $data Term data. (Return empty to skip.)
1730
+ * @param array $meta Meta data.
1731
+ */
1732
+ $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
1733
+ if ( empty( $data ) ) {
1734
+ return false;
1735
+ }
1736
+
1737
+ $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
1738
+
1739
+ /* FIX for OCDI!
1740
+ * As of WP 4.5, export.php returns the SLUG for the term's parent,
1741
+ * rather than an integer ID (this differs from a post_parent)
1742
+ * wp_insert_term and wp_update_term use the key: 'parent' and an integer value 'id'
1743
+ */
1744
+ $term_slug = isset( $data['slug'] ) ? $data['slug'] : '';
1745
+ $parent_slug = isset( $data['parent'] ) ? $data['parent'] : '';
1746
+
1747
+ $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
1748
+ $existing = $this->term_exists( $data );
1749
+ if ( $existing ) {
1750
+ $this->mapping['term'][ $mapping_key ] = $existing;
1751
+ $this->mapping['term_id'][ $original_id ] = $existing;
1752
+ $this->mapping['term_slug'][ $term_slug ] = $existing;
1753
+ return false;
1754
+ }
1755
+
1756
+ // WP really likes to repeat itself in export files
1757
+ if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
1758
+ return false;
1759
+ }
1760
+
1761
+ $termdata = array();
1762
+ $allowed = array(
1763
+ 'slug' => true,
1764
+ 'description' => true,
1765
+ 'parent' => true, // The parent_id may have already been set, so pass this back to the newly inserted term.
1766
+ );
1767
+
1768
+ // Map the parent comment, or mark it as one we need to fix
1769
+ $requires_remapping = false;
1770
+ if ( $parent_slug ) {
1771
+ if ( isset( $this->mapping['term_slug'][ $parent_slug ] ) ) {
1772
+ $data['parent'] = $this->mapping['term_slug'][ $parent_slug ];
1773
+ } else {
1774
+ // Prepare for remapping later
1775
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_slug );
1776
+ $requires_remapping = true;
1777
+
1778
+ // Wipe the parent id for now
1779
+ $data['parent'] = 0;
1780
+ }
1781
+ }
1782
+
1783
+ foreach ( $data as $key => $value ) {
1784
+ if ( ! isset( $allowed[ $key ] ) ) {
1785
+ continue;
1786
+ }
1787
+
1788
+ $termdata[ $key ] = $data[ $key ];
1789
+ }
1790
+
1791
+ $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
1792
+ if ( is_wp_error( $result ) ) {
1793
+ $this->logger->warning( sprintf(
1794
+ __( 'Failed to import %s %s', 'wordpress-importer' ),
1795
+ $data['taxonomy'],
1796
+ $data['name']
1797
+ ) );
1798
+ $this->logger->debug( $result->get_error_message() );
1799
+ do_action( 'wp_import_insert_term_failed', $result, $data );
1800
+
1801
+ /**
1802
+ * Term processing failed.
1803
+ *
1804
+ * @param WP_Error $result Error object.
1805
+ * @param array $data Raw data imported for the term.
1806
+ * @param array $meta Meta data supplied for the term.
1807
+ */
1808
+ do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
1809
+ return false;
1810
+ }
1811
+
1812
+ $term_id = $result['term_id'];
1813
+
1814
+ // Now prepare to map this new term.
1815
+ $this->mapping['term'][ $mapping_key ] = $term_id;
1816
+ $this->mapping['term_id'][ $original_id ] = $term_id;
1817
+ $this->mapping['term_slug'][ $term_slug ] = $term_id;
1818
+
1819
+ /*
1820
+ * Fix for OCDI!
1821
+ * The parent will be updated later in post_process_terms
1822
+ * we will need both the term_id AND the term_taxonomy to retrieve existing
1823
+ * term attributes. Those attributes will be returned with the corrected parent,
1824
+ * using wp_update_term.
1825
+ * Pass both the term_id along with the term_taxonomy as key=>value
1826
+ * in the requires_remapping['term'] array.
1827
+ */
1828
+ if ( $requires_remapping ) {
1829
+ $this->requires_remapping['term'][ $term_id ] = $data['taxonomy'];
1830
+ }
1831
+
1832
+ $this->logger->info( sprintf(
1833
+ __( 'Imported "%s" (%s)', 'wordpress-importer' ),
1834
+ $data['name'],
1835
+ $data['taxonomy']
1836
+ ) );
1837
+ $this->logger->debug( sprintf(
1838
+ __( 'Term %d remapped to %d', 'wordpress-importer' ),
1839
+ $original_id,
1840
+ $term_id
1841
+ ) );
1842
+
1843
+ // Actuall process of the term meta data.
1844
+ $this->process_term_meta( $meta, $term_id, $data );
1845
+
1846
+ do_action( 'wp_import_insert_term', $term_id, $data );
1847
+
1848
+ /**
1849
+ * Term processing completed.
1850
+ *
1851
+ * @param int $term_id New term ID.
1852
+ * @param array $data Raw data imported for the term.
1853
+ */
1854
+ do_action( 'wxr_importer.processed.term', $term_id, $data );
1855
+ }
1856
+
1857
+ /**
1858
+ * Process and import term meta items.
1859
+ *
1860
+ * @param array $meta List of meta data arrays.
1861
+ * @param int $term_id Term ID to associate with.
1862
+ * @param array $term Term data.
1863
+ *
1864
+ * @return int|bool Number of meta items imported on success, false otherwise.
1865
+ */
1866
+ protected function process_term_meta( $meta, $term_id, $term ) {
1867
+ if ( empty( $meta ) ) {
1868
+ return true;
1869
+ }
1870
+
1871
+ foreach ( $meta as $meta_item ) {
1872
+ /**
1873
+ * Pre-process term meta data.
1874
+ *
1875
+ * @param array $meta_item Meta data. (Return empty to skip.)
1876
+ * @param int $term_id Term the meta is attached to.
1877
+ */
1878
+ $meta_item = apply_filters( 'wxr_importer.pre_process.term_meta', $meta_item, $term_id );
1879
+
1880
+ if ( empty( $meta_item ) ) {
1881
+ continue;
1882
+ }
1883
+
1884
+ $key = apply_filters( 'import_term_meta_key', $meta_item['key'], $term_id, $term );
1885
+ $value = false;
1886
+
1887
+ if ( $key ) {
1888
+ // Export gets meta straight from the DB so could have a serialized string.
1889
+ if ( ! $value ) {
1890
+ $value = maybe_unserialize( $meta_item['value'] );
1891
+ }
1892
+
1893
+ $result = add_term_meta( $term_id, $key, $value );
1894
+
1895
+ if ( is_wp_error( $result ) ) {
1896
+ $this->logger->warning( sprintf(
1897
+ __( 'Failed to add metakey: %s, metavalue: %s to term_id: %d', 'wordpress-importer' ),
1898
+ $key,
1899
+ $value,
1900
+ $term_id
1901
+ ) );
1902
+ do_action( 'wxr_importer.process_failed.termmeta', $result, $meta_item, $term_id, $term );
1903
+ }
1904
+ else {
1905
+ $this->logger->debug( sprintf(
1906
+ __( 'Meta for term_id %d : %s => %s ; successfully added!', 'wordpress-importer' ),
1907
+ $term_id,
1908
+ $key,
1909
+ $value
1910
+ ) );
1911
+ }
1912
+
1913
+ do_action( 'import_term_meta', $term_id, $key, $value );
1914
+ }
1915
+ }
1916
+
1917
+ return true;
1918
+ }
1919
+
1920
+ /**
1921
+ * Attempt to download a remote file attachment.
1922
+ *
1923
+ * @param string $url URL of item to fetch.
1924
+ * @param array $post Attachment details.
1925
+ *
1926
+ * @return array|WP_Error Local file location details on success, WP_Error otherwise
1927
+ */
1928
+ protected function fetch_remote_file( $url, $post ) {
1929
+ // extract the file name and extension from the url
1930
+ $file_name = basename( $url );
1931
+
1932
+ // get placeholder file in the upload dir with a unique, sanitized filename
1933
+ $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
1934
+ if ( $upload['error'] ) {
1935
+ return new WP_Error( 'upload_dir_error', $upload['error'] );
1936
+ }
1937
+
1938
+ // fetch the remote url and write it to the placeholder file
1939
+ $response = wp_remote_get( $url, array(
1940
+ 'stream' => true,
1941
+ 'filename' => $upload['file'],
1942
+ ) );
1943
+
1944
+ // request failed
1945
+ if ( is_wp_error( $response ) ) {
1946
+ unlink( $upload['file'] );
1947
+ return $response;
1948
+ }
1949
+
1950
+ $code = (int) wp_remote_retrieve_response_code( $response );
1951
+
1952
+ // make sure the fetch was successful
1953
+ if ( $code !== 200 ) {
1954
+ unlink( $upload['file'] );
1955
+ return new WP_Error(
1956
+ 'import_file_error',
1957
+ sprintf(
1958
+ __( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ),
1959
+ $code,
1960
+ get_status_header_desc( $code ),
1961
+ $url
1962
+ )
1963
+ );
1964
+ }
1965
+
1966
+ $filesize = filesize( $upload['file'] );
1967
+ $headers = wp_remote_retrieve_headers( $response );
1968
+
1969
+ // OCDI fix!
1970
+ // Smaller images with server compression do not pass this rule.
1971
+ // More info here: https://github.com/proteusthemes/WordPress-Importer/pull/2
1972
+ //
1973
+ // if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1974
+ // unlink( $upload['file'] );
1975
+ // return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) );
1976
+ // }
1977
+
1978
+ if ( 0 === $filesize ) {
1979
+ unlink( $upload['file'] );
1980
+ return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) );
1981
+ }
1982
+
1983
+ $max_size = (int) $this->max_attachment_size();
1984
+ if ( ! empty( $max_size ) && $filesize > $max_size ) {
1985
+ unlink( $upload['file'] );
1986
+ $message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) );
1987
+ return new WP_Error( 'import_file_error', $message );
1988
+ }
1989
+
1990
+ return $upload;
1991
+ }
1992
+
1993
+ protected function post_process() {
1994
+ // Time to tackle any left-over bits
1995
+ if ( ! empty( $this->requires_remapping['post'] ) ) {
1996
+ $this->post_process_posts( $this->requires_remapping['post'] );
1997
+ }
1998
+ if ( ! empty( $this->requires_remapping['comment'] ) ) {
1999
+ $this->post_process_comments( $this->requires_remapping['comment'] );
2000
+ }
2001
+ if ( ! empty( $this->requires_remapping['term'] ) ) {
2002
+ $this->post_process_terms( $this->requires_remapping['term'] );
2003
+ }
2004
+ }
2005
+
2006
+ protected function post_process_posts( $todo ) {
2007
+ foreach ( $todo as $post_id => $_ ) {
2008
+ $this->logger->debug( sprintf(
2009
+ // Note: title intentionally not used to skip extra processing
2010
+ // for when debug logging is off
2011
+ __( 'Running post-processing for post %d', 'wordpress-importer' ),
2012
+ $post_id
2013
+ ) );
2014
+
2015
+ $data = array();
2016
+
2017
+ $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
2018
+ if ( ! empty( $parent_id ) ) {
2019
+ // Have we imported the parent now?
2020
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
2021
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
2022
+ } else {
2023
+ $this->logger->warning( sprintf(
2024
+ __( 'Could not find the post parent for "%s" (post #%d)', 'wordpress-importer' ),
2025
+ get_the_title( $post_id ),
2026
+ $post_id
2027
+ ) );
2028
+ $this->logger->debug( sprintf(
2029
+ __( 'Post %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
2030
+ $post_id,
2031
+ $parent_id
2032
+ ) );
2033
+ }
2034
+ }
2035
+
2036
+ $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
2037
+ if ( ! empty( $author_slug ) ) {
2038
+ // Have we imported the user now?
2039
+ if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
2040
+ $data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
2041
+ } else {
2042
+ $this->logger->warning( sprintf(
2043
+ __( 'Could not find the author for "%s" (post #%d)', 'wordpress-importer' ),
2044
+ get_the_title( $post_id ),
2045
+ $post_id
2046
+ ) );
2047
+ $this->logger->debug( sprintf(
2048
+ __( 'Post %d was imported with author "%s", but could not be found', 'wordpress-importer' ),
2049
+ $post_id,
2050
+ $author_slug
2051
+ ) );
2052
+ }
2053
+ }
2054
+
2055
+ $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
2056
+ if ( ! empty( $has_attachments ) ) {
2057
+ $post = get_post( $post_id );
2058
+ $content = $post->post_content;
2059
+
2060
+ // Replace all the URLs we've got
2061
+ $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
2062
+ if ( $new_content !== $content ) {
2063
+ $data['post_content'] = $new_content;
2064
+ }
2065
+ }
2066
+
2067
+ if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
2068
+ $this->post_process_menu_item( $post_id );
2069
+ }
2070
+
2071
+ // Do we have updates to make?
2072
+ if ( empty( $data ) ) {
2073
+ $this->logger->debug( sprintf(
2074
+ __( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ),
2075
+ $post_id
2076
+ ) );
2077
+ continue;
2078
+ }
2079
+
2080
+ // Run the update
2081
+ $data['ID'] = $post_id;
2082
+ $result = wp_update_post( $data, true );
2083
+ if ( is_wp_error( $result ) ) {
2084
+ $this->logger->warning( sprintf(
2085
+ __( 'Could not update "%s" (post #%d) with mapped data', 'wordpress-importer' ),
2086
+ get_the_title( $post_id ),
2087
+ $post_id
2088
+ ) );
2089
+ $this->logger->debug( $result->get_error_message() );
2090
+ continue;
2091
+ }
2092
+
2093
+ // Clear out our temporary meta keys
2094
+ delete_post_meta( $post_id, '_wxr_import_parent' );
2095
+ delete_post_meta( $post_id, '_wxr_import_user_slug' );
2096
+ delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
2097
+ }
2098
+ }
2099
+
2100
+ protected function post_process_menu_item( $post_id ) {
2101
+ $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
2102
+ if ( empty( $menu_object_id ) ) {
2103
+ // No processing needed!
2104
+ return;
2105
+ }
2106
+
2107
+ $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
2108
+ switch ( $menu_item_type ) {
2109
+ case 'taxonomy':
2110
+ if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
2111
+ $menu_object = $this->mapping['term_id'][ $menu_object_id ];
2112
+ }
2113
+ break;
2114
+
2115
+ case 'post_type':
2116
+ if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
2117
+ $menu_object = $this->mapping['post'][ $menu_object_id ];
2118
+ }
2119
+ break;
2120
+
2121
+ default:
2122
+ // Cannot handle this.
2123
+ return;
2124
+ }
2125
+
2126
+ if ( ! empty( $menu_object ) ) {
2127
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
2128
+ } else {
2129
+ $this->logger->warning( sprintf(
2130
+ __( 'Could not find the menu object for "%s" (post #%d)', 'wordpress-importer' ),
2131
+ get_the_title( $post_id ),
2132
+ $post_id
2133
+ ) );
2134
+ $this->logger->debug( sprintf(
2135
+ __( 'Post %d was imported with object "%d" of type "%s", but could not be found', 'wordpress-importer' ),
2136
+ $post_id,
2137
+ $menu_object_id,
2138
+ $menu_item_type
2139
+ ) );
2140
+ }
2141
+
2142
+ delete_post_meta( $post_id, '_wxr_import_menu_item' );
2143
+ }
2144
+
2145
+
2146
+ protected function post_process_comments( $todo ) {
2147
+ foreach ( $todo as $comment_id => $_ ) {
2148
+ $data = array();
2149
+
2150
+ $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
2151
+ if ( ! empty( $parent_id ) ) {
2152
+ // Have we imported the parent now?
2153
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
2154
+ $data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
2155
+ } else {
2156
+ $this->logger->warning( sprintf(
2157
+ __( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ),
2158
+ $comment_id
2159
+ ) );
2160
+ $this->logger->debug( sprintf(
2161
+ __( 'Comment %d was imported with parent %d, but could not be found', 'wordpress-importer' ),
2162
+ $comment_id,
2163
+ $parent_id
2164
+ ) );
2165
+ }
2166
+ }
2167
+
2168
+ $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
2169
+ if ( ! empty( $author_id ) ) {
2170
+ // Have we imported the user now?
2171
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
2172
+ $data['user_id'] = $this->mapping['user'][ $author_id ];
2173
+ } else {
2174
+ $this->logger->warning( sprintf(
2175
+ __( 'Could not find the author for comment #%d', 'wordpress-importer' ),
2176
+ $comment_id
2177
+ ) );
2178
+ $this->logger->debug( sprintf(
2179
+ __( 'Comment %d was imported with author %d, but could not be found', 'wordpress-importer' ),
2180
+ $comment_id,
2181
+ $author_id
2182
+ ) );
2183
+ }
2184
+ }
2185
+
2186
+ // Do we have updates to make?
2187
+ if ( empty( $data ) ) {
2188
+ continue;
2189
+ }
2190
+
2191
+ // Run the update
2192
+ $data['comment_ID'] = $comment_id;
2193
+ $result = wp_update_comment( wp_slash( $data ) );
2194
+ if ( empty( $result ) ) {
2195
+ $this->logger->warning( sprintf(
2196
+ __( 'Could not update comment #%d with mapped data', 'wordpress-importer' ),
2197
+ $comment_id
2198
+ ) );
2199
+ continue;
2200
+ }
2201
+
2202
+ // Clear out our temporary meta keys
2203
+ delete_comment_meta( $comment_id, '_wxr_import_parent' );
2204
+ delete_comment_meta( $comment_id, '_wxr_import_user' );
2205
+ }
2206
+ }
2207
+
2208
+
2209
+ /**
2210
+ * There is no explicit 'top' or 'root' for a hierarchy of WordPress terms
2211
+ * Terms without a parent, or parent=0 are either unconnected (orphans)
2212
+ * or top-level siblings without an explicit root parent
2213
+ * An unconnected term (orphan) should have a null parent_slug
2214
+ * Top-level siblings without an explicit root parent, shall be identified
2215
+ * with the parent_slug: top
2216
+ * [we'll map parent_slug: top into parent 0]
2217
+ *
2218
+ * @param array $terms_to_be_remapped The terms to be remapped.
2219
+ */
2220
+ protected function post_process_terms( $terms_to_be_remapped ) {
2221
+ $this->mapping['term_slug']['top'] = 0;
2222
+ // The term_id and term_taxonomy are passed-in with $this->requires_remapping['term'].
2223
+ foreach ( $terms_to_be_remapped as $termid => $term_taxonomy ) {
2224
+ // Basic check.
2225
+ if( empty( $termid ) || ! is_numeric( $termid ) ) {
2226
+ $this->logger->warning( sprintf(
2227
+ __( 'Faulty term_id provided in terms-to-be-remapped array %s', 'wordpress-importer' ),
2228
+ $termid
2229
+ ) );
2230
+ continue;
2231
+ }
2232
+ // This cast to integer may be unnecessary.
2233
+ $term_id = (int) $termid;
2234
+
2235
+ if( empty( $term_taxonomy ) ){
2236
+ $this->logger->warning( sprintf(
2237
+ __( 'No taxonomy provided in terms-to-be-remapped array for term #%d', 'wordpress-importer' ),
2238
+ $term_id
2239
+ ) );
2240
+ continue;
2241
+ }
2242
+
2243
+ $parent_slug = get_term_meta( $term_id, '_wxr_import_parent', true );
2244
+
2245
+ if ( empty( $parent_slug ) ) {
2246
+ $this->logger->warning( sprintf(
2247
+ __( 'No parent_slug identified in remapping-array for term: %d', 'wordpress-importer' ),
2248
+ $term_id
2249
+ ) );
2250
+ continue;
2251
+ }
2252
+
2253
+ if ( ! isset( $this->mapping['term_slug'][ $parent_slug ] ) || ! is_numeric( $this->mapping['term_slug'][ $parent_slug ] ) ) {
2254
+ $this->logger->warning( sprintf(
2255
+ __( 'The term(%d)"s parent_slug (%s) is not found in the remapping-array.', 'wordpress-importer' ),
2256
+ $term_id,
2257
+ $parent_slug
2258
+ ) );
2259
+ continue;
2260
+ }
2261
+
2262
+ $mapped_parent = (int) $this->mapping['term_slug'][ $parent_slug ];
2263
+
2264
+ $termattributes = get_term_by( 'id', $term_id, $term_taxonomy, ARRAY_A );
2265
+ // Note: the default OBJECT return results in a reserved-word clash with 'parent' [$termattributes->parent], so instead return an associative array.
2266
+
2267
+ if ( empty( $termattributes ) ) {
2268
+ $this->logger->warning( sprintf(
2269
+ __( 'No data returned by get_term_by for term_id #%d', 'wordpress-importer' ),
2270
+ $term_id
2271
+ ) );
2272
+ continue;
2273
+ }
2274
+ // Check if the correct parent id is already correctly mapped.
2275
+ if ( isset( $termattributes['parent'] ) && $termattributes['parent'] == $mapped_parent ) {
2276
+ // Clear out our temporary meta key.
2277
+ delete_term_meta( $term_id, '_wxr_import_parent' );
2278
+ continue;
2279
+ }
2280
+
2281
+ // Otherwise set the mapped parent and update the term.
2282
+ $termattributes['parent'] = $mapped_parent;
2283
+
2284
+ $result = wp_update_term( $term_id, $termattributes['taxonomy'], $termattributes );
2285
+
2286
+ if ( is_wp_error( $result ) ) {
2287
+ $this->logger->warning( sprintf(
2288
+ __( 'Could not update "%s" (term #%d) with mapped data', 'wordpress-importer' ),
2289
+ $termattributes['name'],
2290
+ $term_id
2291
+ ) );
2292
+ $this->logger->debug( $result->get_error_message() );
2293
+ continue;
2294
+ }
2295
+ // Clear out our temporary meta key.
2296
+ delete_term_meta( $term_id, '_wxr_import_parent' );
2297
+ $this->logger->debug( sprintf(
2298
+ __( 'Term %d was successfully updated with parent %d', 'wordpress-importer' ),
2299
+ $term_id,
2300
+ $mapped_parent
2301
+ ) );
2302
+ }
2303
+ }
2304
+
2305
+
2306
+ /**
2307
+ * Use stored mapping information to update old attachment URLs
2308
+ */
2309
+ protected function replace_attachment_urls_in_content() {
2310
+ global $wpdb;
2311
+ // make sure we do the longest urls first, in case one is a substring of another
2312
+ uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
2313
+
2314
+ foreach ( $this->url_remap as $from_url => $to_url ) {
2315
+ // remap urls in post_content
2316
+ $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
2317
+ $wpdb->query( $query );
2318
+
2319
+ // remap enclosure urls
2320
+ $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
2321
+ $wpdb->query( $query );
2322
+ }
2323
+ }
2324
+
2325
+ /**
2326
+ * Update _thumbnail_id meta to new, imported attachment IDs
2327
+ */
2328
+ function remap_featured_images() {
2329
+ if ( empty( $this->featured_images ) ) {
2330
+ return;
2331
+ }
2332
+
2333
+ $this->logger->info( esc_html__( 'Starting remapping of featured images', 'wordpress-importer' ) );
2334
+
2335
+ // Cycle through posts that have a featured image.
2336
+ foreach ( $this->featured_images as $post_id => $value ) {
2337
+ if ( isset( $this->mapping['post'][ $value ] ) ) {
2338
+ $new_id = $this->mapping['post'][ $value ];
2339
+
2340
+ // Only update if there's a difference.
2341
+ if ( $new_id !== $value ) {
2342
+ $this->logger->info( sprintf( esc_html__( 'Remapping featured image ID %d to new ID %d for post ID %d', 'wordpress-importer' ), $value, $new_id, $post_id ) );
2343
+
2344
+ update_post_meta( $post_id, '_thumbnail_id', $new_id );
2345
+ }
2346
+ }
2347
+ }
2348
+ }
2349
+
2350
+ /**
2351
+ * Decide if the given meta key maps to information we will want to import
2352
+ *
2353
+ * @param string $key The meta key to check
2354
+ * @return string|bool The key if we do want to import, false if not
2355
+ */
2356
+ public function is_valid_meta_key( $key ) {
2357
+ // skip attachment metadata since we'll regenerate it from scratch
2358
+ // skip _edit_lock as not relevant for import
2359
+ if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
2360
+ return false;
2361
+ }
2362
+
2363
+ return $key;
2364
+ }
2365
+
2366
+ /**
2367
+ * Decide what the maximum file size for downloaded attachments is.
2368
+ * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
2369
+ *
2370
+ * @return int Maximum attachment file size to import
2371
+ */
2372
+ protected function max_attachment_size() {
2373
+ return apply_filters( 'import_attachment_size_limit', 0 );
2374
+ }
2375
+
2376
+ /**
2377
+ * Added to http_request_timeout filter to force timeout at 60 seconds during import
2378
+ *
2379
+ * @param int $val Time in seconds.
2380
+ * @access protected
2381
+ * @return int 60
2382
+ */
2383
+ function bump_request_timeout($val) {
2384
+ return 60;
2385
+ }
2386
+
2387
+ // return the difference in length between two strings
2388
+ function cmpr_strlen( $a, $b ) {
2389
+ return strlen( $b ) - strlen( $a );
2390
+ }
2391
+
2392
+ /**
2393
+ * Prefill existing post data.
2394
+ *
2395
+ * This preloads all GUIDs into memory, allowing us to avoid hitting the
2396
+ * database when we need to check for existence. With larger imports, this
2397
+ * becomes prohibitively slow to perform SELECT queries on each.
2398
+ *
2399
+ * By preloading all this data into memory, it's a constant-time lookup in
2400
+ * PHP instead. However, this does use a lot more memory, so for sites doing
2401
+ * small imports onto a large site, it may be a better tradeoff to use
2402
+ * on-the-fly checking instead.
2403
+ */
2404
+ protected function prefill_existing_posts() {
2405
+ global $wpdb;
2406
+ $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
2407
+
2408
+ foreach ( $posts as $item ) {
2409
+ $this->exists['post'][ $item->guid ] = $item->ID;
2410
+ }
2411
+ }
2412
+
2413
+ /**
2414
+ * Does the post exist?
2415
+ *
2416
+ * @param array $data Post data to check against.
2417
+ * @return int|bool Existing post ID if it exists, false otherwise.
2418
+ */
2419
+ protected function post_exists( $data ) {
2420
+ // Constant-time lookup if we prefilled
2421
+ $exists_key = $data['guid'];
2422
+
2423
+ if ( $this->options['prefill_existing_posts'] ) {
2424
+ // OCDI: fix for custom post types. The guids in the prefilled section are escaped, so these ones should be as well.
2425
+ $exists_key = htmlentities( $exists_key );
2426
+ return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
2427
+ }
2428
+
2429
+ // No prefilling, but might have already handled it
2430
+ if ( isset( $this->exists['post'][ $exists_key ] ) ) {
2431
+ return $this->exists['post'][ $exists_key ];
2432
+ }
2433
+
2434
+ // Still nothing, try post_exists, and cache it
2435
+ $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
2436
+ $this->exists['post'][ $exists_key ] = $exists;
2437
+
2438
+ return $exists;
2439
+ }
2440
+
2441
+ /**
2442
+ * Mark the post as existing.
2443
+ *
2444
+ * @param array $data Post data to mark as existing.
2445
+ * @param int $post_id Post ID.
2446
+ */
2447
+ protected function mark_post_exists( $data, $post_id ) {
2448
+ $exists_key = $data['guid'];
2449
+ $this->exists['post'][ $exists_key ] = $post_id;
2450
+ }
2451
+
2452
+ /**
2453
+ * Prefill existing comment data.
2454
+ *
2455
+ * @see self::prefill_existing_posts() for justification of why this exists.
2456
+ */
2457
+ protected function prefill_existing_comments() {
2458
+ global $wpdb;
2459
+ $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
2460
+
2461
+ foreach ( $posts as $item ) {
2462
+ $exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
2463
+ $this->exists['comment'][ $exists_key ] = $item->comment_ID;
2464
+ }
2465
+ }
2466
+
2467
+ /**
2468
+ * Does the comment exist?
2469
+ *
2470
+ * @param array $data Comment data to check against.
2471
+ * @return int|bool Existing comment ID if it exists, false otherwise.
2472
+ */
2473
+ protected function comment_exists( $data ) {
2474
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2475
+
2476
+ // Constant-time lookup if we prefilled
2477
+ if ( $this->options['prefill_existing_comments'] ) {
2478
+ return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
2479
+ }
2480
+
2481
+ // No prefilling, but might have already handled it
2482
+ if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
2483
+ return $this->exists['comment'][ $exists_key ];
2484
+ }
2485
+
2486
+ // Still nothing, try comment_exists, and cache it
2487
+ $exists = comment_exists( $data['comment_author'], $data['comment_date'] );
2488
+ $this->exists['comment'][ $exists_key ] = $exists;
2489
+
2490
+ return $exists;
2491
+ }
2492
+
2493
+ /**
2494
+ * Mark the comment as existing.
2495
+ *
2496
+ * @param array $data Comment data to mark as existing.
2497
+ * @param int $comment_id Comment ID.
2498
+ */
2499
+ protected function mark_comment_exists( $data, $comment_id ) {
2500
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2501
+ $this->exists['comment'][ $exists_key ] = $comment_id;
2502
+ }
2503
+
2504
+ /**
2505
+ * Prefill existing term data.
2506
+ *
2507
+ * @see self::prefill_existing_posts() for justification of why this exists.
2508
+ */
2509
+ protected function prefill_existing_terms() {
2510
+ global $wpdb;
2511
+ $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
2512
+ $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
2513
+ $terms = $wpdb->get_results( $query );
2514
+
2515
+ foreach ( $terms as $item ) {
2516
+ $exists_key = sha1( $item->taxonomy . ':' . $item->slug );
2517
+ $this->exists['term'][ $exists_key ] = $item->term_id;
2518
+ }
2519
+ }
2520
+
2521
+ /**
2522
+ * Does the term exist?
2523
+ *
2524
+ * @param array $data Term data to check against.
2525
+ * @return int|bool Existing term ID if it exists, false otherwise.
2526
+ */
2527
+ protected function term_exists( $data ) {
2528
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2529
+
2530
+ // Constant-time lookup if we prefilled
2531
+ if ( $this->options['prefill_existing_terms'] ) {
2532
+ return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
2533
+ }
2534
+
2535
+ // No prefilling, but might have already handled it
2536
+ if ( isset( $this->exists['term'][ $exists_key ] ) ) {
2537
+ return $this->exists['term'][ $exists_key ];
2538
+ }
2539
+
2540
+ // Still nothing, try comment_exists, and cache it
2541
+ $exists = term_exists( $data['slug'], $data['taxonomy'] );
2542
+ if ( is_array( $exists ) ) {
2543
+ $exists = $exists['term_id'];
2544
+ }
2545
+
2546
+ $this->exists['term'][ $exists_key ] = $exists;
2547
+
2548
+ return $exists;
2549
+ }
2550
+
2551
+ /**
2552
+ * Mark the term as existing.
2553
+ *
2554
+ * @param array $data Term data to mark as existing.
2555
+ * @param int $term_id Term ID.
2556
+ */
2557
+ protected function mark_term_exists( $data, $term_id ) {
2558
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2559
+ $this->exists['term'][ $exists_key ] = $term_id;
2560
+ }
2561
+ }