From dee92696a855240d11b1c1fcfc5eafaf45000b17 Mon Sep 17 00:00:00 2001
From: Schnitzel <schnitzel@643820.no-reply.drupal.org>
Date: Tue, 4 Oct 2022 19:06:18 +0000
Subject: [PATCH] Issue #3109082 by phenaproxima, Schnitzel, yash.rode, tedbow,
 wiifm, heddn, Leksat: Allow hosting platforms to declare that they don't
 support Package Manager

---
 automatic_updates.services.yml                |  6 ++
 package_manager/package_manager.services.yml  |  4 +
 .../Validator/EnvironmentSupportValidator.php | 70 +++++++++++++++++
 .../EnvironmentSupportValidatorTest.php       | 75 +++++++++++++++++++
 .../PackageManagerReadinessChecksTest.php     |  1 +
 5 files changed, 156 insertions(+)
 create mode 100644 package_manager/src/Validator/EnvironmentSupportValidator.php
 create mode 100644 package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php

diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index 9b26b15583..e78d85265d 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -73,6 +73,12 @@ services:
       - '@package_manager.validator.settings'
     tags:
       - { name: event_subscriber }
+  automatic_updates.validator.environment_support:
+    class: Drupal\automatic_updates\Validator\PackageManagerReadinessCheck
+    arguments:
+      - '@package_manager.validator.environment_support'
+    tags:
+      - { name: event_subscriber }
   automatic_updates.validator.composer_settings:
     class: Drupal\automatic_updates\Validator\PackageManagerReadinessCheck
     arguments:
diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index cd785118a5..e5d60db0a7 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -51,6 +51,10 @@ services:
       - '@package_manager.path_locator'
 
   # Validators.
+  package_manager.validator.environment_support:
+    class: Drupal\package_manager\Validator\EnvironmentSupportValidator
+    tags:
+      - { name: event_subscriber }
   package_manager.validator.composer_executable:
     class: Drupal\package_manager\Validator\ComposerExecutableValidator
     arguments:
diff --git a/package_manager/src/Validator/EnvironmentSupportValidator.php b/package_manager/src/Validator/EnvironmentSupportValidator.php
new file mode 100644
index 0000000000..76679ba87f
--- /dev/null
+++ b/package_manager/src/Validator/EnvironmentSupportValidator.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\package_manager\Validator;
+
+use Drupal\Core\Link;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\PreOperationStageEvent;
+use Drupal\package_manager\Event\StatusCheckEvent;
+use Drupal\Core\Url;
+
+/**
+ * Checks that the environment has support for Package Manager.
+ *
+ * @internal
+ *   This is an internal part of Package Manager and may be changed or removed
+ *   at any time without warning. External code should not interact with this
+ *   class.
+ */
+final class EnvironmentSupportValidator implements PreOperationStageValidatorInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The name of the environment variable to check.
+   *
+   * This environment variable, if defined, should be parseable by
+   * \Drupal\Core\Url::fromUri() and link to an explanation of why Package
+   * Manager is not supported in the current environment.
+   *
+   * @var string
+   */
+  public const VARIABLE_NAME = 'DRUPAL_PACKAGE_MANAGER_NOT_SUPPORTED_HELP_URL';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateStagePreOperation(PreOperationStageEvent $event): void {
+    $message = $this->t('Package Manager is not supported by your environment.');
+
+    $help_url = getenv(static::VARIABLE_NAME);
+    if (empty($help_url)) {
+      return;
+    }
+    // If the URL is not parseable, catch the exception that Url::fromUri()
+    // would generate.
+    try {
+      $message = Link::fromTextAndUrl($message, Url::fromUri($help_url))
+        ->toString();
+    }
+    catch (\InvalidArgumentException $e) {
+      // No need to do anything here. The message just won't be a link.
+    }
+    $event->addError([$message]);
+    // If Package Manager is unsupported, there's no point in doing any more
+    // validation.
+    $event->stopPropagation();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return [
+      PreCreateEvent::class => ['validateStagePreOperation', 200],
+      StatusCheckEvent::class => ['validateStagePreOperation', 200],
+    ];
+  }
+
+}
diff --git a/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php b/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php
new file mode 100644
index 0000000000..bef49a244a
--- /dev/null
+++ b/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\Tests\package_manager\Kernel;
+
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\PreOperationStageEvent;
+use Drupal\package_manager\ValidationResult;
+use Drupal\package_manager\Validator\EnvironmentSupportValidator;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @covers \Drupal\package_manager\Validator\EnvironmentSupportValidator
+ *
+ * @group package_manager
+ */
+class EnvironmentSupportValidatorTest extends PackageManagerKernelTestBase implements EventSubscriberInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->container->get('event_dispatcher')->addSubscriber($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $map = function (): string {
+      return 'assertValidationStopped';
+    };
+    return array_map($map, EnvironmentSupportValidator::getSubscribedEvents());
+  }
+
+  /**
+   * Ensures that the validator stops any further validation.
+   *
+   * @param \Drupal\package_manager\Event\PreOperationStageEvent $event
+   *   The event object.
+   */
+  public function assertValidationStopped(PreOperationStageEvent $event): void {
+    if ($event->getResults()) {
+      $this->assertTrue($event->isPropagationStopped());
+    }
+  }
+
+  /**
+   * Tests handling of an invalid URL in the environment support variable.
+   */
+  public function testInvalidUrl(): void {
+    putenv(EnvironmentSupportValidator::VARIABLE_NAME . '=broken/url.org');
+
+    $result = ValidationResult::createError([
+      'Package Manager is not supported by your environment.',
+    ]);
+    $this->assertStatusCheckResults([$result]);
+    $this->assertResults([$result], PreCreateEvent::class);
+  }
+
+  /**
+   * Tests that the validation message links to the provided URL.
+   */
+  public function testValidUrl(): void {
+    $url = 'http://www.example.com';
+    putenv(EnvironmentSupportValidator::VARIABLE_NAME . '=' . $url);
+
+    $result = ValidationResult::createError([
+      '<a href="' . $url . '">Package Manager is not supported by your environment.</a>',
+    ]);
+    $this->assertStatusCheckResults([$result]);
+    $this->assertResults([$result], PreCreateEvent::class);
+  }
+
+}
diff --git a/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php b/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
index 2ac412e8c2..96296a11b5 100644
--- a/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
+++ b/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
@@ -54,6 +54,7 @@ class PackageManagerReadinessChecksTest extends AutomaticUpdatesKernelTestBase {
       'Symlink validator' => ['package_manager.validator.symlink'],
       'Settings validator' => ['package_manager.validator.settings'],
       'Patches validator' => ['package_manager.validator.patches'],
+      'Environment support validator' => ['package_manager.validator.environment_support'],
     ];
   }
 
-- 
GitLab