diff --git a/automatic_updates_extensions/src/Form/UpdateReady.php b/automatic_updates_extensions/src/Form/UpdateReady.php index 88ed08ab588214f0405a3aae38807f7b2beab308..9613500f1594b95b44c4334addbce313352d16e3 100644 --- a/automatic_updates_extensions/src/Form/UpdateReady.php +++ b/automatic_updates_extensions/src/Form/UpdateReady.php @@ -2,9 +2,11 @@ namespace Drupal\automatic_updates_extensions\Form; +use Drupal\automatic_updates\Validation\ReadinessTrait; +use Drupal\package_manager\Event\StatusCheckEvent; use Drupal\package_manager\Exception\ApplyFailedException; use Drupal\package_manager\ProjectInfo; -use Drupal\package_manager\Validator\StagedDBUpdateValidator; +use Drupal\package_manager\ValidationResult; use Drupal\automatic_updates_extensions\BatchProcessor; use Drupal\automatic_updates\BatchProcessor as AutoUpdatesBatchProcessor; use Drupal\automatic_updates_extensions\ExtensionUpdater; @@ -18,7 +20,9 @@ use Drupal\Core\State\StateInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\package_manager\Exception\StageException; use Drupal\package_manager\Exception\StageOwnershipException; +use Drupal\system\SystemManager; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Defines a form to commit staged updates. @@ -28,6 +32,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ final class UpdateReady extends FormBase { + use ReadinessTrait { + formatResult as traitFormatResult; + } + /** * The updater service. * @@ -50,18 +58,18 @@ final class UpdateReady extends FormBase { protected $moduleList; /** - * The staged database update validator service. + * The renderer service. * - * @var \Drupal\package_manager\Validator\StagedDBUpdateValidator + * @var \Drupal\Core\Render\RendererInterface */ - protected $stagedDatabaseUpdateValidator; + protected $renderer; /** - * The renderer service. + * The event dispatcher. * - * @var \Drupal\Core\Render\RendererInterface + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - protected $renderer; + protected $eventDispatcher; /** * Constructs a new UpdateReady object. @@ -74,18 +82,18 @@ final class UpdateReady extends FormBase { * The state service. * @param \Drupal\Core\Extension\ModuleExtensionList $module_list * The module list service. - * @param \Drupal\package_manager\Validator\StagedDBUpdateValidator $staged_database_update_validator - * The staged database update validator service. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher + * Event dispatcher service. */ - public function __construct(ExtensionUpdater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, StagedDBUpdateValidator $staged_database_update_validator, RendererInterface $renderer) { + public function __construct(ExtensionUpdater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, RendererInterface $renderer, EventDispatcherInterface $event_dispatcher) { $this->updater = $updater; $this->setMessenger($messenger); $this->state = $state; $this->moduleList = $module_list; - $this->stagedDatabaseUpdateValidator = $staged_database_update_validator; $this->renderer = $renderer; + $this->eventDispatcher = $event_dispatcher; } /** @@ -104,8 +112,8 @@ final class UpdateReady extends FormBase { $container->get('messenger'), $container->get('state'), $container->get('extension.list.module'), - $container->get('package_manager.validator.staged_database_updates'), - $container->get('renderer') + $container->get('renderer'), + $container->get('event_dispatcher') ); } @@ -127,23 +135,6 @@ final class UpdateReady extends FormBase { $messages = []; - // If there are any installed extension with database updates in the staging - // area, warn the user that they might be sent to update.php once the staged - // changes have been applied. - $pending_updates = $this->stagedDatabaseUpdateValidator->getExtensionsWithDatabaseUpdates($this->updater); - if ($pending_updates) { - natcasesort($pending_updates); - $message_item_list = [ - '#theme' => 'item_list', - '#prefix' => '<p>' . $this->t('Possible database updates were detected in the following extensions; you may be redirected to the database update page in order to complete the update process.') . '</p>', - '#items' => $pending_updates, - '#context' => [ - 'list_style' => 'automatic-updates-extensions__pending-database-updates', - ], - ]; - $messages[MessengerInterface::TYPE_WARNING][] = $this->renderer->renderRoot($message_item_list); - } - // Don't set any messages if the form has been submitted, because we don't // want them to be set during form submit. if (!$form_state->getUserInput()) { @@ -177,6 +168,21 @@ final class UpdateReady extends FormBase { '#type' => 'checkbox', '#default_value' => TRUE, ]; + + // Don't run the status checks once the form has been submitted. + if (!$form_state->getUserInput()) { + $event = new StatusCheckEvent($this->updater); + $this->eventDispatcher->dispatch($event); + /** @var \Drupal\package_manager\ValidationResult[] $results */ + $results = $event->getResults(); + // This will have no effect if $results is empty. + $this->displayResults($results, $this->messenger(), $this->renderer); + // If any errors occurred, return the form early so the user cannot + // continue. + if ($this->getOverallSeverity($results) === SystemManager::REQUIREMENT_ERROR) { + return $form; + } + } $form['actions']['submit'] = [ '#type' => 'submit', '#value' => $this->t('Continue'), @@ -332,4 +338,25 @@ final class UpdateReady extends FormBase { ]; } + /** + * {@inheritdoc} + * + * @todo Remove this in https://www.drupal.org/project/automatic_updates/issues/3313414. + */ + protected function formatResult(ValidationResult $result) { + $messages = $result->getMessages(); + + if (count($messages) > 1) { + return [ + '#theme' => 'item_list__automatic_updates_validation_results', + '#prefix' => $result->getSummary(), + '#items' => $messages, + '#context' => [ + 'list_style' => 'automatic-updates-extensions__pending-database-updates', + ], + ]; + } + return $this->traitFormatResult($result); + } + } diff --git a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php index 8747d61d92f44378a9d986d21c46928c2f4279c9..f0088239d4061711ec311db531880e90a3ba0c95 100644 --- a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php +++ b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php @@ -6,9 +6,11 @@ use Drupal\automatic_updates\Event\ReadinessCheckEvent; use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1; use Drupal\package_manager_test_validation\StagedDatabaseUpdateValidator; use Drupal\package_manager\Event\PreApplyEvent; +use Drupal\package_manager\Event\StatusCheckEvent; use Drupal\package_manager\ValidationResult; use Drupal\package_manager_bypass\Committer; use Drupal\package_manager_bypass\Stager; +use Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber; use Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase; use Drupal\Tests\automatic_updates\Traits\ValidationTestTrait; use Drupal\Tests\automatic_updates_extensions\Traits\FormTestTrait; @@ -192,11 +194,11 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { // Ensure that a list of pending database updates is visible, along with a // short explanation, in the warning messages. $warning_messages = $assert_session->elementExists('xpath', '//div[@data-drupal-messages]//div[@aria-label="Warning message"]'); - $this->assertStringContainsString('Possible database updates were detected in the following extensions; you may be redirected to the database update page in order to complete the update process.', $warning_messages->getText()); + $this->assertStringContainsString('Possible database updates have been detected in the following extensions.', $warning_messages->getText()); $pending_updates = $warning_messages->findAll('css', 'ul.item-list__automatic-updates-extensions__pending-database-updates li'); $this->assertCount(2, $pending_updates); - $this->assertSame('Automatic Updates Theme With Updates', $pending_updates[0]->getText()); - $this->assertSame('System', $pending_updates[1]->getText()); + $this->assertSame('System', $pending_updates[0]->getText()); + $this->assertSame('Automatic Updates Theme With Updates', $pending_updates[1]->getText()); $page->pressButton('Continue'); $this->checkForMetaRefresh(); @@ -450,9 +452,63 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $this->clickLink('Update Extensions'); $this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1'); $this->assertUpdatesCount(1); - $assert->pageTextContains(static::$warningsExplanation); + $this->checkForMetaRefresh(); $assert->pageTextNotContains(static::$errorsExplanation); + $assert->elementExists('css', '#edit-projects-semver-test')->check(); + $assert->checkboxChecked('edit-projects-semver-test'); + $assert->pageTextContains(static::$warningsExplanation); $assert->buttonExists('Update'); + + // Add warnings from StatusCheckEvent. + $summary_status_check_event = t('Some summary'); + $messages_status_check_event = [ + "The only thing we're allowed to do is to", + "believe that we won't regret the choice", + "we made.", + ]; + $warning_status_check_event = ValidationResult::createWarning($messages_status_check_event, $summary_status_check_event); + TestSubscriber::setTestResult([$warning_status_check_event], StatusCheckEvent::class); + $this->getSession()->getPage()->pressButton('Update'); + $this->checkForMetaRefresh(); + $assert->buttonExists('Continue'); + $assert->pageTextContains($summary_status_check_event); + foreach ($messages_status_check_event as $message) { + $assert->pageTextContains($message); + } + } + + /** + * Tests that messages from StatusCheckEvent are shown on the confirmation form. + */ + public function testStatusErrorMessages(): void { + $this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml'); + $assert = $this->assertSession(); + $this->setProjectInstalledVersion(['semver_test' => '8.1.0']); + $this->checkForUpdates(); + $this->drupalGet('admin/reports/updates/automatic-update-extensions'); + $this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1'); + $this->assertUpdatesCount(1); + $this->getSession()->reload(); + $assert->elementExists('css', '#edit-projects-semver-test')->check(); + $assert->checkboxChecked('edit-projects-semver-test'); + $assert->buttonExists('Update'); + $messages = [ + "The only thing we're allowed to do is to", + "believe that we won't regret the choice", + "we made.", + ]; + $summary = t('Some summary'); + $error = ValidationResult::createError($messages, $summary); + TestSubscriber::setTestResult([$error], StatusCheckEvent::class); + $this->getSession()->getPage()->pressButton('Update'); + $this->checkForMetaRefresh(); + $assert->pageTextContains(static::$errorsExplanation); + $assert->pageTextNotContains(static::$warningsExplanation); + $assert->pageTextContains($summary); + foreach ($messages as $message) { + $assert->pageTextContains($message); + } + $assert->buttonNotExists('Continue'); } /**