You are here

automatic_updates.module in Automatic Updates 8

Same filename and directory in other branches
  1. 8.2 automatic_updates.module
  2. 7 automatic_updates.module

File

automatic_updates.module
View source
<?php

/**
 * @file
 * Contains automatic_updates.module..
 */
use Drupal\automatic_updates\ReadinessChecker\ReadinessCheckerManagerInterface;
use Drupal\automatic_updates\UpdateMetadata;
use Drupal\Core\Url;
use Drupal\update\UpdateManagerInterface;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;

/**
 * Implements hook_page_top().
 */
function automatic_updates_page_top(array &$page_top) {

  /** @var \Drupal\Core\Routing\AdminContext $admin_context */
  $admin_context = \Drupal::service('router.admin_context');
  $route_match = \Drupal::routeMatch();
  if ($admin_context
    ->isAdminRoute($route_match
    ->getRouteObject()) && \Drupal::currentUser()
    ->hasPermission('administer site configuration')) {
    $disabled_routes = [
      'update.theme_update',
      'system.theme_install',
      'update.module_update',
      'update.module_install',
      'update.status',
      'update.report_update',
      'update.report_install',
      'update.settings',
      'system.status',
      'update.confirmation_page',
    ];

    // These routes don't need additional nagging.
    if (in_array(\Drupal::routeMatch()
      ->getRouteName(), $disabled_routes, TRUE)) {
      return;
    }

    /** @var \Drupal\automatic_updates\Services\AutomaticUpdatesPsaInterface $psa */
    $psa = \Drupal::service('automatic_updates.psa');
    $messages = $psa
      ->getPublicServiceMessages();
    if ($messages) {
      \Drupal::messenger()
        ->addError(t('Public service announcements:'));
      foreach ($messages as $message) {
        \Drupal::messenger()
          ->addError($message);
      }
    }
    $last_check_timestamp = \Drupal::service('automatic_updates.readiness_checker')
      ->timestamp();
    if (\Drupal::time()
      ->getRequestTime() > $last_check_timestamp + ReadinessCheckerManagerInterface::LAST_CHECKED_WARNING) {
      $readiness_settings = Url::fromRoute('automatic_updates.settings');
      \Drupal::messenger()
        ->addError(t('Your site has not recently run an update readiness check. <a href="@link">Administer automatic updates</a> and run readiness checks manually.', [
        '@link' => $readiness_settings
          ->toString(),
      ]));
    }

    /** @var \Drupal\automatic_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker */
    $checker = \Drupal::service('automatic_updates.readiness_checker');
    $results = $checker
      ->getResults(ReadinessCheckerManagerInterface::ERROR);
    if ($results) {
      \Drupal::messenger()
        ->addError(t('Your site is currently failing readiness checks for automatic updates. It cannot be <a href="@readiness_checks">automatically updated</a> until further action is performed:', [
        '@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks',
      ]));
      foreach ($results as $message) {
        \Drupal::messenger()
          ->addError($message);
      }
    }
    $results = $checker
      ->getResults('warning');
    if ($results) {
      \Drupal::messenger()
        ->addWarning(t('Your site does not pass some readiness checks for automatic updates. Depending on the nature of the failures, it might effect the eligibility for <a href="@readiness_checks">automatic updates</a>.', [
        '@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks',
      ]));
      foreach ($results as $message) {
        \Drupal::messenger()
          ->addWarning($message);
      }
    }
  }
}

/**
 * Implements hook_cron().
 */
