Backup of db before drupal security update
[yaffs-website] / web / core / modules / update / src / UpdateManager.php
1 <?php
2
3 namespace Drupal\update;
4
5 use Drupal\Core\Config\ConfigFactoryInterface;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Extension\ThemeHandlerInterface;
9 use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
10 use Drupal\Core\StringTranslation\TranslationInterface;
11 use Drupal\Core\StringTranslation\StringTranslationTrait;
12 use Drupal\Core\Utility\ProjectInfo;
13
14 /**
15  * Default implementation of UpdateManagerInterface.
16  */
17 class UpdateManager implements UpdateManagerInterface {
18   use DependencySerializationTrait;
19   use StringTranslationTrait;
20
21   /**
22    * The update settings
23    *
24    * @var \Drupal\Core\Config\Config
25    */
26   protected $updateSettings;
27
28   /**
29    * Module Handler Service.
30    *
31    * @var \Drupal\Core\Extension\ModuleHandlerInterface
32    */
33   protected $moduleHandler;
34
35   /**
36    * Update Processor Service.
37    *
38    * @var \Drupal\update\UpdateProcessorInterface
39    */
40   protected $updateProcessor;
41
42   /**
43    * An array of installed and enabled projects.
44    *
45    * @var array
46    */
47   protected $projects;
48
49   /**
50    * The key/value store.
51    *
52    * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
53    */
54   protected $keyValueStore;
55
56   /**
57    * Update available releases key/value store.
58    *
59    * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
60    */
61   protected $availableReleasesTempStore;
62
63   /**
64    * The theme handler.
65    *
66    * @var \Drupal\Core\Extension\ThemeHandlerInterface
67    */
68   protected $themeHandler;
69
70   /**
71    * Constructs a UpdateManager.
72    *
73    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
74    *   The config factory.
75    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
76    *   The Module Handler service
77    * @param \Drupal\update\UpdateProcessorInterface $update_processor
78    *   The Update Processor service.
79    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
80    *   The translation service.
81    * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_expirable_factory
82    *   The expirable key/value factory.
83    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
84    *   The theme handler.
85    */
86   public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, UpdateProcessorInterface $update_processor, TranslationInterface $translation, KeyValueFactoryInterface $key_value_expirable_factory, ThemeHandlerInterface $theme_handler) {
87     $this->updateSettings = $config_factory->get('update.settings');
88     $this->moduleHandler = $module_handler;
89     $this->updateProcessor = $update_processor;
90     $this->stringTranslation = $translation;
91     $this->keyValueStore = $key_value_expirable_factory->get('update');
92     $this->themeHandler = $theme_handler;
93     $this->availableReleasesTempStore = $key_value_expirable_factory->get('update_available_releases');
94     $this->projects = [];
95   }
96
97   /**
98    * {@inheritdoc}
99    */
100   public function refreshUpdateData() {
101
102     // Since we're fetching new available update data, we want to clear
103     // of both the projects we care about, and the current update status of the
104     // site. We do *not* want to clear the cache of available releases just yet,
105     // since that data (even if it's stale) can be useful during
106     // \Drupal\Update\UpdateManager::getProjects(); for example, to modules
107     // that implement hook_system_info_alter() such as cvs_deploy.
108     $this->keyValueStore->delete('update_project_projects');
109     $this->keyValueStore->delete('update_project_data');
110
111     $projects = $this->getProjects();
112
113     // Now that we have the list of projects, we should also clear the available
114     // release data, since even if we fail to fetch new data, we need to clear
115     // out the stale data at this point.
116     $this->availableReleasesTempStore->deleteAll();
117
118     foreach ($projects as $project) {
119       $this->updateProcessor->createFetchTask($project);
120     }
121   }
122
123   /**
124    * {@inheritdoc}
125    */
126   public function getProjects() {
127     if (empty($this->projects)) {
128       // Retrieve the projects from storage, if present.
129       $this->projects = $this->projectStorage('update_project_projects');
130       if (empty($this->projects)) {
131         // Still empty, so we have to rebuild.
132         $module_data = system_rebuild_module_data();
133         $theme_data = $this->themeHandler->rebuildThemeData();
134         $project_info = new ProjectInfo();
135         $project_info->processInfoList($this->projects, $module_data, 'module', TRUE);
136         $project_info->processInfoList($this->projects, $theme_data, 'theme', TRUE);
137         if ($this->updateSettings->get('check.disabled_extensions')) {
138           $project_info->processInfoList($this->projects, $module_data, 'module', FALSE);
139           $project_info->processInfoList($this->projects, $theme_data, 'theme', FALSE);
140         }
141         // Allow other modules to alter projects before fetching and comparing.
142         $this->moduleHandler->alter('update_projects', $this->projects);
143         // Store the site's project data for at most 1 hour.
144         $this->keyValueStore->setWithExpire('update_project_projects', $this->projects, 3600);
145       }
146     }
147     return $this->projects;
148   }
149
150   /**
151    * {@inheritdoc}
152    */
153   public function projectStorage($key) {
154     $projects = [];
155
156     // On certain paths, we should clear the data and recompute the projects for
157     // update status of the site to avoid presenting stale information.
158     $route_names = [
159       'update.theme_update',
160       'system.modules_list',
161       'system.theme_install',
162       'update.module_update',
163       'update.module_install',
164       'update.status',
165       'update.report_update',
166       'update.report_install',
167       'update.settings',
168       'system.status',
169       'update.manual_status',
170       'update.confirmation_page',
171       'system.themes_page',
172     ];
173     if (in_array(\Drupal::routeMatch()->getRouteName(), $route_names)) {
174       $this->keyValueStore->delete($key);
175     }
176     else {
177       $projects = $this->keyValueStore->get($key, []);
178     }
179     return $projects;
180   }
181
182   /**
183    * {@inheritdoc}
184    */
185   public function fetchDataBatch(&$context) {
186     if (empty($context['sandbox']['max'])) {
187       $context['finished'] = 0;
188       $context['sandbox']['max'] = $this->updateProcessor->numberOfQueueItems();
189       $context['sandbox']['progress'] = 0;
190       $context['message'] = $this->t('Checking available update data ...');
191       $context['results']['updated'] = 0;
192       $context['results']['failures'] = 0;
193       $context['results']['processed'] = 0;
194     }
195
196     // Grab another item from the fetch queue.
197     for ($i = 0; $i < 5; $i++) {
198       if ($item = $this->updateProcessor->claimQueueItem()) {
199         if ($this->updateProcessor->processFetchTask($item->data)) {
200           $context['results']['updated']++;
201           $context['message'] = $this->t('Checked available update data for %title.', ['%title' => $item->data['info']['name']]);
202         }
203         else {
204           $context['message'] = $this->t('Failed to check available update data for %title.', ['%title' => $item->data['info']['name']]);
205           $context['results']['failures']++;
206         }
207         $context['sandbox']['progress']++;
208         $context['results']['processed']++;
209         $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
210         $this->updateProcessor->deleteQueueItem($item);
211       }
212       else {
213         // If the queue is currently empty, we're done. It's possible that
214         // another thread might have added new fetch tasks while we were
215         // processing this batch. In that case, the usual 'finished' math could
216         // get confused, since we'd end up processing more tasks that we thought
217         // we had when we started and initialized 'max' with numberOfItems(). By
218         // forcing 'finished' to be exactly 1 here, we ensure that batch
219         // processing is terminated.
220         $context['finished'] = 1;
221         return;
222       }
223     }
224   }
225
226 }