Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
UpdateController.php 5.75 KiB
<?php

namespace Drupal\automatic_updates\Controller;

use Drupal\automatic_updates\BatchProcessor;
use Drupal\automatic_updates\Validation\StatusChecker;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Url;
use Drupal\package_manager\Validator\PendingUpdatesValidator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;

/**
 * Defines a controller to handle various stages of an automatic update.
 *
 * @internal
 *   Controller classes are internal.
 */
final class UpdateController extends ControllerBase {

  /**
   * The pending updates validator.
   *
   * @var \Drupal\package_manager\Validator\PendingUpdatesValidator
   */
  protected $pendingUpdatesValidator;

  /**
   * The current route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * The status checker.
   *
   * @var \Drupal\automatic_updates\Validation\StatusChecker
   */
  protected $statusChecker;

  /**
   * Constructs an UpdateController object.
   *
   * @param \Drupal\package_manager\Validator\PendingUpdatesValidator $pending_updates_validator
   *   The pending updates validator.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The current route match.
   * @param \Drupal\automatic_updates\Validation\StatusChecker $status_checker
   *   The status checker service.
   */
  public function __construct(PendingUpdatesValidator $pending_updates_validator, StateInterface $state, RouteMatchInterface $route_match, StatusChecker $status_checker) {
    $this->pendingUpdatesValidator = $pending_updates_validator;
    $this->stateService = $state;
    $this->routeMatch = $route_match;
    $this->statusChecker = $status_checker;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('package_manager.validator.pending_updates'),
      $container->get('state'),
      $container->get('current_route_match'),
      $container->get('automatic_updates.status_checker')
    );
  }

  /**
   * Redirects after staged changes are applied to the active directory.
   *
   * If there are any pending update hooks or post-updates, the user is sent to
   * update.php to run those. Otherwise, they are redirected to the status
   * report.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request object.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect to the appropriate destination.
   */
  public function onFinish(Request $request): RedirectResponse {
    $this->statusChecker->run();
    if ($this->pendingUpdatesValidator->updatesExist()) {
      $message = $this->t('Please apply database updates to complete the update process.');
      $url = Url::fromRoute('system.db_update');
    }
    else {
      $message = $this->t('Update complete!');
      $url = Url::fromRoute('update.status');
      // Now that the update is done, we can put the site back online if it was
      // previously not in maintenance mode.
      if (!$request->getSession()->remove(BatchProcessor::MAINTENANCE_MODE_SESSION_KEY)) {
        $this->state()->set('system.maintenance_mode', FALSE);
        // @todo Remove once the core bug that shows the maintenance mode
        //   message after the site is out of maintenance mode is fixed in
        //   https://www.drupal.org/i/3279246.
        $status_messages = $this->messenger()->messagesByType(MessengerInterface::TYPE_STATUS);
        $status_messages = array_filter($status_messages, function (string $message) {
          return !str_starts_with($message, (string) $this->t('Operating in maintenance mode.'));
        });
        $this->messenger()->deleteByType(MessengerInterface::TYPE_STATUS);
        foreach ($status_messages as $status_message) {
          $this->messenger()->addStatus($status_message);
        }
      }
    }
    $this->messenger()->addStatus($message);
    return new RedirectResponse($url->setAbsolute()->toString());
  }

  /**
   * Redirects deprecated routes and sets an informative message.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect response.
   */
  public function redirectDeprecatedRoute(Request $request): RedirectResponse {
    $route_name = $this->routeMatch->getRouteName();

    switch ($route_name) {
      case 'automatic_updates.module_update':
        $destination = 'update.module_update';
        break;

      case 'automatic_updates.theme_update':
        $destination = 'update.theme_update';
        break;
      case 'automatic_updates.report_update':
        $destination = 'update.report_update';
        break;

      default:
        throw new \InvalidArgumentException("Unknown route: '$route_name'");
    }
    $destination = Url::fromRoute($destination)
      ->setAbsolute()
      ->toString();

    $message = $this->t('This page was accessed from @deprecated_url, which is deprecated and will not work in the next major version of Automatic Updates. Please use <a href=":correct_url">@correct_url</a> instead.', [
      '@deprecated_url' => $request->getUri(),
      ':correct_url' => $destination,
      '@correct_url' => $destination,
    ]);
    $this->messenger()->addStatus($message);

    // 308 is a permanent redirect regardless of HTTP method.
    // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
    return new RedirectResponse($destination, 308);
  }

}