diff --git a/src/Controller/BrowserController.php b/src/Controller/BrowserController.php index 8844705957a2e5bafb583fc60e3d64489355d618..095561cd15efa01c8ee1fa1eeedb190eb4844788 100644 --- a/src/Controller/BrowserController.php +++ b/src/Controller/BrowserController.php @@ -78,6 +78,14 @@ class BrowserController extends ControllerBase { $active_plugins[$source->getPluginId()] = $source->getPluginDefinition()['label']; } + $pm_validation_errors = []; + $pm_validation_messages = []; + if ($ui_install_enabled) { + $pm_validate = $this->installReadiness->validatePackageManager(); + $pm_validation_errors = $pm_validate['errors']; + $pm_validation_messages = $pm_validate['messages']; + } + return [ '#theme' => 'project_browser_main_app', '#attached' => [ @@ -101,7 +109,8 @@ class BrowserController extends ControllerBase { 'current_sources_keys' => $current_sources_keys, 'ui_install' => $ui_install_enabled, 'stage_available' => $ui_install_enabled ? $this->installReadiness->installerAvailable() : FALSE, - 'pm_validation' => $ui_install_enabled ? $this->installReadiness->validatePackageManager() : TRUE, + 'pm_validation_errors' => count($pm_validation_errors) ? $pm_validation_errors : FALSE, + 'pm_validation_messages' => count($pm_validation_messages) ? $pm_validation_messages : FALSE, ], ], ], diff --git a/src/InstallReadiness.php b/src/InstallReadiness.php index 687d842fe644a2346782bb1ec1b10795348a25b0..4c72e1584e692961e5205eb171c7528d603b9fcc 100644 --- a/src/InstallReadiness.php +++ b/src/InstallReadiness.php @@ -4,6 +4,7 @@ namespace Drupal\project_browser; use Drupal\package_manager\StatusCheckTrait; use Drupal\project_browser\ComposerInstaller\Installer; +use Drupal\system\SystemManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -21,22 +22,32 @@ class InstallReadiness { /** * Checks if the environment meets Package Manager install requirements. * - * @return false|string - * FALSE if no validation errors, otherwise an error message. + * @return array[] + * errors - an array of messages with severity 2 + * messages - all other messages below severity 2 (warnings) */ public function validatePackageManager() { - $text = ''; - $results = $this->runStatusCheck($this->installer, $this->eventDispatcher); - foreach ($results as $result) { + $errors = []; + $warnings = []; + foreach ($this->runStatusCheck($this->installer, $this->eventDispatcher) as $result) { $messages = $result->messages; $summary = $result->summary; - if ($summary) { array_unshift($messages, $summary); } - $text .= implode("\n", $messages) . "\n"; + $text = implode("\n", $messages); + + if ($result->severity == SystemManager::REQUIREMENT_ERROR) { + $errors[] = $text; + } + else { + $warnings[] = $text; + } } - return $text ?: FALSE; + return [ + 'errors' => $errors, + 'messages' => $warnings, + ]; } /** diff --git a/sveltejs/public/build/bundle.css b/sveltejs/public/build/bundle.css new file mode 100644 index 0000000000000000000000000000000000000000..1583d982fb62bd8395404706a8c59c89739f8b06 Binary files /dev/null and b/sveltejs/public/build/bundle.css differ diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js index 7b2eaee35b66b6ae05e15334a48ce9094d98b8e4..d04c3c57a46f7c5b6510a1914eab5d0492ae9dc7 100644 Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map index 7f3df18de26eefb49d23d76e3536e2bfcc8f9046..bd094a77375f5f869ca32110ab563e96f203856d 100644 Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ diff --git a/sveltejs/src/Project/ActionButton.svelte b/sveltejs/src/Project/ActionButton.svelte index 0fa751253a15a9504a6215e42fbb520cb8f9201a..3786785ce7cc4fb736b16e5fb63b3028a12edbc5 100644 --- a/sveltejs/src/Project/ActionButton.svelte +++ b/sveltejs/src/Project/ActionButton.svelte @@ -4,7 +4,7 @@ MODULE_STATUS, ORIGIN_URL, ALLOW_UI_INSTALL, - PM_VALIDATION_ERROR, + PM_VALIDATION_ERRORS, } from '../constants'; import Loading from '../Loading.svelte'; import { openPopup, getCommandsPopupMessage } from '../popup'; @@ -212,7 +212,7 @@ </span> {:else} <span> - {#if !PM_VALIDATION_ERROR && ALLOW_UI_INSTALL} + {#if !PM_VALIDATION_ERRORS && ALLOW_UI_INSTALL} {#if loading} <span class="loading-ellipsis">{loadingPhase}</span> <Loading positionAbsolute={true} /> diff --git a/sveltejs/src/Project/AddInstallButton.svelte b/sveltejs/src/Project/AddInstallButton.svelte index 0e2844cb512dd5754465ee748c4f6b83f76e427b..df63992199dc749c658d471559f9ec00a601fdcc 100644 --- a/sveltejs/src/Project/AddInstallButton.svelte +++ b/sveltejs/src/Project/AddInstallButton.svelte @@ -1,6 +1,10 @@ <script> import { openPopup } from '../popup'; - import { MODULE_STATUS, ORIGIN_URL, PM_VALIDATION_ERROR } from '../constants'; + import { + MODULE_STATUS, + ORIGIN_URL, + PM_VALIDATION_ERRORS, + } from '../constants'; import ProjectButtonBase from './ProjectButtonBase.svelte'; import { isPackageManagerRequired } from '../stores'; @@ -162,7 +166,7 @@ downloadModule(true); } }} - disabled={PM_VALIDATION_ERROR && $isPackageManagerRequired} + disabled={PM_VALIDATION_ERRORS && $isPackageManagerRequired} > {alreadyAdded ? Drupal.t('Install') : Drupal.t('Add and Install')}<span class="visually-hidden">{project.title}</span diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte index 6ca1bfa2c66e912376d81959a724085f4836c0dd..76a037cc8787fc449ff5abfa971177f5934b09e6 100644 --- a/sveltejs/src/ProjectBrowser.svelte +++ b/sveltejs/src/ProjectBrowser.svelte @@ -33,7 +33,8 @@ SORT_OPTIONS, MODULE_STATUS, ALLOW_UI_INSTALL, - PM_VALIDATION_ERROR, + PM_VALIDATION_ERRORS, + PM_VALIDATION_MESSAGES, ACTIVE_PLUGINS, } from './constants'; // cspell:ignore tabwise @@ -357,18 +358,38 @@ {/if} </div> - <!-- If Package Manager is required and UI installs are enabled,but the - site configuration does not support them, display a message - informing the user what must be changed for UI installs to work. --> - {#if $isPackageManagerRequired && PM_VALIDATION_ERROR && typeof PM_VALIDATION_ERROR === 'string' && MODULE_STATUS.package_manager && ALLOW_UI_INSTALL} - <div class="project-browser__install-warning"> - <p class="project-browser__warning-header"> - <strong>{Drupal.t('Unable to download modules via the UI')}</strong> - </p> - <p class="project-browser__warning"> - <em>{@html PM_VALIDATION_ERROR}</em> - </p> - </div> + <!-- If Package Manager is required and UI installs are enabled, validate + and print all messages, warnings and errors. If there were errors, + display a message informing the user what must be changed for UI + installs to work. --> + {#if $isPackageManagerRequired && ALLOW_UI_INSTALL && MODULE_STATUS.package_manager} + {#if PM_VALIDATION_ERRORS} + <div class="project-browser__install-warning severity-error"> + <p class="project-browser__warning-header"> + <strong + >{Drupal.t('Unable to download modules via the UI')}</strong + > + </p> + <p class="project-browser__warning"> + <em> + {#each PM_VALIDATION_ERRORS as message} + {@html message}<br /> + {/each} + </em> + </p> + </div> + {/if} + {#if PM_VALIDATION_MESSAGES} + <div class="project-browser__install-warning"> + <p class="project-browser__warning"> + <em> + {#each PM_VALIDATION_MESSAGES as message} + {@html message}<br /> + {/each} + </em> + </p> + </div> + {/if} {/if} <Pagination page={$page} diff --git a/sveltejs/src/constants.js b/sveltejs/src/constants.js index 7f204cdd6651450b3b01c7034f0682cf510ee2dc..3efd14e1232e27727803ad6926fc05cf67f11727 100644 --- a/sveltejs/src/constants.js +++ b/sveltejs/src/constants.js @@ -21,5 +21,6 @@ export const ALLOW_UI_INSTALL = drupalSettings.project_browser.ui_install; export const DARK_COLOR_SCHEME = matchMedia('(forced-colors: active)').matches && matchMedia('(prefers-color-scheme: dark)').matches; -export const PM_VALIDATION_ERROR = drupalSettings.project_browser.pm_validation; +export const PM_VALIDATION_ERRORS = drupalSettings.project_browser.pm_validation_errors; +export const PM_VALIDATION_MESSAGES = drupalSettings.project_browser.pm_validation_messages; export const ACTIVE_PLUGINS = drupalSettings.project_browser.active_plugins; diff --git a/tests/modules/project_browser_test/config/install/project_browser_test.settings.yml b/tests/modules/project_browser_test/config/install/project_browser_test.settings.yml new file mode 100644 index 0000000000000000000000000000000000000000..f8b10ea8b65113f95389c0d91297ea2add81e502 --- /dev/null +++ b/tests/modules/project_browser_test/config/install/project_browser_test.settings.yml @@ -0,0 +1,2 @@ +simulateError: false +simulateWarning: false diff --git a/tests/modules/project_browser_test/config/schema/project_browser_test.schema.yml b/tests/modules/project_browser_test/config/schema/project_browser_test.schema.yml new file mode 100644 index 0000000000000000000000000000000000000000..46ae85ca419014ea361a5b579f2144c7a57311b9 --- /dev/null +++ b/tests/modules/project_browser_test/config/schema/project_browser_test.schema.yml @@ -0,0 +1,10 @@ +project_browser_test.settings: + type: config_object + label: 'Project Browser Test Settings' + mapping: + simulateError: + type: boolean + label: 'Simulate a package manager error' + simulateWarning: + type: boolean + label: 'Simulate a package manager warning' diff --git a/tests/modules/project_browser_test/project_browser_test.services.yml b/tests/modules/project_browser_test/project_browser_test.services.yml index e157cf9c84c9cd5c0e7f0c69d24f52e731f19db1..4e60b58c73cdde1f975a99643384055a72da329e 100644 --- a/tests/modules/project_browser_test/project_browser_test.services.yml +++ b/tests/modules/project_browser_test/project_browser_test.services.yml @@ -7,3 +7,11 @@ services: class: Drupal\project_browser_test\DrupalOrgClientMiddleware tags: - { name: http_client_middleware } + + Drupal\project_browser_test\Validator\ProjectBrowserTestSimulateErrorValidator: + tags: + - { name: event_subscriber } + + Drupal\project_browser_test\Validator\ProjectBrowserTestSimulateWarningValidator: + tags: + - { name: event_subscriber } diff --git a/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateErrorValidator.php b/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateErrorValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..ecf42d376c75a6bce1291b85940df3aeca00fa9d --- /dev/null +++ b/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateErrorValidator.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\project_browser_test\Validator; + +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Drupal\package_manager\Event\PreCreateEvent; +use Drupal\package_manager\Event\PreOperationStageEvent; + +/** + * Simulates a package manager warning. + */ +final class ProjectBrowserTestSimulateErrorValidator implements EventSubscriberInterface { + + use StringTranslationTrait; + + /** + * {@inheritdoc} + */ + public function simulateError(PreOperationStageEvent $event): void { + // Always throw an error when this is enabled. + if (!\Drupal::config('project_browser_test.settings')->get('simulateError')) { + return; + } + $event->addError([$this->t("Simulate an error message for the project browser.")]); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [ + PreCreateEvent::class => 'simulateError', + ]; + } + +} diff --git a/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateWarningValidator.php b/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateWarningValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..c43c5a1702ced20aea695447095f45203ec3fad3 --- /dev/null +++ b/tests/modules/project_browser_test/src/Validator/ProjectBrowserTestSimulateWarningValidator.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\project_browser_test\Validator; + +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\package_manager\Event\StatusCheckEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Simulates a package manager warning. + */ +final class ProjectBrowserTestSimulateWarningValidator implements EventSubscriberInterface { + + use StringTranslationTrait; + + /** + * {@inheritdoc} + */ + public function simulateWarning(StatusCheckEvent $event): void { + if (!\Drupal::config('project_browser_test.settings')->get('simulateWarning')) { + return; + } + // Always throw an error when this is enabled. + $event->addWarning([$this->t("Simulate a warning message for the project browser.")]); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [ + StatusCheckEvent::class => 'simulateError', + ]; + } + +} diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index aeded65d7d15aa5b6e16a8eb90a7040449a57c78..fe46e18f7b55af780cea55cb2b0cc32f21d293ab 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -199,4 +199,46 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { } + /** + * Confirm that a status check error prevents download and install. + */ + public function testPackageManagerErrorPreventsDownload(): void { + \Drupal::configFactory() + ->getEditable('project_browser_test.settings') + ->set('simulateError', TRUE) + ->save(TRUE); + + $assert_session = $this->assertSession(); + $this->drupalGet('admin/modules/browse'); + $this->assertSession()->pageTextContains("Simulate an error message for the project browser."); + $this->svelteInitHelper('text', 'Cream cheese on a bagel'); + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; + $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button"); + $this->assertNotEmpty($download_button); + $this->assertSame('View Commands for Cream cheese on a bagel', $download_button->getText()); + } + + /** + * Confirm that a status check warning allows download and install. + */ + public function testPackageManagerWarningAllowsDownloadInstall(): void { + \Drupal::configFactory() + ->getEditable('project_browser_test.settings') + ->set('simulateWarning', TRUE) + ->save(TRUE); + + $assert_session = $this->assertSession(); + $this->drupalGet('admin/modules/browse'); + $this->assertSession()->pageTextContains("Simulate a warning message for the project browser."); + $this->svelteInitHelper('text', 'Cream cheese on a bagel'); + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; + $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button"); + $this->assertNotEmpty($download_button); + $this->assertSame('Add and Install Cream cheese on a bagel', $download_button->getText()); + $download_button->click(); + $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator"); + $assert_session->waitForText('✓ Cream cheese on a bagel is Installed'); + $this->assertSame('✓ Cream cheese on a bagel is Installed', $installed_action->getText()); + } + }