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