@caweb/cli 1.11.0 → 1.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/commands/index.js +10 -5
- package/commands/sites/convert-site.js +992 -0
- package/commands/sites/create-site.js +102 -0
- package/commands/sites/prompts.js +614 -0
- package/commands/sites/validation.js +68 -0
- package/commands/sync/index.js +484 -234
- package/commands/webpack/webpack.js +2 -4
- package/lib/cli.js +121 -66
- package/lib/helpers.js +96 -6
- package/lib/index.js +7 -1
- package/lib/wordpress/api.js +4 -3
- package/package.json +16 -6
package/commands/sync/index.js
CHANGED
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
promptForSync,
|
|
29
29
|
promptForId
|
|
30
30
|
} from './prompts.js';
|
|
31
|
+
import { get } from 'http';
|
|
32
|
+
import { connect } from 'http2';
|
|
33
|
+
import { create } from 'domain';
|
|
31
34
|
|
|
32
35
|
const errors = [];
|
|
33
36
|
const configFile = path.join(appPath, 'caweb.json');
|
|
@@ -95,7 +98,7 @@ async function getInstanceInfo( config, instance = 'target'){
|
|
|
95
98
|
config = config ?? {};
|
|
96
99
|
|
|
97
100
|
// add the target to sync list
|
|
98
|
-
config
|
|
101
|
+
config['sync'][nickname] = target;
|
|
99
102
|
|
|
100
103
|
fs.writeFileSync(
|
|
101
104
|
configFile,
|
|
@@ -107,6 +110,53 @@ async function getInstanceInfo( config, instance = 'target'){
|
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
/**
|
|
114
|
+
*
|
|
115
|
+
* @param {*} navJson
|
|
116
|
+
* @param {*} destUrl
|
|
117
|
+
* @returns {Array}
|
|
118
|
+
*/
|
|
119
|
+
function getStaticNavItems(navJson, destUrl){
|
|
120
|
+
let navItems = [];
|
|
121
|
+
|
|
122
|
+
// iterate over the nav items
|
|
123
|
+
for( let item of navJson ){
|
|
124
|
+
let i = {
|
|
125
|
+
title: item.label,
|
|
126
|
+
url: item.url
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if( item.description ){
|
|
130
|
+
i.description = item.description;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// if the item url is not the same as the destination url
|
|
134
|
+
// and is not relative, then the item is an external link
|
|
135
|
+
// if( item.url !== destUrl && ! item.url.startsWith('/') ){
|
|
136
|
+
// navItems.push(i)
|
|
137
|
+
// }else
|
|
138
|
+
if( item.url.startsWith('/') ){
|
|
139
|
+
// we remove the forward slash and the file extension if applicable
|
|
140
|
+
i.slug = item.url.replace(/^\//, '').replace(/\.[^/.]+$/, '');
|
|
141
|
+
i.url = `${destUrl}/${i.slug}`;
|
|
142
|
+
i.type = 'post_type';
|
|
143
|
+
i.object = 'page';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// if there is a submenu
|
|
147
|
+
if( item.sub && item.sub.length ){
|
|
148
|
+
i.sub = getStaticNavItems(item.sub, destUrl);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
navItems.push(i)
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return navItems;
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
110
160
|
/**
|
|
111
161
|
* Sync Environments.
|
|
112
162
|
*
|
|
@@ -137,11 +187,10 @@ export default async function sync({
|
|
|
137
187
|
const {workDirectoryPath} = await loadConfig(path.resolve('.'));
|
|
138
188
|
|
|
139
189
|
// read caweb configuration file.
|
|
140
|
-
let serviceConfig = fs.existsSync(configFile) ? JSON.parse( fs.readFileSync(configFile) ) : {
|
|
190
|
+
let serviceConfig = fs.existsSync(configFile) ? JSON.parse( fs.readFileSync(configFile) ) : { sync: {} };
|
|
141
191
|
|
|
142
192
|
process.env.WP_CLI_CONFIG_PATH = path.join(workDirectoryPath, 'config.yml');
|
|
143
193
|
|
|
144
|
-
// get target and dest instance data
|
|
145
194
|
target = serviceConfig.sync[target];
|
|
146
195
|
dest = serviceConfig.sync[dest];
|
|
147
196
|
|
|
@@ -158,7 +207,6 @@ export default async function sync({
|
|
|
158
207
|
}
|
|
159
208
|
}
|
|
160
209
|
|
|
161
|
-
|
|
162
210
|
// if no dest was specified or no dest saved.
|
|
163
211
|
if( ! dest ){
|
|
164
212
|
spinner.stop()
|
|
@@ -179,7 +227,7 @@ export default async function sync({
|
|
|
179
227
|
! target || ! target.url || ! target.user || ! target.pwd ||
|
|
180
228
|
! dest || ! dest.url || ! dest.user || ! dest.pwd
|
|
181
229
|
){
|
|
182
|
-
spinner.fail(`caweb.json is not configured properly
|
|
230
|
+
spinner.fail(`caweb.json is not configured properly.`);
|
|
183
231
|
process.exit(1)
|
|
184
232
|
}
|
|
185
233
|
|
|
@@ -209,7 +257,7 @@ export default async function sync({
|
|
|
209
257
|
* 5) We only collect menus if the taxonomy is undefined or it's set to menus.
|
|
210
258
|
* - We also collect menu items if menus are collected.
|
|
211
259
|
*/
|
|
212
|
-
let settings = [];
|
|
260
|
+
let settings = [{}];
|
|
213
261
|
let mediaLibrary = [];
|
|
214
262
|
let media = [];
|
|
215
263
|
let pages = [];
|
|
@@ -242,274 +290,374 @@ export default async function sync({
|
|
|
242
290
|
|
|
243
291
|
}
|
|
244
292
|
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
debug
|
|
265
|
-
);
|
|
266
|
-
}
|
|
293
|
+
// if the request is going from static,
|
|
294
|
+
// we create a taxonomies object based off of the static data.
|
|
295
|
+
if( 'static' === target.url ){
|
|
296
|
+
let mediaPath = serviceConfig.media;
|
|
297
|
+
|
|
298
|
+
// iterate over pages and create a pages object.
|
|
299
|
+
for( let page of serviceConfig.pages ){
|
|
300
|
+
let content = page.shortcodeContent.replace('\"', '"');
|
|
301
|
+
|
|
302
|
+
pages.push({
|
|
303
|
+
title: page.title,
|
|
304
|
+
status: 'publish',
|
|
305
|
+
slug: page.slug,
|
|
306
|
+
content,
|
|
307
|
+
meta: {
|
|
308
|
+
_et_pb_use_builder: 'on', // this turns on the Divi Builder
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
}
|
|
267
312
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
'description',
|
|
276
|
-
'show_on_front',
|
|
277
|
-
'page_on_front',
|
|
278
|
-
'posts_per_page'
|
|
279
|
-
],
|
|
280
|
-
}, 'settings', debug )
|
|
281
|
-
|
|
282
|
-
}
|
|
313
|
+
// Header Nav
|
|
314
|
+
if( serviceConfig?.site?.header?.nav ){
|
|
315
|
+
menus.push({
|
|
316
|
+
name: 'Header Menu',
|
|
317
|
+
slug: 'header-menu',
|
|
318
|
+
locations: 'header-menu',
|
|
319
|
+
});
|
|
283
320
|
|
|
284
|
-
|
|
285
|
-
if( tax.includes('pages') ){
|
|
286
|
-
// get all pages/posts
|
|
287
|
-
spinner.text = `Collecting pages from ${target.url}`;
|
|
288
|
-
pages = await getTaxonomies({
|
|
289
|
-
...targetOptions,
|
|
290
|
-
orderby: 'parent',
|
|
291
|
-
embed: true,
|
|
292
|
-
include: pageIds && pageIds.length ? pageIds.join(',') : null
|
|
293
|
-
},
|
|
294
|
-
'pages',
|
|
295
|
-
debug
|
|
296
|
-
);
|
|
321
|
+
menuNavItems['header'] = getStaticNavItems(serviceConfig.site.header.nav, destOptions.url);
|
|
297
322
|
|
|
298
|
-
|
|
299
|
-
pages = await getParentItems(
|
|
300
|
-
pages,
|
|
301
|
-
targetOptions,
|
|
302
|
-
'pages',
|
|
303
|
-
debug
|
|
304
|
-
)
|
|
305
|
-
}
|
|
323
|
+
}
|
|
306
324
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
include: postIds && postIds.length ? postIds.join(',') : null
|
|
315
|
-
},
|
|
316
|
-
'posts',
|
|
317
|
-
debug
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
// posts can be nested so we have to collect any parent items.
|
|
321
|
-
posts = await getParentItems(
|
|
322
|
-
posts,
|
|
323
|
-
targetOptions,
|
|
324
|
-
'posts',
|
|
325
|
-
debug
|
|
326
|
-
)
|
|
327
|
-
}
|
|
325
|
+
// Footer Nav
|
|
326
|
+
if( serviceConfig?.site?.footer?.nav ){
|
|
327
|
+
menus.push({
|
|
328
|
+
name: 'Footer Menu',
|
|
329
|
+
slug: 'footer-menu',
|
|
330
|
+
locations: 'footer-menu',
|
|
331
|
+
});
|
|
328
332
|
|
|
329
|
-
|
|
330
|
-
* Media Library Handling
|
|
331
|
-
*
|
|
332
|
-
* We iterate thru the media library to identify which media should be synced.
|
|
333
|
-
* 1) attached media items by default are only possible on pages.
|
|
334
|
-
* 2) featured media by default are only possible on posts.
|
|
335
|
-
* 3) save any linked media in the content of pages and posts.
|
|
336
|
-
*/
|
|
337
|
-
for( let m of mediaLibrary ){
|
|
338
|
-
// remove any new line characters.
|
|
339
|
-
m.source_url = m.source_url.replace('\n','');
|
|
333
|
+
menuNavItems['footer'] = getStaticNavItems(serviceConfig.site.footer.nav, destOptions.url);
|
|
340
334
|
|
|
341
|
-
/**
|
|
342
|
-
* We collect the media if:
|
|
343
|
-
* - media is attached
|
|
344
|
-
* - id was explicitly requested
|
|
345
|
-
* - tax includes media and media ids is blank
|
|
346
|
-
*/
|
|
347
|
-
if(
|
|
348
|
-
m.post ||
|
|
349
|
-
( mediaIds && mediaIds.includes( m.id.toString() ) ) ||
|
|
350
|
-
( tax.includes('media') && undefined === mediaIds )
|
|
351
|
-
){
|
|
352
|
-
media.push( m );
|
|
353
|
-
|
|
354
|
-
// we don't have to check any further.
|
|
355
|
-
continue;
|
|
356
335
|
}
|
|
357
336
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
p.content.rendered.match( new RegExp(`src=”(${m.source_url}.*)”`, 'g') )){
|
|
366
|
-
media.push( m );
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
337
|
+
// if the media path exists
|
|
338
|
+
if( fs.existsSync(mediaPath) ){
|
|
339
|
+
let currentDate = new Date();
|
|
340
|
+
let uploadDir = path.join(
|
|
341
|
+
'wp-content', 'uploads',
|
|
342
|
+
currentDate.getFullYear().toString(), (currentDate.getMonth() + 1).toString().padStart(2, '0')
|
|
343
|
+
);
|
|
370
344
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
345
|
+
// we read all the files in the media directory and create a media object.
|
|
346
|
+
fs.readdirSync(mediaPath, {recursive: true}).forEach( (file) => {
|
|
347
|
+
|
|
348
|
+
// update page content file references
|
|
349
|
+
pages.forEach((page) => {
|
|
350
|
+
// if the page content includes the file, we replace it the WordPress site upload dir.
|
|
351
|
+
if( page.content && page.content.includes(file) ){
|
|
352
|
+
// unescape slashes added from path.join.
|
|
353
|
+
page.content = page.content.replace(
|
|
354
|
+
new RegExp(`"[\\S]*${file}`, 'g'),
|
|
355
|
+
`"${destOptions.url}/` + path.join(uploadDir, file).replace(/\\/g, '/')
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
// if the file is a file, we read it and create a media object.
|
|
362
|
+
let filePath = path.join(mediaPath, file);
|
|
363
|
+
|
|
364
|
+
if( fs.statSync(filePath).isFile() ){
|
|
365
|
+
// read the file and create a media object.
|
|
366
|
+
let mediaBlob = fs.readFileSync(filePath);
|
|
367
|
+
|
|
368
|
+
// and new media object
|
|
369
|
+
media.push({
|
|
370
|
+
source_url: filePath,
|
|
371
|
+
title: path.basename(filePath).replace(/\.[^/.]+$/, ""),
|
|
372
|
+
alt_text: path.basename(filePath).replace(/\.[^/.]+$/, ""),
|
|
373
|
+
media_details:{},
|
|
374
|
+
date: currentDate.toISOString(),
|
|
375
|
+
data: new Blob([mediaBlob])
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
})
|
|
380
379
|
}
|
|
381
380
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
url: m.source_url,
|
|
386
|
-
responseType: 'arraybuffer'
|
|
387
|
-
}
|
|
388
|
-
)
|
|
389
|
-
.then( (img) => { return new Blob([img.data]) })
|
|
390
|
-
.catch( (error) => {
|
|
391
|
-
errors.push(`${m.source_url} could not be downloaded.`)
|
|
392
|
-
return false;
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
if( mediaBlob ){
|
|
396
|
-
m.data = mediaBlob;
|
|
381
|
+
// Settings
|
|
382
|
+
settings[0] = {
|
|
383
|
+
title: serviceConfig?.site?.title,
|
|
397
384
|
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// this has to be done after we have the media data.
|
|
401
|
-
// Lets replace the url references in the content of the pages and posts.
|
|
402
|
-
for( let p of [].concat( pages, posts ) ){
|
|
403
|
-
p.content.rendered = p.content.rendered.replace( new RegExp(target.url, 'g'), dest.url );
|
|
404
|
-
}
|
|
405
385
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
386
|
+
}else{
|
|
387
|
+
|
|
388
|
+
// Media Library.
|
|
389
|
+
if( tax.includes('media', 'pages', 'posts') ){
|
|
390
|
+
spinner.text = `Collecting Media Library ${target.url}`;
|
|
391
|
+
mediaLibrary = await getTaxonomies({
|
|
392
|
+
...targetOptions,
|
|
393
|
+
fields: [
|
|
394
|
+
'id',
|
|
395
|
+
'source_url',
|
|
396
|
+
'title',
|
|
397
|
+
'caption',
|
|
398
|
+
'alt_text',
|
|
399
|
+
'date',
|
|
400
|
+
'mime_type',
|
|
401
|
+
'post',
|
|
402
|
+
'media_details'
|
|
403
|
+
],
|
|
404
|
+
include: mediaIds && mediaIds.length ? mediaIds.join(',') : null
|
|
405
|
+
},
|
|
406
|
+
'media',
|
|
407
|
+
debug
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Site Settings.
|
|
412
|
+
if( tax.includes('settings') ){
|
|
413
|
+
spinner.text = `Collecting Site Settings from ${target.url}`;
|
|
414
|
+
settings = await getTaxonomies({
|
|
425
415
|
...targetOptions,
|
|
426
416
|
fields: [
|
|
427
|
-
'id',
|
|
428
417
|
'title',
|
|
429
|
-
'url',
|
|
430
|
-
'status',
|
|
431
|
-
'attr_title',
|
|
432
418
|
'description',
|
|
433
|
-
'
|
|
434
|
-
'
|
|
435
|
-
'
|
|
436
|
-
'object_id',
|
|
437
|
-
'parent',
|
|
438
|
-
'menu_order',
|
|
439
|
-
'target',
|
|
440
|
-
'classes',
|
|
441
|
-
'xfn',
|
|
442
|
-
'meta',
|
|
443
|
-
'menus'
|
|
419
|
+
'show_on_front',
|
|
420
|
+
'page_on_front',
|
|
421
|
+
'posts_per_page'
|
|
444
422
|
],
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// if the item is a post and it wasn't previously collected
|
|
459
|
-
}else if( 'post' === item.object && ! posts.map(p => p.id ).includes(item.object_id) ){
|
|
460
|
-
missingPosts.push(item.object_id)
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
// if navigation pages weren't previously collected they must be collected.
|
|
465
|
-
if( missingPages.length ){
|
|
466
|
-
missingPages = await getTaxonomies({
|
|
467
|
-
...targetOptions,
|
|
468
|
-
orderby: 'parent',
|
|
469
|
-
embed: true,
|
|
470
|
-
include: missingPages.join(',')
|
|
423
|
+
}, 'settings', debug )
|
|
424
|
+
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Pages.
|
|
428
|
+
if( tax.includes('pages') ){
|
|
429
|
+
// get all pages/posts
|
|
430
|
+
spinner.text = `Collecting pages from ${target.url}`;
|
|
431
|
+
pages = await getTaxonomies({
|
|
432
|
+
...targetOptions,
|
|
433
|
+
orderby: 'parent',
|
|
434
|
+
embed: true,
|
|
435
|
+
include: pageIds && pageIds.length ? pageIds.join(',') : null
|
|
471
436
|
},
|
|
472
437
|
'pages',
|
|
473
438
|
debug
|
|
474
439
|
);
|
|
475
440
|
|
|
476
441
|
// pages can be nested so we have to collect any parent items.
|
|
477
|
-
|
|
478
|
-
|
|
442
|
+
pages = await getParentItems(
|
|
443
|
+
pages,
|
|
479
444
|
targetOptions,
|
|
480
445
|
'pages',
|
|
481
446
|
debug
|
|
482
|
-
)
|
|
447
|
+
)
|
|
483
448
|
|
|
484
|
-
// add the missing pages to the pages array
|
|
485
|
-
pages = pages.concat(missingPages)
|
|
486
449
|
}
|
|
487
450
|
|
|
488
|
-
//
|
|
489
|
-
if(
|
|
451
|
+
// Posts.
|
|
452
|
+
if( tax.includes('posts') ){
|
|
490
453
|
// get all pages/posts
|
|
491
454
|
spinner.text = `Collecting all posts from ${target.url}`;
|
|
492
|
-
|
|
455
|
+
posts = await getTaxonomies({
|
|
493
456
|
...targetOptions,
|
|
494
457
|
orderby: 'parent',
|
|
495
|
-
include:
|
|
458
|
+
include: postIds && postIds.length ? postIds.join(',') : null
|
|
496
459
|
},
|
|
497
|
-
'posts',
|
|
460
|
+
'posts',
|
|
498
461
|
debug
|
|
499
462
|
);
|
|
500
463
|
|
|
501
464
|
// posts can be nested so we have to collect any parent items.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
465
|
+
posts = await getParentItems(
|
|
466
|
+
posts,
|
|
467
|
+
targetOptions,
|
|
468
|
+
'posts',
|
|
469
|
+
debug
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Media Library Handling
|
|
475
|
+
*
|
|
476
|
+
* We iterate thru the media library to identify which media should be synced.
|
|
477
|
+
* 1) attached media items by default are only possible on pages.
|
|
478
|
+
* 2) featured media by default are only possible on posts.
|
|
479
|
+
* 3) save any linked media in the content of pages and posts.
|
|
480
|
+
*/
|
|
481
|
+
for( let m of mediaLibrary ){
|
|
482
|
+
// remove any new line characters.
|
|
483
|
+
m.source_url = m.source_url.replace('\n','');
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* We collect the media if:
|
|
487
|
+
* - media is attached
|
|
488
|
+
* - id was explicitly requested
|
|
489
|
+
* - tax includes media and media ids is blank
|
|
490
|
+
*/
|
|
491
|
+
if(
|
|
492
|
+
m.post ||
|
|
493
|
+
( mediaIds && mediaIds.includes( m.id.toString() ) ) ||
|
|
494
|
+
( tax.includes('media') && undefined === mediaIds )
|
|
495
|
+
){
|
|
496
|
+
media.push( m );
|
|
497
|
+
|
|
498
|
+
// we don't have to check any further.
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
for( let p of [].concat( pages, posts ) ){
|
|
503
|
+
/**
|
|
504
|
+
* We collect the media if:
|
|
505
|
+
* - media is featured image
|
|
506
|
+
* - media is the src attribute in the content
|
|
507
|
+
*/
|
|
508
|
+
if( p.featured_media === m.id ||
|
|
509
|
+
p.content.rendered.match( new RegExp(`src=”(${m.source_url}.*)”`, 'g') )){
|
|
510
|
+
media.push( m );
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// filter any duplicate media.
|
|
516
|
+
media = media.filter((m, index, self) => { return index === self.findIndex((t) => { return t.id === m.id; })} );
|
|
517
|
+
let i = 0;
|
|
518
|
+
|
|
519
|
+
// before we can upload media files we have to generate the media blob data.
|
|
520
|
+
for( let m of media ){
|
|
521
|
+
if( debug ){
|
|
522
|
+
i++;
|
|
523
|
+
spinner.info(`Media ID ${m.id} Collected: ${i}/${media.length}`)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const mediaBlob = await axios.request(
|
|
527
|
+
{
|
|
528
|
+
...targetOptions,
|
|
529
|
+
url: m.source_url,
|
|
530
|
+
responseType: 'arraybuffer'
|
|
531
|
+
}
|
|
507
532
|
)
|
|
533
|
+
.then( (img) => { return new Blob([img.data]) })
|
|
534
|
+
.catch( (error) => {
|
|
535
|
+
errors.push(`${m.source_url} could not be downloaded.`)
|
|
536
|
+
return false;
|
|
537
|
+
});
|
|
508
538
|
|
|
509
|
-
|
|
510
|
-
|
|
539
|
+
if( mediaBlob ){
|
|
540
|
+
m.data = mediaBlob;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// this has to be done after we have the media data.
|
|
545
|
+
// Lets replace the url references in the content of the pages and posts.
|
|
546
|
+
for( let p of [].concat( pages, posts ) ){
|
|
547
|
+
if( p.content.rendered ){
|
|
548
|
+
p.content.rendered = p.content.rendered.replace( new RegExp(target.url, 'g'), dest.url );
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Menu and Nav Items.
|
|
553
|
+
if( tax.includes('menus')){
|
|
554
|
+
spinner.text = `Collecting assigned navigation menus from ${target.url}`;
|
|
555
|
+
// get all menus and navigation links
|
|
556
|
+
menus = await getTaxonomies({
|
|
557
|
+
...targetOptions,
|
|
558
|
+
fields: [
|
|
559
|
+
'id',
|
|
560
|
+
'description',
|
|
561
|
+
'name',
|
|
562
|
+
'slug',
|
|
563
|
+
'meta',
|
|
564
|
+
'locations'
|
|
565
|
+
],
|
|
566
|
+
include: menuIds && menuIds.length ? menuIds.join(',') : null
|
|
567
|
+
}, 'menus', debug);
|
|
568
|
+
|
|
569
|
+
menuNavItems = await getTaxonomies(
|
|
570
|
+
{
|
|
571
|
+
...targetOptions,
|
|
572
|
+
fields: [
|
|
573
|
+
'id',
|
|
574
|
+
'title',
|
|
575
|
+
'url',
|
|
576
|
+
'status',
|
|
577
|
+
'attr_title',
|
|
578
|
+
'description',
|
|
579
|
+
'type',
|
|
580
|
+
'type_label',
|
|
581
|
+
'object',
|
|
582
|
+
'object_id',
|
|
583
|
+
'parent',
|
|
584
|
+
'menu_order',
|
|
585
|
+
'target',
|
|
586
|
+
'classes',
|
|
587
|
+
'xfn',
|
|
588
|
+
'meta',
|
|
589
|
+
'menus'
|
|
590
|
+
],
|
|
591
|
+
menus: menus.map((menu) => { return menu.id; })
|
|
592
|
+
},
|
|
593
|
+
'menu-items',
|
|
594
|
+
debug
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
let missingPages = [];
|
|
598
|
+
let missingPosts = [];
|
|
599
|
+
// we iterate over menu items
|
|
600
|
+
menuNavItems.forEach(item => {
|
|
601
|
+
// if the item is a page and it wasn't previously collected
|
|
602
|
+
if( 'page' === item.object && ! pages.map(p => p.id ).includes(item.object_id) ){
|
|
603
|
+
missingPages.push(item.object_id)
|
|
604
|
+
// if the item is a post and it wasn't previously collected
|
|
605
|
+
}else if( 'post' === item.object && ! posts.map(p => p.id ).includes(item.object_id) ){
|
|
606
|
+
missingPosts.push(item.object_id)
|
|
607
|
+
}
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
// if navigation pages weren't previously collected they must be collected.
|
|
611
|
+
if( missingPages.length ){
|
|
612
|
+
missingPages = await getTaxonomies({
|
|
613
|
+
...targetOptions,
|
|
614
|
+
orderby: 'parent',
|
|
615
|
+
embed: true,
|
|
616
|
+
include: missingPages.join(',')
|
|
617
|
+
},
|
|
618
|
+
'pages',
|
|
619
|
+
debug
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
// pages can be nested so we have to collect any parent items.
|
|
623
|
+
missingPages = await getParentItems(
|
|
624
|
+
missingPages,
|
|
625
|
+
targetOptions,
|
|
626
|
+
'pages',
|
|
627
|
+
debug
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// add the missing pages to the pages array
|
|
631
|
+
pages = pages.concat(missingPages)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// if navigation posts weren't previously collected they must be collected.
|
|
635
|
+
if( missingPosts.length ){
|
|
636
|
+
// get all pages/posts
|
|
637
|
+
spinner.text = `Collecting all posts from ${target.url}`;
|
|
638
|
+
missingPosts = await getTaxonomies({
|
|
639
|
+
...targetOptions,
|
|
640
|
+
orderby: 'parent',
|
|
641
|
+
include: missingPosts.join(',')
|
|
642
|
+
},
|
|
643
|
+
'posts',
|
|
644
|
+
debug
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
// posts can be nested so we have to collect any parent items.
|
|
648
|
+
missingPosts = await getParentItems(
|
|
649
|
+
missingPosts,
|
|
650
|
+
targetOptions,
|
|
651
|
+
'posts',
|
|
652
|
+
debug
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
// add the missing posts to the posts array
|
|
656
|
+
posts = posts.concat(missingPages);
|
|
657
|
+
}
|
|
511
658
|
}
|
|
512
659
|
}
|
|
660
|
+
|
|
513
661
|
|
|
514
662
|
/**
|
|
515
663
|
* Now we have all the data we can begin to create the taxonomies on the target.
|
|
@@ -534,9 +682,10 @@ export default async function sync({
|
|
|
534
682
|
}
|
|
535
683
|
|
|
536
684
|
// Pages.
|
|
685
|
+
let createdPages = [];
|
|
537
686
|
if( pages ){
|
|
538
687
|
spinner.text = `Creating all pages to ${dest.url}`;
|
|
539
|
-
await createTaxonomies( pages, destOptions, 'pages', spinner );
|
|
688
|
+
createdPages = await createTaxonomies( pages, destOptions, 'pages', spinner );
|
|
540
689
|
}
|
|
541
690
|
|
|
542
691
|
// Posts.
|
|
@@ -548,14 +697,115 @@ export default async function sync({
|
|
|
548
697
|
// Menus and Navigation Links.
|
|
549
698
|
if( menus ){
|
|
550
699
|
spinner.text = `Reconstructing navigation menus to ${dest.url}`;
|
|
551
|
-
await createTaxonomies(menus, destOptions, 'menus', spinner);
|
|
552
|
-
|
|
700
|
+
let createdMenus = await createTaxonomies(menus, destOptions, 'menus', spinner);
|
|
701
|
+
|
|
702
|
+
// this variable is only used if the request is going from static
|
|
703
|
+
// we initialize it here outside of the if statement
|
|
704
|
+
// since we need to assign the ids of the created menu items to the sub menu items.
|
|
705
|
+
let subMenus = [];
|
|
706
|
+
|
|
707
|
+
// only if the request is going from static
|
|
708
|
+
if( 'static' === target.url ){
|
|
709
|
+
let headerMenu = createdMenus.find((menu) => { return menu.slug === 'header-menu'; }) || {};
|
|
710
|
+
let footerMenu = createdMenus.find((menu) => { return menu.slug === 'footer-menu'; }) || {};
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Modify menu items.
|
|
714
|
+
*
|
|
715
|
+
* We iterate over the menu items and delete certain properties.
|
|
716
|
+
* We also assign the menu id to the menu items.
|
|
717
|
+
* We also assign the page id to the menu items if they have a slug.
|
|
718
|
+
*
|
|
719
|
+
*/
|
|
720
|
+
Object.entries(menuNavItems).forEach(([key, value]) => {
|
|
721
|
+
let menuId = 'header' === key ? headerMenu?.id : footerMenu?.id;
|
|
722
|
+
|
|
723
|
+
// if the required menu is not created, we delete the menu item
|
|
724
|
+
if( ! menuId ){
|
|
725
|
+
delete menuNavItems[key];
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
value.forEach((v, k) => {
|
|
730
|
+
// assign the newly created menu ids to the menu items
|
|
731
|
+
v.menus = menuId;
|
|
732
|
+
|
|
733
|
+
// if the item has a slug we update the url and remove the slug property
|
|
734
|
+
if( v.slug ){
|
|
735
|
+
// find the page id
|
|
736
|
+
let pageId = createdPages.find((p) => { return p.slug === v.slug; })?.id;
|
|
737
|
+
|
|
738
|
+
v.object_id = pageId;
|
|
739
|
+
|
|
740
|
+
delete v.slug;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// if the item has sub items we have to assign the sub items to the parent item
|
|
744
|
+
if( v.sub ){
|
|
745
|
+
v.sub.forEach((subItem, subIndex) => {
|
|
746
|
+
subItem.menus = menuId;
|
|
747
|
+
|
|
748
|
+
// if the item has a slug we update the url and remove the slug property
|
|
749
|
+
if( subItem.slug ){
|
|
750
|
+
// find the page id
|
|
751
|
+
let subPageId = createdPages.find((p) => { return p.slug === subItem.slug; })?.id;
|
|
752
|
+
|
|
753
|
+
subItem.object_id = subPageId;
|
|
754
|
+
|
|
755
|
+
delete subItem.slug;
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
subMenus[k] = v.sub;
|
|
760
|
+
|
|
761
|
+
delete v.sub;
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
// this ensures that the array contains only the menu items
|
|
766
|
+
// we add the value back to the menuNavItems
|
|
767
|
+
// delete the key from the menuNavItems
|
|
768
|
+
menuNavItems = [
|
|
769
|
+
...menuNavItems,
|
|
770
|
+
...value
|
|
771
|
+
]
|
|
772
|
+
|
|
773
|
+
delete menuNavItems[key];
|
|
774
|
+
})
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
let createdMenuItems = await createTaxonomies(menuNavItems, destOptions, 'menu-items', spinner);
|
|
778
|
+
|
|
779
|
+
// if we have sub menus, we have to create them as well.
|
|
780
|
+
if( subMenus && subMenus.length ){
|
|
781
|
+
// we iterate over the sub menus and assign the parent id to the sub menu items
|
|
782
|
+
subMenus.forEach((sub, index) => {
|
|
783
|
+
sub.forEach((s) => {
|
|
784
|
+
s.parent = createdMenuItems[index].id;
|
|
785
|
+
s.menu_order = index;
|
|
786
|
+
})
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
// now we can create the sub menus
|
|
790
|
+
await createTaxonomies(subMenus.filter( i => i).flat(), destOptions, 'menu-items', spinner);
|
|
791
|
+
}
|
|
553
792
|
}
|
|
554
793
|
|
|
555
794
|
|
|
556
795
|
// Settings.
|
|
557
796
|
if( settings ){
|
|
558
797
|
spinner.text = `Updating site settings to ${dest.url}`;
|
|
798
|
+
|
|
799
|
+
// if going from static, we have to set the homepage and posts page.
|
|
800
|
+
if( 'static' === target.url ){
|
|
801
|
+
let homepageID = createdPages.find((p) => { return 'index' === p.slug; })?.id || 0;
|
|
802
|
+
|
|
803
|
+
if( homepageID ){
|
|
804
|
+
settings[0]['page_on_front'] = homepageID;
|
|
805
|
+
settings[0]['show_on_front'] = 'page';
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
559
809
|
await createTaxonomies(settings, destOptions, 'settings', spinner);
|
|
560
810
|
}
|
|
561
811
|
|