Skip to content

Commit 31e3d71

Browse files
Allow orphan terms to be exported with --allow_orphan_terms flag (#109)
--------- Co-authored-by: Daniel Bachhuber <[email protected]>
1 parent c0dec81 commit 31e3d71

File tree

4 files changed

+164
-33
lines changed

4 files changed

+164
-33
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Quick links: [Using](#using) | [Installing](#installing) | [Contributing](#contr
1010
## Using
1111

1212
~~~
13-
wp export [--dir=<dirname>] [--stdout] [--skip_comments] [--max_file_size=<MB>] [--filename_format=<format>] [--include_once=<before_posts>] [--start_date=<date>] [--end_date=<date>] [--post_type=<post-type>] [--post_type__not_in=<post-type>] [--post__in=<pid>] [--with_attachments] [--start_id=<pid>] [--max_num_posts=<num>] [--author=<author>] [--category=<name|id>] [--post_status=<status>]
13+
wp export [--dir=<dirname>] [--stdout] [--skip_comments] [--max_file_size=<MB>] [--filename_format=<format>] [--include_once=<before_posts>] [--allow_orphan_terms] [--start_date=<date>] [--end_date=<date>] [--post_type=<post-type>] [--post_type__not_in=<post-type>] [--post__in=<pid>] [--with_attachments] [--start_id=<pid>] [--max_num_posts=<num>] [--author=<author>] [--category=<name|id>] [--post_status=<status>]
1414
~~~
1515

1616
Generates one or more WXR files containing authors, terms, posts,
@@ -43,6 +43,9 @@ comments, and attachments. WXR files do not include site configuration
4343
are categories, tags, nav_menu_items, custom_taxonomies_terms. Separate multiple
4444
sections with a comma. Defaults to none.
4545

46+
[--allow_orphan_terms]
47+
Export orphaned terms with `parent=0`, instead of throwing an exception.
48+
4649
**FILTERS**
4750

4851
[--start_date=<date>]

features/export.feature

+88
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,10 @@ Feature: Export content.
996996
Then STDOUT should be a number
997997
And save STDOUT as {EXPORT_CATEGORY_ID}
998998

999+
When I run `wp term create category National --parent={EXPORT_CATEGORY_ID} --porcelain`
1000+
Then STDOUT should be a number
1001+
And save STDOUT as {EXPORT_SUBCATEGORY_ID}
1002+
9991003
When I run `wp term create post_tag Tech --description="Technology-related" --porcelain`
10001004
Then STDOUT should be a number
10011005
And save STDOUT as {EXPORT_TAG_ID}
@@ -1028,6 +1032,10 @@ Feature: Export content.
10281032
"""
10291033
<wp:term_id>{EXPORT_CATEGORY_ID}</wp:term_id>
10301034
"""
1035+
And the {EXPORT_FILE} file should contain:
1036+
"""
1037+
<wp:category_parent>news</wp:category_parent>
1038+
"""
10311039
And the {EXPORT_FILE} file should contain:
10321040
"""
10331041
<wp:cat_name><![CDATA[News]]></wp:cat_name>
@@ -1104,6 +1112,20 @@ Feature: Export content.
11041112
"""
11051113
News
11061114
"""
1115+
And STDOUT should contain:
1116+
"""
1117+
National
1118+
"""
1119+
1120+
When I run `wp term get category news --by=slug --field=id`
1121+
Then STDOUT should be a number
1122+
And save STDOUT as {IMPORT_CATEGORY_ID}
1123+
1124+
When I run `wp term get category national --by=slug --field=parent`
1125+
Then STDOUT should be:
1126+
"""
1127+
{IMPORT_CATEGORY_ID}
1128+
"""
11071129

11081130
When I run `wp term list post_tag`
11091131
Then STDOUT should contain:
@@ -1148,3 +1170,69 @@ Feature: Export content.
11481170
"""
11491171
Test User
11501172
"""
1173+
1174+
@require-wp-5.2
1175+
Scenario: Allow export to proceed when orphaned terms are found
1176+
Given a WP install
1177+
And I run `wp term create category orphan --parent=1`
1178+
And I run `wp term create category parent`
1179+
And I run `wp term create category child --parent=3`
1180+
And I run `wp term create post_tag atag`
1181+
And I run `wp term create post_tag btag`
1182+
And I run `wp term create post_tag ctag`
1183+
And I run `wp db query "DELETE FROM wp_terms WHERE term_id = 1"`
1184+
1185+
When I run `wp export --allow_orphan_terms`
1186+
Then save STDOUT 'Writing to file %s' as {EXPORT_FILE}
1187+
And the {EXPORT_FILE} file should contain:
1188+
"""
1189+
<wp:category_nicename>orphan</wp:category_nicename>
1190+
"""
1191+
And the {EXPORT_FILE} file should contain:
1192+
"""
1193+
<wp:tag_slug>atag</wp:tag_slug>
1194+
"""
1195+
1196+
When I run `wp site empty --yes`
1197+
And I run `wp plugin install wordpress-importer --activate`
1198+
And I run `wp import {EXPORT_FILE} --authors=skip`
1199+
Then STDOUT should contain:
1200+
"""
1201+
Success:
1202+
"""
1203+
1204+
When I run `wp term get post_tag atag --by=slug --field=id`
1205+
Then STDOUT should be a number
1206+
1207+
When I run `wp term get post_tag btag --by=slug --field=id`
1208+
Then STDOUT should be a number
1209+
1210+
When I run `wp term get post_tag ctag --by=slug --field=id`
1211+
Then STDOUT should be a number
1212+
1213+
When I run `wp term get category parent --by=slug --field=id`
1214+
Then STDOUT should be a number
1215+
And save STDOUT as {EXPORT_CATEGORY_PARENT_ID}
1216+
1217+
When I run `wp term get category child --by=slug --field=parent`
1218+
Then STDOUT should be:
1219+
"""
1220+
{EXPORT_CATEGORY_PARENT_ID}
1221+
"""
1222+
1223+
When I run `wp term get category orphan --by=slug --field=parent`
1224+
Then STDOUT should be:
1225+
"""
1226+
0
1227+
"""
1228+
1229+
Scenario: Throw exception when orphaned terms are found
1230+
Given a WP install
1231+
And I run `wp term create category orphan --parent=1`
1232+
And I run `wp db query "DELETE FROM wp_terms WHERE term_id = 1"`
1233+
1234+
When I try `wp export`
1235+
Then STDERR should contain:
1236+
"""
1237+
Error: Term is missing a parent
1238+
"""

src/Export_Command.php

+34-17
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class Export_Command extends WP_CLI_Command {
6565
* are categories, tags, nav_menu_items, custom_taxonomies_terms. Separate multiple
6666
* sections with a comma. Defaults to none.
6767
*
68+
* [--allow_orphan_terms]
69+
* : Export orphaned terms with `parent=0`, instead of throwing an exception.
70+
*
6871
* ## FILTERS
6972
*
7073
* [--start_date=<date>]
@@ -128,23 +131,24 @@ class Export_Command extends WP_CLI_Command {
128131
*/
129132
public function __invoke( $_, $assoc_args ) {
130133
$defaults = [
131-
'dir' => null,
132-
'stdout' => false,
133-
'start_date' => null,
134-
'end_date' => null,
135-
'post_type' => null,
136-
'post_type__not_in' => null,
137-
'max_num_posts' => null,
138-
'author' => null,
139-
'category' => null,
140-
'post_status' => null,
141-
'post__in' => null,
142-
'with_attachments' => true, // or FALSE if user requested some post__in
143-
'start_id' => null,
144-
'skip_comments' => null,
145-
'max_file_size' => 15,
146-
'filename_format' => '{site}.wordpress.{date}.{n}.xml',
147-
'include_once' => null,
134+
'dir' => null,
135+
'stdout' => false,
136+
'start_date' => null,
137+
'end_date' => null,
138+
'post_type' => null,
139+
'post_type__not_in' => null,
140+
'max_num_posts' => null,
141+
'author' => null,
142+
'category' => null,
143+
'post_status' => null,
144+
'post__in' => null,
145+
'with_attachments' => true, // or FALSE if user requested some post__in
146+
'start_id' => null,
147+
'skip_comments' => null,
148+
'max_file_size' => 15,
149+
'filename_format' => '{site}.wordpress.{date}.{n}.xml',
150+
'include_once' => null,
151+
'allow_orphan_terms' => null,
148152
];
149153

150154
if ( ! empty( $assoc_args['stdout'] ) && ( ! empty( $assoc_args['dir'] ) || ! empty( $assoc_args['filename_format'] ) ) ) {
@@ -492,4 +496,17 @@ private function check_include_once( $once ) {
492496

493497
return true;
494498
}
499+
500+
private function check_allow_orphan_terms( $allow_orphan_terms ) {
501+
if ( null === $allow_orphan_terms ) {
502+
return true;
503+
}
504+
505+
if ( 0 !== (int) $allow_orphan_terms && 1 !== (int) $allow_orphan_terms ) {
506+
WP_CLI::warning( 'allow_orphan_terms needs to be 0 (no) or 1 (yes).' );
507+
return false;
508+
}
509+
$this->export_args['allow_orphan_terms'] = $allow_orphan_terms;
510+
return true;
511+
}
495512
}

src/WP_Export_Query.php

+38-15
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ class WP_Export_Query {
1111
const QUERY_CHUNK = 100;
1212

1313
private static $defaults = [
14-
'post_ids' => null,
15-
'post_type' => null,
16-
'status' => null,
17-
'author' => null,
18-
'start_date' => null,
19-
'end_date' => null,
20-
'start_id' => null,
21-
'max_num_posts' => null,
22-
'category' => null,
14+
'post_ids' => null,
15+
'post_type' => null,
16+
'status' => null,
17+
'author' => null,
18+
'start_date' => null,
19+
'end_date' => null,
20+
'start_id' => null,
21+
'max_num_posts' => null,
22+
'category' => null,
23+
'allow_orphan_terms' => null,
2324
];
2425

2526
private $post_ids;
@@ -97,7 +98,7 @@ public function categories() {
9798
}
9899
$categories = (array) get_categories( [ 'get' => 'all' ] );
99100

100-
$this->check_for_orphaned_terms( $categories );
101+
$categories = $this->process_orphaned_terms( $categories );
101102

102103
$categories = self::topologically_sort_terms( $categories );
103104

@@ -110,7 +111,7 @@ public function tags() {
110111
}
111112
$tags = (array) get_tags( [ 'get' => 'all' ] );
112113

113-
$this->check_for_orphaned_terms( $tags );
114+
$tags = $this->process_orphaned_terms( $tags );
114115

115116
return $tags;
116117
}
@@ -122,7 +123,7 @@ public function custom_taxonomies_terms() {
122123
$custom_taxonomies = get_taxonomies( [ '_builtin' => false ] );
123124
// phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found -- Deprecated, but we need to support older versions of WordPress.
124125
$custom_terms = (array) get_terms( $custom_taxonomies, [ 'get' => 'all' ] );
125-
$this->check_for_orphaned_terms( $custom_terms );
126+
$custom_terms = $this->process_orphaned_terms( $custom_terms );
126127
$custom_terms = self::topologically_sort_terms( $custom_terms );
127128
return $custom_terms;
128129
}
@@ -356,9 +357,11 @@ private static function topologically_sort_terms( $terms ) {
356357
return $sorted;
357358
}
358359

359-
private function check_for_orphaned_terms( $terms ) {
360+
private function process_orphaned_terms( $terms ) {
361+
360362
$term_ids = [];
361363
$have_parent = [];
364+
$orphans = [];
362365

363366
foreach ( $terms as $term ) {
364367
$term_ids[ $term->term_id ] = true;
@@ -369,10 +372,30 @@ private function check_for_orphaned_terms( $terms ) {
369372

370373
foreach ( $have_parent as $has_parent ) {
371374
if ( ! isset( $term_ids[ $has_parent->parent ] ) ) {
372-
$this->missing_parents = $has_parent;
373-
throw new WP_Export_Term_Exception( "Term is missing a parent: {$has_parent->slug} ({$has_parent->term_taxonomy_id})" );
375+
if ( $this->filters['allow_orphan_terms'] ) {
376+
$orphans[ $has_parent->term_id ] = true;
377+
} else {
378+
$this->missing_parents = $has_parent;
379+
throw new WP_Export_Term_Exception( "Term is missing a parent: {$has_parent->slug} ({$has_parent->term_taxonomy_id})" );
380+
}
381+
}
382+
}
383+
384+
if ( ! $this->filters['allow_orphan_terms'] ) {
385+
return $terms;
386+
}
387+
388+
if ( count( $orphans ) > 0 ) {
389+
$terms_return = [];
390+
foreach ( $terms as $term ) {
391+
if ( isset( $orphans[ $term->term_id ] ) ) {
392+
$term->parent = 0;
393+
}
394+
$terms_return[] = $term;
374395
}
396+
$terms = $terms_return;
375397
}
398+
return $terms;
376399
}
377400

378401
private static function get_terms_for_post( $post ) {

0 commit comments

Comments
 (0)