From 17713def41ad39d61e33c463139383adce3b7028 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Fri, 21 Aug 2026 12:28:43 -0700 Subject: [PATCH 1/7] adding functionality to Conifer to support removing tags --- lib/Conifer/Site.php | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/Conifer/Site.php b/lib/Conifer/Site.php index 0856841..ade2a69 100644 --- a/lib/Conifer/Site.php +++ b/lib/Conifer/Site.php @@ -831,4 +831,64 @@ public function disable_comments() add_filter('comments_open', '__return_false', 20); add_filter('pings_open', '__return_false', 20); } + + /** + * Removes the tag taxonomy from the specified post types. + * + * @param array $postTypes Post types to remove the tag taxonomy from. + * @return void + */ + public function disable_tags_for_post_types(array $postTypes): void + { + foreach ($postTypes as $postType) { + $this->disable_tags_for_post_type($postType); + } + } + + /** + * Removes the tag taxonomy from all post types except the provided post types. + * + * @param array $excludedPostTypes Post types to exclude from tag removal. + * Defaults to ['tribe_events']. + * @return void + */ + private function disable_tags(array $excludedPostTypes = ['tribe_events']): void + { + $types = get_post_types([], 'names'); + + $filteredPostTypes = array_filter($types, function ($type) use ($excludedPostTypes) { + return !in_array($type, $excludedPostTypes, true); + }); + + // Hide Tags from the admin menu. + // ? This is intended to be a universal solution, but if we provide a public facing function to disable tag support for specific post types, we should + // ? probably reconsider globally removing the tags menu item. If downstream someone explicitly calls disable_tags_for_post_types(['post']) + // ? they may not expect the menu item to be removed for all post types. + add_action('admin_menu', function () { + remove_submenu_page('edit.php', 'edit-tags.php?taxonomy=post_tag'); + }); + + // Taxonomies are registered during init, so remove associations after it runs. + if (did_action('init')) { + $this->disable_tags_for_post_types($filteredPostTypes); + return; + } + + add_action('init', function () use ($filteredPostTypes) { + $this->disable_tags_for_post_types($filteredPostTypes); + }); + } + + /** + * Removes the tag taxonomy from a single post type. + * + * @param string $postType Post type to remove the tag taxonomy from. + * @return bool True if the taxonomy association was removed, false otherwise. + */ + private function disable_tags_for_post_type(string $postType): bool + { + remove_post_type_support($postType, 'post_tag'); + + return unregister_taxonomy_for_object_type('post_tag', $postType); + } } From bd50a8283a77c05015a4ace7480d6d1ded96b659 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Fri, 21 Aug 2026 12:29:07 -0700 Subject: [PATCH 2/7] adding integration tests for tag removal --- test/integration/SiteTagRemovalTest.php | 86 +++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/integration/SiteTagRemovalTest.php diff --git a/test/integration/SiteTagRemovalTest.php b/test/integration/SiteTagRemovalTest.php new file mode 100644 index 0000000..dcc8ed2 --- /dev/null +++ b/test/integration/SiteTagRemovalTest.php @@ -0,0 +1,86 @@ +postTypesWithTags = array_filter( + get_post_types([], 'names'), + function (string $postType): bool { + return is_object_in_taxonomy($postType, 'post_tag'); + } + ); + + register_post_type(self::POST_TYPE_ONE, [ + 'public' => true, + 'taxonomies' => ['post_tag'], + ]); + register_post_type(self::POST_TYPE_TWO, [ + 'public' => true, + 'taxonomies' => ['post_tag'], + ]); + register_post_type(self::EXCLUDED_POST_TYPE, [ + 'public' => true, + 'taxonomies' => ['post_tag'], + ]); + } + + public function tearDown(): void + { + foreach ([self::POST_TYPE_ONE, self::POST_TYPE_TWO, self::EXCLUDED_POST_TYPE] as $postType) { + unregister_post_type($postType); + } + + foreach ($this->postTypesWithTags as $postType) { + register_taxonomy_for_object_type('post_tag', $postType); + } + + parent::tearDown(); + } + + public function test_disable_tags_for_post_types_removes_tags_only_from_specified_post_types() + { + $this->site->disable_tags_for_post_types([self::POST_TYPE_ONE]); + + $this->assertFalse(is_object_in_taxonomy(self::POST_TYPE_ONE, 'post_tag')); + $this->assertTrue(is_object_in_taxonomy(self::POST_TYPE_TWO, 'post_tag')); + } + + public function test_disable_tags_excludes_tribe_events_by_default() + { + $this->site->disable_tags(); + + $this->assertFalse(is_object_in_taxonomy(self::POST_TYPE_ONE, 'post_tag')); + $this->assertTrue(is_object_in_taxonomy(self::EXCLUDED_POST_TYPE, 'post_tag')); + } + + public function test_disable_tags_honors_custom_excluded_post_types() + { + $this->site->disable_tags([self::POST_TYPE_ONE]); + + $this->assertTrue(is_object_in_taxonomy(self::POST_TYPE_ONE, 'post_tag')); + $this->assertFalse(is_object_in_taxonomy(self::POST_TYPE_TWO, 'post_tag')); + } +} From d334d5f9bfc7f6889378eacf8c33c40e041f0060 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Fri, 21 Aug 2026 12:29:20 -0700 Subject: [PATCH 3/7] updating Site docs for tag removal --- docs/site.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/site.md b/docs/site.md index 6c1a030..e8d7933 100644 --- a/docs/site.md +++ b/docs/site.md @@ -68,7 +68,7 @@ $site->configure(function() { }); ``` -This approach of passing a function to `conifigure()` has a couple advantages: +This approach of passing a function to `configure()` has a couple advantages: - You don't have to define your own `Site` subclass. - Wrapping your config code in a closure avoids polluting the global namespace, but also allows it to remain in the familiar functions.php rather than in a random library class, where newcomers to your codebase may not know to look. @@ -101,7 +101,7 @@ $site->configure(); ## Disabling defaults -If you want to *only* run your custom config callback without funning Conifer's default config code, you can pass `false` as the second argument to `configure()`, which tells Conifer to disable its defaults: +If you want to *only* run your custom config callback without running Conifer's default configuration code, pass `false` as the second argument to `configure()`: ```php $site->configure(function() { /* ... */ }, false); @@ -119,6 +119,20 @@ $site->configure(function() { This closes all comments on the frontend, hides all existing comments on all posts on the frontend and within WP Admin, and removes comment management pages from WP Admin. +## Disabling Tags + +Many sites do not need tag support on every post type. Use `disable_tags_for_post_types()` to remove the built-in tag taxonomy from the post types you specify: + +```php +$site->configure(function() { + $postTypes = ['robot', 'android', 'cyborg']; + + $this->disable_tags_for_post_types($postTypes); +}); +``` + +This removes tag support and the `post_tag` taxonomy association only from the listed post types. Tags remain available on all other post types. + ## Directory Cascades A **Directory Cascade** is an ordered list of directories where Conifer looks for Twig views (`*.twig` files), JS files, or stylesheets. Each type of asset (views, JS, CSS) has its own set of directories - its own *cascade* - where Conifer looks for that type of file. Each cascade has its own getter and setter on the `Site` class: @@ -158,7 +172,7 @@ $site->configure(function() { ### Finding generic file paths -Under the hood, the script and style cascades call the generic `file_file()` method, which simply traverses a list of directories looking for a relative file path within each. It returns the full path of the first existing file it finds: +Under the hood, the script and style cascades call the generic `find_file()` method, which traverses a list of directories looking for a relative file path within each. It returns the full path of the first existing file it finds: ```php // directory tree: From f1dc04dacb250dc37e8c531ef2623f408dba2ec2 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Fri, 21 Aug 2026 14:05:09 -0700 Subject: [PATCH 4/7] fixing integration tests --- test/integration/SiteTagRemovalTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/integration/SiteTagRemovalTest.php b/test/integration/SiteTagRemovalTest.php index dcc8ed2..44b89d9 100644 --- a/test/integration/SiteTagRemovalTest.php +++ b/test/integration/SiteTagRemovalTest.php @@ -70,7 +70,9 @@ public function test_disable_tags_for_post_types_removes_tags_only_from_specifie public function test_disable_tags_excludes_tribe_events_by_default() { - $this->site->disable_tags(); + $this->site->configure(function () { + $this->disable_tags(); + }, false); $this->assertFalse(is_object_in_taxonomy(self::POST_TYPE_ONE, 'post_tag')); $this->assertTrue(is_object_in_taxonomy(self::EXCLUDED_POST_TYPE, 'post_tag')); @@ -78,7 +80,10 @@ public function test_disable_tags_excludes_tribe_events_by_default() public function test_disable_tags_honors_custom_excluded_post_types() { - $this->site->disable_tags([self::POST_TYPE_ONE]); + $excludedPostTypes = [self::POST_TYPE_ONE]; + $this->site->configure(function () use ($excludedPostTypes) { + $this->disable_tags($excludedPostTypes); + }, false); $this->assertTrue(is_object_in_taxonomy(self::POST_TYPE_ONE, 'post_tag')); $this->assertFalse(is_object_in_taxonomy(self::POST_TYPE_TWO, 'post_tag')); From e49e397f3f02590d10df6ce3432eac31456e8559 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Fri, 21 Aug 2026 14:08:25 -0700 Subject: [PATCH 5/7] removing dead code, but I'm not 100% sure it is dead code --- lib/Conifer/Site.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Conifer/Site.php b/lib/Conifer/Site.php index ade2a69..058d150 100644 --- a/lib/Conifer/Site.php +++ b/lib/Conifer/Site.php @@ -887,7 +887,8 @@ private function disable_tags(array $excludedPostTypes = ['tribe_events']): void */ private function disable_tags_for_post_type(string $postType): bool { - remove_post_type_support($postType, 'post_tag'); + // Apparently this is a no-op + // remove_post_type_support($postType, 'post_tag'); return unregister_taxonomy_for_object_type('post_tag', $postType); } From 501320eef11dcf97486fcd683d45f706cffe212d Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Tue, 6 Oct 2026 08:16:02 -0700 Subject: [PATCH 6/7] adding code review feedback and updating docs --- docs/site.md | 12 ++++++++++++ lib/Conifer/Site.php | 20 +++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/docs/site.md b/docs/site.md index e8d7933..4da10fb 100644 --- a/docs/site.md +++ b/docs/site.md @@ -133,6 +133,18 @@ $site->configure(function() { This removes tag support and the `post_tag` taxonomy association only from the listed post types. Tags remain available on all other post types. +You can also remove tags for all post types using the included `disable_tags` function. + +```php +$site->configure(function() { + //... + + // Tags will be disabled for all registered post types + $this->disable_tags(); + + //... +}); + ## Directory Cascades A **Directory Cascade** is an ordered list of directories where Conifer looks for Twig views (`*.twig` files), JS files, or stylesheets. Each type of asset (views, JS, CSS) has its own set of directories - its own *cascade* - where Conifer looks for that type of file. Each cascade has its own getter and setter on the `Site` class: diff --git a/lib/Conifer/Site.php b/lib/Conifer/Site.php index c26e59e..5fcb018 100644 --- a/lib/Conifer/Site.php +++ b/lib/Conifer/Site.php @@ -157,10 +157,13 @@ public function configure_defaults() Button::register('button'); Integrations\YoastIntegration::demote_metabox(); + // TODO moar integrations! } /** * Register default Post Class Maps for default Conifer classes + * + * @todo Terms/Users */ public function configure_default_classmaps() { @@ -530,6 +533,7 @@ public function add_default_twig_helpers() public function configure_default_admin_dashboard_widgets() { add_action('wp_dashboard_setup', function () { + // TODO widget API? wp_add_dashboard_widget( 'conifer_guide', __('Welcome to Conifer'), @@ -546,6 +550,7 @@ function () { public function remove_conifer_widget() { add_action('wp_dashboard_setup', function () { + // TODO widget API? remove_meta_box('conifer_guide', 'dashboard', 'normal'); }); } @@ -835,9 +840,21 @@ public function disable_comments() */ public function disable_tags_for_post_types(array $postTypes): void { + // Attempt to disable tags for already registered taxonomies. foreach ($postTypes as $postType) { $this->disable_tags_for_post_type($postType); } + + // If called before init, core taxonomies may not be registered yet; retry after init. + if (did_action('init') || doing_action('init')) { + return; + } + + add_action('init', function () use ($postTypes) { + foreach ($postTypes as $postType) { + $this->disable_tags_for_post_type($postType); + } + }); } /** @@ -882,9 +899,6 @@ private function disable_tags(array $excludedPostTypes = ['tribe_events']): void */ private function disable_tags_for_post_type(string $postType): bool { - // Apparently this is a no-op - // remove_post_type_support($postType, 'post_tag'); - return unregister_taxonomy_for_object_type('post_tag', $postType); } } From 60ef334fcc2690785df93a01db46539ea4982694 Mon Sep 17 00:00:00 2001 From: Alexander Merk Date: Tue, 6 Oct 2026 09:04:45 -0700 Subject: [PATCH 7/7] maybe fixing tag disabled when declaring specific post types --- lib/Conifer/Site.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Conifer/Site.php b/lib/Conifer/Site.php index 5fcb018..e22e155 100644 --- a/lib/Conifer/Site.php +++ b/lib/Conifer/Site.php @@ -846,7 +846,7 @@ public function disable_tags_for_post_types(array $postTypes): void } // If called before init, core taxonomies may not be registered yet; retry after init. - if (did_action('init') || doing_action('init')) { + if (did_action('init') && !doing_action('init')) { return; } @@ -854,7 +854,7 @@ public function disable_tags_for_post_types(array $postTypes): void foreach ($postTypes as $postType) { $this->disable_tags_for_post_type($postType); } - }); + }, PHP_INT_MAX); } /**