Skip to content

Commit 9946b25

Browse files
authored
Use stable term_id-based IDs for Term transformer (#2605)
1 parent e3220cc commit 9946b25

File tree

6 files changed

+138
-9
lines changed

6 files changed

+138
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: changed
3+
4+
Use stable term_id-based IDs for Term transformer to ensure federation consistency.

includes/class-query.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ public function get_queried_object() {
198198
}
199199
}
200200

201+
// Check Term by ID.
202+
if ( ! $queried_object ) {
203+
$term_id = \get_query_var( 'term_id' );
204+
if ( $term_id ) {
205+
$queried_object = \get_term( $term_id );
206+
}
207+
}
208+
201209
// Try to get Author by ID.
202210
if ( ! $queried_object ) {
203211
$url = $this->get_request_url();

includes/class-router.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,28 @@ public static function template_redirect() {
262262
\wp_safe_redirect( $actor->get_url(), 301 );
263263
exit;
264264
}
265+
266+
$term_id = \get_query_var( 'term_id', null );
267+
if ( $term_id ) {
268+
$term = \get_term( $term_id );
269+
270+
// Load a 404-page if `term_id` is set but not valid.
271+
if ( ! $term || \is_wp_error( $term ) ) {
272+
$wp_query->set_404();
273+
return;
274+
}
275+
276+
// Don't redirect for ActivityPub requests.
277+
if ( is_activitypub_request() ) {
278+
return;
279+
}
280+
281+
$term_link = \get_term_link( $term );
282+
if ( ! \is_wp_error( $term_link ) ) {
283+
\wp_safe_redirect( $term_link, 301 );
284+
exit;
285+
}
286+
}
265287
}
266288

267289
/**
@@ -280,6 +302,7 @@ public static function add_query_vars( $vars ) {
280302
$vars[] = 'type';
281303
$vars[] = 'c';
282304
$vars[] = 'p';
305+
$vars[] = 'term_id';
283306

284307
return $vars;
285308
}

includes/transformer/class-term.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,45 @@ public function to_object() {
2222
$base_object = new \Activitypub\Activity\Base_Object();
2323
$base_object->{'@context'} = 'https://www.w3.org/ns/activitystreams';
2424
$base_object->set_type( 'OrderedCollection' );
25-
$base_object->set_id( \get_term_link( $this->item ) );
25+
$base_object->set_id( $this->get_id() );
26+
$base_object->set_url( $this->get_url() );
2627

2728
return $base_object;
2829
}
2930

3031
/**
31-
* Get the OrderedCollection ID (term link).
32+
* Get the OrderedCollection ID.
3233
*
33-
* @return string The OrderedCollection ID (term link).
34+
* @return string The OrderedCollection ID.
3435
*/
3536
public function to_id() {
36-
return \get_term_link( $this->item );
37+
return $this->get_id();
38+
}
39+
40+
/**
41+
* Returns the stable ID of the Term.
42+
*
43+
* Uses term_id query parameter to ensure the ID remains stable
44+
* even if the term slug is changed.
45+
*
46+
* @return string The Term's stable ID.
47+
*/
48+
public function get_id() {
49+
return \add_query_arg( 'term_id', $this->item->term_id, \home_url( '/' ) );
50+
}
51+
52+
/**
53+
* Returns the URL of the Term.
54+
*
55+
* @return string The Term's URL (term link).
56+
*/
57+
public function get_url() {
58+
$term_link = \get_term_link( $this->item );
59+
60+
if ( \is_wp_error( $term_link ) ) {
61+
return '';
62+
}
63+
64+
return \esc_url( $term_link );
3765
}
3866
}

tests/phpunit/tests/includes/class-test-query.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,32 @@ public function test_get_queried_object() {
169169
$this->assertEquals( self::$user_id, $object->ID );
170170
}
171171

