Skip to content

Commit a111eaf

Browse files
committed
Post Types: Introduce unregister_post_type().
This new function can be used to completely unregister non built-in post types. Fixes #14761. git-svn-id: https://develop.svn.wordpress.org/trunk@36316 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 8e8cdb2 commit a111eaf

4 files changed

Lines changed: 313 additions & 16 deletions

File tree

src/wp-includes/capabilities.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
*
1717
* @since 2.0.0
1818
*
19+
* @global array $post_type_meta_caps Used to get post type meta capabilities.
20+
*
1921
* @param string $cap Capability name.
2022
* @param int $user_id User ID.
2123
* @param int $object_id Optional. ID of the specific object to check against if `$cap` is a "meta" cap.
@@ -377,7 +379,7 @@ function map_meta_cap( $cap, $user_id ) {
377379
break;
378380
default:
379381
// Handle meta capabilities for custom post types.
380-
$post_type_meta_caps = _post_type_meta_capabilities();
382+
global $post_type_meta_caps;
381383
if ( isset( $post_type_meta_caps[ $cap ] ) ) {
382384
$args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
383385
return call_user_func_array( 'map_meta_cap', $args );

src/wp-includes/post.php

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,88 @@ function register_post_type( $post_type, $args = array() ) {
11861186
return $args;
11871187
}
11881188

1189+
/**
1190+
* Unregister a post type.
1191+
*
1192+
* Can not be used to unregister built-in post types.
1193+
*
1194+
* @since 4.5.0
1195+
*
1196+
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
1197+
* @global WP $wp Current WordPress environment instance.
1198+
* @global array $_wp_post_type_features Used to remove post type features.
1199+
* @global array $post_type_meta_caps Used to remove meta capabilities.
1200+
* @global array $wp_post_types List of post types.
1201+
*
1202+
* @param string $post_type Post type key.
1203+
* @return bool|WP_Error True on success, WP_Error on failure.
1204+
*/
1205+
function unregister_post_type( $post_type ) {
1206+
if ( ! post_type_exists( $post_type ) ) {
1207+
return new WP_Error( 'invalid_post_type', __( 'Invalid post type' ) );
1208+
}
1209+
1210+
$post_type_args = get_post_type_object( $post_type );
1211+
1212+
// Do not allow unregistering internal post types.
1213+
if ( $post_type_args->_builtin ) {
1214+
return new WP_Error( 'invalid_post_type', __( 'Unregistering a built-in post type is not allowed' ) );
1215+
}
1216+
1217+
global $wp, $wp_rewrite, $_wp_post_type_features, $post_type_meta_caps, $wp_post_types;
1218+
1219+
// Remove query var.
1220+
if ( false !== $post_type_args->query_var ) {
1221+
$wp->remove_query_var( $post_type_args->query_var );
1222+
}
1223+
1224+
// Remove any rewrite rules, permastructs, and rules.
1225+
if ( false !== $post_type_args->rewrite ) {
1226+
remove_rewrite_tag( "%$post_type%" );
1227+
remove_permastruct( $post_type );
1228+
foreach ( $wp_rewrite->extra_rules_top as $regex => $query ) {
1229+
if ( false !== strpos( $query, "index.php?post_type=$post_type" ) ) {
1230+
unset( $wp_rewrite->extra_rules_top[ $regex ] );
1231+
}
1232+
}
1233+
}
1234+
1235+
// Remove registered custom meta capabilities.
1236+
foreach ( $post_type_args->cap as $cap ) {
1237+
unset( $post_type_meta_caps[ $cap ] );
1238+
}
1239+
1240+
// Remove all post type support.
1241+
unset( $_wp_post_type_features[ $post_type ] );
1242+
1243+
// Unregister the post type meta box if a custom callback was specified.
1244+
if ( $post_type_args->register_meta_box_cb ) {
1245+
remove_action( 'add_meta_boxes_' . $post_type, $post_type_args->register_meta_box_cb );
1246+
}
1247+
1248+
// Remove the post type from all taxonomies.
1249+
foreach ( get_object_taxonomies( $post_type ) as $taxonomy ) {
1250+
unregister_taxonomy_for_object_type( $taxonomy, $post_type );
1251+
}
1252+
1253+
// Remove the future post hook action.
1254+
remove_action( 'future_' . $post_type, '_future_post_hook', 5 );
1255+
1256+
// Remove the post type.
1257+
unset( $wp_post_types[ $post_type ] );
1258+
1259+
/**
1260+
* Fires after a post type was unregistered.
1261+
*
1262+
* @since 4.5.0
1263+
*
1264+
* @param string $post_type Post type key.
1265+
*/
1266+
do_action( 'unregistered_post_type', $post_type );
1267+
1268+
return true;
1269+
}
1270+
11891271
/**
11901272
* Build an object with all post type capabilities out of a post type object
11911273
*
@@ -1293,17 +1375,17 @@ function get_post_type_capabilities( $args ) {
12931375
* @since 3.1.0
12941376
* @access private
12951377
*
1296-
* @staticvar array $meta_caps
1378+
* @global array $post_type_meta_caps Used to store meta capabilities.
12971379
*
1298-
* @param array|void $capabilities Post type meta capabilities.
1380+
* @param array $capabilities Post type meta capabilities.
12991381
*/
13001382
function _post_type_meta_capabilities( $capabilities = null ) {
1301-
static $meta_caps = array();
1302-
if ( null === $capabilities )
1303-
return $meta_caps;
1383+
global $post_type_meta_caps;
1384+
13041385
foreach ( $capabilities as $core => $custom ) {
1305-
if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
1306-
$meta_caps[ $custom ] = $core;
1386+
if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) ) {
1387+
$post_type_meta_caps[ $custom ] = $core;
1388+
}
13071389
}
13081390
}
13091391

