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