From 7b55c6b40977128b61c2fee98dc744547148ba1e Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Wed, 15 Jun 2022 19:43:09 +0000
Subject: [PATCH] Issue #3285491 by phenaproxima, Theresa.Grannum, tedbow: Send
 an email when a cron update succeeds

---
 automatic_updates.module             | 16 +++++++++++++
 automatic_updates.services.yml       |  2 ++
 src/CronUpdater.php                  | 35 +++++++++++++++++++++++++++-
 tests/src/Kernel/CronUpdaterTest.php | 24 +++++++++++++++++++
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/automatic_updates.module b/automatic_updates.module
index 9e4065f6a3..17b39d9db6 100644
--- a/automatic_updates.module
+++ b/automatic_updates.module
@@ -34,6 +34,22 @@ function automatic_updates_help($route_name, RouteMatchInterface $route_match) {
   }
 }
 
+/**
+ * Implements hook_mail().
+ */
+function automatic_updates_mail(string $key, array &$message, array $params): void {
+  if ($key === 'cron_successful') {
+    $message['subject'] = t("Drupal core was successfully updated");
+    $message['body'][] = t('Congratulations!');
+    $message['body'][] = t('Drupal core was automatically updated from @previous_version to @updated_version.', [
+      '@previous_version' => $params['previous_version'],
+      '@updated_version' => $params['updated_version'],
+    ]);
+    $message['body'][] = t('This e-mail was sent by the Automatic Updates module, which is an experimental module. Furthermore, unattended updates are not yet fully supported.');
+    $message['body'][] = t('If you are using this feature in production, it is strongly recommended for you to visit your site and ensure that everything still looks good.');
+  }
+}
+
 /**
  * Implements hook_page_top().
  */
diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index 5ac683429c..00e0493964 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -31,6 +31,8 @@ services:
     arguments:
       - '@automatic_updates.release_chooser'
       - '@logger.factory'
+      - '@plugin.manager.mail'
+      - '@language_manager'
       - '@config.factory'
       - '@package_manager.path_locator'
       - '@package_manager.beginner'
diff --git a/src/CronUpdater.php b/src/CronUpdater.php
index 116b17e287..744c5c31d2 100644
--- a/src/CronUpdater.php
+++ b/src/CronUpdater.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\automatic_updates;
 
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Mail\MailManagerInterface;
 use Drupal\package_manager\Exception\StageValidationException;
 
 /**
@@ -58,6 +60,20 @@ class CronUpdater extends Updater {
    */
   protected $releaseChooser;
 
+  /**
+   * The mail manager service.
+   *
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * The language manager service.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
   /**
    * Constructs a CronUpdater object.
    *
@@ -65,13 +81,19 @@ class CronUpdater extends Updater {
    *   The cron release chooser service.
    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
    *   The logger channel factory.
+   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+   *   The mail manager service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager service.
    * @param mixed ...$arguments
    *   Additional arguments to pass to the parent constructor.
    */
-  public function __construct(ReleaseChooser $release_chooser, LoggerChannelFactoryInterface $logger_factory, ...$arguments) {
+  public function __construct(ReleaseChooser $release_chooser, LoggerChannelFactoryInterface $logger_factory, MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, ...$arguments) {
     parent::__construct(...$arguments);
     $this->releaseChooser = $release_chooser;
     $this->logger = $logger_factory->get('automatic_updates');
+    $this->mailManager = $mail_manager;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -137,6 +159,17 @@ class CronUpdater extends Updater {
           '%target_version' => $target_version,
         ]
       );
+
+      // Send notifications about the successful update.
+      $mail_params = [
+        'previous_version' => $installed_version,
+        'updated_version' => $target_version,
+      ];
+      $recipients = $this->configFactory->get('update.settings')
+        ->get('notification.emails');
+      foreach ($recipients as $recipient) {
+        $this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $this->languageManager->getDefaultLanguage()->getId(), $mail_params);
+      }
     }
     catch (\Throwable $e) {
       $this->logger->error($e->getMessage());
diff --git a/tests/src/Kernel/CronUpdaterTest.php b/tests/src/Kernel/CronUpdaterTest.php
index fbca84f421..3aec58c375 100644
--- a/tests/src/Kernel/CronUpdaterTest.php
+++ b/tests/src/Kernel/CronUpdaterTest.php
@@ -7,6 +7,7 @@ use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Logger\RfcLogLevel;
+use Drupal\Core\Test\AssertMailTrait;
 use Drupal\package_manager\Event\PostApplyEvent;
 use Drupal\package_manager\Event\PostCreateEvent;
 use Drupal\package_manager\Event\PostDestroyEvent;
@@ -30,6 +31,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  */
 class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
 
+  use AssertMailTrait;
   use PackageManagerBypassTestTrait;
 
   /**
@@ -352,4 +354,26 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
       ->begin(['drupal' => '9.8.1']);
   }
 
+  /**
+   * Tests that user 1 is emailed when an unattended update succeeds.
+   */
+  public function testEmailOnSuccess(): void {
+    // Use a virtual project so that the test is unaffected by symlinks or other
+    // artifacts that might be in the running code base.
+    $this->createTestProject();
+
+    $this->config('update.settings')
+      ->set('notification.emails', [
+        'emissary@deep.space',
+      ])
+      ->save();
+
+    $this->container->get('cron')->run();
+
+    // Check that we actually sent a success email to the right person.
+    $this->assertMail('to', 'emissary@deep.space');
+    $this->assertMail('subject', "Drupal core was successfully updated");
+    $this->assertMailString('body', "Congratulations!\n\nDrupal core was automatically updated from 9.8.0 to 9.8.1.\n", 1);
+  }
+
 }
-- 
GitLab