<?php /** * @file * Contains automatic_updates.module.. */ use Drupal\automatic_updates\ReadinessChecker\ReadinessCheckerManagerInterface; 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; } // 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) { /** @var \Drupal\automatic_updates\Services\UpdateInterface $updater */ $updater = \Drupal::service('automatic_updates.update'); $updater->update('drupal', 'core', \Drupal::VERSION, $recommended_release['version']); } } else { /** @var \Drupal\automatic_updates\Services\UpdateInterface $updater */ $updater = \Drupal::service('automatic_updates.update'); $updater->update('drupal', 'core', \Drupal::VERSION, $recommended_release['version']); } } } /** @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); } $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' => [], ], ], ]; } /** * 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; }