diff -u b/core/core.services.yml b/core/core.services.yml --- b/core/core.services.yml +++ b/core/core.services.yml @@ -291,7 +291,14 @@ arguments: ['@app.root', '%container.modules%', '@cache.bootstrap'] module_installer: class: Drupal\Core\Extension\ModuleInstaller - arguments: ['@app.root', '@module_handler', '@kernel', '@entity.manager', '@entity.query'] + tags: + - { name: service_collector, tag: 'module_install.uninstall_validator', call: addUninstallValidator } + arguments: ['@app.root', '@module_handler', '@kernel'] + content_uninstall_validator: + class: Drupal\Core\Entity\ContentUninstallValidator + tags: + - { name: module_install.uninstall_validator, priority: -254 } + arguments: ['@entity.manager', '@entity.query'] theme_handler: class: Drupal\Core\Extension\ThemeHandler arguments: ['@app.root', '@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder_indicator'] diff -u b/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php --- b/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -10,8 +10,7 @@ use Drupal\Component\Serialization\Yaml; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\DrupalKernelInterface; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\Query\QueryFactory; +use Drupal\Core\Extension\UninstallValidatorInterface; /** * Default implementation of the module installer. @@ -43,18 +42,11 @@ protected $root; /** - * The entity manager. + * The uninstall validators. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Extension\UninstallValidatorInterface[] */ - protected $entityManager; - - /** - * The entity query factory. - * - * @var \Drupal\Core\Entity\Query\QueryFactory - */ - protected $queryFactory; + protected $uninstallValidators; /** * Modules that can be uninstalled. @@ -75,18 +67,19 @@ - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query - * The entity query factory. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, ModuleHandlerInterface $module_handler, DrupalKernelInterface $kernel, EntityManagerInterface $entity_manager, QueryFactory $entity_query) { + public function __construct($root, ModuleHandlerInterface $module_handler, DrupalKernelInterface $kernel) { $this->root = $root; $this->moduleHandler = $module_handler; $this->kernel = $kernel; - $this->entityManager = $entity_manager; - $this->queryFactory = $entity_query; $this->uninstallValidated = array(); } /** + * {@inheritdoc} + */ + public function addUninstallValidator(UninstallValidatorInterface $uninstall_validator) { + $this->uninstallValidators[] = $uninstall_validator; + } + + /** @@ -500,38 +493,16 @@ continue; } - if ($this->moduleHasContent($module)) { - unset($module_list[$key]); - } - else { - $this->uninstallValidated[] = $module; + foreach ($this->uninstallValidators as $validator) { + if ($validator->validate($module)) { + $this->uninstallValidated[] = $module; + } + else { + unset($module_list[$key]); + } } } return $module_list; } - /** - * Determines if a module has existing content entities. - * - * @param string $module - * A module name. - * - * @return bool - * TRUE if there are content entities. - */ - protected function moduleHasContent($module) { - $entities = $this->entityManager->getDefinitions(); - foreach ($entities as $entity_type) { - if ($module == $entity_type->getProvider() && is_subclass_of($entity_type->getClass(), 'Drupal\Core\Entity\ContentEntityInterface') && $entity_type->getDataTable()) { - $content = $this->queryFactory->get($entity_type->id()) - ->accessCheck(FALSE) - ->range(0, 1) - ->execute(); - if (!empty($content)) { - return TRUE; - } - } - } - return FALSE; - } } diff -u b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php --- b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Extension; +use Drupal\Core\Extension\UninstallValidatorInterface; + /** * Provides the installation of modules with creating the db schema and more. */ @@ -59,6 +61,15 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE); /** + * Adds module uninstall validator services. + * + * @param \Drupal\Core\Extension\UninstallValidatorInterface $uninstall_validator + * The uninstall validation service to add. It is added at the end of the + * priority list (lower priority relative to existing ones). + */ + public function addUninstallValidator(UninstallValidatorInterface $uninstall_validator); + + /** * Determines which modules can be uninstalled. * * @param array $module_list only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php @@ -0,0 +1,49 @@ +entityManager = $entity_manager; + $this->queryFactory = $entity_query; + } + /** + * {@inheritdoc} + */ + public function validate($module) { + $entities = $this->entityManager->getDefinitions(); + foreach ($entities as $entity_type) { + if ($module == $entity_type->getProvider() && is_subclass_of($entity_type->getClass(), 'Drupal\Core\Entity\ContentEntityInterface') && $entity_type->getDataTable()) { + $content = $this->queryFactory->get($entity_type->id()) + ->accessCheck(FALSE) + ->range(0, 1) + ->execute(); + if (!empty($content)) { + return FALSE; + } + } + } + return TRUE; + } + +} only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Extension/UninstallValidatorInterface.php @@ -0,0 +1,24 @@ +