172+
/**
173+
* Test get_queried_object with term_id query var.
174+
*
175+
* @covers ::get_queried_object
176+
*/
177+
public function test_get_queried_object_with_term_id() {
178+
// Create a test term.
179+
$term = self::factory()->term->create_and_get(
180+
array(
181+
'taxonomy' => 'post_tag',
182+
'name' => 'Test Tag',
183+
'slug' => 'test-tag',
184+
)
185+
);
186+
187+
// Test with term_id query var.
188+
Query::get_instance()->__destruct();
189+
$this->go_to( \add_query_arg( 'term_id', $term->term_id, \home_url( '/' ) ) );
190+
\set_query_var( 'term_id', $term->term_id );
191+
$query = Query::get_instance();
192+
$object = $query->get_queried_object();
193+
194+
$this->assertInstanceOf( 'WP_Term', $object );
195+
$this->assertEquals( $term->term_id, $object->term_id );
196+
}
197+
172198
/**
173199
* Test is_activitypub_request method.
174200
*

tests/phpunit/tests/includes/transformer/class-test-term.php

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,13 @@ public function test_to_object() {
7171
// Check type is OrderedCollection.
7272
$this->assertEquals( 'OrderedCollection', $object->get_type() );
7373

74-
// Check ID is the term link.
74+
// Check ID uses stable term_id-based URL.
75+
$expected_id = \add_query_arg( 'term_id', $term->term_id, \home_url( '/' ) );
76+
$this->assertEquals( $expected_id, $object->get_id() );
77+
78+
// Check URL is the term link.
7579
$expected_url = get_term_link( $term );
76-
$this->assertEquals( $expected_url, $object->get_id() );
80+
$this->assertEquals( $expected_url, $object->get_url() );
7781
}
7882

7983
/**
@@ -86,13 +90,43 @@ public function test_to_id() {
8690
$transformer = new Term( $term );
8791
$id = $transformer->to_id();
8892

89-
// Should return the term link.
93+
// Should return stable term_id-based URL.
94+
$expected_id = \add_query_arg( 'term_id', $term->term_id, \home_url( '/' ) );
95+
$this->assertEquals( $expected_id, $id );
96+
}
97+
98+
/**
99+
* Test get_id returns stable ID.
100+
*
101+
* @covers ::get_id
102+
*/
103+
public function test_get_id() {
104+
$term = get_term( self::$term_id );
105+
$transformer = new Term( $term );
106+
107+
$expected_id = \add_query_arg( 'term_id', $term->term_id, \home_url( '/' ) );
108+
$this->assertEquals( $expected_id, $transformer->get_id() );
109+
}
110+
111+
/**
112+
* Test get_url returns term link.
113+
*
114+
* @covers ::get_url
115+
*/
116+
public function test_get_url() {
117+
$term = get_term( self::$term_id );
118+
$transformer = new Term( $term );
119+
90120
$expected_url = get_term_link( $term );
91-
$this->assertEquals( $expected_url, $id );
121+
$this->assertEquals( $expected_url, $transformer->get_url() );
92122
}
93123

94124
/**
95125
* Test with category taxonomy.
126+
*
127+
* @covers ::to_object
128+
* @covers ::get_id
129+
* @covers ::get_url
96130
*/
97131
public function test_category_term() {
98132
$category = self::factory()->term->create_and_get(
@@ -107,6 +141,12 @@ public function test_category_term() {
107141
$object = $transformer->to_object();
108142

109143
$this->assertEquals( 'OrderedCollection', $object->get_type() );
110-
$this->assertEquals( get_term_link( $category ), $object->get_id() );
144+
145+
// ID should use stable term_id-based URL.
146+
$expected_id = \add_query_arg( 'term_id', $category->term_id, \home_url( '/' ) );
147+
$this->assertEquals( $expected_id, $object->get_id() );
148+
149+
// URL should be the term link.
150+
$this->assertEquals( get_term_link( $category ), $object->get_url() );
111151
}
112152
}

0 commit comments

Comments
 (0)