From 6f0f2212a7c6fa33547bf165fd046001e9f69869 Mon Sep 17 00:00:00 2001
From: Andrei Mateescu <andrei@amateescu.me>
Date: Tue, 31 May 2022 20:32:11 +0300
Subject: [PATCH] Make the list of trash-enabled entity types configurable.

---
 README.md                                     |   3 -
 config/install/trash.settings.yml             |   1 +
 config/schema/trash.schema.yml                |   8 ++
 src/Entity/Query/QueryTrait.php               |  10 +-
 src/EventSubscriber/TrashConfigSubscriber.php |  77 +++++++++++
 src/Form/TrashSettingsForm.php                | 130 ++++++++++++++++++
 src/TrashEntityTypeManager.php                |  11 +-
 src/TrashManager.php                          | 107 ++++++++++++++
 src/TrashManagerInterface.php                 |  52 +++++++
 src/TrashStorageTrait.php                     |   2 +-
 src/ViewsQueryAlter.php                       |   8 +-
 .../trash_test/src/Entity/TrashTest.php       |  12 --
 tests/src/Kernel/EntityQueryTest.php          |   4 +-
 tests/src/Kernel/TrashKernelTest.php          |   4 +-
 tests/src/Kernel/TrashKernelTestBase.php      |   8 +-
 trash.install                                 |  37 -----
 trash.module                                  |  48 +------
 trash.services.yml                            |  10 ++
 18 files changed, 413 insertions(+), 119 deletions(-)
 create mode 100644 config/install/trash.settings.yml
 create mode 100644 config/schema/trash.schema.yml
 create mode 100644 src/EventSubscriber/TrashConfigSubscriber.php
 create mode 100644 src/Form/TrashSettingsForm.php
 create mode 100644 src/TrashManager.php
 create mode 100644 src/TrashManagerInterface.php

diff --git a/README.md b/README.md
index 8e620b2..fec02eb 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,3 @@
-## Core patch(es) needed
-* https://www.drupal.org/project/drupal/issues/3065207
-
 ## Areas to work on
 
 * Storage:
