123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- <?php
- namespace Drupal\update;
- use Drupal\Core\Config\ConfigFactoryInterface;
- use Drupal\Core\DependencyInjection\DependencySerializationTrait;
- use Drupal\Core\Extension\ModuleHandlerInterface;
- use Drupal\Core\Extension\ThemeHandlerInterface;
- use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
- use Drupal\Core\StringTranslation\TranslationInterface;
- use Drupal\Core\StringTranslation\StringTranslationTrait;
- use Drupal\Core\Utility\ProjectInfo;
- /**
- * Default implementation of UpdateManagerInterface.
- */
- class UpdateManager implements UpdateManagerInterface {
- use DependencySerializationTrait;
- use StringTranslationTrait;
- /**
- * The update settings
- *
- * @var \Drupal\Core\Config\Config
- */
- protected $updateSettings;
- /**
- * Module Handler Service.
- *
- * @var \Drupal\Core\Extension\ModuleHandlerInterface
- */
- protected $moduleHandler;
- /**
- * Update Processor Service.
- *
- * @var \Drupal\update\UpdateProcessorInterface
- */
- protected $updateProcessor;
- /**
- * An array of installed and enabled projects.
- *
- * @var array
- */
- protected $projects;
- /**
- * The key/value store.
- *
- * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
- */
- protected $keyValueStore;
- /**
- * Update available releases key/value store.
- *
- * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
- */
- protected $availableReleasesTempStore;
- /**
- * The theme handler.
- *
- * @var \Drupal\Core\Extension\ThemeHandlerInterface
- */
- protected $themeHandler;
- /**
- * Constructs a UpdateManager.
- *
- * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
- * The config factory.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * The Module Handler service
- * @param \Drupal\update\UpdateProcessorInterface $update_processor
- * The Update Processor service.
- * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
- * The translation service.
- * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_expirable_factory
- * The expirable key/value factory.
- * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
- * The theme handler.
- */
- public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, UpdateProcessorInterface $update_processor, TranslationInterface $translation, KeyValueFactoryInterface $key_value_expirable_factory, ThemeHandlerInterface $theme_handler) {
- $this->updateSettings = $config_factory->get('update.settings');
- $this->moduleHandler = $module_handler;
- $this->updateProcessor = $update_processor;
- $this->stringTranslation = $translation;
- $this->keyValueStore = $key_value_expirable_factory->get('update');
- $this->themeHandler = $theme_handler;
- $this->availableReleasesTempStore = $key_value_expirable_factory->get('update_available_releases');
- $this->projects = [];
- }
- /**
- * {@inheritdoc}
- */
- public function refreshUpdateData() {
- // Since we're fetching new available update data, we want to clear
- // of both the projects we care about, and the current update status of the
- // site. We do *not* want to clear the cache of available releases just yet,
- // since that data (even if it's stale) can be useful during
- // \Drupal\Update\UpdateManager::getProjects(); for example, to modules
- // that implement hook_system_info_alter() such as cvs_deploy.
- $this->keyValueStore->delete('update_project_projects');
- $this->keyValueStore->delete('update_project_data');
- $projects = $this->getProjects();
- // Now that we have the list of projects, we should also clear the available
- // release data, since even if we fail to fetch new data, we need to clear
- // out the stale data at this point.
- $this->availableReleasesTempStore->deleteAll();
- foreach ($projects as $project) {
- $this->updateProcessor->createFetchTask($project);
- }
- }
- /**
- * {@inheritdoc}
- */
- public function getProjects() {
- if (empty($this->projects)) {
- // Retrieve the projects from storage, if present.
- $this->projects = $this->projectStorage('update_project_projects');
- if (empty($this->projects)) {
- // Still empty, so we have to rebuild.
- $module_data = system_rebuild_module_data();
- $theme_data = $this->themeHandler->rebuildThemeData();
- $project_info = new ProjectInfo();
- $project_info->processInfoList($this->projects, $module_data, 'module', TRUE);
- $project_info->processInfoList($this->projects, $theme_data, 'theme', TRUE);
- if ($this->updateSettings->get('check.disabled_extensions')) {
- $project_info->processInfoList($this->projects, $module_data, 'module', FALSE);
- $project_info->processInfoList($this->projects, $theme_data, 'theme', FALSE);
- }
- // Allow other modules to alter projects before fetching and comparing.
- $this->moduleHandler->alter('update_projects', $this->projects);
- // Store the site's project data for at most 1 hour.
- $this->keyValueStore->setWithExpire('update_project_projects', $this->projects, 3600);
- }
- }
- return $this->projects;
- }
- /**
- * {@inheritdoc}
- */
- public function projectStorage($key) {
- $projects = [];
- // On certain paths, we should clear the data and recompute the projects for
- // update status of the site to avoid presenting stale information.
- $route_names = [
- 'update.theme_update',
- 'system.modules_list',
- 'system.theme_install',
- 'update.module_update',
- 'update.module_install',
- 'update.status',
- 'update.report_update',
- 'update.report_install',
- 'update.settings',
- 'system.status',
- 'update.manual_status',
- 'update.confirmation_page',
- 'system.themes_page',
- ];
- if (in_array(\Drupal::routeMatch()->getRouteName(), $route_names)) {
- $this->keyValueStore->delete($key);
- }
- else {
- $projects = $this->keyValueStore->get($key, []);
- }
- return $projects;
- }
- /**
- * {@inheritdoc}
- */
- public function fetchDataBatch(&$context) {
- if (empty($context['sandbox']['max'])) {
- $context['finished'] = 0;
- $context['sandbox']['max'] = $this->updateProcessor->numberOfQueueItems();
- $context['sandbox']['progress'] = 0;
- $context['message'] = $this->t('Checking available update data ...');
- $context['results']['updated'] = 0;
- $context['results']['failures'] = 0;
- $context['results']['processed'] = 0;
- }
- // Grab another item from the fetch queue.
- for ($i = 0; $i < 5; $i++) {
- if ($item = $this->updateProcessor->claimQueueItem()) {
- if ($this->updateProcessor->processFetchTask($item->data)) {
- $context['results']['updated']++;
- $context['message'] = $this->t('Checked available update data for %title.', ['%title' => $item->data['info']['name']]);
- }
- else {
- $context['message'] = $this->t('Failed to check available update data for %title.', ['%title' => $item->data['info']['name']]);
- $context['results']['failures']++;
- }
- $context['sandbox']['progress']++;
- $context['results']['processed']++;
- $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
- $this->updateProcessor->deleteQueueItem($item);
- }
- else {
- // If the queue is currently empty, we're done. It's possible that
- // another thread might have added new fetch tasks while we were
- // processing this batch. In that case, the usual 'finished' math could
- // get confused, since we'd end up processing more tasks that we thought
- // we had when we started and initialized 'max' with numberOfItems(). By
- // forcing 'finished' to be exactly 1 here, we ensure that batch
- // processing is terminated.
- $context['finished'] = 1;
- return;
- }
- }
- }
- }
|