annotate core/modules/update/src/UpdateManager.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
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 }