Taxonomy term pages are visible by default which shouldn't be open. It may leak some content that shouldn't be public (which can result from some bug or permission misconfiguration). There are multiple ways to hide them, like disabling the View itself.
I offer an alternative way to do it - by event subscriber and a hook to allow some bundles through. This can be a drop-in code in your project.
Create event subscriber inside your module, for example:
File: my_module/src/EventSubscriber/MyModuleTaxonomySubscriber.php
<?php
namespace Drupal\my_module\EventSubscriber;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Event subscriber to disable taxonomy term paths.
*
* Show page not found by default for all taxonomy terms. It is possible
* to disable this behaviour per vocabulary.
*
* @package Drupal\my_module\MyModuleTaxonomySubscriber
*/
class MyModuleTaxonomySubscriber implements EventSubscriberInterface {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* MyModuleTaxonomySubscriber constructor.
*
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
*/
public function __construct(AccountInterface $current_user, ModuleHandlerInterface $module_handler) {
$this->currentUser = $current_user;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events['kernel.request'] = ['onRequest', 27];
return $events;
}
/**
* Show not found page when requesting canonical route.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* The event data object.
*/
public function onRequest(RequestEvent $event): void {
if ($event->getRequest()->get('exception') !== NULL) {
return;
}
if (!$this->currentUser->hasPermission('administer taxonomy')
&& $event->getRequest()->get('_route') === 'entity.taxonomy_term.canonical') {
/** @var \Drupal\taxonomy\Entity\Term $taxonomy_term */
$taxonomy_term = $event->getRequest()->get('taxonomy_term');
$skip_vocabularies = [];
// Invoke alter hooks so other modules could bypass it.
$this->moduleHandler->invokeAll('my_module_show_term_view', [&$skip_vocabularies]);
// If the current term is in list of allowed vocabularies,
// then we allow showing term view.
if (in_array($taxonomy_term->bundle(), $skip_vocabularies, TRUE)) {
return;
}
throw new NotFoundHttpException();
}
}
}
Create or modify your module services file:
File: my_module/my_module.services.yml
services:
my_module.taxonomy_subscriber:
class: Drupal\my_module\EventSubscriber\MyModuleTaxonomySubscriber
arguments: ['@current_user', '@module_handler']
tags:
- { name: event_subscriber }
Now after your module is enabled and caches rebuilt, /taxonomy/term/*
paths should show not found message.
To allow some taxonomy term bundles through, implement this hook.
File: my_module/my_module.module
/**
* Alter list of taxonomies that can show detail view.
*
* By default, all taxonomy term canonical routes show not found message.
* This alter hook can be used to show some terms when needed.
*
* @param array $skip_vocabularies
* Array of taxonomy term bundles to make publicly available.
*/
function my_module_my_module_show_term_view_alter(array &$skip_vocabularies) {
// Show tags vocabulary terms canonical route.
$skip_vocabularies[] = 'tags';
}