function automatic_updates_cron() {
  $state = \Drupal::state();
  $request_time = \Drupal::time()
    ->getRequestTime();
  $last_check = $state
    ->get('automatic_updates.cron_last_check', 0);

  // Only allow cron to run once every hour.
  if ($request_time - $last_check < 3600) {
    return;
  }

  // Checkers should run before updates because of class caching.

  /** @var \Drupal\automatic_updates\Services\NotifyInterface $notify */
  $notify = \Drupal::service('automatic_updates.psa_notify');
  $notify
    ->send();

  /** @var \Drupal\automatic_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker */
  $checker = \Drupal::service('automatic_updates.readiness_checker');
  foreach ($checker
    ->getCategories() as $category) {
    $checker
      ->run($category);
  }

  // In-place updates won't function for dev releases of Drupal core.
  $dev_core = strpos(\Drupal::VERSION, '-dev') !== FALSE;

  /** @var \Drupal\Core\Config\ImmutableConfig $config */
  $config = \Drupal::config('automatic_updates.settings');
  if (!$dev_core && $config
    ->get('enable_cron_updates')) {
    \Drupal::service('update.manager')
      ->refreshUpdateData();
    \Drupal::service('update.processor')
      ->fetchData();
    $available = update_get_available(TRUE);
    $projects = update_calculate_project_data($available);
    $not_recommended_version = $projects['drupal']['status'] !== UpdateManagerInterface::CURRENT;
    $security_update = in_array($projects['drupal']['status'], [
      UpdateManagerInterface::NOT_SECURE,
      UpdateManagerInterface::REVOKED,
    ], TRUE);
    $recommended_release = isset($projects['drupal']['releases'][$projects['drupal']['recommended']]) ? $projects['drupal']['releases'][$projects['drupal']['recommended']] : NULL;
    $existing_minor_version = explode('.', \Drupal::VERSION, -1);
    $recommended_minor_version = explode('.', $recommended_release['version'], -1);
    $major_upgrade = $existing_minor_version !== $recommended_minor_version;
    if ($major_upgrade) {
      foreach (range(1, 30) as $point_version) {
        $potential_version = implode('.', array_merge($existing_minor_version, (array) $point_version));
        if (isset($available['drupal']['releases'][$potential_version])) {
          $recommended_release = $available['drupal']['releases'][$potential_version];
        }
        else {
          break;
        }
      }
    }

    // Don't automatically update major version bumps or from/to same version.
    if ($not_recommended_version && $projects['drupal']['existing_version'] !== $recommended_release['version']) {
      if ($config
        ->get('enable_cron_security_updates')) {
        if ($security_update) {
          $metadata = new UpdateMetadata('drupal', 'core', \Drupal::VERSION, $recommended_release['version']);

          /** @var \Drupal\automatic_updates\Services\UpdateInterface $updater */
          $updater = \Drupal::service('automatic_updates.update');
          $updater
            ->update($metadata);
        }
      }
      else {
        $metadata = new UpdateMetadata('drupal', 'core', \Drupal::VERSION, $recommended_release['version']);

        /** @var \Drupal\automatic_updates\Services\UpdateInterface $updater */
        $updater = \Drupal::service('automatic_updates.update');
        $updater
          ->update($metadata);
      }
    }
  }
  $state
    ->set('automatic_updates.cron_last_check', \Drupal::time()
    ->getCurrentTime());
}

/**
 * Implements hook_theme().
 */
function automatic_updates_theme(array $existing, $type, $theme, $path) {
  return [
    'automatic_updates_psa_notify' => [
      'variables' => [
        'messages' => [],
      ],
    ],
    'automatic_updates_post_update' => [
      'variables' => [
        'success' => NULL,
        'metadata' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_mail().
 */
function automatic_updates_mail($key, &$message, $params) {

  /** @var \Drupal\Core\Render\RendererInterface $renderer */
  $renderer = \Drupal::service('renderer');
  $message['subject'] = $params['subject'];
  $message['body'][] = $renderer
    ->render($params['body']);
}

/**
 * Helper method to execute console command.
 *
 * @param string $command_argument
 *   The command argument.
 *
 * @return \Symfony\Component\Process\Process
 *   The console command process.
 */
function automatic_updates_console_command($command_argument) {
  $module_path = drupal_get_path('module', 'automatic_updates');
  $command = [
    (new PhpExecutableFinder())
      ->find(),
    $module_path . '/scripts/automatic_update_tools',
    $command_argument,
    '--script-filename',
    \Drupal::request()->server
      ->get('SCRIPT_FILENAME'),
    '--base-url',
    \Drupal::request()
      ->getBaseUrl(),
    '--base-path',
    \Drupal::request()
      ->getBasePath(),
  ];
  $process = new Process($command, (string) \Drupal::root(), NULL, NULL, NULL);
  $process
    ->run();
  return $process;
}

Functions

Namesort descending Description
automatic_updates_console_command Helper method to execute console command.
automatic_updates_cron Implements hook_cron().
automatic_updates_mail Implements hook_mail().
automatic_updates_page_top Implements hook_page_top().
automatic_updates_theme Implements hook_theme().