From ec42ee0ad00ae13aa3b73503612e899dbc309105 Mon Sep 17 00:00:00 2001
From: Brian Canini <canini.16@osu.edu>
Date: Tue, 16 Jun 2020 15:14:40 -0400
Subject: [PATCH] Updating drupal/google_tag (1.3.0 => 1.4.0)

---
 composer.json                                 |  2 +-
 composer.lock                                 | 23 +++---
 vendor/composer/installed.json                | 23 +++---
 web/modules/google_tag/README.md              |  4 +-
 web/modules/google_tag/composer.json          |  5 +-
 web/modules/google_tag/google_tag.info.yml    |  8 +-
 web/modules/google_tag/google_tag.install     | 34 ++++----
 web/modules/google_tag/google_tag.module      | 12 ++-
 web/modules/google_tag/src/ConditionBase.php  |  8 +-
 .../src/ContainerAccessControlHandler.php     | 27 ++++--
 .../google_tag/src/ContainerController.php    |  2 +-
 .../google_tag/src/Entity/Container.php       | 22 ++---
 .../src/Entity/ContainerManager.php           | 82 +++++++++++++++++--
 .../src/Entity/ContainerManagerInterface.php  | 30 +++++++
 .../google_tag/src/Form/ContainerForm.php     |  9 --
 .../google_tag/src/Form/ContainerTrait.php    | 39 ++++++---
 .../google_tag/src/Form/SettingsForm.php      | 10 ++-
 .../src/Plugin/Condition/Domain.php           |  6 +-
 .../tests/src/Functional/GTMTestBase.php      | 60 ++++++++++----
 19 files changed, 277 insertions(+), 129 deletions(-)

diff --git a/composer.json b/composer.json
index 311a744cee..b1fc9987cc 100644
--- a/composer.json
+++ b/composer.json
@@ -126,7 +126,7 @@
         "drupal/focal_point": "1.2",
         "drupal/geolocation": "1.10",
         "drupal/google_analytics": "2.5",
-        "drupal/google_tag": "1.3",
+        "drupal/google_tag": "1.4",
         "drupal/honeypot": "1.30",
         "drupal/image_popup": "1.1",
         "drupal/inline_entity_form": "1.0-rc1",
