diff --git a/.cspell-project-words.txt b/.cspell-project-words.txt new file mode 100644 index 0000000000000000000000000000000000000000..6907d42bd67e0b0ec3e1a2a87c8d149da6fe9803 --- /dev/null +++ b/.cspell-project-words.txt @@ -0,0 +1,7 @@ +# Views Advanced Cache dictionary + +# Initialism used in testing. +vact + +# Author names. +poindexter diff --git a/README.md b/README.md index bda891d62aa9acc4835dc0e8bd678fd41e60c954..47f7f67fb338e5fd665cba7d96505338fd5c9fbf 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ url.query_args:my_custom_filter ``` This can be used to override the default pager caching per all page query arguments instead only considering those -relevent to the view. +relevant to the view. Related Modules =============== diff --git a/composer.json b/composer.json index 24605653b443045a5628a468fd7b4c655bec9969..0363b5eae60ca745ab4588256bcd68da16011aba 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,17 @@ "name": "drupal/views_advanced_cache", "type": "drupal-module", "description": "Take more control of views caching with the ability to specify cache tags and contexts.", - "license": "GPL-2.0+", - "extra": { - "branch-alias": { - "dev-8.x-1.x": "1.x-dev" + "license": "GPL-2.0-or-later", + "homepage": "http://drupal.org/project/views_advanced_cache", + "support": { + "issues": "http://drupal.org/project/issues/views_advanced_cache", + "source": "https://git.drupalcode.org/project/views_advanced_cache" + }, + "authors": [ + { + "name": "Malcolm Poindexter", + "homepage": "https://www.drupal.org/u/malcolm_p", + "role": "contributor" } - } + ] } diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..f09956edf22fcb5cd85a894b1b6395133d201f6e --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ruleset name="drupal-views-advanced-cache"> + <description>PHP CodeSniffer configuration for Views Advanced Cache project.</description> + <rule ref="Drupal"/> + <rule ref="DrupalPractice"/> + <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses"> + <properties> + <property name="caseSensitive" value="true"/> + </properties> + </rule> + <arg name="extensions" value="php,inc,module,install,info,test,profile,theme"/> +</ruleset> diff --git a/src/Plugin/views/cache/AdvancedViewsCache.php b/src/Plugin/views/cache/AdvancedViewsCache.php index 9c3f6961d6c37fcbb7f643a6f63da5544d93b65e..ebefbc6a9b555f1b158ed2b30208e585ab91f062 100644 --- a/src/Plugin/views/cache/AdvancedViewsCache.php +++ b/src/Plugin/views/cache/AdvancedViewsCache.php @@ -4,15 +4,16 @@ namespace Drupal\views_advanced_cache\Plugin\views\cache; use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; use Drupal\Core\Link; -use Drupal\Core\Cache\CacheableDependencyInterface; -use Drupal\views\Views; +use Drupal\Core\Url; +use Drupal\Core\Utility\Token; use Drupal\views\Plugin\views\cache\CachePluginBase; use Drupal\views\ResultRow; +use Drupal\views\Views; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -51,16 +52,51 @@ class AdvancedViewsCache extends CachePluginBase { */ protected $time; - protected $lifespans = [60, 300, 900, 1800, 3600, 21600, 43200, 86400, 604800]; + /** + * Token service. + * + * @var \Drupal\Core\Utility\Token + */ + protected $token; + + /** + * Seconds used to build select field options for cache lifetime. + * + * @var array + */ + protected $lifespans = [ + 60, + 300, + 900, + 1800, + 3600, + 21600, + 43200, + 86400, + 604800, + ]; + /** + * Select field options array built from lifespans. + * + * @var array + */ public $lifespanOptions = []; /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, DateFormatterInterface $date_formatter, TimeInterface $datetime_time) { + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + DateFormatterInterface $date_formatter, + TimeInterface $datetime_time, + Token $token, + ) { $this->dateFormatter = $date_formatter; $this->time = $datetime_time; + $this->token = $token; parent::__construct($configuration, $plugin_id, $plugin_definition); @@ -77,7 +113,8 @@ class AdvancedViewsCache extends CachePluginBase { $plugin_id, $plugin_definition, $container->get('date.formatter'), - $container->get('datetime.time') + $container->get('datetime.time'), + $container->get('token') ); } @@ -85,21 +122,21 @@ class AdvancedViewsCache extends CachePluginBase { * {@inheritdoc} */ public function summaryTitle() { - // Display a summary of: cache tags, + // Display a summary of: cache tags. $num_cache_tags = count(array_merge($this->options['cache_tags'], $this->options['cache_tags_exclude']) ?: []); $cache_tags = ''; if ($num_cache_tags > 0) { $cache_tags = "$num_cache_tags tags"; } - // cache contexts, + // Cache contexts. $num_cache_contexts = count(array_merge($this->options['cache_contexts'], $this->options['cache_contexts_exclude']) ?: []); $cache_contexts = ''; if ($num_cache_contexts > 0) { $cache_contexts = "$num_cache_contexts contexts"; } - // and max-age. + // And max-age. $results_lifespan = $this->getLifespan('results'); $output_lifespan = $this->getLifespan('output'); $lifetime = ''; @@ -146,7 +183,7 @@ class AdvancedViewsCache extends CachePluginBase { * * By prefixing the tag with a "-" it will * be removed from the cache metadata. Removing cache tags should be - * used sparingly but may be useful to resolve issues uneccessary cache tags + * used sparingly but may be useful to resolve issues unnecessary cache tags * added by other modules * (ex. https://www.drupal.org/project/drupal/issues/2352175). * @@ -154,7 +191,12 @@ class AdvancedViewsCache extends CachePluginBase { */ public function buildCacheTagOptions(array &$form, FormStateInterface $form_state) { $cache_tags_docs = Link::fromTextAndUrl('documentation', Url::fromUri('https://www.drupal.org/docs/8/api/cache-api/cache-tags'))->toString(); - $cache_tags_exclude = array_map(function ($c) { return "- $c"; }, $this->options['cache_tags_exclude']); + $cache_tags_exclude = array_map( + function ($c) { + return "- $c"; + }, + $this->options['cache_tags_exclude'] + ); // Filter out some of the default cache tags we don't care about. // This should mostly be the entity_type list cache tags ex. node_list. $default_cache_tags = array_diff(parent::getCacheTags(), ['extensions', 'config:views.view.' . $this->view->id()]); @@ -177,7 +219,7 @@ class AdvancedViewsCache extends CachePluginBase { '#default_value' => implode("\n", array_merge($cache_tags, $cache_tags_exclude)), ]; - $optgroup_arguments = (string) t('Arguments'); + $optgroup_arguments = (string) $this->t('Arguments'); $options = []; // $globalTokens = $this->getAvailableGlobalTokens(FALSE, ['current-user']); @@ -231,11 +273,19 @@ class AdvancedViewsCache extends CachePluginBase { '#open' => TRUE, ]; - $cache_contexts_exclude = array_map(function ($c) { return "- $c"; }, $this->options['cache_contexts_exclude']); + $cache_contexts_exclude = array_map( + function ($c) { + return "- $c"; + }, + $this->options['cache_contexts_exclude'] + ); $form['cache_contexts']['cache_contexts'] = [ '#type' => 'textarea', '#title' => $this->t('Cache Contexts'), - '#default_value' => implode("\n", array_merge($this->options['cache_contexts'], $cache_contexts_exclude)) , + '#default_value' => implode("\n", array_merge( + $this->options['cache_contexts'], + $cache_contexts_exclude + )), '#description' => $this->t('List cache contexts (separated by new lines).'), ]; } @@ -302,7 +352,7 @@ class AdvancedViewsCache extends CachePluginBase { public function validateOptionsForm(&$form, FormStateInterface $form_state) { $lifespan = []; $cache_lifespan = $form_state->getValue(['cache_options', 'cache_lifespan']); - // Validate lifespan format + // Validate lifespan format. foreach (['output_lifespan', 'results_lifespan'] as $field) { $custom = $cache_lifespan[$field] == 'custom'; if ($custom && !is_numeric($cache_lifespan[$field . '_custom'])) { @@ -387,7 +437,7 @@ class AdvancedViewsCache extends CachePluginBase { $default_cache_tags = parent::getCacheTags(); $cache_tags = array_map(function ($tag) { $value = $this->view->getStyle()->tokenizeValue($tag, 0); - return \Drupal::token()->replace($value); + return $this->token->replace($value); }, $cache_tags); $cache_tags = Cache::mergeTags($cache_tags, $default_cache_tags); } diff --git a/tests/modules/views_advanced_cache_test/views_advanced_cache_test.info.yml b/tests/modules/views_advanced_cache_test/views_advanced_cache_test.info.yml index 568839ec1a91f33e68fcbc57af63e54cbf8671b4..400908a5364ba47e18409ba43f9024a790d5b835 100644 --- a/tests/modules/views_advanced_cache_test/views_advanced_cache_test.info.yml +++ b/tests/modules/views_advanced_cache_test/views_advanced_cache_test.info.yml @@ -1,12 +1,11 @@ name: Views Advanced Cache Tests type: module description: Test configuration for the views_advanced_cache module. -core_version_requirement: ^8.7.7 || ^9 || ^10 -hidden: true +package: Testing dependencies: -- drupal:node -- drupal:text -- drupal:field -- drupal:menu_ui -- drupal:views -- views_advanced_cache:views_advanced_cache + - drupal:node + - drupal:text + - drupal:field + - drupal:menu_ui + - drupal:views + - views_advanced_cache:views_advanced_cache diff --git a/tests/modules/views_advanced_cache_test/views_advanced_cache_test.module b/tests/modules/views_advanced_cache_test/views_advanced_cache_test.module index 3d383ef4e41c11361e310226732df3cbadfd760b..0e9b5eec41d56f07b21ee2416fa0598d47a6b83f 100644 --- a/tests/modules/views_advanced_cache_test/views_advanced_cache_test.module +++ b/tests/modules/views_advanced_cache_test/views_advanced_cache_test.module @@ -4,8 +4,9 @@ * @file * A test module for views_advanced_cache. */ -use Drupal\node\NodeInterface; + use Drupal\Core\Cache\Cache; +use Drupal\node\NodeInterface; /** * Invalidate a custom node list cache tag on node save. diff --git a/tests/src/Functional/PageCacheTest.php b/tests/src/Functional/PageCacheTest.php index 00432bfc4b9c780e3ef1dde540f960ba53369157..5543a0d3d1cb600a0232adb1e8f4291ac4c5eb7f 100644 --- a/tests/src/Functional/PageCacheTest.php +++ b/tests/src/Functional/PageCacheTest.php @@ -2,13 +2,13 @@ namespace Drupal\Tests\views_advanced_cache\Functional; -use Drupal\Tests\BrowserTestBase; use Drupal\Core\Url; +use Drupal\Tests\BrowserTestBase; use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait; use Drupal\views\Entity\View; /** - * Class PageCacheTest. + * Test advanced cache tags are applied and removed from view as configured. * * @group views_advanced_cache */ @@ -16,27 +16,44 @@ class PageCacheTest extends BrowserTestBase { use AssertPageCacheContextsAndTagsTrait; - protected static $modules = ['views', 'views_advanced_cache_test']; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'views', + 'views_advanced_cache_test', + ]; /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; + /** + * Disable config schema checking temporarily until schema added. + * + * @var bool + * + * @phpcs:disable DrupalPractice.Objects.StrictSchemaDisabled.StrictConfigSchema + */ protected $strictConfigSchema = FALSE; + // phpcs:enable /** * Test page response cache tags for a view. */ public function testPageCache() { - // Create some test content, + // Create some test content. $nodes = []; - for ($i = 1; $i <= 3; $i++) { - $nodes[] = $this->drupalCreateNode(['title' => "Test $i", 'type' => 'test']); - } $node_cache_tags = []; - foreach ($nodes as $node) { - $node_cache_tags[] = 'node:' . $node->id(); + for ($i = 1; $i <= 3; $i++) { + $test_node = $this->drupalCreateNode([ + 'title' => "Test $i", + 'type' => 'test', + ]); + $node_cache_tags[] = 'node:' . $test_node->id(); + + $nodes[] = $test_node; } // And load the page view with our test cache contexts and tags. @@ -56,13 +73,18 @@ class PageCacheTest extends BrowserTestBase { ]; $url = Url::fromRoute('view.views_advanced_cache_test.page_test', []); - $this->assertPageCacheContextsAndTags($url, $cache_contexts, array_merge($cache_tags, $node_cache_tags)); + $this->assertPageCacheContextsAndTags( + $url, + $cache_contexts, + array_merge($cache_tags, $node_cache_tags) + ); - // Then remove our changes to the contexts and tags, + // Then remove our changes to the contexts and tags. $display_name = 'page_test'; - /** @var Drupal\views\Entity\Vie $view */ + /** @var Drupal\views\Entity\View $view */ $view = View::load('views_advanced_cache_test'); - $cache_options = $view->getDisplay($display_name)['display_options']['cache']['options'] ?? []; + $cache_options = $view + ->getDisplay($display_name)['display_options']['cache']['options'] ?? []; $cache_options['cache_tags'] = []; $cache_options['cache_tags_exclude'] = []; $cache_options['cache_contexts'] = []; @@ -89,7 +111,11 @@ class PageCacheTest extends BrowserTestBase { ]; $url = Url::fromRoute('view.views_advanced_cache_test.page_test', []); - $this->assertPageCacheContextsAndTags($url, $cache_contexts, array_merge($cache_tags, $node_cache_tags)); + $this->assertPageCacheContextsAndTags( + $url, + $cache_contexts, + array_merge($cache_tags, $node_cache_tags) + ); } } diff --git a/tests/src/Functional/ViewsCacheMetadataTest.php b/tests/src/Functional/ViewsCacheMetadataTest.php index 3be6e782ec6bbc16ec60828ce75d62e2f81727ff..13403a7290dbf53ef6afcb15167d657b773f0fc7 100644 --- a/tests/src/Functional/ViewsCacheMetadataTest.php +++ b/tests/src/Functional/ViewsCacheMetadataTest.php @@ -2,29 +2,42 @@ namespace Drupal\Tests\views_advanced_cache\Functional; -use Drupal\Tests\BrowserTestBase; use Drupal\Component\Utility\NestedArray; -use Drupal\views\Views; -use Drupal\views\ViewExecutable; +use Drupal\Tests\BrowserTestBase; use Drupal\views\Entity\View; +use Drupal\views\ViewExecutable; +use Drupal\views\Views; /** - * Class ViewsCacheMetadataTest. + * Test manipulating the different caching options for a view using VAC. * * @group views_advanced_cache */ class ViewsCacheMetadataTest extends BrowserTestBase { - protected static $modules = ['views', 'views_advanced_cache_test']; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'views', + 'views_advanced_cache_test', + ]; /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; + /** + * Disable config schema checking temporarily until schema added. + * + * @var bool + * + * @phpcs:disable DrupalPractice.Objects.StrictSchemaDisabled.StrictConfigSchema + */ protected $strictConfigSchema = FALSE; + // phpcs:enable - // # Tests. /** * Test changing the view cache metadata. */ @@ -32,13 +45,13 @@ class ViewsCacheMetadataTest extends BrowserTestBase { $view_name = 'views_advanced_cache_test'; $display_name = 'block_test'; - // Render the view with the default cache tags, + // Render the view with the default cache tags. $view = Views::getView($view_name); $element = $this->render($view, $display_name); // And verify that the default node entity_type list cache tag is present. $this->assertTrue(in_array('node_list', $element['#cache']['tags']), 'The view has the node_list cache tag.'); - // Load the view config entity, + // Load the view config entity. $cacheOptions = $this->getCacheOptions($view->storage, $display_name); // And update its cache tags. $cacheOptions = NestedArray::mergeDeep($cacheOptions, [ @@ -108,7 +121,6 @@ class ViewsCacheMetadataTest extends BrowserTestBase { $this->assertTrue(in_array("node_test:$nid", $element['#cache']['tags']), 'The view has the node_test{nid} cache tag.'); } - // # Helpers. /** * Build a renderable array for the view display. */ diff --git a/tests/src/Kernel/ViewsRowCacheTest.php b/tests/src/Kernel/ViewsRowCacheTest.php index bb9cc66a110e1c6d94528fd3533e2d6f5eb8bd3d..39514135d55a39e7f2763d3f2da375780d3d0a78 100644 --- a/tests/src/Kernel/ViewsRowCacheTest.php +++ b/tests/src/Kernel/ViewsRowCacheTest.php @@ -27,13 +27,14 @@ class ViewsRowCacheTest extends ViewsKernelTestBase { use UserCreationTrait; /** - * Turn off strict config schema check. + * Disable config schema checking temporarily until schema added. * * @var bool * - * @todo Add config schema to this module and set this back to TRUE. + * @phpcs:disable DrupalPractice.Objects.StrictSchemaDisabled.StrictConfigSchema */ protected $strictConfigSchema = FALSE; + // phpcs:enable /** * {@inheritdoc}