update.module

Same filename and directory in other branches
  1. 11.x core/modules/update/update.module
  2. 10 core/modules/update/update.module
  3. 9 core/modules/update/update.module
  4. 8.9.x core/modules/update/update.module
  5. 7.x modules/update/update.module

File

core/modules/update/update.module

View source
<?php


/**
 * @file
 */

use Drupal\Core\Url;
use Drupal\update\UpdateFetcherInterface;
use Drupal\update\UpdateManagerInterface;

/**
 * Returns a warning message when there is no data about available updates.
 */
function _update_no_data() {
  $destination = \Drupal::destination()->getAsArray();
  return t('No update information available. <a href=":run_cron">Run cron</a> or <a href=":check_manually">check manually</a>.', [
    ':run_cron' => Url::fromRoute('system.run_cron', [], [
      'query' => $destination,
    ])->toString(),
    ':check_manually' => Url::fromRoute('update.manual_status', [], [
      'query' => $destination,
    ])->toString(),
  ]);
}

/**
 * Tries to get update information and refreshes it when necessary.
 *
 * In addition to checking the lifetime, this function also ensures that there
 * are no .info.yml files for installed modules or themes that have a newer
 * modification timestamp than the last time we checked for available update
 * data. If any .info.yml file was modified, it almost certainly means a new
 * version of something was installed. Without fresh available update data, the
 * logic in update_calculate_project_data() will be wrong and produce confusing,
 * bogus results.
 *
 * @param bool $refresh
 *   (optional) Boolean to indicate if this method should refresh automatically
 *   if there's no data. Defaults to FALSE.
 *
 * @return array
 *   Array of data about available releases, keyed by project shortname.
 *
 * @see update_refresh()
 * @see \Drupal\update\UpdateManager::getProjects()
 */
function update_get_available($refresh = FALSE) {
  \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.compare');
  $needs_refresh = FALSE;
  // Grab whatever data we currently have.
  $available = \Drupal::keyValueExpirable('update_available_releases')->getAll();
  $projects = \Drupal::service('update.manager')->getProjects();
  foreach ($projects as $key => $project) {
    // If there's no data at all, we clearly need to fetch some.
    if (empty($available[$key])) {
      // update_create_fetch_task($project);
      \Drupal::service('update.processor')->createFetchTask($project);
      $needs_refresh = TRUE;
      continue;
    }
    // See if the .info.yml file is newer than the last time we checked for
    // data, and if so, mark this project's data as needing to be re-fetched.
    // Any time an admin upgrades their local installation, the .info.yml file
    // will be changed, so this is the only way we can be sure we're not showing
    // bogus information right after they upgrade.
    if ($project['info']['_info_file_ctime'] > $available[$key]['last_fetch']) {
      $available[$key]['fetch_status'] = UpdateFetcherInterface::FETCH_PENDING;
    }
    // If we have project data but no release data, we need to fetch. This
    // can be triggered when we fail to contact a release history server.
    if (empty($available[$key]['releases']) && !$available[$key]['last_fetch']) {
      $available[$key]['fetch_status'] = UpdateFetcherInterface::FETCH_PENDING;
    }
    // If we think this project needs to fetch, actually create the task now
    // and remember that we think we're missing some data.
    if (!empty($available[$key]['fetch_status']) && $available[$key]['fetch_status'] == UpdateFetcherInterface::FETCH_PENDING) {
      \Drupal::service('update.processor')->createFetchTask($project);
      $needs_refresh = TRUE;
    }
  }
  if ($needs_refresh && $refresh) {
    // Attempt to drain the queue of fetch tasks.
    update_fetch_data();
    // After processing the queue, we've (hopefully) got better data, so pull
    // the latest data again and use that directly.
    $available = \Drupal::keyValueExpirable('update_available_releases')->getAll();
  }
  return $available;
}

/**
 * Adds a task to the queue for fetching release history data for a project.
 *
 * We only create a new fetch task if there's no task already in the queue for
 * this particular project (based on 'update_fetch_task' key-value collection).
 *
 * @param array $project
 *   Associative array of information about a project as created by
 *   \Drupal\update\UpdateManager::getProjects(), including keys such as 'name'
 *   (short name), and the 'info' array with data from a .info.yml file for the
 *   project.
 *
 * @see \Drupal\update\UpdateFetcher::createFetchTask()
 */
function update_create_fetch_task($project) : void {
  \Drupal::service('update.processor')->createFetchTask($project);
}

/**
 * Refreshes the release data after loading the necessary include file.
 */
function update_refresh() : void {
  \Drupal::service('update.manager')->refreshUpdateData();
}

/**
 * Attempts to fetch update data after loading the necessary include file.
 *
 * @see \Drupal\update\UpdateProcessor::fetchData()
 */
function update_fetch_data() : void {
  \Drupal::service('update.processor')->fetchData();
}

/**
 * Batch callback: Performs actions when all fetch tasks have been completed.
 *
 * @param bool $success
 *   TRUE if the batch operation was successful; FALSE if there were errors.
 * @param array $results
 *   An associative array of results from the batch operation, including the key
 *   'updated' which holds the total number of projects we fetched available
 *   update data for.
 */
