diff --git a/.cspell-project-words.txt b/.cspell-project-words.txt new file mode 100644 index 0000000000000000000000000000000000000000..25c2794156b0e14c165ed283bd786f6b576d6ac9 --- /dev/null +++ b/.cspell-project-words.txt @@ -0,0 +1 @@ +cacheexclude diff --git a/cacheexclude.info.yml b/cacheexclude.info.yml index 1af8a5ff96378b8be9aa396422938700710fdef7..33f63f089c5e501ab049471672f8e735b06c0992 100644 --- a/cacheexclude.info.yml +++ b/cacheexclude.info.yml @@ -2,5 +2,7 @@ name: Cache Exclude type: module description: 'Exclude certain pages from being cached.' version: VERSION -core_version_requirement: ^9 || ^10 +core_version_requirement: ^9 || ^10 || ^11 configure: cacheexclude.settings +dependencies: + - drupal:path_alias diff --git a/cacheexclude.services.yml b/cacheexclude.services.yml index 13f726e67641282d7c143c3f57b8ea8c999ac170..cfacbd382458da7bd3bbc8910a51b22c07b4afb4 100644 --- a/cacheexclude.services.yml +++ b/cacheexclude.services.yml @@ -1,5 +1,6 @@ services: cacheexclude_event_subscriber: class: Drupal\cacheexclude\EventSubscriber\CacheexcludeSubscriber + arguments: ['@config.factory', '@path.matcher', '@path.current', '@path_alias.manager', '@current_route_match', '@page_cache_kill_switch'] tags: - {name: event_subscriber} diff --git a/src/EventSubscriber/CacheexcludeSubscriber.php b/src/EventSubscriber/CacheexcludeSubscriber.php index 1a45cb0d7c8cd1c22cff6f74548e16353d89c0de..6513c71f0360907dc4b03aaa64c13ae76a9bbade 100644 --- a/src/EventSubscriber/CacheexcludeSubscriber.php +++ b/src/EventSubscriber/CacheexcludeSubscriber.php @@ -4,15 +4,88 @@ namespace Drupal\cacheexclude\EventSubscriber; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Path\PathMatcherInterface; +use Drupal\Core\Path\CurrentPathStack; +use Drupal\path_alias\AliasManagerInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\PageCache\ResponsePolicy\KillSwitch; use Drupal\node\NodeInterface; /** - * Class CacheexcludeSubscriber. + * Event subscriber for excluding pages from the cache. * * @package Drupal\cacheexclude. */ class CacheexcludeSubscriber implements EventSubscriberInterface { + /** + * The config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * The path matcher. + * + * @var \Drupal\Core\Path\PathMatcherInterface + */ + protected $pathMatcher; + + /** + * The current path stack. + * + * @var \Drupal\Core\Path\CurrentPathStack + */ + protected $currentPath; + + /** + * The alias manager. + * + * @var \Drupal\path_alias\AliasManagerInterface + */ + protected $aliasManager; + + /** + * The route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + protected $routeMatch; + + /** + * The page cache kill switch. + * + * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch + */ + protected $killSwitch; + + /** + * Constructs a new CacheexcludeSubscriber. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher + * The path matcher. + * @param \Drupal\Core\Path\CurrentPathStack $current_path + * The current path stack. + * @param \Drupal\path_alias\AliasManagerInterface $alias_manager + * The alias manager. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $kill_switch + * The page cache kill switch. + */ + public function __construct(ConfigFactoryInterface $config_factory, PathMatcherInterface $path_matcher, CurrentPathStack $current_path, AliasManagerInterface $alias_manager, RouteMatchInterface $route_match, KillSwitch $kill_switch) { + $this->configFactory = $config_factory; + $this->pathMatcher = $path_matcher; + $this->currentPath = $current_path; + $this->aliasManager = $alias_manager; + $this->routeMatch = $route_match; + $this->killSwitch = $kill_switch; + } + /** * {@inheritdoc} */ @@ -30,24 +103,24 @@ class CacheexcludeSubscriber implements EventSubscriberInterface { // Path checking routine. if ($this->checkPath() === TRUE) { // Disable page cache temporarily. - \Drupal::service('page_cache_kill_switch')->trigger(); + $this->killSwitch->trigger(); return; } // Check if current node type is one we want to exclude from the cache. - $node = \Drupal::routeMatch()->getParameter('node'); + $node = $this->routeMatch->getParameter('node'); if ($node instanceof NodeInterface) { $node_type = $node->getType(); } - $config = \Drupal::config('cacheexclude.settings'); + $config = $this->configFactory->get('cacheexclude.settings'); $node_types = $config->get('cacheexclude_node_types'); if (!is_null($node_types)) { $node_types = array_filter($node_types); if (isset($node_type) && in_array($node_type, $node_types, TRUE)) { // Disable page cache temporarily. - \Drupal::service('page_cache_kill_switch')->trigger(); + $this->killSwitch->trigger(); } } } @@ -60,17 +133,17 @@ class CacheexcludeSubscriber implements EventSubscriberInterface { */ private function checkPath() { // Get cacheexclude page configuration. - $config = \Drupal::config('cacheexclude.settings'); + $config = $this->configFactory->get('cacheexclude.settings'); // Only trim if config exists. $pages = !is_null($config->get('cacheexclude_list')) ? trim($config->get('cacheexclude_list')) : NULL; // If the current page is one we want to exclude from the cache, // disable page cache temporarily. if (!is_null($pages)) { - $current_path = \Drupal::service('path.current')->getPath(); - $current_path_alias = \Drupal::service('path_alias.manager')->getAliasByPath($current_path); - $path_matches = \Drupal::service('path.matcher')->matchPath($current_path, $pages); - $alias_path_matches = \Drupal::service('path.matcher')->matchPath($current_path_alias, $pages); + $current_path = $this->currentPath->getPath(); + $current_path_alias = $this->aliasManager->getAliasByPath($current_path); + $path_matches = $this->pathMatcher->matchPath($current_path, $pages); + $alias_path_matches = $this->pathMatcher->matchPath($current_path_alias, $pages); if ($path_matches || $alias_path_matches) { return TRUE; diff --git a/src/Form/AdminSettingsForm.php b/src/Form/AdminSettingsForm.php index c837dc4172e494857fcf4a3b0b3b095bf3fc26fb..aca49e17d55f08bd928d80242c2a73b2a968e504 100644 --- a/src/Form/AdminSettingsForm.php +++ b/src/Form/AdminSettingsForm.php @@ -61,7 +61,7 @@ class AdminSettingsForm extends ConfigFormBase { // Clear the page cache when new settings are added. drupal_flush_all_caches(); - $config = \Drupal::service('config.factory')->getEditable('cacheexclude.settings'); + $config = $this->configFactory->getEditable('cacheexclude.settings'); $config->set('cacheexclude_list', $form_state->getValue('cacheexclude_list'))->save(); $config->set('cacheexclude_node_types', $form_state->getValue('cacheexclude_node_types'))->save(); parent::submitForm($form, $form_state); diff --git a/tests/src/Functional/CacheExcludeHeaderTest.php b/tests/src/Functional/CacheExcludeHeaderTest.php index 2b8cfc910b81d72e202161b7b0370ae2265403c0..8ee4548caef5c606866721c97673962d20e939d4 100644 --- a/tests/src/Functional/CacheExcludeHeaderTest.php +++ b/tests/src/Functional/CacheExcludeHeaderTest.php @@ -64,7 +64,8 @@ class CacheExcludeHeaderTest extends BrowserTestBase { // No cache for <front>. $this->drupalGet($paths['excluded_path']); - $this->assertSession()->responseHeaderDoesNotExist('X-Drupal-Cache'); + // See https://www.drupal.org/node/2958442 + $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'UNCACHEABLE (response policy)'); // Cache for /node, but first request should MISS. $this->drupalGet($paths['cached_path']); diff --git a/tests/src/Functional/CacheExcludeNodeTypeTest.php b/tests/src/Functional/CacheExcludeNodeTypeTest.php index 8ec4019e30c281da45d276125874ace3b8ad6e90..c20e6f2747ca0fc5c84a26a52853e1ec00830702 100644 --- a/tests/src/Functional/CacheExcludeNodeTypeTest.php +++ b/tests/src/Functional/CacheExcludeNodeTypeTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\cacheexclude\Functional; -use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait; @@ -83,7 +82,8 @@ class CacheExcludeNodeTypeTest extends BrowserTestBase { // No cache for the article. $this->drupalGet($paths['article_path']); - $this->assertSession()->responseHeaderDoesNotExist('X-Drupal-Cache'); + // See https://www.drupal.org/node/2958442 + $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'UNCACHEABLE (response policy)'); // Cache for the page, but first request should MISS. $this->drupalGet($paths['page_path']); diff --git a/tests/src/Kernel/CacheExcludeMigrateTest.php b/tests/src/Kernel/CacheExcludeMigrateTest.php index 3c173a713a3131a0ec61eff6e9a535a99d2bbc15..d9863cd8498ff0d14845cf654bd44a0b130fcc26 100644 --- a/tests/src/Kernel/CacheExcludeMigrateTest.php +++ b/tests/src/Kernel/CacheExcludeMigrateTest.php @@ -19,6 +19,7 @@ class CacheExcludeMigrateTest extends MigrateDrupal7TestBase { // Text module is enabled because we need "text_with_summary" plugin. 'text', 'cacheexclude', + 'path_alias', ]; /**