diff --git a/config/install/trash.settings.yml b/config/install/trash.settings.yml
new file mode 100644
index 0000000..798f8c3
--- /dev/null
+++ b/config/install/trash.settings.yml
@@ -0,0 +1 @@
+enabled_entity_types: { }
diff --git a/config/schema/trash.schema.yml b/config/schema/trash.schema.yml
new file mode 100644
index 0000000..f7ec0d9
--- /dev/null
+++ b/config/schema/trash.schema.yml
@@ -0,0 +1,8 @@
+trash.settings:
+  type: config_object
+  mapping:
+    enabled_entity_types:
+      label: Enabled entity types
+      type: sequence
+      sequence:
+        type: string
diff --git a/src/Entity/Query/QueryTrait.php b/src/Entity/Query/QueryTrait.php
index 6a471b5..140c5b5 100644
--- a/src/Entity/Query/QueryTrait.php
+++ b/src/Entity/Query/QueryTrait.php
@@ -38,14 +38,12 @@ trait QueryTrait {
   public function prepare() {
     parent::prepare();
 
-    if (!in_array($this->entityTypeId, _trash_supported_entity_types(), TRUE)) {
+    if (!\Drupal::service('trash.manager')->isEntityTypeEnabled($this->entityType)) {
       return $this;
     }
 
-    assert($this->entityType->hasKey('deleted'));
-
     $revision_key = $this->entityType->getKey('revision');
-    $deleted_key = $this->entityType->getKey('deleted');
+    $deleted_key = 'deleted';
     $skip_deleted_filter = FALSE;
     // Skip adding a deleted flag for revisions as well as an explicit filter
     // on deleted, so you can still list deleted content, if needed.
@@ -64,10 +62,10 @@ trait QueryTrait {
       // @todo Do we need support for listing deleted and not deleted entities
       // at the same time?
       if ($this->isDeleted) {
-        $this->condition($deleted_key, 0, '>');
+        $this->exists($deleted_key);
       }
       else {
-        $this->condition($deleted_key, 0);
+        $this->notExists($deleted_key);
       }
     }
     return $this;
diff --git a/src/EventSubscriber/TrashConfigSubscriber.php b/src/EventSubscriber/TrashConfigSubscriber.php
new file mode 100644
index 0000000..bab82b1
--- /dev/null
+++ b/src/EventSubscriber/TrashConfigSubscriber.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Drupal\trash\EventSubscriber;
+
+use Drupal\Core\Config\ConfigCrudEvent;
+use Drupal\Core\Config\ConfigEvents;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\trash\TrashManagerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Listens to the config save event for trash.settings.
+ */
+class TrashConfigSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The Trash manager.
+   *
+   * @var \Drupal\trash\TrashManagerInterface
+   */
+  protected $trashManager;
+
+  /**
+   * Constructs the TrashConfigSubscriber.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\trash\TrashManagerInterface $trash_manager
+   *   The trash manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TrashManagerInterface $trash_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->trashManager = $trash_manager;
+  }
+
+  /**
+   * Updates entity type definitions and ensures routes are rebuilt when needed.
+   *
+   * @param \Drupal\Core\Config\ConfigCrudEvent $event
+   *   The ConfigCrudEvent to process.
+   */
+  public function onSave(ConfigCrudEvent $event) {
+    $entity_types = $this->entityTypeManager->getDefinitions();
+    if ($event->getConfig()->getName() === 'trash.settings' && $event->isChanged('enabled_entity_types')) {
+      $enabled_entity_types = $event->getConfig()->get('enabled_entity_types');
+      $original_enabled_entity_types = $event->getConfig()->getOriginal('enabled_entity_types') ?? [];
+
+      foreach ($enabled_entity_types as $entity_type_id) {
+        if (!in_array($entity_type_id, $original_enabled_entity_types, TRUE)) {
+          $this->trashManager->enableEntityType($entity_types[$entity_type_id]);
+        }
+      }
+
+      foreach ($original_enabled_entity_types as $entity_type_id) {
+        if (!in_array($entity_type_id, $enabled_entity_types, TRUE)) {
+          $this->trashManager->disableEntityType($entity_types[$entity_type_id]);
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[ConfigEvents::SAVE][] = ['onSave'];
+    return $events;
+  }
+
+}
diff --git a/src/Form/TrashSettingsForm.php b/src/Form/TrashSettingsForm.php
new file mode 100644
index 0000000..b007f87
--- /dev/null
+++ b/src/Form/TrashSettingsForm.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\trash\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Configure Trash settings for this site.
+ */
+class TrashSettingsForm extends ConfigFormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * Constructs a PathautoSettingsForm.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   Defines the configuration object factory.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Manages entity type plugin definitions.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   Manages the discovery of entity fields.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
+    parent::__construct($config_factory);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'trash_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['trash.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $config = $this->config('trash.settings');
+
+    $form['enabled_entity_types'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+      '#title' => $this->t('Enabled entity types'),
+      '#description' => $this->t('Enable to add a deleted field and allow to define alias patterns for the given type. Disabled types already define a path field themselves or currently have a pattern.'),
+      '#tree' => TRUE,
+    ];
+
+    // Get all applicable entity types.
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      if (is_subclass_of($entity_type->getStorageClass(), SqlEntityStorageInterface::class)) {
+        $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+        $form['enabled_entity_types'][$entity_type_id] = [
+          '#type' => 'checkbox',
+          '#title' => $entity_type->getLabel(),
+          '#default_value' => isset($field_definitions['deleted']) || in_array($entity_type_id, $config->get('enabled_entity_types')),
+          '#disabled' => isset($field_definitions['deleted']) && ($field_definitions['deleted']->getProvider() !== 'trash'),
+        ];
+      }
+    }
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $config = $this->config('trash.settings');
+    $form_state->cleanValues();
+
+    foreach ($form_state->getValues() as $key => $value) {
+      if ($key == 'enabled_entity_types') {
+        $enabled_entity_types = [];
+        foreach ($value as $entity_type_id => $enabled) {
+          $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+          // Verify that the entity type is enabled and that it is not defined
+          // or defined by us before adding it to the configuration, so that
+          // we do not store an entity type that cannot be enabled or disabled.
+          if ($enabled && (!isset($field_definitions['deleted']) || ($field_definitions['deleted']->getProvider() === 'trash'))) {
+            $enabled_entity_types[] = $entity_type_id;
+          }
+        }
+        $value = $enabled_entity_types;
+      }
+      $config->set($key, $value);
+    }
+    $config->save();
+
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/src/TrashEntityTypeManager.php b/src/TrashEntityTypeManager.php
index ffdad88..3cc95cc 100644
--- a/src/TrashEntityTypeManager.php
+++ b/src/TrashEntityTypeManager.php
@@ -5,7 +5,6 @@ namespace Drupal\trash;
 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\PhpStorage\PhpStorageFactory;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -22,13 +21,6 @@ class TrashEntityTypeManager extends DefaultPluginManager implements EntityTypeM
    */
   protected $entityTypeManager;
 
-  /**
-   * The PhpStorage object used for storing the generated storage classes.
-   *
-   * @var \Drupal\Component\PhpStorage\PhpStorageInterface
-   */
-  protected $storage;
-
   /**
    * Contains instantiated storage handlers keyed by entity type.
    *
@@ -41,7 +33,6 @@ class TrashEntityTypeManager extends DefaultPluginManager implements EntityTypeM
    */
   public function __construct(EntityTypeManagerInterface $entity_type_manager) {
     $this->entityTypeManager = $entity_type_manager;
-    $this->storage = PhpStorageFactory::get('trash');
   }
 
   /**
@@ -170,7 +161,7 @@ class TrashEntityTypeManager extends DefaultPluginManager implements EntityTypeM
    * {@inheritdoc}
    */
   public function createHandlerInstance($class, EntityTypeInterface $definition = NULL) {
-    if ($definition->hasKey('deleted')) {
+    if (\Drupal::service('trash.manager')->isEntityTypeEnabled($definition)) {
       $class = _trash_generate_storage_class($class);
     }
 
diff --git a/src/TrashManager.php b/src/TrashManager.php
new file mode 100644
index 0000000..887a888
--- /dev/null
+++ b/src/TrashManager.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Drupal\trash;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
+use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Provides the Trash manager.
+ */
+class TrashManager implements TrashManagerInterface {
+
+  /**
+   * The entity definition update manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * The last installed schema definitions.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $entityLastInstalledSchemaRepository;
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a new TrashManager.
+   *
+   * @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entity_definition_update_manager
+   *   The entity definition update manager.
+   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
+   *   The last installed schema repository service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
+   */
+  public function __construct(EntityDefinitionUpdateManagerInterface $entity_definition_update_manager, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository, ConfigFactoryInterface $config_factory) {
+    $this->entityDefinitionUpdateManager = $entity_definition_update_manager;
+    $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEntityTypeSupported(EntityTypeInterface $entity_type) : bool {
+    return is_subclass_of($entity_type->getStorageClass(), SqlEntityStorageInterface::class);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEntityTypeEnabled(EntityTypeInterface $entity_type) : bool {
+    return in_array($entity_type->id(), $this->configFactory->get('trash.settings')->get('enabled_entity_types'), TRUE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function enableEntityType(EntityTypeInterface $entity_type) {
+    $field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id());
+
+    if (!$this->isEntityTypeSupported($entity_type)) {
+      throw new \InvalidArgumentException("Trash integration can not be enabled for the {$entity_type->id()} entity type.");
+    }
+
+    if (isset($field_storage_definitions['deleted'])) {
+      if ($field_storage_definitions['deleted']->getProvider() !== 'trash') {
+        throw new \InvalidArgumentException("The {$entity_type->id()} entity type already has a 'deleted' field.");
+      }
+      else {
+        throw new \InvalidArgumentException("Trash integration is already enabled for the {$entity_type->id()} entity type.");
+      }
+    }
+
+    $storage_definition = BaseFieldDefinition::create('timestamp')
+      ->setLabel(t('Deleted'))
+      ->setDescription(t('Time when the item got deleted'))
+      ->setInternal(TRUE)
+      ->setTranslatable(FALSE)
+      ->setRevisionable(TRUE);
+
+    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('deleted', $entity_type->id(), 'trash', $storage_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function disableEntityType(EntityTypeInterface $entity_type) {
+    $field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id());
+    if (isset($field_storage_definitions['deleted'])) {
+      $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_storage_definitions['deleted']);
+    }
+  }
+
+}
diff --git a/src/TrashManagerInterface.php b/src/TrashManagerInterface.php
new file mode 100644
index 0000000..0837b2f
--- /dev/null
+++ b/src/TrashManagerInterface.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\trash;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides an interface for the Trash manager.
+ */
+interface TrashManagerInterface {
+
+  /**
+   * Determines whether an entity type is supported by Trash.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   An entity type object.
+   *
+   * @return bool
+   */
+  public function isEntityTypeSupported(EntityTypeInterface $entity_type) : bool;
+
+  /**
+   * Determines whether Trash integration is enabled for an entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   An entity type object.
+   *
+   * @return bool
+   */
+  public function isEntityTypeEnabled(EntityTypeInterface $entity_type) : bool;
+
+  /**
+   * Enables Trash integration for an entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   An entity type object.
+   *
+   * @throws \InvalidArgumentException
+   *   Thrown when Trash integration can not be enabled or is already enabled
+   *   for an entity type.
+   */
+  public function enableEntityType(EntityTypeInterface $entity_type);
+
+  /**
+   * Disables Trash integration for an entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   An entity type object.
+   */
+  public function disableEntityType(EntityTypeInterface $entity_type);
+
+}
diff --git a/src/TrashStorageTrait.php b/src/TrashStorageTrait.php
index 86962be..e4b4b9d 100644
--- a/src/TrashStorageTrait.php
+++ b/src/TrashStorageTrait.php
@@ -8,7 +8,7 @@ trait TrashStorageTrait {
    * {@inheritdoc}
    */
   public function delete(array $entities) {
-    $field_name = $this->getEntityType()->getKey('deleted');
+    $field_name = 'deleted';
     $revisionable = $this->getEntityType()->isRevisionable();
 
     foreach ($entities as $entity) {
diff --git a/src/ViewsQueryAlter.php b/src/ViewsQueryAlter.php
index 126f2d7..33576f7 100644
--- a/src/ViewsQueryAlter.php
+++ b/src/ViewsQueryAlter.php
@@ -102,7 +102,7 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
 
     $entity_type_definitions = $this->entityTypeManager->getDefinitions();
     foreach ($entity_type_ids as $entity_type_id) {
-      if (in_array($entity_type_id, _trash_supported_entity_types(), TRUE)) {
+      if (\Drupal::service('trash.manager')->isEntityTypeEnabled($entity_type_definitions[$entity_type_id])) {
         $this->alterQueryForEntityType($query, $entity_type_definitions[$entity_type_id]);
       }
     }
@@ -120,7 +120,7 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
    *   The entity type definition.
    */
   protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type) {
-    $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping(); 
+    $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping();
     assert($table_mapping instanceof DefaultTableMapping);
     $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
 
@@ -129,8 +129,8 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
     // Try to find out whether any filter (normal or conditional filter) filters
     // by the delete column. In case it does opt out of adding a specific
     // delete column.
-    $deleted_table_name = $table_mapping->getFieldTableName($entity_type->getKey('deleted'));
-    $deleted_table_column = $table_mapping->getFieldColumnName($field_storage_definitions[$entity_type->getKey('deleted')], 'value');
+    $deleted_table_name = $table_mapping->getFieldTableName('deleted');
+    $deleted_table_column = $table_mapping->getFieldColumnName($field_storage_definitions['deleted'], 'value');
     foreach ($query->where as $group_index => $group) {
       if (!empty($group['conditions'])) {
         foreach ($group['conditions'] as $condition_index => $condition) {
diff --git a/tests/modules/trash_test/src/Entity/TrashTest.php b/tests/modules/trash_test/src/Entity/TrashTest.php
index c2b9ee3..03010d8 100644
--- a/tests/modules/trash_test/src/Entity/TrashTest.php
+++ b/tests/modules/trash_test/src/Entity/TrashTest.php
@@ -30,7 +30,6 @@ use Drupal\Core\Entity\ContentEntityBase;
  *     "revision" = "revision",
  *     "label" = "label",
  *     "uuid" = "uuid",
- *     "deleted" = "deleted",
  *   },
  *   links = {
  *     "canonical" = "/trash_test/{trash_test}",
@@ -41,15 +40,4 @@ use Drupal\Core\Entity\ContentEntityBase;
  */
 class TrashTest extends ContentEntityBase {
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getEntityKey($key) {
-    // Typically this protected method is used internally by entity classes and
-    // exposed publicly through more specific getter methods. So that test cases
-    // are able to set and access entity keys dynamically, update the visibility
-    // of this method to public.
-    return parent::getEntityKey($key);
-  }
-
 }
diff --git a/tests/src/Kernel/EntityQueryTest.php b/tests/src/Kernel/EntityQueryTest.php
index cdc92a9..5e76ae8 100644
--- a/tests/src/Kernel/EntityQueryTest.php
+++ b/tests/src/Kernel/EntityQueryTest.php
@@ -67,7 +67,7 @@ class EntityQueryTest extends TrashKernelTestBase {
     $query = $entity_storage->getQuery();
     assert($query instanceof Query);
     $query->accessCheck(FALSE);
-    $query->condition($entity_type->getKey('deleted'), 0, '>');
+    $query->condition('deleted', 0, '>');
     $this->assertCount(3, $query->execute());
 
     // Try to fetch not deleted content.
@@ -81,7 +81,7 @@ class EntityQueryTest extends TrashKernelTestBase {
     $query = $entity_storage->getQuery();
     assert($query instanceof Query);
     $query->accessCheck(FALSE);
-    $query->condition($entity_type->getKey('deleted'), 0, '>');
+    $query->condition('deleted', 0, '>');
     $this->assertCount(3, $query->execute());
   }
 
diff --git a/tests/src/Kernel/TrashKernelTest.php b/tests/src/Kernel/TrashKernelTest.php
index 44336b4..b29bfee 100644
--- a/tests/src/Kernel/TrashKernelTest.php
+++ b/tests/src/Kernel/TrashKernelTest.php
@@ -19,14 +19,14 @@ class TrashKernelTest extends TrashKernelTestBase {
     $entity->save();
 
     $this->assertNotNull(TrashTest::load($entity->id()));
-    $this->assertEquals(0, $entity->getEntityKey('deleted'));
+    $this->assertEquals(0, $entity->get('deleted')->value);
     $entity->delete();
 
     $entity = TrashTest::load($entity->id());
     assert($entity instanceof ContentEntityInterface);
 
     $this->assertNotNull($entity, "Ensure that deleting an entity doesn't actually deletes it.");
-    $this->assertEquals(\Drupal::time()->getRequestTime(), $entity->getEntityKey('deleted'));
+    $this->assertEquals(\Drupal::time()->getRequestTime(), $entity->get('deleted')->value);
   }
 
 }
diff --git a/tests/src/Kernel/TrashKernelTestBase.php b/tests/src/Kernel/TrashKernelTestBase.php
index cdd425f..c49a7f1 100644
--- a/tests/src/Kernel/TrashKernelTestBase.php
+++ b/tests/src/Kernel/TrashKernelTestBase.php
@@ -22,8 +22,14 @@ abstract class TrashKernelTestBase extends KernelTestBase {
   protected function setUp() {
     parent::setUp();
 
+    $this->installConfig(['trash', 'trash_test']);
     $this->installEntitySchema('trash_test_entity');
-    $this->installConfig('trash_test');
+
+    $config = \Drupal::configFactory()->getEditable('trash.settings');
+    $enabled_entity_types = $config->get('enabled_entity_types');
+    $enabled_entity_types[] = 'trash_test_entity';
+    $config->set('enabled_entity_types', $enabled_entity_types);
+    $config->save();
   }
 
 }
diff --git a/trash.install b/trash.install
index 6b3aa28..6355a8f 100644
--- a/trash.install
+++ b/trash.install
@@ -4,40 +4,3 @@
  * @file
  * Install, update, and uninstall functions for the Trash module.
  */
-
-/**
- * Implements hook_module_preinstall().
- */
-function trash_module_preinstall($module) {
-  if ($module !== 'trash') {
-    return;
-  }
-
-  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
-  foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) {
-    if (in_array($entity_type->id(), _trash_supported_entity_types(), TRUE)) {
-      $entity_keys = $entity_type->getKeys();
-      if (!isset($entity_keys['deleted'])) {
-        $entity_keys['deleted'] = 'deleted';
-
-        $entity_type->set('entity_keys', $entity_keys);
-        $entity_definition_update_manager->updateEntityType($entity_type);
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_uninstall().
- */
-function trash_uninstall() {
-  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
-  foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) {
-    if (in_array($entity_type->id(), _trash_supported_entity_types(), TRUE)) {
-      $entity_keys = $entity_type->getKeys();
-      unset($entity_keys['deleted']);
-      $entity_type->set('entity_keys', $entity_keys);
-      $entity_definition_update_manager->updateEntityType($entity_type);
-    }
-  }
-}
diff --git a/trash.module b/trash.module
index d565a5f..c5b14c8 100644
--- a/trash.module
+++ b/trash.module
@@ -7,57 +7,23 @@ use Drupal\trash\ViewsQueryAlter;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
 use Drupal\views\ViewExecutable;
 
-/**
- * Implements hook_entity_type_build().
- */
-function trash_entity_type_build(array &$entity_types) {
-  foreach ($entity_types as $entity_type) {
-    if (in_array($entity_type->id(), _trash_supported_entity_types(), TRUE)) {
-      // Add the required entity key.
-      $entity_keys = $entity_type->getKeys();
-      if (!isset($entity_keys['deleted'])) {
-        $entity_keys['deleted'] = 'deleted';
-
-        $entity_type->set('entity_keys', $entity_keys);
-      }
-    }
-  }
-}
-
 /**
  * Implements hook_entity_base_field_info().
  */
 function trash_entity_base_field_info(EntityTypeInterface $entity_type) {
   // Add the 'deleted' base field.
-  /** @var \Drupal\Core\Entity\ContentEntityType $entity_type */
-  if ($entity_type->hasKey('deleted')) {
-      $field_name = $entity_type->getKey('deleted');
-      $base_field_definitions[$field_name] = BaseFieldDefinition::create('timestamp')
-        ->setLabel(t('Deleted'))
-        ->setDescription(t('Time when the item got deleted'))
-        ->setInternal(TRUE)
-        ->setTranslatable(FALSE)
-        ->setRevisionable(TRUE);
+  if (\Drupal::service('trash.manager')->isEntityTypeEnabled($entity_type)) {
+    $base_field_definitions['deleted'] = BaseFieldDefinition::create('timestamp')
+      ->setLabel(t('Deleted'))
+      ->setDescription(t('Time when the item got deleted'))
+      ->setInternal(TRUE)
+      ->setTranslatable(FALSE)
+      ->setRevisionable(TRUE);
 
     return $base_field_definitions;
   }
 }
 
-/**
- * Returns the entity types supported by the Trash module.
- *
- * @return array
- */
-function _trash_supported_entity_types() {
-  // @todo How to expand this list?
-  return [
-//    'node',
-//    'media',
-//    'taxonomy_term',
-    'trash_test_entity',
-  ];
-}
-
 /**
  * Implements hook_views_query_alter().
  */
diff --git a/trash.services.yml b/trash.services.yml
index 45bbe31..dcbc7bc 100644
--- a/trash.services.yml
+++ b/trash.services.yml
@@ -1,4 +1,14 @@
 services:
+  trash.manager:
+    class: Drupal\trash\TrashManager
+    arguments: ['@entity.definition_update_manager', '@entity.last_installed_schema.repository', '@config.factory']
+
+  trash.config_subscriber:
+    class: Drupal\trash\EventSubscriber\TrashConfigSubscriber
+    arguments: ['@entity_type.manager', '@trash.manager']
+    tags:
+      - { name: event_subscriber }
+
   trash.entity.query.sql:
     class: Drupal\trash\Entity\Query\Sql\QueryFactory
     public: false
-- 
GitLab