function update_fetch_data_finished($success, $results) : void {
  if ($success) {
    if (!empty($results)) {
      if (!empty($results['updated'])) {
        \Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($results['updated'], 'Checked available update data for one project.', 'Checked available update data for @count projects.'));
      }
      if (!empty($results['failures'])) {
        \Drupal::messenger()->addError(\Drupal::translation()->formatPlural($results['failures'], 'Failed to get available update data for one project.', 'Failed to get available update data for @count projects.'));
      }
    }
  }
  else {
    \Drupal::messenger()->addError(t('An error occurred trying to get available update data.'), 'error');
  }
}

/**
 * Returns the appropriate message text when site is out of date or not secure.
 *
 * These error messages are shared by both update_requirements() for the
 * site-wide status report at admin/reports/status and in the body of the
 * notification email messages generated by update_cron().
 *
 * @param string $msg_type
 *   String to indicate what kind of message to generate. Can be either 'core'
 *   or 'contrib'.
 * @param int $msg_reason
 *   Integer constant specifying why message is generated.
 * @param string $langcode
 *   (optional) A language code to use. Defaults to NULL.
 *
 * @return \Drupal\Core\StringTranslation\TranslatableMarkup
 *   The properly translated error message for the given key.
 */
function _update_message_text($msg_type, $msg_reason, $langcode = NULL) {
  $text = '';
  switch ($msg_reason) {
    case UpdateManagerInterface::NOT_SECURE:
      if ($msg_type == 'core') {
        $text = t('There is a security update available for your version of Drupal. To ensure the security of your server, you should update immediately!', [], [
          'langcode' => $langcode,
        ]);
      }
      else {
        $text = t('There are security updates available for one or more of your modules or themes. To ensure the security of your server, you should update immediately!', [], [
          'langcode' => $langcode,
        ]);
      }
      break;

    case UpdateManagerInterface::REVOKED:
      if ($msg_type == 'core') {
        $text = t('Your version of Drupal has been revoked and is no longer available for download. Upgrading is strongly recommended!', [], [
          'langcode' => $langcode,
        ]);
      }
      else {
        $text = t('The installed version of at least one of your modules or themes has been revoked and is no longer available for download. Upgrading or uninstalling is strongly recommended!', [], [
          'langcode' => $langcode,
        ]);
      }
      break;

    case UpdateManagerInterface::NOT_SUPPORTED:
      if ($msg_type == 'core') {
        $text = t('Your version of Drupal is no longer supported. Upgrading is strongly recommended!', [], [
          'langcode' => $langcode,
        ]);
      }
      else {
        $text = t('The installed version of at least one of your modules or themes is no longer supported. Upgrading or uninstalling is strongly recommended. See the project homepage for more details.', [], [
          'langcode' => $langcode,
        ]);
      }
      break;

    case UpdateManagerInterface::NOT_CURRENT:
      if ($msg_type == 'core') {
        $text = t('There are updates available for your version of Drupal. To ensure the proper functioning of your site, you should update as soon as possible.', [], [
          'langcode' => $langcode,
        ]);
      }
      else {
        $text = t('There are updates available for one or more of your modules or themes. To ensure the proper functioning of your site, you should update as soon as possible.', [], [
          'langcode' => $langcode,
        ]);
      }
      break;

    case UpdateFetcherInterface::UNKNOWN:
    case UpdateFetcherInterface::NOT_CHECKED:
    case UpdateFetcherInterface::NOT_FETCHED:
    case UpdateFetcherInterface::FETCH_PENDING:
      if ($msg_type == 'core') {
        $text = t('There was a problem checking <a href=":update-report">available updates</a> for Drupal.', [
          ':update-report' => Url::fromRoute('update.status')->toString(),
        ], [
          'langcode' => $langcode,
        ]);
      }
      else {
        $text = t('There was a problem checking <a href=":update-report">available updates</a> for your modules or themes.', [
          ':update-report' => Url::fromRoute('update.status')->toString(),
        ], [
          'langcode' => $langcode,
        ]);
      }
      break;

  }
  return $text;
}

/**
 * Orders projects based on their status.
 *
 * Callback for uasort() within update_requirements().
 */
function _update_project_status_sort($a, $b) {
  // The status constants are numerically in the right order, so we can
  // usually subtract the two to compare in the order we want. However,
  // negative status values should be treated as if they are huge, since we
  // always want them at the bottom of the list.
  $a_status = $a['status'] > 0 ? $a['status'] : -10 * $a['status'];
  $b_status = $b['status'] > 0 ? $b['status'] : -10 * $b['status'];
  return $a_status - $b_status;
}

/**
 * Invalidates stored data relating to update status.
 */
function update_storage_clear() : void {
  \Drupal::keyValueExpirable('update')->deleteAll();
  \Drupal::keyValueExpirable('update_available_release')->deleteAll();
}

Functions

Title Deprecated Summary
update_create_fetch_task Adds a task to the queue for fetching release history data for a project.
update_fetch_data Attempts to fetch update data after loading the necessary include file.
update_fetch_data_finished Batch callback: Performs actions when all fetch tasks have been completed.
update_get_available Tries to get update information and refreshes it when necessary.
update_refresh Refreshes the release data after loading the necessary include file.
update_storage_clear Invalidates stored data relating to update status.
_update_message_text Returns the appropriate message text when site is out of date or not secure.
_update_no_data Returns a warning message when there is no data about available updates.
_update_project_status_sort Orders projects based on their status.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.