Skip to content
Snippets Groups Projects
Commit 422fc626 authored by Kunal Sachdev's avatar Kunal Sachdev Committed by Ted Bowman
Browse files

Issue #3267577 by kunal.sachdev: Clean up in hook_uninstall

parent 5e5c7173
No related branches found
No related tags found
1 merge request!233Issue #3267577: Clean up in hook_uninstall
...@@ -7,6 +7,13 @@ ...@@ -7,6 +7,13 @@
use Drupal\automatic_updates\Validation\ReadinessRequirements; use Drupal\automatic_updates\Validation\ReadinessRequirements;
/**
* Implements hook_uninstall().
*/
function automatic_updates_uninstall() {
\Drupal::service('automatic_updates.updater')->destroy(TRUE);
}
/** /**
* Implements hook_requirements(). * Implements hook_requirements().
*/ */
......
...@@ -137,3 +137,11 @@ services: ...@@ -137,3 +137,11 @@ services:
- '@package_manager.path_locator' - '@package_manager.path_locator'
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
package_manager.uninstall_validator:
class: Drupal\package_manager\PackageManagerUninstallValidator
tags:
- { name: module_install.uninstall_validator }
parent: container.trait
calls:
- ['setContainer', ['@service_container']]
lazy: true
<?php
namespace Drupal\package_manager;
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Prevents any module from being uninstalled if update is in process.
*/
class PackageManagerUninstallValidator implements ModuleUninstallValidatorInterface, ContainerAwareInterface {
use ContainerAwareTrait;
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function validate($module) {
$stage = new Stage(
$this->container->get('config.factory'),
$this->container->get('package_manager.path_locator'),
$this->container->get('package_manager.beginner'),
$this->container->get('package_manager.stager'),
$this->container->get('package_manager.committer'),
$this->container->get('file_system'),
$this->container->get('event_dispatcher'),
$this->container->get('tempstore.shared'),
$this->container->get('datetime.time')
);
if ($stage->isAvailable() || !$stage->isApplying()) {
return [];
}
if ($stage->isApplying()) {
$reasons[] = $this->t('Modules cannot be uninstalled while Package Manager is applying staged changes to the active code base.');
}
return $reasons;
}
}
<?php
// phpcs:ignoreFile
/**
* This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\package_manager\PackageManagerUninstallValidator' "modules/contrib/automatic_updates/package_manager/src".
*/
namespace Drupal\package_manager\ProxyClass {
/**
* Provides a proxy class for \Drupal\package_manager\PackageManagerUninstallValidator.
*
* @see \Drupal\Component\ProxyBuilder
*/
class PackageManagerUninstallValidator implements \Drupal\Core\Extension\ModuleUninstallValidatorInterface
{
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* The id of the original proxied service.
*
* @var string
*/
protected $drupalProxyOriginalServiceId;
/**
* The real proxied service, after it was lazy loaded.
*
* @var \Drupal\package_manager\PackageManagerUninstallValidator
*/
protected $service;
/**
* The service container.
*
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* Constructs a ProxyClass Drupal proxy object.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The container.
* @param string $drupal_proxy_original_service_id
* The service ID of the original service.
*/
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
{
$this->container = $container;
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
}
/**
* Lazy loads the real service from the container.
*
* @return object
* Returns the constructed real service.
*/
protected function lazyLoadItself()
{
if (!isset($this->service)) {
$this->service = $this->container->get($this->drupalProxyOriginalServiceId);
}
return $this->service;
}
/**
* {@inheritdoc}
*/
public function validate($module)
{
return $this->lazyLoadItself()->validate($module);
}
/**
* {@inheritdoc}
*/
public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
{
return $this->lazyLoadItself()->setStringTranslation($translation);
}
}
}
...@@ -359,12 +359,7 @@ class Stage { ...@@ -359,12 +359,7 @@ class Stage {
if (!$force) { if (!$force) {
$this->checkOwnership(); $this->checkOwnership();
} }
if ($this->isApplying()) {
// If we started applying staged changes to the active directory less than
// an hour ago, prevent the stage from being destroyed.
// @see :apply()
$apply_time = $this->tempStore->get(self::TEMPSTORE_APPLY_TIME_KEY);
if (isset($apply_time) && $this->time->getRequestTime() - $apply_time < 3600) {
throw new StageException('Cannot destroy the staging area while it is being applied to the active directory.'); throw new StageException('Cannot destroy the staging area while it is being applied to the active directory.');
} }
...@@ -547,4 +542,21 @@ class Stage { ...@@ -547,4 +542,21 @@ class Stage {
return FileSystem::getOsTemporaryDirectory() . DIRECTORY_SEPARATOR . '.package_manager' . $site_id; return FileSystem::getOsTemporaryDirectory() . DIRECTORY_SEPARATOR . '.package_manager' . $site_id;
} }
/**
* Checks if staged changes are being applied to the active directory.
*
* @return bool
* TRUE if the staged changes are being applied to the active directory, and
* it has been less than an hour since that operation began. If more than an
* hour has elapsed since the changes started to be applied, FALSE is
* returned even if the stage internally thinks that changes are still being
* applied.
*
* @see ::apply()
*/
final public function isApplying(): bool {
$apply_time = $this->tempStore->get(self::TEMPSTORE_APPLY_TIME_KEY);
return isset($apply_time) && $this->time->getRequestTime() - $apply_time < 3600;
}
} }
...@@ -4,6 +4,7 @@ namespace Drupal\Tests\package_manager\Kernel; ...@@ -4,6 +4,7 @@ namespace Drupal\Tests\package_manager\Kernel;
use Drupal\Component\Datetime\Time; use Drupal\Component\Datetime\Time;
use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Extension\ModuleUninstallValidatorException;
use Drupal\package_manager\Event\PostApplyEvent; use Drupal\package_manager\Event\PostApplyEvent;
use Drupal\package_manager\Event\PreApplyEvent; use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\StageEvent; use Drupal\package_manager\Event\StageEvent;
...@@ -12,6 +13,8 @@ use Drupal\package_manager\Exception\StageException; ...@@ -12,6 +13,8 @@ use Drupal\package_manager\Exception\StageException;
/** /**
* @coversDefaultClass \Drupal\package_manager\Stage * @coversDefaultClass \Drupal\package_manager\Stage
* *
* @covers \Drupal\package_manager\PackageManagerUninstallValidator
*
* @group package_manager * @group package_manager
*/ */
class StageTest extends PackageManagerKernelTestBase { class StageTest extends PackageManagerKernelTestBase {
...@@ -179,6 +182,32 @@ class StageTest extends PackageManagerKernelTestBase { ...@@ -179,6 +182,32 @@ class StageTest extends PackageManagerKernelTestBase {
$stage->apply(); $stage->apply();
} }
/**
* Test uninstalling any module while the staged changes are being applied.
*/
public function testUninstallModuleDuringApply(): void {
$listener = function (PreApplyEvent $event): void {
$this->assertTrue($event->getStage()->isApplying());
// Trying to uninstall any module while the stage is being applied should
// result in a module uninstall validation error.
try {
$this->container->get('module_installer')
->uninstall(['package_manager_bypass']);
$this->fail('Expected an exception to be thrown while uninstalling a module.');
}
catch (ModuleUninstallValidatorException $e) {
$this->assertStringContainsString('Modules cannot be uninstalled while Package Manager is applying staged changes to the active code base.', $e->getMessage());
}
};
$this->container->get('event_dispatcher')
->addListener(PreApplyEvent::class, $listener);
$stage = $this->createStage();
$stage->create();
$stage->apply();
}
} }
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment