Backup of db before drupal security update
[yaffs-website] / web / core / modules / update / src / Form / UpdateManagerUpdate.php
1 <?php
2
3 namespace Drupal\update\Form;
4
5 use Drupal\Core\Extension\ModuleHandlerInterface;
6 use Drupal\Core\Form\FormBase;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\Core\State\StateInterface;
9 use Drupal\Core\Url;
10 use Symfony\Component\DependencyInjection\ContainerInterface;
11
12 /**
13  * Configure update settings for this site.
14  */
15 class UpdateManagerUpdate extends FormBase {
16
17   /**
18    * The module handler.
19    *
20    * @var \Drupal\Core\Extension\ModuleHandlerInterface
21    */
22   protected $moduleHandler;
23
24   /**
25    * The Drupal state storage service.
26    *
27    * @var \Drupal\Core\State\StateInterface
28    */
29   protected $state;
30
31   /**
32    * Constructs a new UpdateManagerUpdate object.
33    *
34    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
35    *   The module handler.
36    * @param \Drupal\Core\State\StateInterface $state
37    *   The state service.
38    */
39   public function __construct(ModuleHandlerInterface $module_handler, StateInterface $state) {
40     $this->moduleHandler = $module_handler;
41     $this->state = $state;
42   }
43
44   /**
45    * {@inheritdoc}
46    */
47   public function getFormId() {
48     return 'update_manager_update_form';
49   }
50
51   /**
52    * {@inheritdoc}
53    */
54   public static function create(ContainerInterface $container) {
55     return new static(
56       $container->get('module_handler'),
57       $container->get('state')
58     );
59   }
60
61   /**
62    * {@inheritdoc}
63    */
64   public function buildForm(array $form, FormStateInterface $form_state) {
65     $this->moduleHandler->loadInclude('update', 'inc', 'update.manager');
66
67     $last_markup = [
68       '#theme' => 'update_last_check',
69       '#last' => $this->state->get('update.last_check') ?: 0,
70     ];
71     $form['last_check'] = [
72       '#markup' => drupal_render($last_markup),
73     ];
74
75     if (!_update_manager_check_backends($form, 'update')) {
76       return $form;
77     }
78
79     $available = update_get_available(TRUE);
80     if (empty($available)) {
81       $form['message'] = [
82         '#markup' => $this->t('There was a problem getting update information. Try again later.'),
83       ];
84       return $form;
85     }
86
87     $form['#attached']['library'][] = 'update/drupal.update.admin';
88
89     // This will be a nested array. The first key is the kind of project, which
90     // can be either 'enabled', 'disabled', 'manual' (projects which require
91     // manual updates, such as core). Then, each subarray is an array of
92     // projects of that type, indexed by project short name, and containing an
93     // array of data for cells in that project's row in the appropriate table.
94     $projects = [];
95
96     // This stores the actual download link we're going to update from for each
97     // project in the form, regardless of if it's enabled or disabled.
98     $form['project_downloads'] = ['#tree' => TRUE];
99     $this->moduleHandler->loadInclude('update', 'inc', 'update.compare');
100     $project_data = update_calculate_project_data($available);
101     foreach ($project_data as $name => $project) {
102       // Filter out projects which are up to date already.
103       if ($project['status'] == UPDATE_CURRENT) {
104         continue;
105       }
106       // The project name to display can vary based on the info we have.
107       if (!empty($project['title'])) {
108         if (!empty($project['link'])) {
109           $project_name = $this->l($project['title'], Url::fromUri($project['link']));
110         }
111         else {
112           $project_name = $project['title'];
113         }
114       }
115       elseif (!empty($project['info']['name'])) {
116         $project_name = $project['info']['name'];
117       }
118       else {
119         $project_name = $name;
120       }
121       if ($project['project_type'] == 'theme' || $project['project_type'] == 'theme-disabled') {
122         $project_name .= ' ' . $this->t('(Theme)');
123       }
124
125       if (empty($project['recommended'])) {
126         // If we don't know what to recommend they upgrade to, we should skip
127         // the project entirely.
128         continue;
129       }
130
131       $recommended_release = $project['releases'][$project['recommended']];
132       $recommended_version = '{{ release_version }} (<a href="{{ release_link }}" title="{{ project_title }}">{{ release_notes }}</a>)';
133       if ($recommended_release['version_major'] != $project['existing_major']) {
134         $recommended_version .= '<div title="{{ major_update_warning_title }}" class="update-major-version-warning">{{ major_update_warning_text }}</div>';
135       }
136
137       $recommended_version = [
138         '#type' => 'inline_template',
139         '#template' => $recommended_version,
140         '#context' => [
141           'release_version' => $recommended_release['version'],
142           'release_link' => $recommended_release['release_link'],
143           'project_title' => $this->t('Release notes for @project_title', ['@project_title' => $project['title']]),
144           'major_update_warning_title' => $this->t('Major upgrade warning'),
145           'major_update_warning_text' => $this->t('This update is a major version update which means that it may not be backwards compatible with your currently running version. It is recommended that you read the release notes and proceed at your own risk.'),
146           'release_notes' => $this->t('Release notes'),
147         ],
148       ];
149
150       // Create an entry for this project.
151       $entry = [
152         'title' => $project_name,
153         'installed_version' => $project['existing_version'],
154         'recommended_version' => ['data' => $recommended_version],
155       ];
156
157       switch ($project['status']) {
158         case UPDATE_NOT_SECURE:
159         case UPDATE_REVOKED:
160           $entry['title'] .= ' ' . $this->t('(Security update)');
161           $entry['#weight'] = -2;
162           $type = 'security';
163           break;
164
165         case UPDATE_NOT_SUPPORTED:
166           $type = 'unsupported';
167           $entry['title'] .= ' ' . $this->t('(Unsupported)');
168           $entry['#weight'] = -1;
169           break;
170
171         case UPDATE_UNKNOWN:
172         case UPDATE_NOT_FETCHED:
173         case UPDATE_NOT_CHECKED:
174         case UPDATE_NOT_CURRENT:
175           $type = 'recommended';
176           break;
177
178         default:
179           // Jump out of the switch and onto the next project in foreach.
180           continue 2;
181       }
182
183       // Use the project title for the tableselect checkboxes.
184       $entry['title'] = ['data' => [
185         '#title' => $entry['title'],
186         '#markup' => $entry['title'],
187       ]];
188       $entry['#attributes'] = ['class' => ['update-' . $type]];
189
190       // Drupal core needs to be upgraded manually.
191       $needs_manual = $project['project_type'] == 'core';
192
193       if ($needs_manual) {
194         // There are no checkboxes in the 'Manual updates' table so it will be
195         // rendered by '#theme' => 'table', not '#theme' => 'tableselect'. Since
196         // the data formats are incompatible, we convert now to the format
197         // expected by '#theme' => 'table'.
198         unset($entry['#weight']);
199         $attributes = $entry['#attributes'];
200         unset($entry['#attributes']);
201         $entry = [
202           'data' => $entry,
203         ] + $attributes;
204       }
205       else {
206         $form['project_downloads'][$name] = [
207           '#type' => 'value',
208           '#value' => $recommended_release['download_link'],
209         ];
210       }
211
212       // Based on what kind of project this is, save the entry into the
213       // appropriate subarray.
214       switch ($project['project_type']) {
215         case 'core':
216           // Core needs manual updates at this time.
217           $projects['manual'][$name] = $entry;
218           break;
219
220         case 'module':
221         case 'theme':
222           $projects['enabled'][$name] = $entry;
223           break;
224
225         case 'module-disabled':
226         case 'theme-disabled':
227           $projects['disabled'][$name] = $entry;
228           break;
229       }
230     }
231
232     if (empty($projects)) {
233       $form['message'] = [
234         '#markup' => $this->t('All of your projects are up to date.'),
235       ];
236       return $form;
237     }
238
239     $headers = [
240       'title' => [
241         'data' => $this->t('Name'),
242         'class' => ['update-project-name'],
243       ],
244       'installed_version' => $this->t('Installed version'),
245       'recommended_version' => $this->t('Recommended version'),
246     ];
247
248     if (!empty($projects['enabled'])) {
249       $form['projects'] = [
250         '#type' => 'tableselect',
251         '#header' => $headers,
252         '#options' => $projects['enabled'],
253       ];
254       if (!empty($projects['disabled'])) {
255         $form['projects']['#prefix'] = '<h2>' . $this->t('Enabled') . '</h2>';
256       }
257     }
258
259     if (!empty($projects['disabled'])) {
260       $form['disabled_projects'] = [
261         '#type' => 'tableselect',
262         '#header' => $headers,
263         '#options' => $projects['disabled'],
264         '#weight' => 1,
265         '#prefix' => '<h2>' . $this->t('Disabled') . '</h2>',
266       ];
267     }
268
269     // If either table has been printed yet, we need a submit button and to
270     // validate the checkboxes.
271     if (!empty($projects['enabled']) || !empty($projects['disabled'])) {
272       $form['actions'] = ['#type' => 'actions'];
273       $form['actions']['submit'] = [
274         '#type' => 'submit',
275         '#value' => $this->t('Download these updates'),
276       ];
277     }
278
279     if (!empty($projects['manual'])) {
280       $prefix = '<h2>' . $this->t('Manual updates required') . '</h2>';
281       $prefix .= '<p>' . $this->t('Updates of Drupal core are not supported at this time.') . '</p>';
282       $form['manual_updates'] = [
283         '#type' => 'table',
284         '#header' => $headers,
285         '#rows' => $projects['manual'],
286         '#prefix' => $prefix,
287         '#weight' => 120,
288       ];
289     }
290
291     return $form;
292   }
293
294   /**
295    * {@inheritdoc}
296    */
297   public function validateForm(array &$form, FormStateInterface $form_state) {
298     if (!$form_state->isValueEmpty('projects')) {
299       $enabled = array_filter($form_state->getValue('projects'));
300     }
301     if (!$form_state->isValueEmpty('disabled_projects')) {
302       $disabled = array_filter($form_state->getValue('disabled_projects'));
303     }
304     if (empty($enabled) && empty($disabled)) {
305       $form_state->setErrorByName('projects', $this->t('You must select at least one project to update.'));
306     }
307   }
308
309   /**
310    * {@inheritdoc}
311    */
312   public function submitForm(array &$form, FormStateInterface $form_state) {
313     $this->moduleHandler->loadInclude('update', 'inc', 'update.manager');
314     $projects = [];
315     foreach (['projects', 'disabled_projects'] as $type) {
316       if (!$form_state->isValueEmpty($type)) {
317         $projects = array_merge($projects, array_keys(array_filter($form_state->getValue($type))));
318       }
319     }
320     $operations = [];
321     foreach ($projects as $project) {
322       $operations[] = [
323         'update_manager_batch_project_get',
324         [
325           $project,
326           $form_state->getValue(['project_downloads', $project]),
327         ],
328       ];
329     }
330     $batch = [
331       'title' => $this->t('Downloading updates'),
332       'init_message' => $this->t('Preparing to download selected updates'),
333       'operations' => $operations,
334       'finished' => 'update_manager_download_batch_finished',
335       'file' => drupal_get_path('module', 'update') . '/update.manager.inc',
336     ];
337     batch_set($batch);
338   }
339
340 }