diff --git a/automatic_updates.module b/automatic_updates.module index 3a4373ee7d3b79c05e0c4be75c38e5adb3450c89..cb0c512d7fcc6f6504022f23d6b9f1cdc023a64c 100644 --- a/automatic_updates.module +++ b/automatic_updates.module @@ -29,12 +29,20 @@ function automatic_updates_help($route_name, RouteMatchInterface $route_match) { $output .= '<p>' . t('Additionally, Automatic Updates periodically runs checks to ensure that updates can be installed, and will warn site administrators if problems are detected.') . '</p>'; $output .= '<h3>' . t('Requirements') . '</h3>'; $output .= '<p>' . t('Automatic Updates requires a Composer executable whose version satisfies <code>@version</code>, and PHP must have permission to run it. The path to the executable may be set in the <code>package_manager.settings:executables.composer</code> config setting, or it will be automatically detected.', ['@version' => ComposerExecutableValidator::MINIMUM_COMPOSER_VERSION_CONSTRAINT]) . '</p>'; - $output .= '<p>' . t('For more information, see the <a href=":automatic-updates-documentation">online documentation for the Automatic Updates module</a>.', [':automatic-updates-documentation' => 'https://www.drupal.org/docs/8/update/automatic-updates']) . '</p>'; $output .= '<p id="cron-alternate-port">' . t('If your site is running on the built-in PHP web server, unattended (i.e., cron) updates may not work without one of the following workarounds:') . '</p>'; $output .= '<ul>'; $output .= '<li>' . t('Use a multithreaded web server, such as Apache, NGINX, or on Windows, IIS.') . '</li>'; $output .= '<li>' . t('Run another instance of the built-in PHP web server on a different port and configure automatic updates accordingly: <code>$config["automatic_updates.settings"]["cron_port"] = $alternate_port_number;</code>') . '</li>'; $output .= '</ul>'; + $output .= '<p>' . t('For more information, see the <a href=":automatic-updates-documentation">online documentation for the Automatic Updates module</a>.', [':automatic-updates-documentation' => 'https://www.drupal.org/docs/8/update/automatic-updates']) . '</p>'; + $output .= '<h3 id="minor-update">' . t('Updating to another minor version of Drupal') . '</h3>'; + $output .= '<p>'; + $output .= t('Automatic Updates supports updating from one minor version of Drupal core to another; for example, from Drupal 9.4.8 to Drupal 9.5.0. This is only allowed when updating via <a href=":url">the user interface</a>. Unattended background updates can only update <em>within</em> the currently installed minor version (for example, Drupal 9.4.6 to 9.4.8).', [ + ':url' => Url::fromRoute('update.report_update')->toString(), + ]); + $output .= '</p>'; + $output .= '<p>' . t('This is because updating from one minor version of Drupal to another is riskier than staying within the current minor version. New minor versions of Drupal introduce changes that can, in some situations, be incompatible with installed modules and themes.') . '</p>'; + $output .= '<p>' . t('Therefore, if you want to use Automatic Updates to update to another minor version of Drupal, it is strongly recommended to do a test update first, ideally on an isolated development copy of your site, before updating your production site.') . '</p>'; return $output; } } diff --git a/src/Form/UpdaterForm.php b/src/Form/UpdaterForm.php index b169e475a2bcb54043ea1e18510fc3b760ad0c40..2884a1b46795c82d33677cf984bb43bdfd9a6951 100644 --- a/src/Form/UpdaterForm.php +++ b/src/Form/UpdaterForm.php @@ -4,6 +4,7 @@ namespace Drupal\automatic_updates\Form; use Drupal\automatic_updates\BatchProcessor; use Drupal\automatic_updates\Event\ReadinessCheckEvent; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\package_manager\FailureMarker; use Drupal\package_manager\ProjectInfo; use Drupal\automatic_updates\ReleaseChooser; @@ -82,6 +83,13 @@ final class UpdaterForm extends FormBase { */ protected $failureMarker; + /** + * The module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * Constructs a new UpdaterForm object. * @@ -97,14 +105,17 @@ final class UpdaterForm extends FormBase { * The renderer service. * @param \Drupal\package_manager\FailureMarker $failure_marker * The failure marker service. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. */ - public function __construct(StateInterface $state, Updater $updater, EventDispatcherInterface $event_dispatcher, ReleaseChooser $release_chooser, RendererInterface $renderer, FailureMarker $failure_marker) { + public function __construct(StateInterface $state, Updater $updater, EventDispatcherInterface $event_dispatcher, ReleaseChooser $release_chooser, RendererInterface $renderer, FailureMarker $failure_marker, ModuleHandlerInterface $module_handler) { $this->updater = $updater; $this->state = $state; $this->eventDispatcher = $event_dispatcher; $this->releaseChooser = $release_chooser; $this->renderer = $renderer; $this->failureMarker = $failure_marker; + $this->moduleHandler = $module_handler; } /** @@ -124,7 +135,8 @@ final class UpdaterForm extends FormBase { $container->get('event_dispatcher'), $container->get('automatic_updates.release_chooser'), $container->get('renderer'), - $container->get('package_manager.failure_marker') + $container->get('package_manager.failure_marker'), + $container->get('module_handler') ); } @@ -284,6 +296,22 @@ final class UpdaterForm extends FormBase { throw new \LogicException("Release information for Drupal $first_release_version is not available."); } + if ($this->moduleHandler->moduleExists('help')) { + $url = Url::fromRoute('help.page') + ->setRouteParameter('name', 'automatic_updates') + ->setOption('fragment', 'minor-update'); + + // @todo Updating this wording in https://www.drupal.org/i/3280403 to + // reflect that multiple minor branches may be visible. + $form['minor_update_help'] = [ + '#markup' => $this->t('The following updates are in the next minor version of Drupal. <a href=":url">Learn more about updating to another minor version.</a>', [ + ':url' => $url->toString(), + ]), + '#prefix' => '<p>', + '#suffix' => '</p>', + ]; + } + $form['next_minor'] = $this->createReleaseTable( $next_minor_release, $installed_minor_release ? $this->t('Minor update') : $release_status, diff --git a/tests/src/Functional/UpdaterFormTest.php b/tests/src/Functional/UpdaterFormTest.php index 0600764b72136f729a4ac305ef76ecc081bb1bed..fbedae923750d36e47abe4319da2aa29ad281b04 100644 --- a/tests/src/Functional/UpdaterFormTest.php +++ b/tests/src/Functional/UpdaterFormTest.php @@ -41,6 +41,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { 'block', 'automatic_updates', 'automatic_updates_test', + 'help', ]; /** @@ -127,9 +128,18 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { * @dataProvider providerTableLooksCorrect */ public function testTableLooksCorrect(string $access_page): void { + $assert_session = $this->assertSession(); + + $assert_minor_update_help = function () use ($assert_session): void { + $assert_session->pageTextContains('The following updates are in the next minor version of Drupal. Learn more about updating to another minor version.'); + $assert_session->linkExists('Learn more about updating to another minor version.'); + }; + $assert_no_minor_update_help = function () use ($assert_session): void { + $assert_session->pageTextNotContains('The following updates are in the next minor version of Drupal. Learn more about updating to another minor version.'); + }; + $page = $this->getSession()->getPage(); $this->drupalPlaceBlock('local_tasks_block', ['primary' => TRUE]); - $assert_session = $this->assertSession(); $this->setCoreVersion('9.8.0'); $this->checkForUpdates(); @@ -146,17 +156,19 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { } $this->clickLink('Update'); - // Check the form when there is an updates in the next minor only. + // Check the form when there is an update in the installed minor only. $assert_session->pageTextContainsOnce('Currently installed: 9.8.0 (Security update required!)'); $this->checkReleaseTable('#edit-installed-minor', '.update-update-security', '9.8.1', TRUE, 'Latest version of Drupal 9.8 (currently installed):'); $assert_session->elementNotExists('css', '#edit-next-minor'); + $assert_no_minor_update_help(); - // Check the form when there is an updates in the next minor only. + // Check the form when there is an update in the next minor only. $this->config('automatic_updates.settings')->set('allow_core_minor_updates', TRUE)->save(); $this->setCoreVersion('9.7.0'); $page->clickLink('Check manually'); $this->checkForMetaRefresh(); $this->checkReleaseTable('#edit-next-minor', '.update-update-recommended', '9.8.1', TRUE, 'Latest version of Drupal 9.8 (next minor) (Release notes):'); + $assert_minor_update_help(); $this->assertReleaseNotesLink(9, 8); $assert_session->pageTextContainsOnce('Currently installed: 9.7.0 (Not supported!)'); $assert_session->elementNotExists('css', '#edit-installed-minor'); @@ -170,6 +182,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $assert_session->pageTextContainsOnce('Currently installed: 9.7.0 (Update available)'); $this->checkReleaseTable('#edit-installed-minor', '.update-update-recommended', '9.7.1', TRUE, 'Latest version of Drupal 9.7 (currently installed):'); $assert_session->elementNotExists('css', '#edit-next-minor'); + $assert_no_minor_update_help(); // Check that if minor updates are enabled the update in the next minor will // be visible. @@ -178,6 +191,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $this->checkReleaseTable('#edit-installed-minor', '.update-update-recommended', '9.7.1', TRUE, 'Latest version of Drupal 9.7 (currently installed):'); $this->checkReleaseTable('#edit-next-minor', '.update-update-optional', '9.8.2', FALSE, 'Latest version of Drupal 9.8 (next minor) (Release notes):'); $this->assertReleaseNotesLink(9, 8); + $assert_minor_update_help(); $this->setCoreVersion('9.7.1'); $page->clickLink('Check manually'); @@ -186,8 +200,18 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $assert_session->elementNotExists('css', '#edit-installed-minor'); $this->checkReleaseTable('#edit-next-minor', '.update-update-recommended', '9.8.2', FALSE, 'Latest version of Drupal 9.8 (next minor) (Release notes):'); $this->assertReleaseNotesLink(9, 8); + $assert_minor_update_help(); $this->assertUpdateStagedTimes(0); + + // If the minor update help link exists, ensure it links to the right place. + $help_link = $page->findLink('Learn more about updating to another minor version.'); + if ($help_link) { + $this->assertStringEndsWith('#minor-update', $help_link->getAttribute('href')); + $help_link->click(); + $assert_session->statusCodeEquals(200); + $assert_session->responseContains('id="minor-update"'); + } } /**