diff --git a/composer.lock b/composer.lock
index 992b987fbc..79999a2a0b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "befbf9746c7e545e958d0e71cb89862f",
+    "content-hash": "e15289ca1fab854688e9bf41d83ffb0b",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -5168,29 +5168,26 @@
         },
         {
             "name": "drupal/google_tag",
-            "version": "1.3.0",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/google_tag.git",
-                "reference": "8.x-1.3"
+                "reference": "8.x-1.4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/google_tag-8.x-1.3.zip",
-                "reference": "8.x-1.3",
-                "shasum": "2619d19c9a6b1d8f7a2d0c2715d009cf3b11d697"
+                "url": "https://ftp.drupal.org/files/projects/google_tag-8.x-1.4.zip",
+                "reference": "8.x-1.4",
+                "shasum": "1bdc6f93d1c79c27738320597f2185f5de37432f"
             },
             "require": {
-                "drupal/core": "~8.0"
+                "drupal/core": "^8.8 || ^9"
             },
             "type": "drupal-module",
             "extra": {
-                "branch-alias": {
-                    "dev-1.x": "1.x-dev"
-                },
                 "drupal": {
-                    "version": "8.x-1.3",
-                    "datestamp": "1575649087",
+                    "version": "8.x-1.4",
+                    "datestamp": "1591383264",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -5199,7 +5196,7 @@
             },
             "notification-url": "https://packages.drupal.org/8/downloads",
             "license": [
-                "GPL 2.0"
+                "GPL-2.0-or-later"
             ],
             "authors": [
                 {
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 6611d6c969..2e6d6a2fff 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -5322,30 +5322,27 @@
     },
     {
         "name": "drupal/google_tag",
-        "version": "1.3.0",
-        "version_normalized": "1.3.0.0",
+        "version": "1.4.0",
+        "version_normalized": "1.4.0.0",
         "source": {
             "type": "git",
             "url": "https://git.drupalcode.org/project/google_tag.git",
-            "reference": "8.x-1.3"
+            "reference": "8.x-1.4"
         },
         "dist": {
             "type": "zip",
-            "url": "https://ftp.drupal.org/files/projects/google_tag-8.x-1.3.zip",
-            "reference": "8.x-1.3",
-            "shasum": "2619d19c9a6b1d8f7a2d0c2715d009cf3b11d697"
+            "url": "https://ftp.drupal.org/files/projects/google_tag-8.x-1.4.zip",
+            "reference": "8.x-1.4",
+            "shasum": "1bdc6f93d1c79c27738320597f2185f5de37432f"
         },
         "require": {
-            "drupal/core": "~8.0"
+            "drupal/core": "^8.8 || ^9"
         },
         "type": "drupal-module",
         "extra": {
-            "branch-alias": {
-                "dev-1.x": "1.x-dev"
-            },
             "drupal": {
-                "version": "8.x-1.3",
-                "datestamp": "1575649087",
+                "version": "8.x-1.4",
+                "datestamp": "1591383264",
                 "security-coverage": {
                     "status": "covered",
                     "message": "Covered by Drupal's security advisory policy"
@@ -5355,7 +5352,7 @@
         "installation-source": "dist",
         "notification-url": "https://packages.drupal.org/8/downloads",
         "license": [
-            "GPL 2.0"
+            "GPL-2.0-or-later"
         ],
         "authors": [
             {
diff --git a/web/modules/google_tag/README.md b/web/modules/google_tag/README.md
index ebea4f7c8f..eb14d22b19 100644
--- a/web/modules/google_tag/README.md
+++ b/web/modules/google_tag/README.md
@@ -48,14 +48,14 @@ conditions on which the tags are inserted on a page response. Conditions exist
 for: page paths, user roles, and response statuses. See:
 
  * Administration » Configuration » System » Google Tag Manager  
- * admin/config/system/google_tag/settings
+ * admin/config/system/google-tag/settings
 
 From the container management page, manage the containers to be inserted on a
 page response. Add one or more containers with separate container IDs and the
 snippet insertion conditions. See:
 
  * Administration » Configuration » System » Google Tag Manager  
- * admin/config/system/google_tag
+ * admin/config/system/google-tag
 
 For development purposes, create a GTM environment for your website and enter
 the 'environment ID' on the 'Advanced' tab of the settings form.
diff --git a/web/modules/google_tag/composer.json b/web/modules/google_tag/composer.json
index 66fb62a149..47b00aa103 100644
--- a/web/modules/google_tag/composer.json
+++ b/web/modules/google_tag/composer.json
@@ -2,5 +2,8 @@
     "name": "drupal/google_tag",
     "description": "Allows your website analytics to be managed using Google Tag Manager.",
     "type": "drupal-module",
-    "license": "GPL 2.0"
+    "license": "GPL-2.0-or-later",
+    "require": {
+        "drupal/core": "^8.8 || ^9"
+    }
 }
diff --git a/web/modules/google_tag/google_tag.info.yml b/web/modules/google_tag/google_tag.info.yml
index b7d194a483..9b15fbf6a8 100644
--- a/web/modules/google_tag/google_tag.info.yml
+++ b/web/modules/google_tag/google_tag.info.yml
@@ -2,10 +2,10 @@ name: 'Google Tag Manager'
 type: module
 description: 'Allows your website analytics to be managed using Google Tag Manager.'
 package: 'Statistics'
-core: 8.x
+core_version_requirement: ^8.8 || ^9
 configure: google_tag.settings_form
 
-# Information added by Drupal.org packaging script on 2019-12-06
-version: '8.x-1.3'
+# Information added by Drupal.org packaging script on 2020-06-05
+version: '8.x-1.4'
 project: 'google_tag'
-datestamp: 1575649137
+datestamp: 1591383266
diff --git a/web/modules/google_tag/google_tag.install b/web/modules/google_tag/google_tag.install
index 1a3be9b019..2f84f4985d 100644
--- a/web/modules/google_tag/google_tag.install
+++ b/web/modules/google_tag/google_tag.install
@@ -14,17 +14,17 @@
  * Implements hook_requirements().
  */
 function google_tag_requirements($phase) {
-  $requirements = array();
+  $requirements = [];
   if ($phase == 'runtime') {
     $containers = \Drupal::service('entity_type.manager')->getStorage('google_tag_container')->loadMultiple();
     if (empty($containers)) {
       // Google Tag Manager container ID has not been set.
-      $requirements['google_tag'] = array(
+      $requirements['google_tag'] = [
         'title' => t('Google Tag Manager'),
-        'description' => t('Configure default settings on the <a href=":url1">module settings page</a>. Afterwards, add a container on the <a href=":url2">container management page</a>.', array(':url1' => Url::fromRoute('google_tag.settings_form')->toString(), ':url2' => Url::fromRoute('entity.google_tag_container.collection')->toString())),
+        'description' => t('Configure default settings on the <a href=":url1">module settings page</a>. Afterwards, add a container on the <a href=":url2">container management page</a>.', [':url1' => Url::fromRoute('google_tag.settings_form')->toString(), ':url2' => Url::fromRoute('entity.google_tag_container.collection')->toString()]),
         'severity' => REQUIREMENT_WARNING,
         'value' => t('Not configured'),
-      );
+      ];
     }
   }
   if ($phase == 'runtime' || $phase == 'update' || $phase == 'install') {
@@ -33,12 +33,12 @@ function google_tag_requirements($phase) {
     $directory = \Drupal::config('google_tag.settings')->get('uri');
     if (empty($directory)) {
       if ($phase == 'runtime' || $phase == 'update') {
-        $requirements['google_tag_snippet_parent_directory'] = array(
+        $requirements['google_tag_snippet_parent_directory'] = [
           'title' => t('Google Tag Manager'),
-          'description' => t('The snippet parent directory is not set. Configure default settings on the <a href=":url1">module settings page</a>.', array(':url1' => Url::fromRoute('google_tag.settings_form')->toString())),
+          'description' => t('The snippet parent directory is not set. Configure default settings on the <a href=":url1">module settings page</a>.', [':url1' => Url::fromRoute('google_tag.settings_form')->toString()]),
           'severity' => REQUIREMENT_ERROR,
           'value' => t('Not configured'),
-        );
+        ];
         return $requirements;
       }
       $directory = 'public:/';
@@ -66,40 +66,40 @@ function google_tag_requirements($phase) {
       }
 
       if (!$is_directory) {
-        $error = t('The directory %directory does not exist.', array('%directory' => $path));
+        $error = t('The directory %directory does not exist.', ['%directory' => $path]);
         $description = t('An automated attempt to create the directory failed, possibly due to a permissions problem. Create the directory and make it writable.');
         $value = t('Does not exist');
       }
       elseif (!$is_writable) {
-        $error = t('The directory %directory is not writable.', array('%directory' => $path));
+        $error = t('The directory %directory is not writable.', ['%directory' => $path]);
         $description = t('An automated attempt to make the directory writable failed, possibly due to a permissions problem. Make the directory writable.');
         $value = t('Not writable');
       }
       else {
-        $error = t('The directory %directory is not searchable.', array('%directory' => $path));
+        $error = t('The directory %directory is not searchable.', ['%directory' => $path]);
         $description = t('An automated attempt to make the directory searchable failed, possibly due to a permissions problem. Make the directory searchable.');
         $value = t('Not searchable');
       }
       $extra = '';
       if ($phase == 'install') {
-        $extra = t('For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', array(':handbook_url' => 'https://www.drupal.org/server-permissions'));
+        $extra = t('For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
         $value = '';
       }
-      $description = array(
+      $description = [
         '#type' => 'inline_template',
         '#template' => '{{ error }} {{ description }} {{ extra }}',
-        '#context' => array(
+        '#context' => [
           'error' => $error,
           'description' => $description,
           'extra' => $extra,
-        ),
-      );
-      $requirements['google_tag_snippet_directory'] = array(
+        ],
+      ];
+      $requirements['google_tag_snippet_directory'] = [
         'title' => t('Google Tag Manager snippet directory'),
         'description' => $description,
         'severity' => REQUIREMENT_ERROR,
         'value' => $value,
-      );
+      ];
     }
   }
   return $requirements;
diff --git a/web/modules/google_tag/google_tag.module b/web/modules/google_tag/google_tag.module
index 9e3db9714c..b622e66497 100644
--- a/web/modules/google_tag/google_tag.module
+++ b/web/modules/google_tag/google_tag.module
@@ -12,6 +12,7 @@
 
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManager;
 
 /**
  * Default for matching all items except listed.
@@ -38,6 +39,7 @@ function google_tag_help($route_name, RouteMatchInterface $route_match) {
  * Implements hook_rebuild().
  */
 function google_tag_rebuild() {
+  _google_tag_assets_delete();
   $rebuild_snippets = \Drupal::config('google_tag.settings')->get('rebuild_snippets');
   if ($rebuild_snippets) {
     _google_tag_assets_create();
@@ -52,6 +54,14 @@ function _google_tag_assets_create() {
   $manager->createAllAssets();
 }
 
+/**
+ * Deletes snippet files for enabled containers.
+ */
+function _google_tag_assets_delete() {
+  $manager = \Drupal::service('google_tag.container_manager');
+  $manager->deleteAllAssets();
+}
+
 /**
  * Implements hook_page_attachments().
  */
@@ -106,7 +116,7 @@ function google_tag_plugin_filter_condition_alter(array &$definitions, array $ex
  */
 function _file_prepare_directory(&$directory, $options = FileSystemInterface::MODIFY_PERMISSIONS) {
   $file_system = \Drupal::service('file_system');
-  if (!$file_system->validScheme($file_system->uriScheme($directory))) {
+  if (!\Drupal::service('stream_wrapper_manager')->isValidScheme(StreamWrapperManager::getScheme($directory))) {
     // Only trim if we're not dealing with a stream.
     $directory = rtrim($directory, '/\\');
   }
diff --git a/web/modules/google_tag/src/ConditionBase.php b/web/modules/google_tag/src/ConditionBase.php
index ce9c19e56f..22c6ed6590 100644
--- a/web/modules/google_tag/src/ConditionBase.php
+++ b/web/modules/google_tag/src/ConditionBase.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\google_tag;
 
-use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Condition\ConditionInterface;
 use Drupal\Core\Executable\ExecutableManagerInterface;
 use Drupal\Core\Executable\ExecutablePluginBase;
@@ -65,6 +64,13 @@ abstract class ConditionBase extends ExecutablePluginBase implements ConditionIn
    */
   protected $options = [];
 
+  /**
+   * The selected options (for the summary message).
+   *
+   * @var array
+   */
+  protected $values = [];
+
   /**
    * {@inheritdoc}
    */
diff --git a/web/modules/google_tag/src/ContainerAccessControlHandler.php b/web/modules/google_tag/src/ContainerAccessControlHandler.php
index a385fa26c7..db5c2db691 100644
--- a/web/modules/google_tag/src/ContainerAccessControlHandler.php
+++ b/web/modules/google_tag/src/ContainerAccessControlHandler.php
@@ -8,7 +8,6 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableDependencyInterface;
 // use Drupal\Core\Condition\ConditionAccessResolverTrait;
-use Drupal\Core\Condition\ConditionInterface;
 use Drupal\Core\Entity\EntityAccessControlHandler;
 use Drupal\Core\Entity\EntityHandlerInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -43,6 +42,13 @@ class ContainerAccessControlHandler extends EntityAccessControlHandler implement
    */
   protected $contextRepository;
 
+  /**
+   * The container entity for which to check access.
+   *
+   * @var \Drupal\google_tag\Entity\Container
+   */
+  protected $entity;
+
   /**
    * Constructs a container access control handler.
    *
@@ -82,8 +88,8 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       return AccessResult::forbidden()->addCacheableDependency($entity);
     }
 
-   // @todo Why is this not default code for an entity that uses the condition
-   // plugin interface? Most of it applies generally.
+    // @todo Why is this not default code for an entity that uses the condition
+    // plugin interface? Most of it applies generally.
 
     // Store entity to have access in resolveConditions().
     /** @var \Drupal\google_tag\Entity\Container $entity */
@@ -112,12 +118,17 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       // Because cacheable metadata might be missing, forbid cache write.
       $access = AccessResult::forbidden()->setCacheMaxAge(0);
     }
+/*
     elseif ($missing_value) {
-      // The contexts exist but have no value. Allow access without
-      // disabling caching. For example the node type condition will have a
-      // missing context on any non-node route like the frontpage.
+      // The context exists but has no value. For example, the node type
+      // condition will have a missing context value on any non-node route like
+      // the frontpage.
+      // @todo Checking this state here prevents the evaluation of other
+      // conditions. If this condition does not apply, then that should NOT
+      // preclude further evaluation NOR result in automatic access.
       $access = AccessResult::allowed();
     }
+*/
     elseif ($this->resolveConditions($conditions, 'and') !== FALSE) {
       $access = AccessResult::allowed();
     }
@@ -153,7 +164,9 @@ protected function mergeCacheabilityFromConditions(AccessResult $access, array $
   }
 
   /**
-   * Override the resolveConditions() routine to avoid these calls:
+   * Override the resolveConditions() routine.
+   *
+   * Avoid these calls:
    *   $condition->execute()
    *   $this->executableManager->execute($this);
    * on plugins defined by this module.
diff --git a/web/modules/google_tag/src/ContainerController.php b/web/modules/google_tag/src/ContainerController.php
index e7856c711a..7a246440d6 100644
--- a/web/modules/google_tag/src/ContainerController.php
+++ b/web/modules/google_tag/src/ContainerController.php
@@ -25,7 +25,7 @@ class ContainerController extends EntityController {
    */
   public function addTitle($entity_type_id) {
     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
-    return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getLowercaseLabel()]);
+    return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getSingularLabel()]);
   }
 
   /**
diff --git a/web/modules/google_tag/src/Entity/Container.php b/web/modules/google_tag/src/Entity/Container.php
index 851e77e739..8b5f113674 100644
--- a/web/modules/google_tag/src/Entity/Container.php
+++ b/web/modules/google_tag/src/Entity/Container.php
@@ -6,7 +6,6 @@
 use Drupal\Core\Condition\ConditionPluginCollection;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 
@@ -15,7 +14,10 @@
  *
  * @ConfigEntityType(
  *   id = "google_tag_container",
- *   label = @Translation("Container configuration"),
+ *   label = @Translation("Container"),
+ *   label_singular = @Translation("container"),
+ *   label_plural = @Translation("containers"),
+ *   label_collection = @Translation("Containers"),
  *   handlers = {
  *     "storage" = "Drupal\Core\Config\Entity\ConfigEntityStorage",
  *     "list_builder" = "Drupal\google_tag\ContainerListBuilder",
@@ -277,13 +279,6 @@ protected function scriptSnippet() {
     if ($compact) {
       $script = str_replace(["\n", '  '], '', $script);
     }
-/*
-    $script = <<<EOS
-<!-- Google Tag Manager -->
-$script
-<!-- End Google Tag Manager -->
-EOS;
-*/
     return $script;
   }
 
@@ -307,13 +302,6 @@ protected function noscriptSnippet() {
     if ($compact) {
       $noscript = str_replace("\n", '', $noscript);
     }
-/*
-    $noscript = <<<EOS
-<!-- Google Tag Manager -->
-$noscript
-<!-- End Google Tag Manager -->
-EOS;
-*/
     return $noscript;
   }
 
@@ -456,7 +444,7 @@ protected function pathCheck() {
     else {
       $request = \Drupal::request();
       $current_path = \Drupal::service('path.current');
-      $alias_manager = \Drupal::service('path.alias_manager');
+      $alias_manager = \Drupal::service('path_alias.manager');
       $path_matcher = \Drupal::service('path.matcher');
       // @todo Are not some paths case sensitive???
       // Compare the lowercase path alias (if any) and internal path.
diff --git a/web/modules/google_tag/src/Entity/ContainerManager.php b/web/modules/google_tag/src/Entity/ContainerManager.php
index 805fdfc2b3..f266d285cc 100644
--- a/web/modules/google_tag/src/Entity/ContainerManager.php
+++ b/web/modules/google_tag/src/Entity/ContainerManager.php
@@ -2,8 +2,7 @@
 
 namespace Drupal\google_tag\Entity;
 
-use Drupal\google_tag\Entity\ContainerManagerInterface;
-
+// use Drupal\google_tag\Entity\ContainerManagerInterface;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -35,7 +34,7 @@ class ContainerManager implements ContainerManagerInterface {
 
   /**
    * The file system.
-  *
+   *
    * @var \Drupal\Core\File\FileSystemInterface
    */
   protected $fileSystem;
@@ -49,7 +48,7 @@ class ContainerManager implements ContainerManagerInterface {
 
   /**
    * The logger.
-  *
+   *
    * @var \Psr\Log\LoggerInterface
    */
   protected $logger;
@@ -137,7 +136,9 @@ public function displayMessage($message, array $args = [], $type = MessengerInte
    *   The entity ID array.
    */
   public function loadContainerIDs() {
-    return \Drupal::entityQuery('google_tag_container')
+    return $this->entityTypeManager
+      ->getStorage('google_tag_container')
+      ->getQuery()
       ->condition('status', 1)
       ->sort('weight')
       ->execute();
@@ -154,6 +155,11 @@ public function getScriptAttachments(array &$attachments) {
         continue;
       }
 
+      if (!$this->findAssets($container)) {
+        // Create snippet files (e.g. after cache rebuild).
+        $this->createAssets($container);
+      }
+
       static $weight = 9;
       $include_script_as_file = \Drupal::config('google_tag.settings')->get('include_file');
       $include_classes = $container->get('include_classes');
@@ -203,20 +209,78 @@ public function getNoScriptAttachments(array &$page) {
   public function createAllAssets() {
     $ids = $this->loadContainerIDs();
     if (!$ids) {
-      return;
+      return TRUE;
     }
+    // Create snippet files for enabled containers.
+    $containers = $this->entityTypeManager->getStorage('google_tag_container')->loadMultiple($ids);
+    $result = TRUE;
+    foreach ($containers as $container) {
+      $result = !$this->createAssets($container) ? FALSE : $result;
+    }
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAllAssets() {
     if (\Drupal::config('google_tag.settings')->get('flush_snippets')) {
       $directory = \Drupal::config('google_tag.settings')->get('uri');
       if (!empty($directory)) {
         // Remove any stale files (e.g. module update or machine name change).
-        $this->fileSystem->deleteRecursive($directory . '/google_tag');
+        return $this->fileSystem->deleteRecursive($directory . '/google_tag');
       }
     }
-    // Create snippet files for enabled containers.
+
+    $ids = $this->loadContainerIDs();
+    if (!$ids) {
+      return TRUE;
+    }
+    // Delete snippet files for enabled containers.
     $containers = $this->entityTypeManager->getStorage('google_tag_container')->loadMultiple($ids);
     $result = TRUE;
     foreach ($containers as $container) {
-      $result = !$this->createAssets($container) ? FALSE : $result;
+      $result = !$this->deleteAssets($container) ? FALSE : $result;
+    }
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAssets(ConfigEntityInterface $container) {
+    $include_classes = $container->get('include_classes');
+    $types = $include_classes ? ['data_layer', 'script', 'noscript'] : ['script', 'noscript'];
+    $directory = $container->snippetDirectory();
+    $result = $this->fileSystem->deleteRecursive($directory);
+
+    $args = ['@count' => count($types), '%container' => $container->get('label')];
+    if (!$result) {
+      $message = 'An error occurred deleting @count snippet files for %container container. Contact the site administrator if this persists.';
+      $this->displayMessage($message, $args, MessengerInterface::TYPE_ERROR);
+      $this->logger->error($message, $args);
+    }
+    else {
+      $message = 'Deleted @count snippet files for %container container.';
+      $this->displayMessage($message, $args);
+      // In case this is not called during core cache rebuild, then [OMIT?]
+      // Reset the URL query argument so browsers reload snippet files.
+      // @todo Do these snippet files have the js token in a query argument? Yes. [OMIT]
+      _drupal_flush_css_js();
+    }
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function findAssets(ConfigEntityInterface $container) {
+    $include_classes = $container->get('include_classes');
+    $types = $include_classes ? ['data_layer', 'script', 'noscript'] : ['script', 'noscript'];
+    $result = TRUE;
+    foreach ($types as $type) {
+      $uri = $container->snippetURI($type);
+      $result = !is_file($uri) ? FALSE : $result;
     }
     return $result;
   }
diff --git a/web/modules/google_tag/src/Entity/ContainerManagerInterface.php b/web/modules/google_tag/src/Entity/ContainerManagerInterface.php
index d94403a608..0dd0ec0b7d 100644
--- a/web/modules/google_tag/src/Entity/ContainerManagerInterface.php
+++ b/web/modules/google_tag/src/Entity/ContainerManagerInterface.php
@@ -78,4 +78,34 @@ public function getNoScriptAttachments(array &$page);
    */
   public function createAllAssets();
 
+  /**
+   * Deletes snippet files for all containers.
+   *
+   * @return bool
+   *   Whether the files were deleted.
+   */
+  public function deleteAllAssets();
+
+  /**
+   * Deletes snippet files for a container.
+   *
+   * @param Drupal\Core\Config\Entity\ConfigEntityInterface $container
+   *   The container configuration entity.
+   *
+   * @return bool
+   *   Whether the files were deleted.
+   */
+  public function deleteAssets(ConfigEntityInterface $container);
+
+  /**
+   * Finds snippet files for a container.
+   *
+   * @param Drupal\Core\Config\Entity\ConfigEntityInterface $container
+   *   The container configuration entity.
+   *
+   * @return bool
+   *   Whether the files were found.
+   */
+  public function findAssets(ConfigEntityInterface $container);
+
 }
diff --git a/web/modules/google_tag/src/Form/ContainerForm.php b/web/modules/google_tag/src/Form/ContainerForm.php
index 244721cfc6..5b5a596466 100644
--- a/web/modules/google_tag/src/Form/ContainerForm.php
+++ b/web/modules/google_tag/src/Form/ContainerForm.php
@@ -3,14 +3,12 @@
 namespace Drupal\google_tag\Form;
 
 use Drupal\Core\Condition\ConditionInterface;
-use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Executable\ExecutableManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Form\SubformState;
 use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Defines the Google tag manager container settings form.
@@ -33,13 +31,6 @@ class ContainerForm extends EntityForm {
    */
   protected $contextRepository;
 
-  /**
-   * The container entity.
-   *
-   * @var \Drupal\google_tag\Entity\Container
-   */
-  protected $container;
-
   /**
    * {@inheritdoc}
    */
diff --git a/web/modules/google_tag/src/Form/ContainerTrait.php b/web/modules/google_tag/src/Form/ContainerTrait.php
index e2a312add5..ea21ffa135 100644
--- a/web/modules/google_tag/src/Form/ContainerTrait.php
+++ b/web/modules/google_tag/src/Form/ContainerTrait.php
@@ -4,8 +4,25 @@
 
 use Drupal\Core\Form\FormStateInterface;
 
+/**
+ * Defines shared routines for the container and settings forms.
+ */
 trait ContainerTrait {
 
+  /**
+   * The container entity.
+   *
+   * @var \Drupal\google_tag\Entity\Container
+   */
+  protected $container;
+
+  /**
+   * The property prefix that allows reuse by container and settings forms.
+   *
+   * @var string
+   */
+  protected $prefix;
+
   /**
    * Fieldset builder for the container settings form.
    */
@@ -115,17 +132,17 @@ public function statesArray($variable) {
   public function pathFieldset(FormStateInterface &$form_state) {
     $fieldset_title = $this->t('Request path');
     $fieldset_description = $this->t('On this and the following tabs, specify the conditions on which the GTM JavaScript snippet will either be inserted on or omitted from the page response, thereby enabling or disabling tracking and other analytics. All conditions must be satisfied for the snippet to be inserted. The snippet will be omitted if any condition is not met.');
-    $args = array(
+    $args = [
       '%node' => '/node',
       '%user-wildcard' => '/user/*',
       '%front' => '<front>',
-    );
+    ];
     $description = $this->t('Enter one relative path per line using the "*" character as a wildcard. Example paths are: "%node" for the node page, "%user-wildcard" for each individual user, and "%front" for the front page.', $args);
     $rows = 10;
     $singular = 'path';
     $plural = 'paths';
     $adjective = 'listed';
-    $config = compact(array('fieldset_title', 'fieldset_description', 'singular', 'plural', 'adjective', 'description', 'rows'));
+    $config = compact(['fieldset_title', 'fieldset_description', 'singular', 'plural', 'adjective', 'description', 'rows']);
     return $this->genericFieldset($config, $form_state);
   }
 
@@ -139,7 +156,7 @@ public function roleFieldset(FormStateInterface &$form_state) {
     $options = array_map(function ($role) {
       return $role->label();
     }, user_roles());
-    $config = compact(array('fieldset_title', 'singular', 'plural', 'options'));
+    $config = compact(['fieldset_title', 'singular', 'plural', 'options']);
     return $this->genericFieldset($config, $form_state);
   }
 
@@ -153,7 +170,7 @@ public function statusFieldset(FormStateInterface &$form_state) {
     $singular = 'status';
     $plural = 'statuses';
     $adjective = 'listed';
-    $config = compact(array('fieldset_title', 'singular', 'plural', 'adjective', 'description', 'rows'));
+    $config = compact(['fieldset_title', 'singular', 'plural', 'adjective', 'description', 'rows']);
     return $this->genericFieldset($config, $form_state);
   }
 
@@ -164,15 +181,15 @@ public function genericFieldset(array $config, FormStateInterface &$form_state)
     $container = $this->container;
 
     // Gather data.
-    $config += array('fieldset_description' => '', 'adjective' => 'selected');
+    $config += ['fieldset_description' => '', 'adjective' => 'selected'];
     extract($config);
     $toggle = "{$singular}_toggle";
     $list = "{$singular}_list";
-    $args = array(
+    $args = [
       '@adjective' => $adjective,
       '@uc_adjective' => ucfirst($adjective),
       '@plural' => $plural,
-    );
+    ];
 
     // Build form elements.
     $fieldset = [
@@ -249,7 +266,7 @@ public function validateFormValues(array &$form, FormStateInterface $form_state)
 
     // Specific to the settings form.
     $uri = $form_state->getValue('uri');
-    if (!is_null($uri)) {
+    if (!is_null($uri) && $form['#form_id'] == 'google_tag_settings') {
       $uri = trim($uri);
       $form_state->setValue('uri', $uri);
 
@@ -257,7 +274,7 @@ public function validateFormValues(array &$form, FormStateInterface $form_state)
       if (substr($directory, -3) == '://') {
         $args = ['%directory' => $directory];
         $message = 'The snippet parent uri %directory is invalid. Enter a single trailing slash to specify a plain stream wrapper.';
-        $form_state->setError($form['settings']['uri'], $this->t($message, $args));
+        $form_state->setError($form['module']['uri'], $this->t($message, $args));
       }
 
       // Allow for a plain stream wrapper with one trailing slash.
@@ -265,7 +282,7 @@ public function validateFormValues(array &$form, FormStateInterface $form_state)
       if (!is_dir($directory) || !_google_tag_is_writable($directory) || !_google_tag_is_executable($directory)) {
         $args = ['%directory' => $directory];
         $message = 'The snippet parent uri %directory is invalid, possibly due to file system permissions. The directory either does not exist, or is not writable or searchable.';
-        $form_state->setError($form['settings']['uri'], $this->t($message, $args));
+        $form_state->setError($form['module']['uri'], $this->t($message, $args));
       }
     }
 
diff --git a/web/modules/google_tag/src/Form/SettingsForm.php b/web/modules/google_tag/src/Form/SettingsForm.php
index da521ae974..0411e564e5 100644
--- a/web/modules/google_tag/src/Form/SettingsForm.php
+++ b/web/modules/google_tag/src/Form/SettingsForm.php
@@ -35,7 +35,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     $this->prefix = '_default_container.';
 
     // Build form elements.
-    $description = $this->t('<br />After configuring the module settings and default properties for a new container, <strong>add a container</strong> on the <a href=":url">container management page</a>.', array(':url' => Url::fromRoute('entity.google_tag_container.collection')->toString()));
+    $description = $this->t('<br />After configuring the module settings and default properties for a new container, <strong>add a container</strong> on the <a href=":url">container management page</a>.', [':url' => Url::fromRoute('entity.google_tag_container.collection')->toString()]);
 
     $form['instruction'] = [
       '#type' => 'markup',
@@ -110,14 +110,14 @@ public function moduleFieldset(FormStateInterface $form_state) {
     $fieldset['rebuild_snippets'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Recreate snippets on cache rebuild'),
-      '#description' => $this->t('If checked, then the JavaScript snippet files will be created during a cache rebuild. This is <strong>recommended on production sites</strong>.'),
+      '#description' => $this->t('If checked, then the JavaScript snippet files will be created during a cache rebuild. This is <strong>recommended on production sites</strong>. If not checked, any missing snippet files will be created during a page response.'),
       '#default_value' => $config->get('rebuild_snippets'),
     ];
 
     $fieldset['flush_snippets'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Flush snippet directory on cache rebuild'),
-      '#description' => $this->t('If checked, then the snippet directory will be deleted and recreated during a cache rebuild. If not checked, then manual intervention may be required to tidy up the snippet directory (e.g. remove snippet files for a deleted container).'),
+      '#description' => $this->t('If checked, then the snippet directory will be deleted during a cache rebuild. If not checked, then manual intervention may be required to tidy up the snippet directory (e.g. remove snippet files for a deleted container).'),
       '#default_value' => $config->get('flush_snippets'),
     ];
 
@@ -159,6 +159,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $config->save();
 
     parent::submitForm($form, $form_state);
+    // @todo Only display if a container exists?
+    $message = 'Changes to default container settings and insertion conditions <strong>only apply to new containers</strong>. To modify settings for existing containers, click the container management link below.';
+    $args = ['%directory' => $old_uri . '/google_tag'];
+    $this->messenger()->addWarning($this->t($message, $args));
 
     $new_uri = $config->get('uri');
     if ($old_uri != $new_uri) {
diff --git a/web/modules/google_tag/src/Plugin/Condition/Domain.php b/web/modules/google_tag/src/Plugin/Condition/Domain.php
index 4ae6c4873e..d82bc49ce9 100644
--- a/web/modules/google_tag/src/Plugin/Condition/Domain.php
+++ b/web/modules/google_tag/src/Plugin/Condition/Domain.php
@@ -15,7 +15,7 @@
  * @Condition(
  *   id = "gtag_domain",
  *   label = @Translation("Domain"),
- *   context = {
+ *   context_definitions = {
  *     "entity:domain" = @ContextDefinition("entity:domain", label = @Translation("Domain"), required = TRUE)
  *   }
  * )
@@ -34,8 +34,8 @@ class Domain extends ConditionBase implements ContainerFactoryPluginInterface {
    *
    * @param \Drupal\domain\DomainNegotiator $domain_negotiator
    *   The domain negotiator service.
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $storage_manager
+   *   The entity storage manager.
    * @param array $configuration
    *   A configuration array containing information about the plugin instance.
    * @param string $plugin_id
diff --git a/web/modules/google_tag/tests/src/Functional/GTMTestBase.php b/web/modules/google_tag/tests/src/Functional/GTMTestBase.php
index ee3ebf4a35..3743f9995f 100644
--- a/web/modules/google_tag/tests/src/Functional/GTMTestBase.php
+++ b/web/modules/google_tag/tests/src/Functional/GTMTestBase.php
@@ -31,12 +31,41 @@ abstract class GTMTestBase extends BrowserTestBase {
    */
   protected $types = ['script', 'noscript'];
 
+  /**
+   * The snippet base URI.
+   *
+   * @var string
+   */
+  protected $basePath;
+
+  /**
+   * The admin user.
+   *
+   * @var \Drupal\user\Entity\User
+   */
+  protected $adminUser;
+
+  /**
+   * The non-admin user.
+   *
+   * @var \Drupal\user\Entity\User
+   */
+  protected $nonAdminUser;
+
+  /**
+   * The test variables.
+   *
+   * @var array
+   */
+  protected $variables = [];
+
   /**
    * {@inheritdoc}
    */
   protected function setUp() {
+    $this->defaultTheme = 'stark';
     parent::setUp();
-    $this->basePath = \Drupal::config('google_tag.settings')->get('uri');
+    $this->basePath = $this->config('google_tag.settings')->get('uri');
   }
 
   /**
@@ -57,7 +86,7 @@ public function testModule() {
       $this->checkSnippetFiles();
       $this->checkPageResponse();
     }
-    catch (Exception $e) {
+    catch (\Exception $e) {
       parent::assertTrue(TRUE, t('Inside CATCH block'));
       watchdog_exception('gtm_test', $e);
     }
@@ -72,7 +101,7 @@ public function testModule() {
   protected function modifySettings() {
     // Modify default settings.
     // These should propagate to each container created in test.
-    $config = \Drupal::service('config.factory')->getEditable('google_tag.settings');
+    $config = $this->config('google_tag.settings');
     $settings = $config->get();
     unset($settings['_core']);
     $settings['flush_snippets'] = 1;
@@ -119,7 +148,7 @@ protected function saveContainers() {
       $container->save();
 
       // Create snippet files.
-      $manager = \Drupal::service('google_tag.container_manager');
+      $manager = $this->container->get('google_tag.container_manager');
       $manager->createAssets($container);
     }
   }
@@ -130,13 +159,13 @@ protected function saveContainers() {
   protected function deleteContainers() {
     // Delete containers.
     foreach ($this->variables as $key => $variables) {
-      // also \Drupal::entityTypeManager()
-      $container = \Drupal::service('entity_type.manager')->getStorage('google_tag_container')->load($key);
+      // Also exposed as \Drupal::entityTypeManager().
+      $container = $this->container->get('entity_type.manager')->getStorage('google_tag_container')->load($key);
       $container->delete();
     }
 
     // Confirm no containers.
-    $manager = \Drupal::service('google_tag.container_manager');
+    $manager = $this->container->get('google_tag.container_manager');
     $ids = $manager->loadContainerIDs();
     $message = 'No containers found after delete';
     parent::assertTrue(empty($ids), $message);
@@ -144,11 +173,11 @@ protected function deleteContainers() {
     // @todo Next statement will not delete files as containers are gone.
     // $manager->createAllAssets();
     // Delete snippet files.
-    $directory = \Drupal::config('google_tag.settings')->get('uri');
-    if (\Drupal::config('google_tag.settings')->get('flush_snippets')) {
+    $directory = $this->config('google_tag.settings')->get('uri');
+    if ($this->config('google_tag.settings')->get('flush_snippets')) {
       if (!empty($directory)) {
         // Remove any stale files (e.g. module update or machine name change).
-        \Drupal::service('file_system')->deleteRecursive($directory . '/google_tag');
+        $this->container->get('file_system')->deleteRecursive($directory . '/google_tag');
       }
     }
 
@@ -168,15 +197,14 @@ protected function submitContainers() {
       $this->drupalPostForm('/admin/config/system/google-tag/add', $edit, 'Save');
 
       $text = 'Created @count snippet files for %container container based on configuration.';
-      $args = array('@count' => 3, '%container' => $variables->label);
+      $args = ['@count' => 3, '%container' => $variables->label];
       $text = t($text, $args);
-      $message = 'Found snippet confirmation message in page response';
-      $this->assertRaw($text, $message);
+      $this->assertSession()->responseContains($text);
 
       $text = 'Created @count snippet files for @container container based on configuration.';
-      $args = array('@count' => 3, '@container' => $variables->label);
+      $args = ['@count' => 3, '@container' => $variables->label];
       $text = t($text, $args);
-      $this->assertText($text, $message);
+      $this->assertSession()->pageTextContains($text);
     }
   }
 
@@ -237,7 +265,7 @@ protected function checkPageResponse() {
    * Verify the tag in page response.
    */
   protected function verifyScriptTag($realpath) {
-    $query_string = \Drupal::state()->get('system.css_js_query_string') ?: '0';
+    $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
     $text = "src=\"$realpath?$query_string\"";
     $this->assertSession()->responseContains($text);
 
-- 
GitLab