From fbb6b583252b8f6ef4c903c7a03603a5f465be5e Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 13:30:55 +0200 Subject: [PATCH 1/6] convert update template preprocess --- core/modules/update/src/Hook/UpdateHooks.php | 44 +- .../update/src/Hook/UpdateThemeHooks.php | 453 ++++++++++++++++++ core/modules/update/update.module | 88 +++- 3 files changed, 539 insertions(+), 46 deletions(-) create mode 100644 core/modules/update/src/Hook/UpdateThemeHooks.php diff --git a/core/modules/update/src/Hook/UpdateHooks.php b/core/modules/update/src/Hook/UpdateHooks.php index 6577f7f1fc28..61d72854d8b0 100644 --- a/core/modules/update/src/Hook/UpdateHooks.php +++ b/core/modules/update/src/Hook/UpdateHooks.php @@ -3,6 +3,7 @@ namespace Drupal\update\Hook; use Drupal\Core\Extension\Requirement\RequirementSeverity; +use Drupal\Core\Link; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\update\UpdateManagerInterface; use Drupal\Core\Url; @@ -106,49 +107,6 @@ public function pageTop(): void { } } - /** - * Implements hook_theme(). - */ - #[Hook('theme')] - public function theme() : array { - return [ - 'update_last_check' => [ - 'variables' => [ - 'last' => 0, - ], - ], - 'update_report' => [ - 'variables' => [ - 'data' => NULL, - ], - 'file' => 'update.report.inc', - ], - 'update_project_status' => [ - 'variables' => [ - 'project' => [], - ], - 'file' => 'update.report.inc', - ], - // We are using template instead of '#type' => 'table' here to keep markup - // out of preprocess and allow for easier changes to markup. - 'update_version' => [ - 'variables' => [ - 'version' => NULL, - 'title' => NULL, - 'attributes' => [], - ], - 'file' => 'update.report.inc', - ], - 'update_fetch_error_message' => [ - 'file' => 'update.report.inc', - 'render element' => 'element', - 'variables' => [ - 'error_message' => [], - ], - ], - ]; - } - /** * Implements hook_cron(). */ diff --git a/core/modules/update/src/Hook/UpdateThemeHooks.php b/core/modules/update/src/Hook/UpdateThemeHooks.php new file mode 100644 index 000000000000..a9f3c845ed55 --- /dev/null +++ b/core/modules/update/src/Hook/UpdateThemeHooks.php @@ -0,0 +1,453 @@ +<?php + +namespace Drupal\update\Hook; + +use Drupal\Core\Datetime\DateFormatterInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Link; +use Drupal\Core\Routing\RedirectDestinationInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\State\StateInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Template\Attribute; +use Drupal\Core\Url; +use Drupal\update\ProjectRelease; +use Drupal\update\UpdateFetcherInterface; +use Drupal\update\UpdateManagerInterface; + +class UpdateThemeHooks { + + use StringTranslationTrait; + + public function __construct( + protected StateInterface $state, + protected DateFormatterInterface $dateFormatter, + protected ModuleHandlerInterface $moduleHandler, + protected AccountInterface $currentUser, + protected RedirectDestinationInterface $redirectDestination, + ) { + + } + + /** + * Implements hook_theme(). + */ + #[Hook('theme')] + public function theme() : array { + return [ + 'update_last_check' => [ + 'initial preprocess' => static::class . ':preprocessUpdateLastCheck', + 'variables' => [ + 'last' => 0, + ], + ], + 'update_report' => [ + 'initial preprocess' => static::class . ':preprocessUpdateReport', + 'variables' => [ + 'data' => NULL, + ], + ], + 'update_project_status' => [ + 'initial preprocess' => static::class . ':preprocessUpdateProjectStatus', + 'variables' => [ + 'project' => [], + ], + ], + // We are using template instead of '#type' => 'table' here to keep markup + // out of preprocess and allow for easier changes to markup. + 'update_version' => [ + 'initial preprocess' => static::class . ':preprocessUpdateVersion', + 'variables' => [ + 'version' => NULL, + 'title' => NULL, + 'attributes' => [], + ], + ], + 'update_fetch_error_message' => [ + 'initial preprocess' => static::class . ':preprocessUpdateFetchErrorMessage', + 'render element' => 'element', + 'variables' => [ + 'error_message' => [], + ], + ], + ]; + } + + /** + * Prepares variables for last time update data was checked templates. + * + * Default template: update-last-check.html.twig. + * + * In addition to properly formatting the given timestamp, this function also + * provides a "Check manually" link that refreshes the available update and + * redirects back to the same page. + * + * @param array $variables + * An associative array containing: + * - last: The timestamp when the site last checked for available updates. + * + * @see theme_update_report() + */ + public function preprocessUpdateLastCheck(array &$variables): void { + $variables['time'] = $this->dateFormatter->formatTimeDiffSince($variables['last']); + $variables['link'] = Link::fromTextAndUrl($this->t('Check manually'), Url::fromRoute('update.manual_status', [], ['query' => $this->redirectDestination->getAsArray()]))->toString(); + } + + /** + * Prepares variables for project status report templates. + * + * Default template: update-report.html.twig. + * + * @param array $variables + * An associative array containing: + * - data: An array of data about each project's status. + */ + public function preprocessUpdateReport(array &$variables): void { + $data = isset($variables['data']) && is_array($variables['data']) ? $variables['data'] : []; + + $last = $this->state->get('update.last_check', 0); + + $variables['last_checked'] = [ + '#theme' => 'update_last_check', + '#last' => $last, + // Attach the library to a variable that gets printed always. + '#attached' => [ + 'library' => [ + 'update/drupal.update.admin', + ], + ], + ]; + + // For no project update data, populate no data message. + if (empty($data)) { + $variables['no_updates_message'] = _update_no_data(); + } + + $rows = []; + + foreach ($data as $project) { + $project_status = [ + '#theme' => 'update_project_status', + '#project' => $project, + ]; + + // Build project rows. + if (!isset($rows[$project['project_type']])) { + $rows[$project['project_type']] = [ + '#type' => 'table', + '#attributes' => ['class' => ['update']], + ]; + } + $row_key = !empty($project['title']) ? mb_strtolower($project['title']) : mb_strtolower($project['name']); + + // Add the project status row and details. + $rows[$project['project_type']][$row_key]['status'] = $project_status; + + // Add project status class attribute to the table row. + switch ($project['status']) { + case UpdateManagerInterface::CURRENT: + $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-success']]; + break; + + case UpdateFetcherInterface::UNKNOWN: + case UpdateFetcherInterface::FETCH_PENDING: + case UpdateFetcherInterface::NOT_FETCHED: + case UpdateManagerInterface::NOT_SECURE: + case UpdateManagerInterface::REVOKED: + case UpdateManagerInterface::NOT_SUPPORTED: + $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-error']]; + break; + + case UpdateFetcherInterface::NOT_CHECKED: + case UpdateManagerInterface::NOT_CURRENT: + default: + $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-warning']]; + break; + } + } + + $project_types = [ + 'core' => $this->t('Drupal core'), + 'module' => $this->t('Modules'), + 'theme' => $this->t('Themes'), + 'module-uninstalled' => $this->t('Uninstalled modules'), + 'theme-uninstalled' => $this->t('Uninstalled themes'), + ]; + + $variables['project_types'] = []; + foreach ($project_types as $type_name => $type_label) { + if (!empty($rows[$type_name])) { + ksort($rows[$type_name]); + $variables['project_types'][] = [ + 'label' => $type_label, + 'table' => $rows[$type_name], + ]; + } + } + } + + /** + * Prepares variables for update version templates. + * + * Default template: update-version.html.twig. + * + * @param array $variables + * An associative array containing: + * - version: An array of information about the release version. + */ + public function preprocessUpdateVersion(array &$variables): void { + $release = ProjectRelease::createFromArray($variables['version']); + if (!$release->getCoreCompatibilityMessage()) { + return; + } + $core_compatible = $release->isCoreCompatible(); + $variables['core_compatibility_details'] = [ + '#type' => 'details', + '#title' => $core_compatible ? $this->t('Compatible') : $this->t('Not compatible'), + '#open' => !$core_compatible, + 'message' => [ + '#markup' => $release->getCoreCompatibilityMessage(), + ], + '#attributes' => [ + 'class' => [ + $core_compatible ? 'compatible' : 'not-compatible', + ], + ], + ]; + } + + /** + * Prepares variables for update project status templates. + * + * Default template: update-project-status.html.twig. + * + * @param array $variables + * An associative array containing: + * - project: An array of information about the project. + */ + public function preprocessUpdateProjectStatus(array &$variables): void { + // Storing by reference because we are sorting the project values. + $project = &$variables['project']; + + // Set the project title and URL. + $variables['title'] = (isset($project['title'])) ? $project['title'] : $project['name']; + $variables['url'] = (isset($project['link'])) ? Url::fromUri($project['link'])->toString() : NULL; + + $variables['install_type'] = $project['install_type']; + if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) { + $variables['datestamp'] = $this->dateFormatter->format($project['datestamp'], 'custom', 'Y-M-d'); + } + + $variables['existing_version'] = $project['existing_version']; + + $versions_inner = []; + $security_class = []; + $version_class = []; + if (isset($project['recommended'])) { + if ($project['status'] != UpdateManagerInterface::CURRENT || $project['existing_version'] !== $project['recommended']) { + + // First, figure out what to recommend. + // If there's only 1 security update and it has the same version we're + // recommending, give it the same CSS class as if it was recommended, + // but don't print out a separate "Recommended" line for this project. + if (!empty($project['security updates']) + && count($project['security updates']) == 1 + && $project['security updates'][0]['version'] === $project['recommended'] + ) { + $security_class[] = 'project-update__version--recommended'; + $security_class[] = 'project-update__version---strong'; + } + else { + $version_class[] = 'project-update__version--recommended'; + // Apply an extra class if we're displaying both a recommended + // version and anything else for an extra visual hint. + if ($project['recommended'] !== $project['latest_version'] + || !empty($project['also']) + || ($project['install_type'] == 'dev' + && isset($project['dev_version']) + && $project['latest_version'] !== $project['dev_version'] + && $project['recommended'] !== $project['dev_version']) + || (isset($project['security updates'][0]) + && $project['recommended'] !== $project['security updates'][0]) + ) { + $version_class[] = 'project-update__version--recommended-strong'; + } + $versions_inner[] = [ + '#theme' => 'update_version', + '#version' => $project['releases'][$project['recommended']], + '#title' => $this->t('Recommended version:'), + '#attributes' => ['class' => $version_class], + ]; + } + + // Now, print any security updates. + if (!empty($project['security updates'])) { + $security_class[] = 'version-security'; + foreach ($project['security updates'] as $security_update) { + $versions_inner[] = [ + '#theme' => 'update_version', + '#version' => $security_update, + '#title' => $this->t('Security update:'), + '#attributes' => ['class' => $security_class], + ]; + } + } + } + + if ($project['recommended'] !== $project['latest_version']) { + $versions_inner[] = [ + '#theme' => 'update_version', + '#version' => $project['releases'][$project['latest_version']], + '#title' => $this->t('Latest version:'), + '#attributes' => ['class' => ['version-latest']], + ]; + } + if ($project['install_type'] == 'dev' + && $project['status'] != UpdateManagerInterface::CURRENT + && isset($project['dev_version']) + && $project['recommended'] !== $project['dev_version']) { + $versions_inner[] = [ + '#theme' => 'update_version', + '#version' => $project['releases'][$project['dev_version']], + '#title' => $this->t('Development version:'), + '#attributes' => ['class' => ['version-latest']], + ]; + } + } + + if (isset($project['also'])) { + foreach ($project['also'] as $also) { + $versions_inner[] = [ + '#theme' => 'update_version', + '#version' => $project['releases'][$also], + '#title' => $this->t('Also available:'), + '#attributes' => ['class' => ['version-also-available']], + ]; + } + } + + if (!empty($versions_inner)) { + $variables['versions'] = $versions_inner; + } + + if (!empty($project['disabled'])) { + sort($project['disabled']); + $variables['disabled'] = $project['disabled']; + } + + sort($project['includes']); + $variables['includes'] = $project['includes']; + + $variables['extras'] = []; + if (!empty($project['extra'])) { + foreach ($project['extra'] as $value) { + $extra_item = []; + $extra_item['attributes'] = new Attribute(); + $extra_item['label'] = $value['label']; + $extra_item['data'] = [ + '#prefix' => '<em>', + '#markup' => $value['data'], + '#suffix' => '</em>', + ]; + $variables['extras'][] = $extra_item; + } + } + + // Set the project status details. + $status_label = NULL; + switch ($project['status']) { + case UpdateManagerInterface::NOT_SECURE: + $status_label = $this->t('Security update required!'); + break; + + case UpdateManagerInterface::REVOKED: + $status_label = $this->t('Revoked!'); + break; + + case UpdateManagerInterface::NOT_SUPPORTED: + $status_label = $this->t('Not supported!'); + break; + + case UpdateManagerInterface::NOT_CURRENT: + $status_label = $this->t('Update available'); + break; + + case UpdateManagerInterface::CURRENT: + $status_label = $this->t('Up to date'); + break; + } + $variables['status']['label'] = $status_label; + $variables['status']['attributes'] = new Attribute(); + $variables['status']['reason'] = (isset($project['reason'])) ? $project['reason'] : NULL; + + switch ($project['status']) { + case UpdateManagerInterface::CURRENT: + $uri = 'core/misc/icons/73b355/check.svg'; + $text = $this->t('Ok'); + break; + + case UpdateFetcherInterface::UNKNOWN: + case UpdateFetcherInterface::FETCH_PENDING: + case UpdateFetcherInterface::NOT_FETCHED: + $uri = 'core/misc/icons/e29700/warning.svg'; + $text = $this->t('Warning'); + break; + + case UpdateManagerInterface::NOT_SECURE: + case UpdateManagerInterface::REVOKED: + case UpdateManagerInterface::NOT_SUPPORTED: + $uri = 'core/misc/icons/e32700/error.svg'; + $text = $this->t('Error'); + break; + + case UpdateFetcherInterface::NOT_CHECKED: + case UpdateManagerInterface::NOT_CURRENT: + default: + $uri = 'core/misc/icons/e29700/warning.svg'; + $text = $this->t('Warning'); + break; + } + + $variables['status']['icon'] = [ + '#theme' => 'image', + '#width' => 18, + '#height' => 18, + '#uri' => $uri, + '#alt' => $text, + '#title' => $text, + ]; + } + + /** + * Prepares variables for update fetch error message templates. + * + * Default template: update-fetch-error-message.html.twig. + * + * @param array $variables + * An associative array of template variables. + */ + public function preprocessUpdateFetchErrorMessage(array &$variables): void { + $variables['error_message'] = [ + 'message' => [ + '#markup' => $this->t('Failed to fetch available update data:'), + ], + 'items' => [ + '#theme' => 'item_list', + '#items' => [ + 'documentation_link' => $this->t('See <a href="@url">PHP OpenSSL requirements</a> in the Drupal.org handbook for possible reasons this could happen and what you can do to resolve them.', ['@url' => 'https://www.drupal.org/node/3170647']), + ], + ], + ]; + if ($this->moduleHandler->moduleExists('dblog') && $this->currentUser->hasPermission('access site reports')) { + $options = ['query' => ['type' => ['update']]]; + $dblog_url = Url::fromRoute('dblog.overview', [], $options); + $variables['error_message']['items']['#items']['dblog'] = $this->t('Check <a href="@url">your local system logs</a> for additional error messages.', ['@url' => $dblog_url->toString()]); + } + else { + $variables['error_message']['items']['#items']['logs'] = $this->t('Check your local system logs for additional error messages.'); + } + + } + +} diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 861744de5562..9652cda02f0a 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -6,8 +6,11 @@ use Drupal\Core\File\Exception\FileException; use Drupal\Core\Link; +use Drupal\Core\Template\Attribute; use Drupal\Core\Url; use Drupal\Core\Site\Settings; +use Drupal\update\Hook\UpdateThemeHooks; +use Drupal\update\ProjectRelease; use Drupal\update\UpdateFetcherInterface; use Drupal\update\UpdateManagerInterface; @@ -254,11 +257,90 @@ function _update_project_status_sort($a, $b) { * An associative array containing: * - last: The timestamp when the site last checked for available updates. * - * @see theme_update_report() + * @deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial + * template_preprocess functions are registered directly in hook_theme(). + * + * @see https://www.drupal.org/node/3504125 */ function template_preprocess_update_last_check(&$variables): void { - $variables['time'] = \Drupal::service('date.formatter')->formatTimeDiffSince($variables['last']); - $variables['link'] = Link::fromTextAndUrl(t('Check manually'), Url::fromRoute('update.manual_status', [], ['query' => \Drupal::destination()->getAsArray()]))->toString(); + @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial template_preprocess functions are registered directly in hook_theme(). See https://www.drupal.org/node/3504125', E_USER_DEPRECATED); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateLastCheck($variables); +} + + +/** + * Prepares variables for project status report templates. + * + * Default template: update-report.html.twig. + * + * @param array $variables + * An associative array containing: + * - data: An array of data about each project's status. + * + * @deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial + * template_preprocess functions are registered directly in hook_theme(). + * + * @see https://www.drupal.org/node/3504125 + */ +function template_preprocess_update_report(&$variables): void { + @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial template_preprocess functions are registered directly in hook_theme(). See https://www.drupal.org/node/3504125', E_USER_DEPRECATED); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateReport($variables); +} + +/** + * Prepares variables for update version templates. + * + * Default template: update-version.html.twig. + * + * @param array $variables + * An associative array containing: + * - version: An array of information about the release version. + * + * @deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial + * template_preprocess functions are registered directly in hook_theme(). + * + * @see https://www.drupal.org/node/3504125 + */ +function template_preprocess_update_version(array &$variables): void { + @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial template_preprocess functions are registered directly in hook_theme(). See https://www.drupal.org/node/3504125', E_USER_DEPRECATED); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateVersion($variables); +} + +/** + * Prepares variables for update project status templates. + * + * Default template: update-project-status.html.twig. + * + * @param array $variables + * An associative array containing: + * - project: An array of information about the project. + * + * @deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial + * template_preprocess functions are registered directly in hook_theme(). + * + * @see https://www.drupal.org/node/3504125 + */ +function template_preprocess_update_project_status(&$variables): void { + @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial template_preprocess functions are registered directly in hook_theme(). See https://www.drupal.org/node/3504125', E_USER_DEPRECATED); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateProjectStatus($variables); +} + +/** + * Prepares variables for update fetch error message templates. + * + * Default template: update-fetch-error-message.html.twig. + * + * @param array $variables + * An associative array of template variables. + * + * @deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial + * template_preprocess functions are registered directly in hook_theme(). + * + * @see https://www.drupal.org/node/3504125 + */ +function template_preprocess_update_fetch_error_message(&$variables): void { + @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.3.0 and is removed from drupal:12.0.0. Initial template_preprocess functions are registered directly in hook_theme(). See https://www.drupal.org/node/3504125', E_USER_DEPRECATED); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateFetchErrorMessage($variables); } /** -- GitLab From b0c68c6c4b3ee5bf5867240e62ccb2952ab8b589 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 23:06:19 +0200 Subject: [PATCH 2/6] cleanup --- core/modules/update/src/Hook/UpdateHooks.php | 1 - core/modules/update/update.module | 3 - core/modules/update/update.report.inc | 367 ------------------- 3 files changed, 371 deletions(-) delete mode 100644 core/modules/update/update.report.inc diff --git a/core/modules/update/src/Hook/UpdateHooks.php b/core/modules/update/src/Hook/UpdateHooks.php index 61d72854d8b0..e96ce531ee5c 100644 --- a/core/modules/update/src/Hook/UpdateHooks.php +++ b/core/modules/update/src/Hook/UpdateHooks.php @@ -3,7 +3,6 @@ namespace Drupal\update\Hook; use Drupal\Core\Extension\Requirement\RequirementSeverity; -use Drupal\Core\Link; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\update\UpdateManagerInterface; use Drupal\Core\Url; diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 9652cda02f0a..3ca29b3b8f45 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -5,12 +5,9 @@ */ use Drupal\Core\File\Exception\FileException; -use Drupal\Core\Link; -use Drupal\Core\Template\Attribute; use Drupal\Core\Url; use Drupal\Core\Site\Settings; use Drupal\update\Hook\UpdateThemeHooks; -use Drupal\update\ProjectRelease; use Drupal\update\UpdateFetcherInterface; use Drupal\update\UpdateManagerInterface; diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc deleted file mode 100644 index 75957754bd87..000000000000 --- a/core/modules/update/update.report.inc +++ /dev/null @@ -1,367 +0,0 @@ -<?php - -/** - * @file - */ - -use Drupal\Core\Template\Attribute; -use Drupal\Core\Url; -use Drupal\update\ProjectRelease; -use Drupal\update\UpdateFetcherInterface; -use Drupal\update\UpdateManagerInterface; - -/** - * Prepares variables for project status report templates. - * - * Default template: update-report.html.twig. - * - * @param array $variables - * An associative array containing: - * - data: An array of data about each project's status. - */ -function template_preprocess_update_report(&$variables): void { - $data = isset($variables['data']) && is_array($variables['data']) ? $variables['data'] : []; - - $last = \Drupal::state()->get('update.last_check', 0); - - $variables['last_checked'] = [ - '#theme' => 'update_last_check', - '#last' => $last, - // Attach the library to a variable that gets printed always. - '#attached' => [ - 'library' => [ - 'update/drupal.update.admin', - ], - ], - ]; - - // For no project update data, populate no data message. - if (empty($data)) { - $variables['no_updates_message'] = _update_no_data(); - } - - $rows = []; - - foreach ($data as $project) { - $project_status = [ - '#theme' => 'update_project_status', - '#project' => $project, - ]; - - // Build project rows. - if (!isset($rows[$project['project_type']])) { - $rows[$project['project_type']] = [ - '#type' => 'table', - '#attributes' => ['class' => ['update']], - ]; - } - $row_key = !empty($project['title']) ? mb_strtolower($project['title']) : mb_strtolower($project['name']); - - // Add the project status row and details. - $rows[$project['project_type']][$row_key]['status'] = $project_status; - - // Add project status class attribute to the table row. - switch ($project['status']) { - case UpdateManagerInterface::CURRENT: - $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-success']]; - break; - - case UpdateFetcherInterface::UNKNOWN: - case UpdateFetcherInterface::FETCH_PENDING: - case UpdateFetcherInterface::NOT_FETCHED: - case UpdateManagerInterface::NOT_SECURE: - case UpdateManagerInterface::REVOKED: - case UpdateManagerInterface::NOT_SUPPORTED: - $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-error']]; - break; - - case UpdateFetcherInterface::NOT_CHECKED: - case UpdateManagerInterface::NOT_CURRENT: - default: - $rows[$project['project_type']][$row_key]['#attributes'] = ['class' => ['color-warning']]; - break; - } - } - - $project_types = [ - 'core' => t('Drupal core'), - 'module' => t('Modules'), - 'theme' => t('Themes'), - 'module-uninstalled' => t('Uninstalled modules'), - 'theme-uninstalled' => t('Uninstalled themes'), - ]; - - $variables['project_types'] = []; - foreach ($project_types as $type_name => $type_label) { - if (!empty($rows[$type_name])) { - ksort($rows[$type_name]); - $variables['project_types'][] = [ - 'label' => $type_label, - 'table' => $rows[$type_name], - ]; - } - } -} - -/** - * Prepares variables for update version templates. - * - * Default template: update-version.html.twig. - * - * @param array $variables - * An associative array containing: - * - version: An array of information about the release version. - */ -function template_preprocess_update_version(array &$variables): void { - $release = ProjectRelease::createFromArray($variables['version']); - if (!$release->getCoreCompatibilityMessage()) { - return; - } - $core_compatible = $release->isCoreCompatible(); - $variables['core_compatibility_details'] = [ - '#type' => 'details', - '#title' => $core_compatible ? t('Compatible') : t('Not compatible'), - '#open' => !$core_compatible, - 'message' => [ - '#markup' => $release->getCoreCompatibilityMessage(), - ], - '#attributes' => [ - 'class' => [ - $core_compatible ? 'compatible' : 'not-compatible', - ], - ], - ]; -} - -/** - * Prepares variables for update project status templates. - * - * Default template: update-project-status.html.twig. - * - * @param array $variables - * An associative array containing: - * - project: An array of information about the project. - */ -function template_preprocess_update_project_status(&$variables): void { - // Storing by reference because we are sorting the project values. - $project = &$variables['project']; - - // Set the project title and URL. - $variables['title'] = (isset($project['title'])) ? $project['title'] : $project['name']; - $variables['url'] = (isset($project['link'])) ? Url::fromUri($project['link'])->toString() : NULL; - - $variables['install_type'] = $project['install_type']; - if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) { - $variables['datestamp'] = \Drupal::service('date.formatter')->format($project['datestamp'], 'custom', 'Y-M-d'); - } - - $variables['existing_version'] = $project['existing_version']; - - $versions_inner = []; - $security_class = []; - $version_class = []; - if (isset($project['recommended'])) { - if ($project['status'] != UpdateManagerInterface::CURRENT || $project['existing_version'] !== $project['recommended']) { - - // First, figure out what to recommend. - // If there's only 1 security update and it has the same version we're - // recommending, give it the same CSS class as if it was recommended, - // but don't print out a separate "Recommended" line for this project. - if (!empty($project['security updates']) - && count($project['security updates']) == 1 - && $project['security updates'][0]['version'] === $project['recommended'] - ) { - $security_class[] = 'project-update__version--recommended'; - $security_class[] = 'project-update__version---strong'; - } - else { - $version_class[] = 'project-update__version--recommended'; - // Apply an extra class if we're displaying both a recommended - // version and anything else for an extra visual hint. - if ($project['recommended'] !== $project['latest_version'] - || !empty($project['also']) - || ($project['install_type'] == 'dev' - && isset($project['dev_version']) - && $project['latest_version'] !== $project['dev_version'] - && $project['recommended'] !== $project['dev_version']) - || (isset($project['security updates'][0]) - && $project['recommended'] !== $project['security updates'][0]) - ) { - $version_class[] = 'project-update__version--recommended-strong'; - } - $versions_inner[] = [ - '#theme' => 'update_version', - '#version' => $project['releases'][$project['recommended']], - '#title' => t('Recommended version:'), - '#attributes' => ['class' => $version_class], - ]; - } - - // Now, print any security updates. - if (!empty($project['security updates'])) { - $security_class[] = 'version-security'; - foreach ($project['security updates'] as $security_update) { - $versions_inner[] = [ - '#theme' => 'update_version', - '#version' => $security_update, - '#title' => t('Security update:'), - '#attributes' => ['class' => $security_class], - ]; - } - } - } - - if ($project['recommended'] !== $project['latest_version']) { - $versions_inner[] = [ - '#theme' => 'update_version', - '#version' => $project['releases'][$project['latest_version']], - '#title' => t('Latest version:'), - '#attributes' => ['class' => ['version-latest']], - ]; - } - if ($project['install_type'] == 'dev' - && $project['status'] != UpdateManagerInterface::CURRENT - && isset($project['dev_version']) - && $project['recommended'] !== $project['dev_version']) { - $versions_inner[] = [ - '#theme' => 'update_version', - '#version' => $project['releases'][$project['dev_version']], - '#title' => t('Development version:'), - '#attributes' => ['class' => ['version-latest']], - ]; - } - } - - if (isset($project['also'])) { - foreach ($project['also'] as $also) { - $versions_inner[] = [ - '#theme' => 'update_version', - '#version' => $project['releases'][$also], - '#title' => t('Also available:'), - '#attributes' => ['class' => ['version-also-available']], - ]; - } - } - - if (!empty($versions_inner)) { - $variables['versions'] = $versions_inner; - } - - if (!empty($project['disabled'])) { - sort($project['disabled']); - $variables['disabled'] = $project['disabled']; - } - - sort($project['includes']); - $variables['includes'] = $project['includes']; - - $variables['extras'] = []; - if (!empty($project['extra'])) { - foreach ($project['extra'] as $value) { - $extra_item = []; - $extra_item['attributes'] = new Attribute(); - $extra_item['label'] = $value['label']; - $extra_item['data'] = [ - '#prefix' => '<em>', - '#markup' => $value['data'], - '#suffix' => '</em>', - ]; - $variables['extras'][] = $extra_item; - } - } - - // Set the project status details. - $status_label = NULL; - switch ($project['status']) { - case UpdateManagerInterface::NOT_SECURE: - $status_label = t('Security update required!'); - break; - - case UpdateManagerInterface::REVOKED: - $status_label = t('Revoked!'); - break; - - case UpdateManagerInterface::NOT_SUPPORTED: - $status_label = t('Not supported!'); - break; - - case UpdateManagerInterface::NOT_CURRENT: - $status_label = t('Update available'); - break; - - case UpdateManagerInterface::CURRENT: - $status_label = t('Up to date'); - break; - } - $variables['status']['label'] = $status_label; - $variables['status']['attributes'] = new Attribute(); - $variables['status']['reason'] = (isset($project['reason'])) ? $project['reason'] : NULL; - - switch ($project['status']) { - case UpdateManagerInterface::CURRENT: - $uri = 'core/misc/icons/73b355/check.svg'; - $text = t('Ok'); - break; - - case UpdateFetcherInterface::UNKNOWN: - case UpdateFetcherInterface::FETCH_PENDING: - case UpdateFetcherInterface::NOT_FETCHED: - $uri = 'core/misc/icons/e29700/warning.svg'; - $text = t('Warning'); - break; - - case UpdateManagerInterface::NOT_SECURE: - case UpdateManagerInterface::REVOKED: - case UpdateManagerInterface::NOT_SUPPORTED: - $uri = 'core/misc/icons/e32700/error.svg'; - $text = t('Error'); - break; - - case UpdateFetcherInterface::NOT_CHECKED: - case UpdateManagerInterface::NOT_CURRENT: - default: - $uri = 'core/misc/icons/e29700/warning.svg'; - $text = t('Warning'); - break; - } - - $variables['status']['icon'] = [ - '#theme' => 'image', - '#width' => 18, - '#height' => 18, - '#uri' => $uri, - '#alt' => $text, - '#title' => $text, - ]; -} - -/** - * Prepares variables for update fetch error message templates. - * - * Default template: update-fetch-error-message.html.twig. - * - * @param array $variables - * An associative array of template variables. - */ -function template_preprocess_update_fetch_error_message(&$variables): void { - $variables['error_message'] = [ - 'message' => [ - '#markup' => t('Failed to fetch available update data:'), - ], - 'items' => [ - '#theme' => 'item_list', - '#items' => [ - 'documentation_link' => t('See <a href="@url">PHP OpenSSL requirements</a> in the Drupal.org handbook for possible reasons this could happen and what you can do to resolve them.', ['@url' => 'https://www.drupal.org/node/3170647']), - ], - ], - ]; - if (\Drupal::moduleHandler()->moduleExists('dblog') && \Drupal::currentUser()->hasPermission('access site reports')) { - $options = ['query' => ['type' => ['update']]]; - $dblog_url = Url::fromRoute('dblog.overview', [], $options); - $variables['error_message']['items']['#items']['dblog'] = t('Check <a href="@url">your local system logs</a> for additional error messages.', ['@url' => $dblog_url->toString()]); - } - else { - $variables['error_message']['items']['#items']['logs'] = t('Check your local system logs for additional error messages.'); - } - -} -- GitLab From 1167633e39ca0d83d99b1834fdb2c9c7a4c4ddb2 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 23:11:07 +0200 Subject: [PATCH 3/6] more cleanup --- core/modules/update/src/Hook/UpdateThemeHooks.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/modules/update/src/Hook/UpdateThemeHooks.php b/core/modules/update/src/Hook/UpdateThemeHooks.php index a9f3c845ed55..d584d5ed1c0b 100644 --- a/core/modules/update/src/Hook/UpdateThemeHooks.php +++ b/core/modules/update/src/Hook/UpdateThemeHooks.php @@ -16,6 +16,9 @@ use Drupal\update\UpdateFetcherInterface; use Drupal\update\UpdateManagerInterface; +/** + * Theme hooks for update module. + */ class UpdateThemeHooks { use StringTranslationTrait; -- GitLab From cf9edcdc6ddef94fd3690ed30f2a94f207a36aa7 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 23:19:37 +0200 Subject: [PATCH 4/6] more cleanup --- .../update/tests/src/Kernel/UpdateReportTest.php | 13 +++++-------- core/modules/update/update.module | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/core/modules/update/tests/src/Kernel/UpdateReportTest.php b/core/modules/update/tests/src/Kernel/UpdateReportTest.php index cee75a38b155..03fbe3b8188e 100644 --- a/core/modules/update/tests/src/Kernel/UpdateReportTest.php +++ b/core/modules/update/tests/src/Kernel/UpdateReportTest.php @@ -8,6 +8,7 @@ use Drupal\Core\Url; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\user\Traits\UserCreationTrait; +use Drupal\update\Hook\UpdateThemeHooks; /** * Tests update report functionality. @@ -31,11 +32,9 @@ class UpdateReportTest extends KernelTestBase { * @dataProvider providerTemplatePreprocessUpdateReport */ public function testTemplatePreprocessUpdateReport($variables): void { - \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.report'); - // The function should run without an exception being thrown when the value // of $variables['data'] is not set or is not an array. - template_preprocess_update_report($variables); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateReport($variables); // Test that the key "no_updates_message" has been set. $this->assertArrayHasKey('no_updates_message', $variables); @@ -73,9 +72,8 @@ public function testTemplatePreprocessUpdateFetchErrorMessageNoDblog(): void { $this->render($build); $this->assertRaw('Failed to fetch available update data:<ul><li>See <a href="https://www.drupal.org/node/3170647">PHP OpenSSL requirements</a> in the Drupal.org handbook for possible reasons this could happen and what you can do to resolve them.</li><li>Check your local system logs for additional error messages.</li></ul>'); - \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.report'); $variables = []; - template_preprocess_update_fetch_error_message($variables); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateFetchErrorMessage($variables); $this->assertArrayHasKey('error_message', $variables); $this->assertEquals('Failed to fetch available update data:', $variables['error_message']['message']['#markup']); $this->assertArrayHasKey('documentation_link', $variables['error_message']['items']['#items']); @@ -89,7 +87,6 @@ public function testTemplatePreprocessUpdateFetchErrorMessageNoDblog(): void { * @see template_preprocess_update_fetch_error_message() */ public function testTemplatePreprocessUpdateFetchErrorMessageWithDblog(): void { - \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.report'); $this->enableModules(['dblog', 'user']); $this->installEntitySchema('user'); @@ -104,7 +101,7 @@ public function testTemplatePreprocessUpdateFetchErrorMessageWithDblog(): void { $this->assertRaw('Failed to fetch available update data:<ul><li>See <a href="https://www.drupal.org/node/3170647">PHP OpenSSL requirements</a> in the Drupal.org handbook for possible reasons this could happen and what you can do to resolve them.</li><li>Check your local system logs for additional error messages.</li></ul>'); $variables = []; - template_preprocess_update_fetch_error_message($variables); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateFetchErrorMessage($variables); $this->assertArrayHasKey('error_message', $variables); $this->assertEquals('Failed to fetch available update data:', $variables['error_message']['message']['#markup']); $this->assertArrayHasKey('documentation_link', $variables['error_message']['items']['#items']); @@ -121,7 +118,7 @@ public function testTemplatePreprocessUpdateFetchErrorMessageWithDblog(): void { $this->assertRaw(' for additional error messages.</li></ul>'); $variables = []; - template_preprocess_update_fetch_error_message($variables); + \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateFetchErrorMessage($variables); $this->assertArrayHasKey('error_message', $variables); $this->assertEquals('Failed to fetch available update data:', $variables['error_message']['message']['#markup']); $this->assertArrayHasKey('documentation_link', $variables['error_message']['items']['#items']); diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 3ca29b3b8f45..1a217fa54da6 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -264,7 +264,6 @@ function template_preprocess_update_last_check(&$variables): void { \Drupal::service(UpdateThemeHooks::class)->preprocessUpdateLastCheck($variables); } - /** * Prepares variables for project status report templates. * -- GitLab From 219a910a7fcd4b6dcd18f47a3bf8f628990d8b98 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 23:29:18 +0200 Subject: [PATCH 5/6] more references --- .../update/templates/update-last-check.html.twig | 2 +- .../update/templates/update-project-status.html.twig | 2 +- core/modules/update/templates/update-report.html.twig | 2 +- core/modules/update/templates/update-version.html.twig | 2 +- .../update/tests/src/Kernel/UpdateReportTest.php | 10 ++++++---- .../claro/templates/admin/update-version.html.twig | 2 +- .../admin/update-fetch-error-message.html.twig | 2 +- .../templates/admin/update-last-check.html.twig | 2 +- .../templates/admin/update-project-status.html.twig | 2 +- .../stable9/templates/admin/update-report.html.twig | 2 +- .../stable9/templates/admin/update-version.html.twig | 2 +- 11 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/modules/update/templates/update-last-check.html.twig b/core/modules/update/templates/update-last-check.html.twig index 213f724c0059..9c370a4e84df 100644 --- a/core/modules/update/templates/update-last-check.html.twig +++ b/core/modules/update/templates/update-last-check.html.twig @@ -8,7 +8,7 @@ * - time: The formatted time since the site last checked for updates. * - link: A link to check for updates manually. * - * @see template_preprocess_update_last_check() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateLastCheck() * * @ingroup themeable */ diff --git a/core/modules/update/templates/update-project-status.html.twig b/core/modules/update/templates/update-project-status.html.twig index dceb552054d8..713e5d37e6dc 100644 --- a/core/modules/update/templates/update-project-status.html.twig +++ b/core/modules/update/templates/update-project-status.html.twig @@ -22,7 +22,7 @@ * - includes: The projects within the project. * - disabled: The currently disabled projects in the project. * - * @see template_preprocess_update_project_status() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateProjectStatus() * * @ingroup themeable */ diff --git a/core/modules/update/templates/update-report.html.twig b/core/modules/update/templates/update-report.html.twig index ae121cc3b424..b44625486959 100644 --- a/core/modules/update/templates/update-report.html.twig +++ b/core/modules/update/templates/update-report.html.twig @@ -10,7 +10,7 @@ * - label: The project type label. * - table: The project status table. * - * @see template_preprocess_update_report() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateReport() * * @ingroup themeable */ diff --git a/core/modules/update/templates/update-version.html.twig b/core/modules/update/templates/update-version.html.twig index 7c18ea15fa90..984308a71224 100644 --- a/core/modules/update/templates/update-version.html.twig +++ b/core/modules/update/templates/update-version.html.twig @@ -20,7 +20,7 @@ * contained within the 'core_compatibility_details' variable documented * above. This message is not set for the Drupal core project itself. * - * @see template_preprocess_update_version() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateVersion() * * @ingroup themeable */ diff --git a/core/modules/update/tests/src/Kernel/UpdateReportTest.php b/core/modules/update/tests/src/Kernel/UpdateReportTest.php index 03fbe3b8188e..cf40c3000e69 100644 --- a/core/modules/update/tests/src/Kernel/UpdateReportTest.php +++ b/core/modules/update/tests/src/Kernel/UpdateReportTest.php @@ -13,7 +13,7 @@ /** * Tests update report functionality. * - * @covers template_preprocess_update_report + * @coversDefaultClass \Drupal\update\Hook\UpdateThemeHooks * @group update */ class UpdateReportTest extends KernelTestBase { @@ -30,6 +30,7 @@ class UpdateReportTest extends KernelTestBase { /** * @dataProvider providerTemplatePreprocessUpdateReport + * @covers ::preprocessUpdateReport */ public function testTemplatePreprocessUpdateReport($variables): void { // The function should run without an exception being thrown when the value @@ -44,7 +45,8 @@ public function testTemplatePreprocessUpdateReport($variables): void { * Provides data for testTemplatePreprocessUpdateReport(). * * @return array - * Array of $variables for template_preprocess_update_report(). + * Array of $variables for + * \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateReport(). */ public static function providerTemplatePreprocessUpdateReport() { return [ @@ -63,7 +65,7 @@ public static function providerTemplatePreprocessUpdateReport() { /** * Tests the error message when failing to fetch data without dblog installed. * - * @see template_preprocess_update_fetch_error_message() + * @covers ::preprocessUpdateFetchErrorMessage */ public function testTemplatePreprocessUpdateFetchErrorMessageNoDblog(): void { $build = [ @@ -84,7 +86,7 @@ public function testTemplatePreprocessUpdateFetchErrorMessageNoDblog(): void { /** * Tests the error message when failing to fetch data with dblog installed. * - * @see template_preprocess_update_fetch_error_message() + * @covers ::preprocessUpdateFetchErrorMessage */ public function testTemplatePreprocessUpdateFetchErrorMessageWithDblog(): void { diff --git a/core/themes/claro/templates/admin/update-version.html.twig b/core/themes/claro/templates/admin/update-version.html.twig index 042c9ba4fa2e..14e91a9d4b1e 100644 --- a/core/themes/claro/templates/admin/update-version.html.twig +++ b/core/themes/claro/templates/admin/update-version.html.twig @@ -20,7 +20,7 @@ * contained within the 'core_compatibility_details' variable documented * above. This message is not set for the Drupal core project itself. * - * @see template_preprocess_update_version() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateVersion() */ #} <div class="{{ attributes.class }} project-update__version"{{ attributes|without('class') }}> diff --git a/core/themes/stable9/templates/admin/update-fetch-error-message.html.twig b/core/themes/stable9/templates/admin/update-fetch-error-message.html.twig index fd4a967e7256..6ab1946be3e2 100644 --- a/core/themes/stable9/templates/admin/update-fetch-error-message.html.twig +++ b/core/themes/stable9/templates/admin/update-fetch-error-message.html.twig @@ -6,7 +6,7 @@ * Available variables: * - error_message: A render array containing the appropriate error message. * - * @see template_preprocess_update_fetch_error_message() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateFetchErrorMessage() * * @ingroup themeable */ diff --git a/core/themes/stable9/templates/admin/update-last-check.html.twig b/core/themes/stable9/templates/admin/update-last-check.html.twig index b94d6dceac7c..c8a0d37981b5 100644 --- a/core/themes/stable9/templates/admin/update-last-check.html.twig +++ b/core/themes/stable9/templates/admin/update-last-check.html.twig @@ -8,7 +8,7 @@ * - time: The formatted time since the site last checked for updates. * - link: A link to check for updates manually. * - * @see template_preprocess_update_last_check() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateLastCheck() */ #} <p> diff --git a/core/themes/stable9/templates/admin/update-project-status.html.twig b/core/themes/stable9/templates/admin/update-project-status.html.twig index 6d2d4c85cd15..e1c5790ba9fb 100644 --- a/core/themes/stable9/templates/admin/update-project-status.html.twig +++ b/core/themes/stable9/templates/admin/update-project-status.html.twig @@ -22,7 +22,7 @@ * - includes: The projects within the project. * - disabled: The currently disabled projects in the project. * - * @see template_preprocess_update_project_status() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateProjectStatus() */ #} {% diff --git a/core/themes/stable9/templates/admin/update-report.html.twig b/core/themes/stable9/templates/admin/update-report.html.twig index 9efebc06aec9..a15dab20075d 100644 --- a/core/themes/stable9/templates/admin/update-report.html.twig +++ b/core/themes/stable9/templates/admin/update-report.html.twig @@ -10,7 +10,7 @@ * - label: The project type label. * - table: The project status table. * - * @see template_preprocess_update_report() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateReport() */ #} {{ last_checked }} diff --git a/core/themes/stable9/templates/admin/update-version.html.twig b/core/themes/stable9/templates/admin/update-version.html.twig index 4b0389ea60ce..cb5345a82c20 100644 --- a/core/themes/stable9/templates/admin/update-version.html.twig +++ b/core/themes/stable9/templates/admin/update-version.html.twig @@ -20,7 +20,7 @@ * contained within the 'core_compatibility_details' variable documented * above. This message is not set for the Drupal core project itself. * - * @see template_preprocess_update_version() + * @see \Drupal\update\Hook\UpdateThemeHooks::preprocessUpdateVersion() */ #} <div class="{{ attributes.class }} project-update__version"{{ attributes|without('class') }}> -- GitLab From 5e73488a704a2794091d29821449bc9ad235bd77 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 5 Jul 2025 23:31:36 +0200 Subject: [PATCH 6/6] phpcs --- core/modules/update/tests/src/Kernel/UpdateReportTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/update/tests/src/Kernel/UpdateReportTest.php b/core/modules/update/tests/src/Kernel/UpdateReportTest.php index cf40c3000e69..5558896363b9 100644 --- a/core/modules/update/tests/src/Kernel/UpdateReportTest.php +++ b/core/modules/update/tests/src/Kernel/UpdateReportTest.php @@ -13,7 +13,7 @@ /** * Tests update report functionality. * - * @coversDefaultClass \Drupal\update\Hook\UpdateThemeHooks + * @coversDefaultClass \Drupal\update\Hook\UpdateThemeHooks * @group update */ class UpdateReportTest extends KernelTestBase { -- GitLab