From d7758d65656d5114b84190e7811acbb76048f7cc Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Thu, 29 Oct 2020 10:46:22 +0000
Subject: [PATCH] Issue #3132426 by alexpott, dww, GuyPaddock, catch,
 VladimirAus: Notice: Undefined index: title in
 Drupal\update\ProjectSecurityRequirement

---
 .../update/src/UpdateManagerInterface.php     |  6 +++
 core/modules/update/src/UpdateProcessor.php   |  5 +++
 .../release-history/drupal.broken.xml         |  3 ++
 .../tests/src/Functional/UpdateCoreTest.php   | 37 +++++++++++++++++++
 4 files changed, 51 insertions(+)
 create mode 100644 core/modules/update/tests/fixtures/release-history/drupal.broken.xml

diff --git a/core/modules/update/src/UpdateManagerInterface.php b/core/modules/update/src/UpdateManagerInterface.php
index 74dac9f67c9f..32eb34cbba8d 100644
--- a/core/modules/update/src/UpdateManagerInterface.php
+++ b/core/modules/update/src/UpdateManagerInterface.php
@@ -82,8 +82,14 @@ public function getProjects();
   /**
    * Processes a step in batch for fetching available update data.
    *
+   * Before calling this method, call
+   * UpdateManagerInterface::refreshUpdateData() to clear existing update data
+   * and initiate re-fetching.
+   *
    * @param array $context
    *   Reference to an array used for Batch API storage.
+   *
+   * @see \Drupal\update\UpdateManagerInterface::refreshUpdateData()
    */
   public function fetchDataBatch(&$context);
 
diff --git a/core/modules/update/src/UpdateProcessor.php b/core/modules/update/src/UpdateProcessor.php
index 25dfc7a004bc..8d42dd53622a 100644
--- a/core/modules/update/src/UpdateProcessor.php
+++ b/core/modules/update/src/UpdateProcessor.php
@@ -127,6 +127,11 @@ public function createFetchTask($project) {
    */
   public function fetchData() {
     $end = time() + $this->updateSettings->get('fetch.timeout');
+    if ($this->fetchQueue->numberOfItems()) {
+      // Delete any stored project data as that needs refreshing when
+      // update_calculate_project_data() is called.
+      $this->tempStore->delete('update_project_data');
+    }
     while (time() < $end && ($item = $this->fetchQueue->claimItem())) {
       $this->processFetchTask($item->data);
       $this->fetchQueue->deleteItem($item);
diff --git a/core/modules/update/tests/fixtures/release-history/drupal.broken.xml b/core/modules/update/tests/fixtures/release-history/drupal.broken.xml
new file mode 100644
index 000000000000..1ba5d5e408ac
--- /dev/null
+++ b/core/modules/update/tests/fixtures/release-history/drupal.broken.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project xmlns:dc="http://purl.org/dc/elements/1.1/">
+</project>
diff --git a/core/modules/update/tests/src/Functional/UpdateCoreTest.php b/core/modules/update/tests/src/Functional/UpdateCoreTest.php
index 63a5b603a5b5..7cab37d06798 100644
--- a/core/modules/update/tests/src/Functional/UpdateCoreTest.php
+++ b/core/modules/update/tests/src/Functional/UpdateCoreTest.php
@@ -862,6 +862,43 @@ public function testRevokedRelease() {
     }
   }
 
+  /**
+   * Checks that Drupal recovers after problems connecting to update server.
+   */
+  public function testBrokenThenFixedUpdates() {
+    $this->drupalLogin($this->drupalCreateUser([
+      'administer site configuration',
+      'access administration pages',
+    ]));
+    $this->setSystemInfo('8.0.0');
+    // Instead of using refreshUpdateStatus(), set these manually.
+    $this->config('update.settings')
+      ->set('fetch.url', Url::fromRoute('update_test.update_test')->setAbsolute()->toString())
+      ->save();
+    // Use update XML that has no information to simulate a broken response from
+    // the update server.
+    $this->config('update_test.settings')
+      ->set('xml_map', ['drupal' => 'broken'])
+      ->save();
+
+    // This will retrieve broken updates.
+    $this->cronRun();
+    $this->drupalGet('admin/reports/status');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('There was a problem checking available updates for Drupal.');
+    $this->config('update_test.settings')
+      ->set('xml_map', ['drupal' => 'sec.0.2'])
+      ->save();
+    // Simulate the update_available_releases state expiring before cron is run
+    // and the state is used by \Drupal\update\UpdateManager::getProjects().
+    \Drupal::keyValueExpirable('update_available_releases')->deleteAll();
+    // This cron run should retrieve fixed updates.
+    $this->cronRun();
+    $this->drupalGet('admin/structure');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('There is a security update available for your version of Drupal.');
+  }
+
   /**
    * Tests messages when a project release is marked unsupported.
    *
-- 
GitLab