tests/phpunit/includes/utils.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,7 @@ function str_getcsv( $input, $delimiter = ',', $enclosure = '"', $escape = "\\"
335335
* Removes the post type and its taxonomy associations.
336336
*/
337337
function _unregister_post_type( $cpt_name ) {
338-
unset( $GLOBALS['wp_post_types'][ $cpt_name ] );
339-
unset( $GLOBALS['_wp_post_type_features'][ $cpt_name ] );
340-
341-
foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy ) {
342-
if ( false !== $key = array_search( $cpt_name, $taxonomy->object_type ) ) {
343-
unset( $taxonomy->object_type[$key] );
344-
}
345-
}
338+
unregister_post_type( $cpt_name );
346339
}
347340

348341
function _unregister_taxonomy( $taxonomy_name ) {

tests/phpunit/tests/post/types.php

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,224 @@ public function test_get_post_type_object_casting() {
159159

160160
_unregister_post_type( 'foo' );
161161
}
162+
163+
/**
164+
* @ticket 14761
165+
*/
166+
public function test_unregister_post_type() {
167+
register_post_type( 'foo' );
168+
$this->assertTrue( unregister_post_type( 'foo' ) );
169+
}
170+
171+
/**
172+
* @ticket 14761
173+
*/
174+
public function test_unregister_post_type_unknown_post_type() {
175+
$this->assertWPError( unregister_post_type( 'foo' ) );
176+
}
177+
178+
/**
179+
* @ticket 14761
180+
*/
181+
public function test_unregister_post_type_twice() {
182+
register_post_type( 'foo' );
183+
$this->assertTrue( unregister_post_type( 'foo' ) );
184+
$this->assertWPError( unregister_post_type( 'foo' ) );
185+
}
186+
187+
/**
188+
* @ticket 14761
189+
*/
190+
public function test_unregister_post_type_disallow_builtin_post_type() {
191+
$this->assertWPError( unregister_post_type( 'post' ) );
192+
$this->assertWPError( unregister_post_type( 'page' ) );
193+
$this->assertWPError( unregister_post_type( 'attachment' ) );
194+
$this->assertWPError( unregister_post_type( 'revision' ) );
195+
$this->assertWPError( unregister_post_type( 'nav_menu_item' ) );
196+
}
197+
198+
/**
199+
* @ticket 14761
200+
*/
201+
public function test_unregister_post_type_removes_query_vars() {
202+
global $wp;
203+
204+
register_post_type( 'foo', array(
205+
'public' => true,
206+
'query_var' => 'bar',
207+
) );
208+
209+
$this->assertInternalType( 'int', array_search( 'bar', $wp->public_query_vars ) );
210+
$this->assertTrue( unregister_post_type( 'foo' ) );
211+
$this->assertFalse( array_search( 'bar', $wp->public_query_vars ) );
212+
}
213+
214+
/**
215+
* @ticket 14761
216+
*/
217+
public function test_unregister_post_type_removes_rewrite_tags() {
218+
$this->set_permalink_structure( '/%postname%' );
219+
220+
global $wp_rewrite;
221+
222+
register_post_type( 'foo', array(
223+
'public' => true,
224+
'query_var' => 'bar',
225+
) );
226+
227+
$count_before = count( $wp_rewrite->rewritereplace );
228+
229+
$this->assertContains( '%foo%', $wp_rewrite->rewritecode );
230+
$this->assertContains( 'bar=', $wp_rewrite->queryreplace );
231+
$this->assertTrue( unregister_post_type( 'foo' ) );
232+
$this->assertNotContains( '%foo%', $wp_rewrite->rewritecode );
233+
$this->assertNotContains( 'bar=', $wp_rewrite->queryreplace );
234+
$this->assertSame( -- $count_before, count( $wp_rewrite->rewritereplace ) ); // Array was reduced by one value.
235+
}
236+
237+
/**
238+
* @ticket 14761
239+
*/
240+
public function test_unregister_post_type_removes_rewrite_rules() {
241+
$this->set_permalink_structure( '/%postname%' );
242+
243+
global $wp_rewrite;
244+
245+
register_post_type( 'foo', array(
246+
'public' => true,
247+
'has_archive' => true,
248+
) );
249+
250+
$this->assertContains( 'index.php?post_type=foo', $wp_rewrite->extra_rules_top );
251+
$this->assertTrue( unregister_post_type( 'foo' ) );
252+
$this->assertNotContains( 'index.php?post_type=foo', $wp_rewrite->extra_rules_top );
253+
}
254+
255+
/**
256+
* @ticket 14761
257+
*/
258+
public function test_unregister_post_type_removes_custom_meta_capabilities() {
259+
global $post_type_meta_caps;
260+
261+
register_post_type( 'foo', array(
262+
'public' => true,
263+
'capability_type' => 'bar',
264+
'map_meta_cap' => true,
265+
) );
266+
267+
$this->assertSame( 'read_post', $post_type_meta_caps['read_bar'] );
268+
$this->assertSame( 'delete_post', $post_type_meta_caps['delete_bar'] );
269+
$this->assertSame( 'edit_post', $post_type_meta_caps['edit_bar'] );
270+
271+
$this->assertTrue( unregister_post_type( 'foo' ) );
272+
273+
$this->assertFalse( isset( $post_type_meta_caps['read_bar'] ) );
274+
$this->assertFalse( isset( $post_type_meta_caps['delete_bar'] ) );
275+
$this->assertFalse( isset( $post_type_meta_caps['edit_bar'] ) );
276+
}
277+
278+
/**
279+
* @ticket 14761
280+
*/
281+
public function test_unregister_post_type_removes_post_type_supports() {
282+
global $_wp_post_type_features;
283+
284+
register_post_type( 'foo', array(
285+
'public' => true,
286+
'supports' => array( 'editor', 'author', 'title' ),
287+
) );
288+
289+
$this->assertEqualSetsWithIndex(
290+
array(
291+
'editor' => true,
292+
'author' => true,
293+
'title' => true,
294+
),
295+
$_wp_post_type_features['foo']
296+
);
297+
$this->assertTrue( unregister_post_type( 'foo' ) );
298+
$this->assertFalse( isset( $_wp_post_type_features['foo'] ) );
299+
}
300+
301+
/**
302+
* @ticket 14761
303+
*/
304+
public function test_unregister_post_type_removes_post_type_from_taxonomies() {
305+
global $wp_taxonomies;
306+
307+
register_post_type( 'foo', array(
308+
'public' => true,
309+
'taxonomies' => array( 'category', 'post_tag' ),
310+
) );
311+
312+
$this->assertInternalType( 'int', array_search( 'foo', $wp_taxonomies['category']->object_type, true ) );
313+
$this->assertInternalType( 'int', array_search( 'foo', $wp_taxonomies['post_tag']->object_type, true ) );
314+
$this->assertTrue( unregister_post_type( 'foo' ) );
315+
$this->assertFalse( array_search( 'foo', $wp_taxonomies['category']->object_type, true ) );
316+
$this->assertFalse( array_search( 'foo', $wp_taxonomies['post_tag']->object_type, true ) );
317+
$this->assertEmpty( get_object_taxonomies( 'foo' ) );
318+
}
319+
320+
/**
321+
* @ticket 14761
322+
*/
323+
public function test_unregister_post_type_removes_the_future_post_hooks() {
324+
global $wp_filter;
325+
326+
register_post_type( 'foo', array(
327+
'public' => true,
328+
) );
329+
330+
$this->assertSame( 1, count( $wp_filter['future_foo'] ) );
331+
$this->assertTrue( unregister_post_type( 'foo' ) );
332+
$this->assertSame( array(), $wp_filter['future_foo'] );
333+
}
334+
335+
/**
336+
* @ticket 14761
337+
*/
338+
public function test_unregister_post_type_removes_meta_box_callback() {
339+
global $wp_filter;
340+
341+
register_post_type( 'foo', array(
342+
'public' => true,
343+
'register_meta_box_cb' => '__return_empty_string',
344+
) );
345+
346+
$this->assertSame( 1, count( $wp_filter['add_meta_boxes_foo'] ) );
347+
$this->assertTrue( unregister_post_type( 'foo' ) );
348+
$this->assertSame( array(), $wp_filter['add_meta_boxes_foo'] );
349+
}
350+
351+
/**
352+
* @ticket 14761
353+
*/
354+
public function test_unregister_post_type_removes_post_type_from_global() {
355+
global $wp_post_types;
356+
357+
register_post_type( 'foo', array(
358+
'public' => true,
359+
) );
360+
361+
$this->assertInternalType( 'object', $wp_post_types['foo'] );
362+
$this->assertInternalType( 'object', get_post_type_object( 'foo' ) );
363+
364+
$this->assertTrue( unregister_post_type( 'foo' ) );
365+
366+
$this->assertFalse( isset( $wp_post_types['foo'] ) );
367+
$this->assertNull( get_post_type_object( 'foo' ) );
368+
}
369+
370+
/**
371+
* @ticket 14761
372+
*/
373+
public function test_post_type_does_not_exist_after_unregister_post_type() {
374+
register_post_type( 'foo', array(
375+
'public' => true,
376+
) );
377+
378+
$this->assertTrue( unregister_post_type( 'foo' ) );
379+
380+
$this->assertFalse( post_type_exists( 'foo' ) );
381+
}
162382
}

0 commit comments

Comments
 (0)