<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[54212] trunk: Upgrade/Install: Add plugin URL to the automatic update email.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/54212">54212</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/54212","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>davidbaumwald</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-09-19 20:26:09 +0000 (Mon, 19 Sep 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Upgrade/Install: Add plugin URL to the automatic update email.

For each automatic plugin update, both successful and failed, information about each plugin is included in the email upon completion of the process.  This change adds the plugin URL, if known, to the information included for each plugin that was processed.

This change also adds unit tests to validate the email contents after various automatic plugin update scenarios.

Props JosVelasco, pbiron, oliverstapelfeldt, ChrisHardie, Ipstenu, dd32, peterwilsoncc, audrasjb, costdev.
Fixes <a href="https://core.trac.wordpress.org/ticket/53049">#53049</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludesclasswpautomaticupdaterphp">trunk/src/wp-admin/includes/class-wp-automatic-updater.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsadminwpAutomaticUpdaterphp">trunk/tests/phpunit/tests/admin/wpAutomaticUpdater.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminincludesclasswpautomaticupdaterphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/class-wp-automatic-updater.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-automatic-updater.php        2022-09-19 20:12:02 UTC (rev 54211)
+++ trunk/src/wp-admin/includes/class-wp-automatic-updater.php  2022-09-19 20:26:09 UTC (rev 54212)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1101,23 +1101,34 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $body[] = __( 'These plugins failed to update:' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                foreach ( $failed_updates['plugin'] as $item ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        $body_message = '';
+                                       $item_url     = '';
+
+                                       if ( ! empty( $item->item->url ) ) {
+                                               $item_url = ' : ' . esc_url( $item->item->url );
+                                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                         if ( $item->item->current_version ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                $body[] = sprintf(
-                                                       /* translators: 1: Plugin name, 2: Current version number, 3: New version number. */
-                                                       __( '- %1$s (from version %2$s to %3$s)' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         $body_message .= sprintf(
+                                                       /* translators: 1: Plugin name, 2: Current version number, 3: New version number, 4: Plugin URL. */
+                                                       __( '- %1$s (from version %2$s to %3$s)%4$s' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         $item->name,
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $item->item->current_version,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        $item->item->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 $item->item->new_version,
+                                                       $item_url
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 );
</span><span class="cx" style="display: block; padding: 0 10px">                                        } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                $body[] = sprintf(
-                                                       /* translators: 1: Plugin name, 2: Version number. */
-                                                       __( '- %1$s version %2$s' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         $body_message .= sprintf(
+                                                       /* translators: 1: Plugin name, 2: Version number, 3: Plugin URL. */
+                                                       __( '- %1$s version %2$s%3$s' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         $item->name,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        $item->item->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 $item->item->new_version,
+                                                       $item_url
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 );
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        $body[] = $body_message;
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                         $past_failure_emails[ $item->item->plugin ] = $item->item->new_version;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1162,22 +1173,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $body[] = __( 'These plugins are now up to date:' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                foreach ( $successful_updates['plugin'] as $item ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        $body_message = '';
+                                       $item_url     = '';
+
+                                       if ( ! empty( $item->item->url ) ) {
+                                               $item_url = ' : ' . esc_url( $item->item->url );
+                                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                         if ( $item->item->current_version ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                $body[] = sprintf(
-                                                       /* translators: 1: Plugin name, 2: Current version number, 3: New version number. */
-                                                       __( '- %1$s (from version %2$s to %3$s)' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         $body_message .= sprintf(
+                                                       /* translators: 1: Plugin name, 2: Current version number, 3: New version number, 4: Plugin URL. */
+                                                       __( '- %1$s (from version %2$s to %3$s)%4$s' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         $item->name,
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $item->item->current_version,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        $item->item->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 $item->item->new_version,
+                                                       $item_url
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 );
</span><span class="cx" style="display: block; padding: 0 10px">                                        } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                $body[] = sprintf(
-                                                       /* translators: 1: Plugin name, 2: Version number. */
-                                                       __( '- %1$s version %2$s' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         $body_message .= sprintf(
+                                                       /* translators: 1: Plugin name, 2: Version number, 3: Plugin URL. */
+                                                       __( '- %1$s version %2$s%3$s' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         $item->name,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        $item->item->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 $item->item->new_version,
+                                                       $item_url
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 );
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        $body[] = $body_message;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                        unset( $past_failure_emails[ $item->item->plugin ] );
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span></span></pre></div>
<a id="trunktestsphpunittestsadminwpAutomaticUpdaterphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/admin/wpAutomaticUpdater.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/admin/wpAutomaticUpdater.php                            (rev 0)
+++ trunk/tests/phpunit/tests/admin/wpAutomaticUpdater.php      2022-09-19 20:26:09 UTC (rev 54212)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,575 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * @group upgrade
+ *
+ * @covers WP_Automatic_Updater
+ */
+class Tests_Admin_WpAutomaticUpdater extends WP_UnitTestCase {
+       /**
+        * An instance of WP_Automatic_Updater.
+        *
+        * @var WP_Automatic_Updater
+        */
+       private static $updater;
+
+       /**
+        * WP_Automatic_Updater::send_plugin_theme_email
+        * made accessible.
+        *
+        * @var ReflectionMethod
+        */
+       private static $send_plugin_theme_email;
+
+       /**
+        * Sets up shared fixtures.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php';
+               self::$updater = new WP_Automatic_Updater();
+
+               self::$send_plugin_theme_email = new ReflectionMethod( self::$updater, 'send_plugin_theme_email' );
+               self::$send_plugin_theme_email->setAccessible( true );
+       }
+
+       public function set_up() {
+               parent::set_up();
+               add_filter( 'pre_wp_mail', '__return_false' );
+       }
+
+       /**
+        * Tests that `WP_Automatic_Updater::send_plugin_theme_email()` appends
+        * plugin URLs.
+        *
+        * @ticket 53049
+        *
+        * @covers WP_Automatic_Updater::send_plugin_theme_email
+        *
+        * @dataProvider data_send_plugin_theme_email_should_append_plugin_urls
+        *
+        * @param string[] $urls       The URL(s) to search for. Must not be empty.
+        * @param object[] $successful An array of successful plugin update objects.
+        * @param object[] $failed     An array of failed plugin update objects.
+        */
+       public function test_send_plugin_theme_email_should_append_plugin_urls( $urls, $successful, $failed ) {
+               add_filter(
+                       'wp_mail',
+                       function( $args ) use ( $urls ) {
+                               foreach ( $urls as $url ) {
+                                       $this->assertStringContainsString(
+                                               $url,
+                                               $args['message'],
+                                               'The email message should contain ' . $url
+                                       );
+                               }
+                       }
+               );
+
+               $has_successful = ! empty( $successful );
+               $has_failed     = ! empty( $failed );
+
+               if ( ! $has_successful && ! $has_failed ) {
+                       $this->markTestSkipped( 'This test requires at least one successful or failed plugin update object.' );
+               }
+
+               $type = $has_successful && $has_failed ? 'mixed' : ( ! $has_failed ? 'success' : 'fail' );
+
+               $args = array( $type, array( 'plugin' => $successful ), array( 'plugin' => $failed ) );
+               self::$send_plugin_theme_email->invokeArgs( self::$updater, $args );
+       }
+
+       /**
+        * Data provider: Provides an array of plugin update objects that should
+        * have their URLs appended to the email message.
+        *
+        * @return array
+        */
+       public function data_send_plugin_theme_email_should_append_plugin_urls() {
+               return array(
+                       'successful updates, the current version and the plugin url'       => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(),
+                       ),
+                       'successful updates, no current version and the plugin url'  => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(),
+                       ),
+                       'failed updates, the current version and the plugin url'       => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'failed updates, no current version and the plugin url'  => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version and a successful plugin url' => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, no current version and a successful plugin url'  => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version and a failed plugin url' => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, no current version and a failed plugin url'  => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version and both successful and failed plugin urls' => array(
+                               'urls'       => array(
+                                       'http://example.org/successful-plugin',
+                                       'http://example.org/failed-plugin',
+                               ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, no current version and both successful and failed plugin urls'  => array(
+                               'urls'       => array(
+                                       'http://example.org/successful-plugin',
+                                       'http://example.org/failed-plugin',
+                               ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Tests that `WP_Automatic_Updater::send_plugin_theme_email()` does not
+        * append plugin URLs.
+        *
+        * @ticket 53049
+        *
+        * @covers WP_Automatic_Updater::send_plugin_theme_email
+        *
+        * @dataProvider data_send_plugin_theme_email_should_not_append_plugin_urls
+        *
+        * @param string[] $urls       The URL(s) to search for. Must not be empty.
+        * @param object[] $successful An array of successful plugin update objects.
+        * @param object[] $failed     An array of failed plugin update objects.
+        */
+       public function test_send_plugin_theme_email_should_not_append_plugin_urls( $urls, $successful, $failed ) {
+               add_filter(
+                       'wp_mail',
+                       function( $args ) use ( $urls ) {
+                               foreach ( $urls as $url ) {
+                                       $this->assertStringNotContainsString(
+                                               $url,
+                                               $args['message'],
+                                               'The email message should not contain ' . $url
+                                       );
+                               }
+                       }
+               );
+
+               $has_successful = ! empty( $successful );
+               $has_failed     = ! empty( $failed );
+
+               if ( ! $has_successful && ! $has_failed ) {
+                       $this->markTestSkipped( 'This test requires at least one successful or failed plugin update object.' );
+               }
+
+               $type = $has_successful && $has_failed ? 'mixed' : ( ! $has_failed ? 'success' : 'fail' );
+
+               $args = array( $type, array( 'plugin' => $successful ), array( 'plugin' => $failed ) );
+               self::$send_plugin_theme_email->invokeArgs( self::$updater, $args );
+       }
+
+       /**
+        * Data provider: Provides an array of plugin update objects that should
+        * not have their URL appended to the email message.
+        *
+        * @return array
+        */
+       public function data_send_plugin_theme_email_should_not_append_plugin_urls() {
+               return array(
+                       'successful updates, the current version, but no plugin url'    => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(),
+                       ),
+                       'successful updates, but no current version or plugin url' => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(),
+                       ),
+                       'failed updates, the current version, but no plugin url'    => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'failed updates, but no current version or plugin url' => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version, but no successful plugin url' => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, but no current version or successful plugin url'  => array(
+                               'urls'       => array( 'http://example.org/successful-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => 'http://example.org/failed-plugin',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version, but no failed plugin url' => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, no current version or failed plugin url'  => array(
+                               'urls'       => array( 'http://example.org/failed-plugin' ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => 'http://example.org/successful-plugin',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, the current version and no successful or failed plugin urls' => array(
+                               'urls'       => array(
+                                       'http://example.org/successful-plugin',
+                                       'http://example.org/failed-plugin',
+                               ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '1.0.0',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+                       'mixed updates, no current version and no successful or failed plugin urls'  => array(
+                               'urls'       => array(
+                                       'http://example.org/successful-plugin',
+                                       'http://example.org/failed-plugin',
+                               ),
+                               'successful' => array(
+                                       (object) array(
+                                               'name' => 'Successful Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'successful-plugin/successful-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                               'failed'     => array(
+                                       (object) array(
+                                               'name' => 'Failed Plugin',
+                                               'item' => (object) array(
+                                                       'current_version' => '',
+                                                       'new_version'     => '2.0.0',
+                                                       'plugin'          => 'failed-plugin/failed-plugin.php',
+                                                       'url'             => '',
+                                               ),
+                                       ),
+                               ),
+                       ),
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/admin/wpAutomaticUpdater.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>