summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/update/update.install
blob: d7a95d60013c3343481dc3d9d0cf43420cc9c2ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<?php

/**
 * @file
 * Install, update, and uninstall functions for the Update Manager module.
 */

use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\update\ProjectSecurityData;
use Drupal\update\ProjectSecurityRequirement;
use Drupal\update\UpdateFetcherInterface;
use Drupal\update\UpdateManagerInterface;

/**
 * Implements hook_requirements().
 *
 * Describes the status of the site regarding available updates. If
 * there is no update data, only one record will be returned, indicating that
 * the status of core can't be determined. If data is available, there will be
 * two records: one for core, and another for all of contrib (assuming there
 * are any contributed modules or themes installed on the site). In addition to
 * the fields expected by hook_requirements ('value', 'severity', and
 * optionally 'description'), this array will contain a 'reason' attribute,
 * which is an integer constant to indicate why the given status is being
 * returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or UPDATE_UNKNOWN). This
 * is used for generating the appropriate email notification messages during
 * update_cron(), and might be useful for other modules that invoke
 * update_requirements() to find out if the site is up to date or not.
 *
 * @see _update_message_text()
 * @see _update_cron_notify()
 * @see \Drupal\update\UpdateManagerInterface
 */
function update_requirements($phase) {
  $requirements = [];
  if ($phase == 'runtime') {
    if ($available = update_get_available(FALSE)) {
      \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.compare');
      $data = update_calculate_project_data($available);
      // First, populate the requirements for core:
      $requirements['update_core'] = _update_requirement_check($data['drupal'], 'core');
      if (!empty($available['drupal']['releases'])) {
        $security_data = ProjectSecurityData::createFromProjectDataAndReleases($data['drupal'], $available['drupal']['releases'])->getCoverageInfo();
        if ($core_coverage_requirement = ProjectSecurityRequirement::createFromProjectDataAndSecurityCoverageInfo($data['drupal'], $security_data)->getRequirement()) {
          $requirements['coverage_core'] = $core_coverage_requirement;
        }
      }

      // We don't want to check drupal a second time.
      unset($data['drupal']);
      if (!empty($data)) {
        // Now, sort our $data array based on each project's status. The
        // status constants are numbered in the right order of precedence, so
        // we just need to make sure the projects are sorted in ascending
        // order of status, and we can look at the first project we find.
        uasort($data, '_update_project_status_sort');
        $first_project = reset($data);
        $requirements['update_contrib'] = _update_requirement_check($first_project, 'contrib');
      }
    }
    else {
      $requirements['update_core']['title'] = t('Drupal core update status');
      $requirements['update_core']['value'] = t('No update data available');
      $requirements['update_core']['severity'] = REQUIREMENT_WARNING;
      $requirements['update_core']['reason'] = UpdateFetcherInterface::UNKNOWN;
      $requirements['update_core']['description'] = _update_no_data();
    }
  }
  return $requirements;
}

/**
 * Implements hook_install().
 */
function update_install() {
  $queue = \Drupal::queue('update_fetch_tasks', TRUE);
  $queue->createQueue();
}

/**
 * Implements hook_uninstall().
 */
function update_uninstall() {
  \Drupal::state()->delete('update.last_check');
  \Drupal::state()->delete('update.last_email_notification');

  $queue = \Drupal::queue('update_fetch_tasks');
  $queue->deleteQueue();
}

/**
 * Fills in the requirements array.
 *
 * This is shared for both core and contrib to generate the right elements in
 * the array for hook_requirements().
 *
 * @param $project
 *   Array of information about the project we're testing as returned by
 *   update_calculate_project_data().
 * @param $type
 *   What kind of project this is ('core' or 'contrib').
 *
 * @return array
 *   An array to be included in the nested $requirements array.
 *
 * @see hook_requirements()
 * @see update_requirements()
 * @see update_calculate_project_data()
 */
function _update_requirement_check($project, $type) {
  $requirement = [];
  if ($type == 'core') {
    $requirement['title'] = t('Drupal core update status');
  }
  else {
    $requirement['title'] = t('Module and theme update status');
  }
  $status = $project['status'];
  if ($status != UpdateManagerInterface::CURRENT) {
    $requirement['reason'] = $status;
    $requirement['severity'] = REQUIREMENT_ERROR;
    // When updates are available, append the available updates link to the
    // message from _update_message_text(), and format the two translated
    // strings together in a single paragraph.
    $requirement['description'][] = ['#markup' => _update_message_text($type, $status)];
    if (!in_array($status, [UpdateFetcherInterface::UNKNOWN, UpdateFetcherInterface::NOT_CHECKED, UpdateFetcherInterface::NOT_FETCHED, UpdateFetcherInterface::FETCH_PENDING])) {
      if (_update_manager_access()) {
        $requirement['description'][] = ['#prefix' => ' ', '#markup' => t('See the <a href=":available_updates">available updates</a> page for more information and to update your software.', [':available_updates' => Url::fromRoute('update.report_update')->toString()])];
      }
      else {
        $requirement['description'][] = ['#prefix' => ' ', '#markup' => t('See the <a href=":available_updates">available updates</a> page for more information.', [':available_updates' => Url::fromRoute('update.status')->toString()])];
      }
    }
  }
  switch ($status) {
    case UpdateManagerInterface::NOT_SECURE:
      $requirement_label = t('Not secure!');
      break;

    case UpdateManagerInterface::REVOKED:
      $requirement_label = t('Revoked!');
      break;

    case UpdateManagerInterface::NOT_SUPPORTED:
      $requirement_label = t('Unsupported release');
      break;

    case UpdateManagerInterface::NOT_CURRENT:
      $requirement_label = t('Out of date');
      $requirement['severity'] = REQUIREMENT_WARNING;
      break;

    case UpdateFetcherInterface::UNKNOWN:
    case UpdateFetcherInterface::NOT_CHECKED:
    case UpdateFetcherInterface::NOT_FETCHED:
    case UpdateFetcherInterface::FETCH_PENDING:
      $requirement_label = $project['reason'] ?? t('Can not determine status');
      $requirement['severity'] = REQUIREMENT_WARNING;
      break;

    default:
      $requirement_label = t('Up to date');
  }
  if ($status != UpdateManagerInterface::CURRENT && $type == 'core' && isset($project['recommended'])) {
    $requirement_label .= ' ' . t('(version @version available)', ['@version' => $project['recommended']]);
  }
  $requirement['value'] = Link::fromTextAndUrl($requirement_label, Url::fromRoute(_update_manager_access() ? 'update.report_update' : 'update.status'))->toString();
  return $requirement;
}

/**
 * Implements hook_update_last_removed().
 */
function update_update_last_removed() {
  return 8001;
}