Skip to content
Snippets Groups Projects
automatic_updates.module 8.45 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Contains hook implementations for Automatic Updates.
    
    use Drupal\Core\Routing\RouteMatchInterface;
    
    use Drupal\automatic_updates\CronUpdater;
    
    use Drupal\automatic_updates\UpdateRecommender;
    
    use Drupal\automatic_updates\Validation\AdminReadinessMessages;
    
    use Drupal\Core\Extension\ExtensionVersion;
    
    use Drupal\Core\Form\FormStateInterface;
    
    use Drupal\system\Controller\DbUpdateController;
    
    use Drupal\update\ProjectSecurityData;
    
    /**
     * Implements hook_help().
     */
    function automatic_updates_help($route_name, RouteMatchInterface $route_match) {
      // @todo Fully document all the modules features in
      //   https://www.drupal.org/i/3253591.
      switch ($route_name) {
        case 'help.page.auto_updates':
          $output = '<h3>' . t('About') . '</h3>';
          $output .= '<p>' . t('Automatic Updates lets you update Drupal core.') . '</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>';
          return $output;
      }
    }
    
    
    function automatic_updates_page_top() {
      /** @var \Drupal\automatic_updates\Validation\AdminReadinessMessages $readiness_messages */
      $readiness_messages = \Drupal::classResolver(AdminReadinessMessages::class);
      $readiness_messages->displayAdminPageMessages();
    
    
      // @todo Rely on the route option after https://www.drupal.org/i/3236497 is
      //   committed.
      // @todo Remove 'system.batch_page.html' after
      //   https://www.drupal.org/i/3238311 is committed.
      $skip_routes = [
        'system.batch_page.html',
        'automatic_updates.confirmation_page',
        'automatic_updates.report_update',
        'automatic_updates.module_update',
      ];
    
      // @see auto_updates_module_implements_alter()
    
      $route_name = \Drupal::routeMatch()->getRouteName();
      if (!in_array($route_name, $skip_routes, TRUE) && function_exists('update_page_top')) {
        update_page_top();
      }
    }
    
    /**
     * Implements hook_module_implements_alter().
     *
     * @todo Remove after https://www.drupal.org/i/3236497 is committed.
     */
    function automatic_updates_module_implements_alter(&$implementations, $hook) {
      if ($hook === 'page_top') {
        // Remove hook_page_top() implementation from the Update module. This '
        // implementation displays error messages about security releases. We call
        // this implementation in our own automatic_updates_page_top() except on our
    
        // own routes to avoid these messages while an update is in progress.
    
        unset($implementations['update']);
      }
    
    }
    
    /**
     * Implements hook_cron().
     */
    function automatic_updates_cron() {
    
      \Drupal::service('automatic_updates.cron_updater')->handleCron();
    
      /** @var \Drupal\automatic_updates\Validation\ReadinessValidationManager $checker_manager */
      $checker_manager = \Drupal::service('automatic_updates.readiness_validation_manager');
      $last_results = $checker_manager->getResults();
      $last_run_time = $checker_manager->getLastRunTime();
      // Do not run readiness checks more than once an hour unless there are no
      // results available.
      if ($last_results === NULL || !$last_run_time || \Drupal::time()->getRequestTime() - $last_run_time > 3600) {
        $checker_manager->run();
    
     * Implements hook_modules_installed().
    
    function automatic_updates_modules_installed() {
      // Run the readiness checkers if needed when any modules are installed in
      // case they provide readiness checker services.
      /** @var \Drupal\automatic_updates\Validation\ReadinessValidationManager $checker_manager */
      $checker_manager = \Drupal::service('automatic_updates.readiness_validation_manager');
    
     * Implements hook_modules_uninstalled().
    
    function automatic_updates_modules_uninstalled() {
      // Run the readiness checkers if needed when any modules are uninstalled in
      // case they provided readiness checker services.
      /** @var \Drupal\automatic_updates\Validation\ReadinessValidationManager $checker_manager */
      $checker_manager = \Drupal::service('automatic_updates.readiness_validation_manager');
    
     * Implements hook_form_FORM_ID_alter() for 'update_manager_update_form'.
    
    function automatic_updates_form_update_manager_update_form_alter(&$form, FormStateInterface $form_state, $form_id) {
      // Remove current message that core updates are not supported with a link to
    
      // use this module's form. The local task to 'update_manager_update_form' is
      // replaced by our own from but this original form would still accessible via
      // by its original URL.
    
      if (isset($form['manual_updates']['#rows']['drupal']['data']['title'])) {
    
        $current_route = \Drupal::routeMatch()->getRouteName();
        if ($current_route === 'update.module_update') {
          $redirect_route = 'automatic_updates.module_update';
        }
        elseif ($current_route === 'update.report_update') {
          $redirect_route = 'automatic_updates.report_update';
        }
        if (!empty($redirect_route)) {
          $core_updates_message = t(
            '<h2>Core updates required</h2>Drupal core updates are supported by the enabled <a href="@url">Automatic Updates module</a>',
            ['@url' => Url::fromRoute($redirect_route)->toString()]
          );
          $form['manual_updates']['#prefix'] = $core_updates_message;
        }
    
    /**
     * Implements hook_form_FORM_ID_alter() for 'update_settings' form.
     */
    function automatic_updates_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
      $recommender = new UpdateRecommender();
      $drupal_project = $recommender->getProjectInfo();
      $version = ExtensionVersion::createFromVersionString($drupal_project['existing_version']);
      $current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
    
      // @todo In https://www.drupal.org/node/2998285 use the update XML to
      //   determine when the installed of core will become unsupported.
    
      $supported_until_version = $version->getMajorVersion() . '.'
          . ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
          . '.0';
    
      $form['automatic_updates_cron'] = [
        '#type' => 'radios',
        '#title' => t('Automatically update Drupal core'),
        '#options' => [
          CronUpdater::DISABLED => t('Disabled'),
          CronUpdater::ALL => t('All supported updates'),
          CronUpdater::SECURITY => t('Security updates only'),
        ],
        '#default_value' => \Drupal::config('automatic_updates.settings')->get('cron'),
        '#description' => t(
          'If enabled, Drupal core will be automatically updated when an update is available. Automatic updates are only supported for @current_minor.x versions of Drupal core. Drupal @current_minor will receive security updates until @supported_until_version is released.',
          [
            '@current_minor' => $current_minor,
            '@supported_until_version' => $supported_until_version,
          ]
        ),
      ];
      $form += [
        '#submit' => ['::submitForm'],
      ];
      $form['#submit'][] = '_automatic_updates_update_settings_form_submit';
    }
    
    /**
     * Submit function for the 'update_settings' form.
     */
    function _automatic_updates_update_settings_form_submit(array &$form, FormStateInterface $form_state) {
      \Drupal::configFactory()
        ->getEditable('automatic_updates.settings')
        ->set('cron', $form_state->getValue('automatic_updates_cron'))
        ->save();
    }
    
    
    /**
     * Implements hook_local_tasks_alter().
     */
    function automatic_updates_local_tasks_alter(array &$local_tasks) {
      // The Update module's update form only allows updating modules and themes
      // via archive files, which could produce unexpected results on a site using
      // our Composer-based updater.
      $new_routes = [
        'update.report_update' => 'automatic_updates.report_update',
        'update.module_update' => 'automatic_updates.module_update',
        'update.theme_update' => 'automatic_updates.theme_update',
      ];
      foreach ($new_routes as $local_task_id => $new_route) {
        if (!empty($local_tasks[$local_task_id])) {
          $local_tasks[$local_task_id]['route_name'] = $new_route;
        }
      }
    }
    
    
    /**
     * Implements hook_batch_alter().
     *
     * @todo Remove this in https://www.drupal.org/i/3267817.
     */
    function automatic_updates_batch_alter(array &$batch): void {
      foreach ($batch['sets'] as &$batch_set) {
        if (!empty($batch_set['finished']) && $batch_set['finished'] === [DbUpdateController::class, 'batchFinished']) {
          $batch_set['finished'] = [BatchProcessor::class, 'dbUpdateBatchFinished'];
        }
      }
    }