<!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>[37714] trunk: Update/Install: Shiny Updates v2.</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 { 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/37714">37714</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/37714","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>obenland</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-06-15 16:36:07 +0000 (Wed, 15 Jun 2016)</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'>Update/Install: Shiny Updates v2.

Gone are the days of isolation and feelings of "meh", brought on by The Bleak Screen of Sadness. For a shiny knight has arrived to usher our plugins and themes along their arduous journey of installation, updates, and the inevitable fate of ultimate deletion.

Props swissspidy, adamsilverstein, mapk, afragen, ocean90, ryelle, j-falk, michael-arestad, melchoyce, DrewAPicture, AdamSoucie, ethitter, pento, dd32, kraftbj, Ipstenu, jorbin, afercia, stephdau, paulwilde, jipmoors, khag7, svovaf, jipmoors, obenland.
Fixes <a href="https://core.trac.wordpress.org/ticket/22029">#22029</a>, <a href="https://core.trac.wordpress.org/ticket/25828">#25828</a>, <a href="https://core.trac.wordpress.org/ticket/31002">#31002</a>, <a href="https://core.trac.wordpress.org/ticket/31529">#31529</a>, <a href="https://core.trac.wordpress.org/ticket/31530">#31530</a>, <a href="https://core.trac.wordpress.org/ticket/31773">#31773</a>, <a href="https://core.trac.wordpress.org/ticket/33637">#33637</a>, <a href="https://core.trac.wordpress.org/ticket/35032">#35032</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminadminajaxphp">trunk/src/wp-admin/admin-ajax.php</a></li>
<li><a href="#trunksrcwpadmincsscommoncss">trunk/src/wp-admin/css/common.css</a></li>
<li><a href="#trunksrcwpadmincssformscss">trunk/src/wp-admin/css/forms.css</a></li>
<li><a href="#trunksrcwpadmincsslisttablescss">trunk/src/wp-admin/css/list-tables.css</a></li>
<li><a href="#trunksrcwpadmincssthemescss">trunk/src/wp-admin/css/themes.css</a></li>
<li><a href="#trunksrcwpadminimportphp">trunk/src/wp-admin/import.php</a></li>
<li><a href="#trunksrcwpadminincludesajaxactionsphp">trunk/src/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswpfilesystembasephp">trunk/src/wp-admin/includes/class-wp-filesystem-base.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswpmsthemeslisttablephp">trunk/src/wp-admin/includes/class-wp-ms-themes-list-table.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswpplugininstalllisttablephp">trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswppluginslisttablephp">trunk/src/wp-admin/includes/class-wp-plugins-list-table.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswpupgraderskinphp">trunk/src/wp-admin/includes/class-wp-upgrader-skin.php</a></li>
<li><a href="#trunksrcwpadminincludesclasswpupgraderphp">trunk/src/wp-admin/includes/class-wp-upgrader.php</a></li>
<li><a href="#trunksrcwpadminincludesplugininstallphp">trunk/src/wp-admin/includes/plugin-install.php</a></li>
<li><a href="#trunksrcwpadminincludesthemephp">trunk/src/wp-admin/includes/theme.php</a></li>
<li><a href="#trunksrcwpadminincludesupdatephp">trunk/src/wp-admin/includes/update.php</a></li>
<li><a href="#trunksrcwpadminjscommonjs">trunk/src/wp-admin/js/common.js</a></li>
<li><a href="#trunksrcwpadminjsthemejs">trunk/src/wp-admin/js/theme.js</a></li>
<li><a href="#trunksrcwpadminjsupdatesjs">trunk/src/wp-admin/js/updates.js</a></li>
<li><a href="#trunksrcwpadminnetworkthemesphp">trunk/src/wp-admin/network/themes.php</a></li>
<li><a href="#trunksrcwpadminplugininstallphp">trunk/src/wp-admin/plugin-install.php</a></li>
<li><a href="#trunksrcwpadminpluginsphp">trunk/src/wp-admin/plugins.php</a></li>
<li><a href="#trunksrcwpadminthemeinstallphp">trunk/src/wp-admin/theme-install.php</a></li>
<li><a href="#trunksrcwpadminthemesphp">trunk/src/wp-admin/themes.php</a></li>
<li><a href="#trunksrcwpcontentthemestwentytenlanguagestwentytenpot">trunk/src/wp-content/themes/twentyten/languages/twentyten.pot</a></li>
<li><a href="#trunksrcwpincludesjswputiljs">trunk/src/wp-includes/js/wp-util.js</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunktestsqunitindexhtml">trunk/tests/qunit/index.html</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsqunitfixturesupdatesjs">trunk/tests/qunit/fixtures/updates.js</a></li>
<li><a href="#trunktestsqunitwpadminjsupdatesjs">trunk/tests/qunit/wp-admin/js/updates.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminadminajaxphp"></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/admin-ajax.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/admin-ajax.php 2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/admin-ajax.php   2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -62,7 +62,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">        'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
</span><span class="cx" style="display: block; padding: 0 10px">        'save-user-color-scheme', 'update-widget', 'query-themes', 'parse-embed', 'set-attachment-thumbnail',
</span><span class="cx" style="display: block; padding: 0 10px">        'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'press-this-save-post',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username', 'delete-plugin',
+       'search-plugins', 'search-install-plugins', 'activate-plugin', 'update-theme', 'delete-theme',
+       'install-theme',
</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"> // Deprecated
</span></span></pre></div>
<a id="trunksrcwpadmincsscommoncss"></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/css/common.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/common.css 2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/css/common.css   2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1397,6 +1397,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        background-color: #e5f5fa;
</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">+.update-message p:before,
+.updating-message p:before,
+.updated-message p:before,
+.import-php .updating-message:before,
+.button.updating-message:before,
+.button.updated-message:before,
+.button.installed:before,
+.button.installing:before {
+       display: inline-block;
+       font: normal 20px/1 'dashicons';
+       -webkit-font-smoothing: antialiased;
+       -moz-osx-font-smoothing: grayscale;
+       vertical-align: top;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> .wrap .notice,
</span><span class="cx" style="display: block; padding: 0 10px"> .wrap div.updated,
</span><span class="cx" style="display: block; padding: 0 10px"> .wrap div.error,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1405,6 +1420,45 @@
</span><span class="cx" style="display: block; padding: 0 10px">        margin: 5px 0 15px;
</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">+/* Update icon. */
+.update-message p:before,
+.updating-message p:before,
+.import-php .updating-message:before,
+.button.updating-message:before,
+.button.installing:before {
+       color: #f56e28;
+       content: "\f463";
+}
+
+/* Spins the update icon. */
+.updating-message p:before,
+.import-php .updating-message:before,
+.button.updating-message:before,
+.button.installing:before {
+       -webkit-animation: rotation 2s infinite linear;
+       animation: rotation 2s infinite linear;
+}
+
+/* Updated icon (check mark). */
+.updated-message p:before,
+.installed p:before,
+.button.updated-message:before {
+       color: #79ba49;
+       content: '\f147';
+}
+
+/* Error icon. */
+.update-message.notice-error p:before {
+        color: #dc3232;
+        content: "\f534";
+}
+
+.wrap .notice p:before,
+.import-php .updating-message:before {
+       margin-right: 6px;
+       vertical-align: bottom;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> #update-nag,
</span><span class="cx" style="display: block; padding: 0 10px"> .update-nag {
</span><span class="cx" style="display: block; padding: 0 10px">        display: inline-block;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1419,10 +1473,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        box-shadow: 0 1px 1px 0 rgba(0,0,0,0.1);
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.update-message {
-       color: #000;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> ul#dismissed-updates {
</span><span class="cx" style="display: block; padding: 0 10px">        display: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1454,6 +1504,50 @@
</span><span class="cx" style="display: block; padding: 0 10px">        margin-left: 2em;
</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">+.button.updating-message:before,
+.button.updated-message:before,
+.button.installed:before,
+.button.installing:before {
+       margin: 3px 5px 0 -2px;
+}
+
+.button-primary.updating-message:before {
+       color: #fff;
+}
+
+.button-primary.updated-message:before {
+       color: #66c6e4;
+}
+
+.button.updated-message,
+.notice .button-link {
+       -webkit-transition-property: border, background, color;
+       transition-property: border, background, color;
+       -webkit-transition-duration: .05s;
+       transition-duration: .05s;
+       -webkit-transition-timing-function: ease-in-out;
+       transition-timing-function: ease-in-out;
+}
+
+.notice .button-link {
+       color: #0073aa;
+}
+
+.notice .button-link:hover,
+.notice .button-link:active {
+       color: #00a0d2;
+}
+
+@media aural {
+       .wrap .notice p:before,
+       .button.installing:before,
+       .button.installed:before,
+       .update-message p:before {
+               speak: none;
+       }
+}
+
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /* @todo: this does not need its own section anymore */
</span><span class="cx" style="display: block; padding: 0 10px"> /*------------------------------------------------------------------------------
</span><span class="cx" style="display: block; padding: 0 10px">   6.0 - Admin Header
</span></span></pre></div>
<a id="trunksrcwpadmincssformscss"></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/css/forms.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/forms.css  2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/css/forms.css    2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1044,7 +1044,44 @@
</span><span class="cx" style="display: block; padding: 0 10px">        display: inline;
</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">+.request-filesystem-credentials-dialog .ftp-username,
+.request-filesystem-credentials-dialog .ftp-password {
+       float: none;
+       width: auto;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.request-filesystem-credentials-dialog .ftp-username {
+       margin-bottom: 1em;
+}
+
+.request-filesystem-credentials-dialog .ftp-password {
+       margin: 0;
+}
+
+.request-filesystem-credentials-dialog .ftp-password em {
+       color: #888;
+}
+
+.request-filesystem-credentials-dialog label {
+       display: block;
+       line-height: 1.5;
+       margin-bottom: 1em;
+}
+
+.request-filesystem-credentials-form legend {
+       padding-bottom: 0;
+}
+
+.request-filesystem-credentials-form #ssh-keys legend {
+       font-size: 1.3em;
+}
+
+.request-filesystem-credentials-form .notice {
+       margin: 0 0 20px 0;
+       clear: both;
+}
+
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /* =Media Queries
</span><span class="cx" style="display: block; padding: 0 10px"> -------------------------------------------------------------- */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadmincsslisttablescss"></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/css/list-tables.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/list-tables.css    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/css/list-tables.css      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1271,10 +1271,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        border-bottom: 0;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugin-update-tr td {
-       border-top: 0;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> .plugins .inactive td,
</span><span class="cx" style="display: block; padding: 0 10px"> .plugins .inactive th,
</span><span class="cx" style="display: block; padding: 0 10px"> .plugins .active td,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1309,22 +1305,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        box-shadow: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugins .active.update td,
-.plugins .active.update th,
-tr.active.update + tr.plugin-update-tr .plugin-update {
-       background-color: #fef7f1;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> .plugins .active th.check-column,
</span><span class="cx" style="display: block; padding: 0 10px"> .plugin-update-tr.active td {
</span><span class="cx" style="display: block; padding: 0 10px">        border-left: 4px solid #00a0d2;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugins .active.update th.check-column,
-.plugins .active.update + .plugin-update-tr .plugin-update {
-       border-left: 4px solid #d54e21;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> #wpbody-content .plugins .plugin-title,
</span><span class="cx" style="display: block; padding: 0 10px"> #wpbody-content .plugins .theme-title {
</span><span class="cx" style="display: block; padding: 0 10px">        padding-right: 12px;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1358,42 +1343,33 @@
</span><span class="cx" style="display: block; padding: 0 10px">        border-top-width: 1px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugin-update-tr .update-message {
-       font-size: 13px;
-       font-weight: normal;
-       margin: 0 10px 8px 31px;
-       padding: 6px 12px 8px 40px;
-       background-color: #f7f7f7;
-       background-color: rgba(0,0,0,0.03);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.plugins .plugin-update-tr .plugin-update {
+       -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
+       box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
+       overflow: hidden; /* clearfix */
+       padding: 0;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugin-update-tr .update-message:before,
-.plugin-card .update-now:before,
-.plugin-card .install-now:before {
-       color: #d54e21;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.plugins .plugin-update-tr .notice {
+       margin: 5px 20px 15px 40px;
+}
+
+.plugins .notice p {
+       margin: 0.5em 0;
+}
+
+.plugin-card .update-now:before {
+       color: #f56e28;
+       content: "\f463";
</ins><span class="cx" style="display: block; padding: 0 10px">         display: inline-block;
</span><span class="cx" style="display: block; padding: 0 10px">        font: normal 20px/1 dashicons;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        margin: 3px 5px 0 -2px;
</ins><span class="cx" style="display: block; padding: 0 10px">         speak: none;
</span><span class="cx" style="display: block; padding: 0 10px">        -webkit-font-smoothing: antialiased;
</span><span class="cx" style="display: block; padding: 0 10px">        -moz-osx-font-smoothing: grayscale;
</span><span class="cx" style="display: block; padding: 0 10px">        vertical-align: top;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugin-update-tr .update-message:before,
-.plugin-card .update-now:before {
-       content: "\f463";
-}
-
-.plugin-update-tr .update-message:before {
-       margin: 0 10px 0 -30px;
-}
-
-.plugin-card .update-now:before,
-.plugin-card .install-now:before {
-       margin: 3px 5px 0 -2px;
-}
-
-.plugin-update-tr .updating-message:before,
</del><span class="cx" style="display: block; padding: 0 10px"> .plugin-card .updating-message:before {
</span><span class="cx" style="display: block; padding: 0 10px">        content: "\f463";
</span><span class="cx" style="display: block; padding: 0 10px">        -webkit-animation: rotation 2s infinite linear;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1422,28 +1398,11 @@
</span><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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.plugin-update-tr .updated-message:before,
</del><span class="cx" style="display: block; padding: 0 10px"> .plugin-card .updated-message:before {
</span><span class="cx" style="display: block; padding: 0 10px">        color: #79ba49;
</span><span class="cx" style="display: block; padding: 0 10px">        content: "\f147";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.wp-list-table.plugins tbody tr.plugin-update-tr td.plugin-update {
-       overflow: hidden; /* clearfix */
-       padding: 0;
-       -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
-       box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
-}
-
-/* update notices for active plugins */
-tr.active + tr.plugin-update-tr .plugin-update {
-       background-color: #f7fcfe;
-}
-
-tr.active + tr.plugin-update-tr:not(.updated) .plugin-update .update-message {
-       background-color: #fcf3ef;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> .plugin-install-php h2 {
</span><span class="cx" style="display: block; padding: 0 10px">        clear: both;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2140,6 +2099,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                margin-left: 0;
</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">+        .plugins .active.update + .plugin-update-tr:before {
+               background-color: #f7fcfe;
+               border-left: 4px solid #00a0d2;
+       }
+
+       .plugins .plugin-update-tr .update-message {
+               margin-left: 0;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         .wp-list-table.plugins .plugin-title strong,
</span><span class="cx" style="display: block; padding: 0 10px">        .wp-list-table.plugins .theme-title strong {
</span><span class="cx" style="display: block; padding: 0 10px">                font-size: 1.4em;
</span></span></pre></div>
<a id="trunksrcwpadmincssthemescss"></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/css/themes.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/themes.css 2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/css/themes.css   2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -11,15 +11,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        clear: both;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.themes-php .wrap h1 {
-       float: left;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.themes-php:not(.network-admin) .wrap h1 {
</ins><span class="cx" style="display: block; padding: 0 10px">         margin-bottom: 15px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.network-admin.themes-php .wrap h1 {
-       margin-bottom: 0;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> .themes-php .wrap h1 .button {
</span><span class="cx" style="display: block; padding: 0 10px">        margin-left: 20px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -37,11 +32,13 @@
</span><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"> /* Position admin messages */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.themes-php div.updated,
-.themes-php div.error,
-.themes-php div.notice {
-       margin: 0 0 20px 0;
-       clear: both;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.theme .notice,
+.theme .notice.is-dismissible {
+       left: 0;
+       margin: 0;
+       position: absolute;
+       right: 0;
+       top: 0;
</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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -207,43 +204,6 @@
</span><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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Displays a theme update notice
- * when an update is available.
- */
-.theme-browser .theme .theme-update,
-.theme-browser .theme .theme-installed {
-       background: #d54e21;
-       background: rgba(213, 78, 33, 0.95);
-       color: #fff;
-       display: block;
-       font-size: 13px;
-       font-weight: 400;
-       height: 48px;
-       line-height: 48px;
-       padding: 0 10px;
-       position: absolute;
-       top: 0;
-       right: 0;
-       left: 0;
-       border-bottom: 1px solid rgba(0,0,0,0.25);
-       overflow: hidden;
-}
-
-.theme-browser .theme .theme-update:before,
-.theme-browser .theme .theme-installed:before {
-       content: "\f463";
-       display: inline-block;
-       font: normal 20px/1 dashicons;
-       margin: 0 6px 0 0;
-       opacity: 0.8;
-       position: relative;
-       top: 5px;
-       speak: none;
-       -webkit-font-smoothing: antialiased;
-}
-
-
-/**
</del><span class="cx" style="display: block; padding: 0 10px">  * The currently active theme
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> .theme-browser .theme.active .theme-name {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -951,7 +911,6 @@
</span><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"> @media only screen and (max-width: 650px) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        .theme-overlay .theme-update,
</del><span class="cx" style="display: block; padding: 0 10px">         .theme-overlay .theme-description {
</span><span class="cx" style="display: block; padding: 0 10px">                margin-left: 0;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1041,11 +1000,18 @@
</span><span class="cx" style="display: block; padding: 0 10px"> .theme-browser .theme .theme-installed {
</span><span class="cx" style="display: block; padding: 0 10px">        background: #0073aa;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.theme-browser .theme .theme-installed:before {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.theme-browser .theme .notice-success p:before {
+       color: #79ba49;
</ins><span class="cx" style="display: block; padding: 0 10px">         content: "\f147";
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        display: inline-block;
+       font: normal 20px/1 'dashicons';
+       -webkit-font-smoothing: antialiased;
+       -moz-osx-font-smoothing: grayscale;
+       vertical-align: top;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.theme-browser .theme.is-installed .theme-actions .button-primary {
-       display: none !important;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+.theme-install.updated-message:before {
+       content: '';
</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"> .theme-install-php .wp-filter {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1394,6 +1360,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        pointer-events: none;
</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">+.theme-install-overlay .close-full-overlay,
+.theme-install-overlay .previous-theme,
+.theme-install-overlay .next-theme {
+       border-left: 0;
+       border-top: 0;
+       border-bottom: 0;
+}
+
+.theme-install-overlay .close-full-overlay:before,
+.theme-install-overlay .previous-theme:before,
+.theme-install-overlay .next-theme:before {
+       top: 2px;
+       left: 0;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /* Collapse Button */
</span><span class="cx" style="display: block; padding: 0 10px"> .wp-core-ui .wp-full-overlay .collapse-sidebar {
</span><span class="cx" style="display: block; padding: 0 10px">        position: fixed;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1708,7 +1689,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        max-width: 100%;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.theme-install-overlay .wp-full-overlay-header .theme-install {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.theme-install-overlay .wp-full-overlay-header .button {
</ins><span class="cx" style="display: block; padding: 0 10px">         float: right;
</span><span class="cx" style="display: block; padding: 0 10px">        margin: 8px 10px 0 0;
</span><span class="cx" style="display: block; padding: 0 10px">        /* For when .theme-install is a span rather than a.button-primary (already installed theme) */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1803,3 +1784,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                line-height: normal;
</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">+
+@media aural {
+       .theme .notice:before,
+       .theme-info .updating-message:before,
+       .theme-info .updated-message:before,
+       .theme-install.updating-message:before {
+               speak: none;
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpadminimportphp"></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/import.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/import.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/import.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -46,6 +46,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> add_thickbox();
</span><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'plugin-install' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_enqueue_script( 'updates' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once( ABSPATH . 'wp-admin/admin-header.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> $parent_file = 'tools.php';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -131,5 +132,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_print_request_filesystem_credentials_modal();
+wp_print_admin_notice_templates();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> include( ABSPATH . 'wp-admin/admin-footer.php' );
</span></span></pre></div>
<a id="trunksrcwpadminincludesajaxactionsphp"></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/ajax-actions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/ajax-actions.php      2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/ajax-actions.php        2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2861,6 +2861,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
</span><span class="cx" style="display: block; padding: 0 10px">                ), $update_php );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( current_user_can( 'switch_themes' ) ) {
+                       if ( is_multisite() ) {
+                               $theme->activate_url = add_query_arg( array(
+                                       'action'   => 'enable',
+                                       '_wpnonce' => wp_create_nonce( 'enable-theme_' . $theme->slug ),
+                                       'theme'    => $theme->slug,
+                               ), network_admin_url( 'themes.php' ) );
+                       } else {
+                               $theme->activate_url = add_query_arg( array(
+                                       'action'     => 'activate',
+                                       '_wpnonce'   => wp_create_nonce( 'switch-theme_' . $theme->slug ),
+                                       'stylesheet' => $theme->slug,
+                               ), admin_url( 'themes.php' ) );
+                       }
+               }
+
+               if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
+                       $theme->customize_url = add_query_arg( array(
+                               'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
+                       ), wp_customize_url( $theme->slug ) );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $theme->name        = wp_kses( $theme->name, $themes_allowedtags );
</span><span class="cx" style="display: block; padding: 0 10px">                $theme->author      = wp_kses( $theme->author, $themes_allowedtags );
</span><span class="cx" style="display: block; padding: 0 10px">                $theme->version     = wp_kses( $theme->version, $themes_allowedtags );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3069,98 +3091,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        wp_send_json_success( array( 'message' => $message ) );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * AJAX handler for updating a plugin.
- *
- * @since 4.2.0
- *
- * @see Plugin_Upgrader
- */
-function wp_ajax_update_plugin() {
-       global $wp_filesystem;
-
-       $plugin = urldecode( $_POST['plugin'] );
-
-       $status = array(
-               'update'     => 'plugin',
-               'plugin'     => $plugin,
-               'slug'       => sanitize_key( $_POST['slug'] ),
-               'oldVersion' => '',
-               'newVersion' => '',
-       );
-
-       $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
-       if ( $plugin_data['Version'] ) {
-               $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
-       }
-
-       if ( ! current_user_can( 'update_plugins' ) ) {
-               $status['error'] = __( 'You do not have sufficient permissions to update plugins for this site.' );
-               wp_send_json_error( $status );
-       }
-
-       check_ajax_referer( 'updates' );
-
-       include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
-
-       wp_update_plugins();
-
-       $skin = new Automatic_Upgrader_Skin();
-       $upgrader = new Plugin_Upgrader( $skin );
-       $result = $upgrader->bulk_upgrade( array( $plugin ) );
-
-       if ( is_array( $result ) && empty( $result[$plugin] ) && is_wp_error( $skin->result ) ) {
-               $result = $skin->result;
-       }
-
-       if ( is_array( $result ) && !empty( $result[ $plugin ] ) ) {
-               $plugin_update_data = current( $result );
-
-               /*
-                * If the `update_plugins` site transient is empty (e.g. when you update
-                * two plugins in quick succession before the transient repopulates),
-                * this may be the return.
-                *
-                * Preferably something can be done to ensure `update_plugins` isn't empty.
-                * For now, surface some sort of error here.
-                */
-               if ( $plugin_update_data === true ) {
-                       $status['error'] = __( 'Plugin update failed.' );
-                       wp_send_json_error( $status );
-               }
-
-               $plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] );
-               $plugin_data = reset( $plugin_data );
-
-               if ( $plugin_data['Version'] ) {
-                       $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
-               }
-
-               wp_send_json_success( $status );
-       } else if ( is_wp_error( $result ) ) {
-               $status['error'] = $result->get_error_message();
-               wp_send_json_error( $status );
-
-       } else if ( is_bool( $result ) && ! $result ) {
-               $status['errorCode'] = 'unable_to_connect_to_filesystem';
-               $status['error'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
-
-               // Pass through the error from WP_Filesystem if one was raised
-               if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
-                       $status['error'] = $wp_filesystem->errors->get_error_message();
-               }
-
-               wp_send_json_error( $status );
-
-       } else {
-               // An unhandled error occured
-               $status['error'] = __( 'Plugin update failed.' );
-               wp_send_json_error( $status );
-       }
-}
-
-/**
</del><span class="cx" style="display: block; padding: 0 10px">  * AJAX handler for saving a post from Press This.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.2.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3333,3 +3264,571 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * AJAX handler for installing a theme.
+ *
+ * @since 4.6.0
+ */
+function wp_ajax_install_theme() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['slug'] ) ) {
+               wp_send_json_error( array(
+                       'slug'         => '',
+                       'errorCode'    => 'no_theme_specified',
+                       'errorMessage' => __( 'No theme specified.' ),
+               ) );
+       }
+
+       $slug = sanitize_key( wp_unslash( $_POST['slug'] ) );
+
+       $status = array(
+               'install' => 'theme',
+               'slug'    => $slug,
+       );
+
+       if ( ! current_user_can( 'install_themes' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to install themes on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
+       include_once( ABSPATH . 'wp-admin/includes/theme.php' );
+
+       $api = themes_api( 'theme_information', array(
+               'slug'   => $slug,
+               'fields' => array( 'sections' => false ),
+       ) );
+
+       if ( is_wp_error( $api ) ) {
+               $status['errorMessage'] = $api->get_error_message();
+               wp_send_json_error( $status );
+       }
+
+       $upgrader = new Theme_Upgrader( new Automatic_Upgrader_Skin() );
+       $result   = $upgrader->install( $api->download_link );
+
+       if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+               $status['debug'] = $upgrader->skin->get_upgrade_messages();
+       }
+
+       if ( is_wp_error( $result ) ) {
+               $status['errorMessage'] = $result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( is_null( $result ) ) {
+               global $wp_filesystem;
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       if ( current_user_can( 'switch_themes' ) ) {
+               if ( is_multisite() ) {
+                       $status['activateUrl'] = add_query_arg( array(
+                               'action'   => 'enable',
+                               '_wpnonce' => wp_create_nonce( 'enable-theme_' . $slug ),
+                               'theme'    => $slug,
+                       ), network_admin_url( 'themes.php' ) );
+               } else {
+                       $status['activateUrl'] = add_query_arg( array(
+                               'action'     => 'activate',
+                               '_wpnonce'   => wp_create_nonce( 'switch-theme_' . $slug ),
+                               'stylesheet' => $slug,
+                       ), admin_url( 'themes.php' ) );
+               }
+       }
+
+       if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
+               $status['customizeUrl'] = add_query_arg( array(
+                       'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
+               ), wp_customize_url( $slug ) );
+       }
+
+       /*
+        * See WP_Theme_Install_List_Table::_get_theme_status() if we wanted to check
+        * on post-install status.
+        */
+       wp_send_json_success( $status );
+}
+
+/**
+ * AJAX handler for updating a theme.
+ *
+ * @since 4.6.0
+ *
+ * @see Theme_Upgrader
+ */
+function wp_ajax_update_theme() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['slug'] ) ) {
+               wp_send_json_error( array(
+                       'slug'         => '',
+                       'errorCode'    => 'no_theme_specified',
+                       'errorMessage' => __( 'No theme specified.' ),
+               ) );
+       }
+
+       $stylesheet = sanitize_key( wp_unslash( $_POST['slug'] ) );
+       $status     = array(
+               'update'     => 'theme',
+               'slug'       => $stylesheet,
+               'newVersion' => '',
+       );
+
+       if ( ! current_user_can( 'update_themes' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to update themes on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
+
+       $current = get_site_transient( 'update_themes' );
+       if ( empty( $current ) ) {
+               wp_update_themes();
+       }
+
+       $upgrader = new Theme_Upgrader( new Automatic_Upgrader_Skin() );
+       $result   = $upgrader->bulk_upgrade( array( $stylesheet ) );
+
+       if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+               $status['debug'] = $upgrader->skin->get_upgrade_messages();
+       }
+
+       if ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) {
+
+               // Theme is already at the latest version.
+               if ( true === $result[ $stylesheet ] ) {
+                       $status['errorMessage'] = $upgrader->strings['up_to_date'];
+                       wp_send_json_error( $status );
+               }
+
+               $theme = wp_get_theme( $stylesheet );
+               if ( $theme->get( 'Version' ) ) {
+                       $status['newVersion'] = $theme->get( 'Version' );
+               }
+
+               wp_send_json_success( $status );
+       } elseif ( is_wp_error( $upgrader->skin->result ) ) {
+               $status['errorCode']    = $upgrader->skin->result->get_error_code();
+               $status['errorMessage'] = $upgrader->skin->result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( false === $result ) {
+               global $wp_filesystem;
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       // An unhandled error occurred.
+       $status['errorMessage'] = __( 'Update failed.' );
+       wp_send_json_error( $status );
+}
+
+/**
+ * AJAX handler for deleting a theme.
+ *
+ * @since 4.6.0
+ */
+function wp_ajax_delete_theme() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['slug'] ) ) {
+               wp_send_json_error( array(
+                       'slug'         => '',
+                       'errorCode'    => 'no_theme_specified',
+                       'errorMessage' => __( 'No theme specified.' ),
+               ) );
+       }
+
+       $stylesheet = sanitize_key( wp_unslash( $_POST['slug'] ) );
+       $status     = array(
+               'delete' => 'theme',
+               'slug'   => $stylesheet,
+       );
+
+       if ( ! current_user_can( 'delete_themes' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to delete themes on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       if ( ! wp_get_theme( $stylesheet )->exists() ) {
+               $status['errorMessage'] = __( 'The requested theme does not exist.' );
+               wp_send_json_error( $status );
+       }
+
+       // Check filesystem credentials. `delete_plugins()` will bail otherwise.
+       ob_start();
+       $url = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet );
+       if ( false === ( $credentials = request_filesystem_credentials( $url ) ) || ! WP_Filesystem( $credentials ) ) {
+               global $wp_filesystem;
+               ob_end_clean();
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       include_once( ABSPATH . 'wp-admin/includes/theme.php' );
+
+       $result = delete_theme( $stylesheet );
+
+       if ( is_wp_error( $result ) ) {
+               $status['errorMessage'] = $result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( false === $result ) {
+               $status['errorMessage'] = __( 'Theme could not be deleted.' );
+               wp_send_json_error( $status );
+       }
+
+       wp_send_json_success( $status );
+}
+
+/**
+ * AJAX handler for installing a plugin.
+ *
+ * @since 4.6.0
+ */
+function wp_ajax_install_plugin() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['slug'] ) ) {
+               wp_send_json_error( array(
+                       'slug'         => '',
+                       'errorCode'    => 'no_plugin_specified',
+                       'errorMessage' => __( 'No plugin specified.' ),
+               ) );
+       }
+
+       $status = array(
+               'install' => 'plugin',
+               'slug'    => sanitize_key( wp_unslash( $_POST['slug'] ) ),
+       );
+
+       if ( ! current_user_can( 'install_plugins' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to install plugins on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
+       include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
+
+       $api = plugins_api( 'plugin_information', array(
+               'slug'   => sanitize_key( wp_unslash( $_POST['slug'] ) ),
+               'fields' => array(
+                       'sections' => false,
+               ),
+       ) );
+
+       if ( is_wp_error( $api ) ) {
+               $status['errorMessage'] = $api->get_error_message();
+               wp_send_json_error( $status );
+       }
+
+       $status['pluginName'] = $api->name;
+
+       $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
+       $result   = $upgrader->install( $api->download_link );
+
+       if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+               $status['debug'] = $upgrader->skin->get_upgrade_messages();
+       }
+
+       if ( is_wp_error( $result ) ) {
+               $status['errorMessage'] = $result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( is_null( $result ) ) {
+               global $wp_filesystem;
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       $install_status = install_plugin_install_status( $api );
+
+       if ( current_user_can( 'activate_plugins' ) && is_plugin_inactive( $install_status['file'] ) ) {
+               $status['activateUrl'] = add_query_arg( array(
+                       '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $install_status['file'] ),
+                       'action'   => 'activate',
+                       'plugin'   => $install_status['file'],
+               ), network_admin_url( 'plugins.php' ) );
+       }
+
+       if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) {
+               $status['activateUrl'] = add_query_arg( array( 'networkwide' => 1 ), $status['activateUrl'] );
+       }
+
+       wp_send_json_success( $status );
+}
+
+/**
+ * AJAX handler for updating a plugin.
+ *
+ * @since 4.2.0
+ *
+ * @see Plugin_Upgrader
+ */
+function wp_ajax_update_plugin() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['plugin'] ) || empty( $_POST['slug'] ) ) {
+               wp_send_json_error( array(
+                       'slug'         => '',
+                       'errorCode'    => 'no_plugin_specified',
+                       'errorMessage' => __( 'No plugin specified.' ),
+               ) );
+       }
+
+       $plugin      = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) );
+       $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
+
+       $status = array(
+               'update'     => 'plugin',
+               'plugin'     => $plugin,
+               'slug'       => sanitize_key( wp_unslash( $_POST['slug'] ) ),
+               'pluginName' => $plugin_data['Name'],
+               'oldVersion' => '',
+               'newVersion' => '',
+       );
+
+       if ( $plugin_data['Version'] ) {
+               /* translators: %s: Plugin version */
+               $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
+       }
+
+       if ( ! current_user_can( 'update_plugins' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to update plugins for this site.' );
+               wp_send_json_error( $status );
+       }
+
+       include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+       wp_update_plugins();
+
+       $skin     = new Automatic_Upgrader_Skin();
+       $upgrader = new Plugin_Upgrader( $skin );
+       $result   = $upgrader->bulk_upgrade( array( $plugin ) );
+
+       if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+               $status['debug'] = $upgrader->skin->get_upgrade_messages();
+       }
+
+       if ( is_array( $result ) && empty( $result[ $plugin ] ) && is_wp_error( $skin->result ) ) {
+               $result = $skin->result;
+       }
+
+       if ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) {
+               $plugin_update_data = current( $result );
+
+               /*
+                * If the `update_plugins` site transient is empty (e.g. when you update
+                * two plugins in quick succession before the transient repopulates),
+                * this may be the return.
+                *
+                * Preferably something can be done to ensure `update_plugins` isn't empty.
+                * For now, surface some sort of error here.
+                */
+               if ( true === $plugin_update_data ) {
+                       $status['errorMessage'] = __( 'Plugin update failed.' );
+                       wp_send_json_error( $status );
+               }
+
+               $plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] );
+               $plugin_data = reset( $plugin_data );
+
+               if ( $plugin_data['Version'] ) {
+                       /* translators: %s: Plugin version */
+                       $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
+               }
+               wp_send_json_success( $status );
+       } elseif ( is_wp_error( $result ) ) {
+               $status['errorMessage'] = $result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( false === $result ) {
+               global $wp_filesystem;
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       // An unhandled error occurred.
+       $status['errorMessage'] = __( 'Plugin update failed.' );
+       wp_send_json_error( $status );
+}
+
+/**
+ * AJAX handler for deleting a plugin.
+ *
+ * @since 4.6.0
+ */
+function wp_ajax_delete_plugin() {
+       check_ajax_referer( 'updates' );
+
+       if ( empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) {
+               wp_send_json_error( array( 'errorCode' => 'no_plugin_specified' ) );
+       }
+
+       $plugin      = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) );
+       $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
+
+       $status = array(
+               'delete'     => 'plugin',
+               'slug'       => sanitize_key( wp_unslash( $_POST['slug'] ) ),
+               'plugin'     => $plugin,
+               'pluginName' => $plugin_data['Name'],
+       );
+
+       if ( ! current_user_can( 'delete_plugins' ) ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to delete plugins for this site.' );
+               wp_send_json_error( $status );
+       }
+
+       if ( is_plugin_active( $plugin ) ) {
+               $status['errorMessage'] = __( 'You cannot delete a plugin while it is active on the main site.' );
+               wp_send_json_error( $status );
+       }
+
+       // Check filesystem credentials. `delete_plugins()` will bail otherwise.
+       ob_start();
+       $url = wp_nonce_url( 'plugins.php?action=delete-selected&verify-delete=1&checked[]=' . $plugin, 'bulk-plugins' );
+       if ( false === ( $credentials = request_filesystem_credentials( $url ) ) || ! WP_Filesystem( $credentials ) ) {
+               global $wp_filesystem;
+               ob_end_clean();
+
+               $status['errorCode']    = 'unable_to_connect_to_filesystem';
+               $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
+
+               // Pass through the error from WP_Filesystem if one was raised.
+               if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+                       $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
+               }
+
+               wp_send_json_error( $status );
+       }
+
+       $result = delete_plugins( array( $plugin ) );
+
+       if ( is_wp_error( $result ) ) {
+               $status['errorMessage'] = $result->get_error_message();
+               wp_send_json_error( $status );
+       } elseif ( false === $result ) {
+               $status['errorMessage'] = __( 'Plugin could not be deleted.' );
+               wp_send_json_error( $status );
+       }
+
+       wp_send_json_success( $status );
+}
+
+/**
+ * AJAX handler for searching plugins.
+ *
+ * @since 4.6.0
+ *
+ * @global WP_List_Table $wp_list_table Current list table instance.
+ * @global string        $hook_suffix   Current admin page.
+ * @global string        $s             Search term.
+ */
+function wp_ajax_search_plugins() {
+       check_ajax_referer( 'updates' );
+
+       global $wp_list_table, $hook_suffix, $s;
+       $hook_suffix = 'plugins.php';
+
+       /** @var WP_Plugins_List_Table $wp_list_table */
+       $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
+       $status        = array();
+
+       if ( ! $wp_list_table->ajax_user_can() ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to manage plugins on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       // Set the correct requester, so pagination works.
+       $_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array(
+               '_ajax_nonce' => null,
+               'action'      => null,
+       ) ), network_admin_url( 'plugins.php', 'relative' ) );
+
+       $s = sanitize_text_field( $_POST['s'] );
+
+       $wp_list_table->prepare_items();
+
+       ob_start();
+       $wp_list_table->display();
+       $status['items'] = ob_get_clean();
+
+       wp_send_json_success( $status );
+}
+
+/**
+ * AJAX handler for searching plugins to install.
+ *
+ * @since 4.6.0
+ *
+ * @global WP_List_Table $wp_list_table Current list table instance.
+ * @global string        $hook_suffix   Current admin page.
+ */
+function wp_ajax_search_install_plugins() {
+       check_ajax_referer( 'updates' );
+
+       global $wp_list_table, $hook_suffix;
+       $hook_suffix = 'plugin-install.php';
+
+       /** @var WP_Plugin_Install_List_Table $wp_list_table */
+       $wp_list_table = _get_list_table( 'WP_Plugin_Install_List_Table' );
+       $status        = array();
+
+       if ( ! $wp_list_table->ajax_user_can() ) {
+               $status['errorMessage'] = __( 'You do not have sufficient permissions to manage plugins on this site.' );
+               wp_send_json_error( $status );
+       }
+
+       // Set the correct requester, so pagination works.
+       $_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array(
+               '_ajax_nonce' => null,
+               'action'      => null,
+       ) ), network_admin_url( 'plugin-install.php', 'relative' ) );
+
+       $wp_list_table->prepare_items();
+
+       ob_start();
+       $wp_list_table->display();
+       $status['items'] = ob_get_clean();
+
+       wp_send_json_success( $status );
+}
</ins></span></pre></div>
<a id="trunksrcwpadminincludesclasswpfilesystembasephp"></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-filesystem-base.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-filesystem-base.php  2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-filesystem-base.php    2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -41,6 +41,7 @@
</span><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">         * @access public
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @var WP_Error
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public $errors = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpmsthemeslisttablephp"></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-ms-themes-list-table.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-ms-themes-list-table.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-ms-themes-list-table.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -149,6 +149,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->has_items = ! empty( $themes['all'] );
</span><span class="cx" style="display: block; padding: 0 10px">                $total_this_page = $totals[ $status ];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
+                       'totals' => $totals,
+               ) );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $orderby ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $orderby = ucfirst( $orderby );
</span><span class="cx" style="display: block; padding: 0 10px">                        $order = strtoupper( $order );
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpplugininstalllisttablephp"></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-plugin-install-list-table.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php        2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php  2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -461,18 +461,42 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        /* translators: 1: Plugin name and version. */
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $action_links[] = '<a class="install-now button" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Install %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Install Now' ) . '</a>';
</span><span class="cx" style="display: block; padding: 0 10px">                                                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                break;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                break;
</del><span class="cx" style="display: block; padding: 0 10px">                                         case 'update_available':
</span><span class="cx" style="display: block; padding: 0 10px">                                                if ( $status['url'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                                        /* translators: 1: Plugin name and version */
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $action_links[] = '<a class="update-now button aria-button-if-js" data-plugin="' . esc_attr( $status['file'] ) . '" data-slug="' . esc_attr( $plugin['slug'] ) . '" href="' . esc_url( $status['url'] ) . '" aria-label="' . esc_attr( sprintf( __( 'Update %s now' ), $name ) ) . '" data-name="' . esc_attr( $name ) . '">' . __( 'Update Now' ) . '</a>';
</span><span class="cx" style="display: block; padding: 0 10px">                                                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                break;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                break;
</del><span class="cx" style="display: block; padding: 0 10px">                                         case 'latest_installed':
</span><span class="cx" style="display: block; padding: 0 10px">                                        case 'newer_installed':
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                $action_links[] = '<span class="button button-disabled">' . _x( 'Installed', 'plugin' ) . '</span>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         if ( is_plugin_active( $status['file'] ) ) {
+                                                       $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Active', 'plugin' ) . '</button>';
+                                               } elseif ( current_user_can( 'activate_plugins' ) ) {
+                                                       $button_text  = __( 'Activate' );
+                                                       $activate_url = add_query_arg( array(
+                                                               '_wpnonce'    => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
+                                                               'action'      => 'activate',
+                                                               'plugin'      => $status['file'],
+                                                       ), network_admin_url( 'plugins.php' ) );
+
+                                                       if ( is_network_admin() ) {
+                                                               $button_text  = __( 'Network Activate' );
+                                                               $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
+                                                       }
+
+                                                       $action_links[] = sprintf(
+                                                               '<a href="%1$s" class="button activate-now button-secondary" aria-label="%2$s">%3$s</a>',
+                                                               esc_url( $activate_url ),
+                                                               /* translators: %s: Plugin name */
+                                                               esc_attr( sprintf( __( 'Activate %s' ), $plugin['name'] ) ),
+                                                               $button_text
+                                                       );
+                                               } else {
+                                                       $action_links[] = '<button type="button" class="button button-disabled" disabled="disabled">' . _x( 'Installed', 'plugin' ) . '</button>';
+                                               }
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 break;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswppluginslisttablephp"></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-plugins-list-table.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-plugins-list-table.php       2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-plugins-list-table.php 2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -246,6 +246,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $total_this_page = $totals[ $status ];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $js_plugins = array();
+               foreach ( $plugins as $key => $list ) {
+                       $js_plugins[ $key ] = array_keys( (array) $list );
+               }
+
+               wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
+                       'plugins' => $js_plugins,
+               ) );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! $orderby ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $orderby = 'Name';
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpupgraderskinphp"></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-upgrader-skin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-upgrader-skin.php    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-upgrader-skin.php      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -18,6 +18,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $upgrader;
</span><span class="cx" style="display: block; padding: 0 10px">        public $done_header = false;
</span><span class="cx" style="display: block; padding: 0 10px">        public $done_footer = false;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        *
+        * @var string|false|WP_Error
+        */
</ins><span class="cx" style="display: block; padding: 0 10px">         public $result = false;
</span><span class="cx" style="display: block; padding: 0 10px">        public $options = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpupgraderphp"></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-upgrader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-upgrader.php 2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/class-wp-upgrader.php   2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -61,7 +61,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 2.8.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @access public
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var WP_Upgrader_Skin $skin
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @var Automatic_Upgrader_Skin|WP_Upgrader_Skin $skin
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public $skin = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminincludesplugininstallphp"></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/plugin-install.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/plugin-install.php    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/plugin-install.php      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -540,41 +540,50 @@
</span><span class="cx" style="display: block; padding: 0 10px">        echo "</div>\n";
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
</ins><span class="cx" style="display: block; padding: 0 10px">         <div class="fyi">
</span><span class="cx" style="display: block; padding: 0 10px">                <ul>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php if ( ! empty( $api->version ) ) { ?>
-                       <li><strong><?php _e( 'Version:' ); ?></strong> <?php echo $api->version; ?></li>
-               <?php } if ( ! empty( $api->author ) ) { ?>
-                       <li><strong><?php _e( 'Author:' ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?></li>
-               <?php } if ( ! empty( $api->last_updated ) ) { ?>
-                       <li><strong><?php _e( 'Last Updated:' ); ?></strong>
-                               <?php printf( __( '%s ago' ), human_time_diff( strtotime( $api->last_updated ) ) ); ?>
-                       </li>
-               <?php } if ( ! empty( $api->requires ) ) { ?>
-                       <li><strong><?php _e( 'Requires WordPress Version:' ); ?></strong> <?php printf( __( '%s or higher' ), $api->requires ); ?></li>
-               <?php } if ( ! empty( $api->tested ) ) { ?>
-                       <li><strong><?php _e( 'Compatible up to:' ); ?></strong> <?php echo $api->tested; ?></li>
-               <?php } if ( ! empty( $api->active_installs ) ) { ?>
-                       <li><strong><?php _e( 'Active Installs:' ); ?></strong> <?php
-                               if ( $api->active_installs >= 1000000 ) {
-                                       _ex( '1+ Million', 'Active plugin installs' );
-                               } else {
-                                       echo number_format_i18n( $api->active_installs ) . '+';
-                               }
-                       ?></li>
-               <?php } if ( ! empty( $api->slug ) && empty( $api->external ) ) { ?>
-                       <li><a target="_blank" href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php _e( 'WordPress.org Plugin Page &#187;' ); ?></a></li>
-               <?php } if ( ! empty( $api->homepage ) ) { ?>
-                       <li><a target="_blank" href="<?php echo esc_url( $api->homepage ); ?>"><?php _e( 'Plugin Homepage &#187;' ); ?></a></li>
-               <?php } if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) { ?>
-                       <li><a target="_blank" href="<?php echo esc_url( $api->donate_link ); ?>"><?php _e( 'Donate to this plugin &#187;' ); ?></a></li>
-               <?php } ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <?php if ( ! empty( $api->version ) ) { ?>
+                               <li><strong><?php _e( 'Version:' ); ?></strong> <?php echo $api->version; ?></li>
+                       <?php } if ( ! empty( $api->author ) ) { ?>
+                               <li><strong><?php _e( 'Author:' ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?></li>
+                       <?php } if ( ! empty( $api->last_updated ) ) { ?>
+                               <li><strong><?php _e( 'Last Updated:' ); ?></strong>
+                                       <?php
+                                       /* translators: %s: Time since the last update */
+                                       printf( __( '%s ago' ), human_time_diff( strtotime( $api->last_updated ) ) );
+                                       ?>
+                               </li>
+                       <?php } if ( ! empty( $api->requires ) ) { ?>
+                               <li>
+                                       <strong><?php _e( 'Requires WordPress Version:' ); ?></strong>
+                                       <?php
+                                       /* translators: %s: WordPress version */
+                                       printf( __( '%s or higher' ), $api->requires );
+                                       ?>
+                               </li>
+                       <?php } if ( ! empty( $api->tested ) ) { ?>
+                               <li><strong><?php _e( 'Compatible up to:' ); ?></strong> <?php echo $api->tested; ?></li>
+                       <?php } if ( ! empty( $api->active_installs ) ) { ?>
+                               <li><strong><?php _e( 'Active Installs:' ); ?></strong> <?php
+                                       if ( $api->active_installs >= 1000000 ) {
+                                               _ex( '1+ Million', 'Active plugin installs' );
+                                       } else {
+                                               echo number_format_i18n( $api->active_installs ) . '+';
+                                       }
+                                       ?></li>
+                       <?php } if ( ! empty( $api->slug ) && empty( $api->external ) ) { ?>
+                               <li><a target="_blank" href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php _e( 'WordPress.org Plugin Page &#187;' ); ?></a></li>
+                       <?php } if ( ! empty( $api->homepage ) ) { ?>
+                               <li><a target="_blank" href="<?php echo esc_url( $api->homepage ); ?>"><?php _e( 'Plugin Homepage &#187;' ); ?></a></li>
+                       <?php } if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) { ?>
+                               <li><a target="_blank" href="<?php echo esc_url( $api->donate_link ); ?>"><?php _e( 'Donate to this plugin &#187;' ); ?></a></li>
+                       <?php } ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </ul>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( ! empty( $api->rating ) ) { ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <h3><?php _e( 'Average Rating' ); ?></h3>
-               <?php wp_star_rating( array( 'rating' => $api->rating, 'type' => 'percent', 'number' => $api->num_ratings ) ); ?>
-               <p aria-hidden="true" class="fyi-description"><?php printf( _n( '(based on %s rating)', '(based on %s ratings)', $api->num_ratings ), number_format_i18n( $api->num_ratings ) ); ?></p>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <h3><?php _e( 'Average Rating' ); ?></h3>
+                       <?php wp_star_rating( array( 'rating' => $api->rating, 'type' => 'percent', 'number' => $api->num_ratings ) ); ?>
+                       <p aria-hidden="true" class="fyi-description"><?php printf( _n( '(based on %s rating)', '(based on %s ratings)', $api->num_ratings ), number_format_i18n( $api->num_ratings ) ); ?></p>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <?php }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) { ?>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -591,11 +600,11 @@
</span><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">                                <div class="counter-container">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <span class="counter-label"><a href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
-                                               target="_blank" aria-label="<?php echo $aria_label; ?>"><?php printf( _n( '%d star', '%d stars', $key ), $key ); ?></a></span>
-                                       <span class="counter-back">
-                                               <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
-                                       </span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <span class="counter-label"><a href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
+                                                                              target="_blank" aria-label="<?php echo $aria_label; ?>"><?php printf( _n( '%d star', '%d stars', $key ), $key ); ?></a></span>
+                                               <span class="counter-back">
+                                                       <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
+                                               </span>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <span class="counter-count" aria-hidden="true"><?php echo number_format_i18n( $ratecount ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                </div>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -628,24 +637,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div id="section-holder" class="wrap">
</span><span class="cx" style="display: block; padding: 0 10px">        <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
-                       echo '<div class="notice notice-warning notice-alt"><p>' . __( '<strong>Warning:</strong> This plugin has <strong>not been tested</strong> with your current version of WordPress.' ) . '</p></div>';
-               } elseif ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
-                       echo '<div class="notice notice-warning notice-alt"><p>' . __( '<strong>Warning:</strong> This plugin has <strong>not been marked as compatible</strong> with your version of WordPress.' ) . '</p></div>';
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
+               echo '<div class="notice notice-warning notice-alt"><p>' . __( '<strong>Warning:</strong> This plugin has <strong>not been tested</strong> with your current version of WordPress.' ) . '</p></div>';
+       } elseif ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
+               echo '<div class="notice notice-warning notice-alt"><p>' . __( '<strong>Warning:</strong> This plugin has <strong>not been marked as compatible</strong> with your version of WordPress.' ) . '</p></div>';
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( (array) $api->sections as $section_name => $content ) {
-                       $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
-                       $content = links_add_target( $content, '_blank' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ foreach ( (array) $api->sections as $section_name => $content ) {
+               $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
+               $content = links_add_target( $content, '_blank' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $san_section = esc_attr( $section_name );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $san_section = esc_attr( $section_name );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $display = ( $section_name === $section ) ? 'block' : 'none';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $display = ( $section_name === $section ) ? 'block' : 'none';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
-                       echo $content;
-                       echo "\t</div>\n";
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
+               echo $content;
+               echo "\t</div>\n";
+       }
</ins><span class="cx" style="display: block; padding: 0 10px">         echo "</div>\n";
</span><span class="cx" style="display: block; padding: 0 10px">        echo "</div>\n";
</span><span class="cx" style="display: block; padding: 0 10px">        echo "</div>\n"; // #plugin-information-scrollable
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -655,7 +664,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                switch ( $status['status'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'install':
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( $status['url'] ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        echo '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Now' ) . '</a>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 echo '<a data-slug="' . esc_attr( $api->slug ) . '" id="plugin_install_from_iframe" class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Now' ) . '</a>';
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'update_available':
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -664,6 +673,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'newer_installed':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                /* translators: %s: Plugin version */
</ins><span class="cx" style="display: block; padding: 0 10px">                                 echo '<a class="button button-primary right disabled">' . sprintf( __( 'Newer Version (%s) Installed'), $status['version'] ) . '</a>';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'latest_installed':
</span></span></pre></div>
<a id="trunksrcwpadminincludesthemephp"></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/theme.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/theme.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/theme.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -172,7 +172,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( !is_multisite() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! current_user_can('update_themes') ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: theme name, 2: theme details URL, 3: accessibility text, 4: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" aria-label="%3$s">View version %4$s details</a>.' ) . '</strong></p>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a>.' ) . '</strong></p>',
</ins><span class="cx" style="display: block; padding: 0 10px">                                         $theme_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                        esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                        /* translators: 1: theme name, 2: version number */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -181,7 +181,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px">                        } elseif ( empty( $update['package'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: theme name, 2: theme details URL, 3: accessibility text, 4: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" aria-label="%3$s">View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ) . '</strong></p>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ) . '</strong></p>',
</ins><span class="cx" style="display: block; padding: 0 10px">                                         $theme_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                        esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                        /* translators: 1: theme name, 2: version number */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -190,7 +190,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px">                        } else {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: theme name, 2: theme details URL, 3: accessibility text, 4: version number, 5: update URL, 6: accessibility text */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox" aria-label="%3$s">View version %4$s details</a> or <a href="%5$s" aria-label="%6$s">update now</a>.' ) . '</strong></p>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a> or <a href="%5$s" aria-label="%6$s" id="update-theme" data-slug="%7$s">update now</a>.' ) . '</strong></p>',
</ins><span class="cx" style="display: block; padding: 0 10px">                                         $theme_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                        esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                        /* translators: 1: theme name, 2: version number */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -198,7 +198,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        $update['new_version'],
</span><span class="cx" style="display: block; padding: 0 10px">                                        $update_url,
</span><span class="cx" style="display: block; padding: 0 10px">                                        /* translators: %s: theme name */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) ),
+                                       $stylesheet
</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></span></pre></div>
<a id="trunksrcwpadminincludesupdatephp"></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/update.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/update.php    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/includes/update.php      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -329,33 +329,43 @@
</span><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">+ * Displays update information for a plugin.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param string $file
- * @param array  $plugin_data
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param string $file        Plugin basename.
+ * @param array  $plugin_data Plugin information.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @return false|void
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wp_plugin_update_row( $file, $plugin_data ) {
</span><span class="cx" style="display: block; padding: 0 10px">        $current = get_site_transient( 'update_plugins' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( !isset( $current->response[ $file ] ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! isset( $current->response[ $file ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 return false;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $r = $current->response[ $file ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $response = $current->response[ $file ];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $plugins_allowedtags = array('a' => array('href' => array(),'title' => array()),'abbr' => array('title' => array()),'acronym' => array('title' => array()),'code' => array(),'em' => array(),'strong' => array());
-       $plugin_name = wp_kses( $plugin_data['Name'], $plugins_allowedtags );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $plugins_allowedtags = array(
+               'a'       => array( 'href' => array(), 'title' => array() ),
+               'abbr'    => array( 'title' => array() ),
+               'acronym' => array( 'title' => array() ),
+               'code'    => array(),
+               'em'      => array(),
+               'strong'  => array(),
+       );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $details_url = self_admin_url('plugin-install.php?tab=plugin-information&plugin=' . $r->slug . '&section=changelog&TB_iframe=true&width=600&height=800');
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $plugin_name   = wp_kses( $plugin_data['Name'], $plugins_allowedtags );
+       $details_url   = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $response->slug . '&section=changelog&TB_iframe=true&width=600&height=800' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $wp_list_table = _get_list_table('WP_Plugins_List_Table');
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /** @var WP_Plugins_List_Table $wp_list_table */
+       $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( is_network_admin() || !is_multisite() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( is_network_admin() || ! is_multisite() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( is_network_admin() ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $active_class = is_plugin_active_for_network( $file ) ? ' active': '';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $active_class = is_plugin_active_for_network( $file ) ? ' active' : '';
</ins><span class="cx" style="display: block; padding: 0 10px">                 } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        $active_class = is_plugin_active( $file ) ? ' active' : '';
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                echo '<tr class="plugin-update-tr' . $active_class . '" id="' . esc_attr( $r->slug . '-update' ) . '" data-slug="' . esc_attr( $r->slug ) . '" data-plugin="' . esc_attr( $file ) . '"><td colspan="' . esc_attr( $wp_list_table->get_column_count() ) . '" class="plugin-update colspanchange"><div class="update-message">';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         echo '<tr class="plugin-update-tr' . $active_class . '" id="' . esc_attr( $response->slug . '-update' ) . '" data-slug="' . esc_attr( $response->slug ) . '" data-plugin="' . esc_attr( $file ) . '"><td colspan="' . esc_attr( $wp_list_table->get_column_count() ) . '" class="plugin-update colspanchange"><div class="update-message notice inline notice-warning notice-alt"><p>';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! current_user_can( 'update_plugins' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -363,17 +373,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $plugin_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: plugin name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $r->new_version ) ),
-                               $r->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ),
+                               $response->new_version
</ins><span class="cx" style="display: block; padding: 0 10px">                         );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                } elseif ( empty( $r->package ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         } elseif ( empty( $response->package ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         /* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number */
</span><span class="cx" style="display: block; padding: 0 10px">                        printf( __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a>. <em>Automatic update is unavailable for this plugin.</em>' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                $plugin_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: plugin name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $r->new_version ) ),
-                               $r->new_version
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ),
+                               $response->new_version
</ins><span class="cx" style="display: block; padding: 0 10px">                         );
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number, 5: update URL, 6: accessibility text */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -381,13 +391,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $plugin_name,
</span><span class="cx" style="display: block; padding: 0 10px">                                esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: plugin name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $r->new_version ) ),
-                               $r->new_version,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ),
+                               $response->new_version,
</ins><span class="cx" style="display: block; padding: 0 10px">                                 wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $file, 'upgrade-plugin_' . $file ),
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: %s: plugin name */
</span><span class="cx" style="display: block; padding: 0 10px">                                esc_attr( sprintf( __( 'Update %s now' ), $plugin_name ) )
</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">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Fires at the end of the update message container in each
</span><span class="cx" style="display: block; padding: 0 10px">                 * row of the plugins list table.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -400,32 +411,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param array $plugin_data {
</span><span class="cx" style="display: block; padding: 0 10px">                 *     An array of plugin metadata.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 *     @type string $name         The human-readable name of the plugin.
-                *     @type string $plugin_uri   Plugin URI.
-                *     @type string $version      Plugin version.
-                *     @type string $description  Plugin description.
-                *     @type string $author       Plugin author.
-                *     @type string $author_uri   Plugin author URI.
-                *     @type string $text_domain  Plugin text domain.
-                *     @type string $domain_path  Relative path to the plugin's .mo file(s).
-                *     @type bool   $network      Whether the plugin can only be activated network wide.
-                *     @type string $title        The human-readable title of the plugin.
-                *     @type string $author_name  Plugin author's name.
-                *     @type bool   $update       Whether there's an available update. Default null.
-                * }
-                * @param array $r {
-                *     An array of metadata about the available plugin update.
-                *
-                *     @type int    $id           Plugin ID.
-                *     @type string $slug         Plugin slug.
-                *     @type string $new_version  New plugin version.
-                *     @type string $url          Plugin URL.
-                *     @type string $package      Plugin update package URL.
-                * }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          *     @type string $name        The human-readable name of the plugin.
+                *     @type string $plugin_uri  Plugin URI.
+                *     @type string $version     Plugin version.
+                *     @type string $description Plugin description.
+                *     @type string $author      Plugin author.
+                *     @type string $author_uri  Plugin author URI.
+                *     @type string $text_domain Plugin text domain.
+                *     @type string $domain_path Relative path to the plugin's .mo file(s).
+                *     @type bool   $network     Whether the plugin can only be activated network wide.
+                *     @type string $title       The human-readable title of the plugin.
+                *     @type string $author_name Plugin author's name.
+                *     @type bool   $update      Whether there's an available update. Default null.
+                * }
+                * @param array $response {
+                *     An array of metadata about the available plugin update.
+                *
+                *     @type int    $id          Plugin ID.
+                *     @type string $slug        Plugin slug.
+                *     @type string $new_version New plugin version.
+                *     @type string $url         Plugin URL.
+                *     @type string $package     Plugin update package URL.
+                * }
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                do_action( "in_plugin_update_message-{$file}", $plugin_data, $r );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         do_action( "in_plugin_update_message-{$file}", $plugin_data, $response );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                echo '</div></td></tr>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         echo '</p></div></td></tr>';
</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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -466,58 +477,65 @@
</span><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">+ * Displays update information for a theme.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param string   $theme_key
- * @param WP_Theme $theme
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param string   $theme_key Theme stylesheet.
+ * @param WP_Theme $theme     Theme object.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @return false|void
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wp_theme_update_row( $theme_key, $theme ) {
</span><span class="cx" style="display: block; padding: 0 10px">        $current = get_site_transient( 'update_themes' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( !isset( $current->response[ $theme_key ] ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       if ( ! isset( $current->response[ $theme_key ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 return false;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $r = $current->response[ $theme_key ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $response = $current->response[ $theme_key ];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $theme_name = $theme['Name'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $details_url = add_query_arg( array(
+               'TB_iframe' => 'true',
+               'width'     => 1024,
+               'height'    => 800,
+       ), $current->response[ $theme_key ]['url'] );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $details_url = add_query_arg( array( 'TB_iframe' => 'true', 'width' => 1024, 'height' => 800 ), $current->response[ $theme_key ]['url'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /** @var WP_MS_Themes_List_Table $wp_list_table */
+       $wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $wp_list_table = _get_list_table('WP_MS_Themes_List_Table');
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $active = $theme->is_allowed( 'network' ) ? ' active' : '';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $active = $theme->is_allowed( 'network' ) ? ' active': '';
-
-       echo '<tr class="plugin-update-tr' . $active . '" id="' . esc_attr( $theme->get_stylesheet() . '-update' ) . '" data-slug="' . esc_attr( $theme->get_stylesheet() ) . '"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
-       if ( ! current_user_can('update_themes') ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ echo '<tr class="plugin-update-tr' . $active . '" id="' . esc_attr( $theme->get_stylesheet() . '-update' ) . '" data-slug="' . esc_attr( $theme->get_stylesheet() ) . '"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message notice inline notice-warning notice-alt"><p>';
+       if ( ! current_user_can( 'update_themes' ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 /* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number */
</span><span class="cx" style="display: block; padding: 0 10px">                printf( __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a>.'),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $theme_name,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $theme['Name'],
</ins><span class="cx" style="display: block; padding: 0 10px">                         esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: 1: theme name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $r['new_version'] ) ),
-                       $r['new_version']
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ),
+                       $response['new_version']
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        } elseif ( empty( $r['package'] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } elseif ( empty( $response['package'] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 /* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number */
</span><span class="cx" style="display: block; padding: 0 10px">                printf( __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $theme_name,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $theme['Name'],
</ins><span class="cx" style="display: block; padding: 0 10px">                         esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: 1: theme name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $r['new_version'] ) ),
-                       $r['new_version']
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ),
+                       $response['new_version']
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">        } else {
</span><span class="cx" style="display: block; padding: 0 10px">                /* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number, 5: update URL, 6: accessibility text */
</span><span class="cx" style="display: block; padding: 0 10px">                printf( __( 'There is a new version of %1$s available. <a href="%2$s" class="thickbox open-plugin-details-modal" aria-label="%3$s">View version %4$s details</a> or <a href="%5$s" class="update-link" aria-label="%6$s">update now</a>.' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $theme_name,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $theme['Name'],
</ins><span class="cx" style="display: block; padding: 0 10px">                         esc_url( $details_url ),
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: 1: theme name, 2: version number */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $r['new_version'] ) ),
-                       $r['new_version'],
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ),
+                       $response['new_version'],
</ins><span class="cx" style="display: block; padding: 0 10px">                         wp_nonce_url( self_admin_url( 'update.php?action=upgrade-theme&theme=' ) . $theme_key, 'upgrade-theme_' . $theme_key ),
</span><span class="cx" style="display: block; padding: 0 10px">                        /* translators: %s: theme name */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 esc_attr( sprintf( __( 'Update %s now' ), $theme['Name'] ) )
</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">+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Fires at the end of the update message container in each
</span><span class="cx" style="display: block; padding: 0 10px">         * row of the themes list table.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -527,8 +545,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.1.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param WP_Theme $theme The WP_Theme object.
-        * @param array    $r {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param WP_Theme $theme    The WP_Theme object.
+        * @param array    $response {
</ins><span class="cx" style="display: block; padding: 0 10px">          *     An array of metadata about the available theme update.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         *     @type string $new_version New theme version.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -536,9 +554,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *     @type string $package     Theme update package URL.
</span><span class="cx" style="display: block; padding: 0 10px">         * }
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        do_action( "in_theme_update_message-{$theme_key}", $theme, $r );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ do_action( "in_theme_update_message-{$theme_key}", $theme, $response );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        echo '</div></td></tr>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ echo '</p></div></td></tr>';
</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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -577,3 +595,136 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        echo "<div class='update-nag'>$msg</div>";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Prints the JavaScript templates for update admin notices.
+ *
+ * Template takes one argument with four values:
+ *
+ *     param {object} data {
+ *         Arguments for admin notice.
+ *
+ *         @type string id        ID of the notice.
+ *         @type string className Class names for the notice.
+ *         @type string message   The notice's message.
+ *         @type string type      The type of update the notice is for. Either 'plugin' or 'theme'.
+ *     }
+ *
+ * @since 4.6.0
+ */
+function wp_print_admin_notice_templates() {
+       ?>
+       <script id="tmpl-wp-updates-admin-notice" type="text/html">
+               <div <# if ( data.id ) { #>id="{{ data.id }}"<# } #> class="notice {{ data.className }}"><p>{{{ data.message }}}</p></div>
+       </script>
+       <script id="tmpl-wp-bulk-updates-admin-notice" type="text/html">
+               <div id="{{ data.id }}" class="notice <# if ( data.errors ) { #>notice-error<# } else { #>notice-success<# } #>">
+                       <p>
+                               <# if ( data.successes ) { #>
+                                       <# if ( 1 === data.successes ) { #>
+                                               <# if ( 'plugin' === data.type ) { #>
+                                                       <?php
+                                                       /* translators: %s: Number of plugins */
+                                                       printf( __( '%s plugin successfully updated.' ), '{{ data.successes }}' );
+                                                       ?>
+                                               <# } else { #>
+                                                       <?php
+                                                       /* translators: %s: Number of themes */
+                                                       printf( __( '%s theme successfully updated.' ), '{{ data.successes }}' );
+                                                       ?>
+                                               <# } #>
+                                       <# } else { #>
+                                               <# if ( 'plugin' === data.type ) { #>
+                                                       <?php
+                                                       /* translators: %s: Number of plugins */
+                                                       printf( __( '%s plugins successfully updated.' ), '{{ data.successes }}' );
+                                                       ?>
+                                               <# } else { #>
+                                                       <?php
+                                                       /* translators: %s: Number of themes */
+                                                       printf( __( '%s themes successfully updated.' ), '{{ data.successes }}' );
+                                                       ?>
+                                               <# } #>
+                                       <# } #>
+                               <# } #>
+                               <# if ( data.errors ) { #>
+                                       <# if ( 1 === data.errors ) { #>
+                                               <button class="button-link">
+                                                       <?php
+                                                       /* translators: %s: Number of failures */
+                                                       printf( __( '%s failure.' ), '{{ data.errors }}' );
+                                                       ?>
+                                               </button>
+                                       <# } else { #>
+                                               <button class="button-link">
+                                                       <?php
+                                                       /* translators: %s: Number of failures */
+                                                       printf( __( '%s failures.' ), '{{ data.errors }}' );
+                                                       ?>
+                                               </button>
+                                       <# } #>
+                               <# } #>
+                       </p>
+                       <# if ( data.errors ) { #>
+                               <ul class="hidden">
+                                       <# _.each( data.errorMessages, function( errorMessage ) { #>
+                                               <li>{{ errorMessage }}</li>
+                                       <# } ); #>
+                               </ul>
+                       <# } #>
+               </div>
+       </script>
+       <?php
+}
+
+/**
+ * Prints the JavaScript templates for update and deletion rows in list tables.
+ *
+ * The update template takes one argument with four values:
+ *
+ *     param {object} data {
+ *         Arguments for the update row
+ *
+ *         @type string slug    Plugin slug.
+ *         @type string plugin  Plugin base name.
+ *         @type string colspan The number of table columns this row spans.
+ *         @type string content The row content.
+ *     }
+ *              
+ * The delete template takes one argument with four values:
+ *
+ *     param {object} data {
+ *         Arguments for the update row
+ *
+ *         @type string slug    Plugin slug.
+ *         @type string plugin  Plugin base name.
+ *         @type string name    Plugin name.
+ *         @type string colspan The number of table columns this row spans.
+ *     }
+ *
+ * @since 4.6.0
+ */
+function wp_print_update_row_templates() {
+       ?>
+       <script id="tmpl-item-update-row" type="text/template">
+               <tr class="plugin-update-tr update" id="{{ data.slug }}-update" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
+                       <td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
+                               {{{ data.content }}}
+                       </td>
+               </tr>
+       </script>
+       <script id="tmpl-item-deleted-row" type="text/template">
+               <tr class="plugin-deleted-tr inactive deleted" id="{{ data.slug }}-deleted" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
+                       <td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
+                               <?php
+                               printf(
+                                       /* translators: %s: Plugin or Theme name */
+                                       __( '%s was successfully deleted.' ),
+                                       '<strong>{{{ data.name }}}</strong>'
+                               );
+                               ?>
+                       </td>
+               </tr>
+       </script>
+       <?php
+}
</ins></span></pre></div>
<a id="trunksrcwpadminjscommonjs"></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/js/common.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/common.js   2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/js/common.js     2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -421,9 +421,7 @@
</span><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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $document.on( 'wp-plugin-update-error', function() {
-               makeNoticesDismissible();
-       });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $document.on( 'wp-updates-notice-added wp-plugin-install-error wp-plugin-update-error wp-plugin-delete-error wp-theme-install-error wp-theme-delete-error', makeNoticesDismissible );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // Init screen meta
</span><span class="cx" style="display: block; padding: 0 10px">        screenMeta.init();
</span></span></pre></div>
<a id="trunksrcwpadminjsthemejs"></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/js/theme.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/theme.js    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/js/theme.js      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -375,17 +375,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'keydown': themes.isInstall ? 'preview': 'expand',
</span><span class="cx" style="display: block; padding: 0 10px">                'touchend': themes.isInstall ? 'preview': 'expand',
</span><span class="cx" style="display: block; padding: 0 10px">                'keyup': 'addFocus',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'touchmove': 'preventExpand'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'touchmove': 'preventExpand',
+               'click .theme-install': 'installTheme',
+               'click .update-message': 'updateTheme'
</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">        touchDrag: false,
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        initialize: function() {
+               this.model.on( 'change', this.render, this );
+       },
+
</ins><span class="cx" style="display: block; padding: 0 10px">         render: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                var data = this.model.toJSON();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Render themes using the html template
</span><span class="cx" style="display: block; padding: 0 10px">                this.$el.html( this.html( data ) ).attr({
</span><span class="cx" style="display: block; padding: 0 10px">                        tabindex: 0,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'aria-describedby' : data.id + '-action ' + data.id + '-name'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'aria-describedby' : data.id + '-action ' + data.id + '-name',
+                       'data-slug': data.id
</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">                // Renders active theme styles
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -394,10 +402,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( this.model.get( 'displayAuthor' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.$el.addClass( 'display-author' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               if ( this.model.get( 'installed' ) ) {
-                       this.$el.addClass( 'is-installed' );
-               }
</del><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">        // Adds a class to the currently active theme
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -439,6 +443,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</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">+                // Prevent the modal from showing when the user clicks one of the direct action buttons.
+               if ( $( event.target ).is( '.theme-actions a, .update-message, .button-link, .notice-dismiss' ) ) {
+                       return;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Set focused theme to current element
</span><span class="cx" style="display: block; padding: 0 10px">                themes.focusedTheme = this.$el;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -461,7 +470,7 @@
</span><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">                // Allow direct link path to installing a theme.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $( event.target ).hasClass( 'button-primary' ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $( event.target ).not( '.install-theme-preview' ).parents( '.theme-actions' ).length ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return;
</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">@@ -579,6 +588,47 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( _.isUndefined( this.model.collection.at( this.model.collection.indexOf( current ) + 1 ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $themeInstaller.find( '.next-theme' ).addClass( 'disabled' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        },
+
+       installTheme: function( event ) {
+               var _this = this;
+
+               event.preventDefault();
+
+               wp.updates.maybeRequestFilesystemCredentials( event );
+
+               $( document ).on( 'wp-install-theme-success', function( event, response ) {
+                       if ( _this.model.get( 'id' ) === response.slug ) {
+                               _this.model.set( { 'installed': true } );
+                       }
+               } );
+
+               wp.updates.installTheme( {
+                       slug: $( event.target ).data( 'slug' )
+               } );
+       },
+
+       updateTheme: function( event ) {
+               var _this = this;
+               event.preventDefault();
+               this.$el.off( 'click', '.update-message' );
+
+               wp.updates.maybeRequestFilesystemCredentials( event );
+
+               $( document ).on( 'wp-theme-update-success', function( event, response ) {
+                       _this.model.off( 'change', _this.render, _this );
+                       if ( _this.model.get( 'id' ) === response.slug ) {
+                               _this.model.set( {
+                                       hasUpdate: false,
+                                       version: response.newVersion
+                               } );
+                       }
+                       _this.model.on( 'change', _this.render, _this );
+               } );
+
+               wp.updates.updateTheme( {
+                       slug: $( event.target ).parents( 'div.theme' ).first().data( 'slug' )
+               } );
</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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -593,7 +643,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'click': 'collapse',
</span><span class="cx" style="display: block; padding: 0 10px">                'click .delete-theme': 'deleteTheme',
</span><span class="cx" style="display: block; padding: 0 10px">                'click .left': 'previousTheme',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'click .right': 'nextTheme'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'click .right': 'nextTheme',
+               'click #update-theme': 'updateTheme'
</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">        // The HTML template for the theme overlay
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -713,11 +764,58 @@
</span><span class="cx" style="display: block; padding: 0 10px">                this.trigger( 'theme:collapse' );
</span><span class="cx" style="display: block; padding: 0 10px">        },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        // Confirmation dialog for deleting a theme
-       deleteTheme: function() {
-               return confirm( themes.data.settings.confirmDelete );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ updateTheme: function( event ) {
+               var _this = this;
+               event.preventDefault();
+
+               wp.updates.maybeRequestFilesystemCredentials( event );
+
+               $( document ).on( 'wp-theme-update-success', function( event, response ) {
+                       if ( _this.model.get( 'id' ) === response.slug ) {
+                               _this.model.set( {
+                                       hasUpdate: false,
+                                       version: response.newVersion
+                               } );
+                       }
+                       _this.render();
+               } );
+
+               wp.updates.updateTheme( {
+                       slug: $( event.target ).data( 'slug' )
+               } );
</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">+        deleteTheme: function( event ) {
+               var _this = this,
+                   _collection = _this.model.collection,
+                   _themes = themes;
+               event.preventDefault();
+
+               // Confirmation dialog for deleting a theme.
+               if ( ! window.confirm( wp.themes.data.settings.confirmDelete ) ) {
+                       return;
+               }
+
+               wp.updates.maybeRequestFilesystemCredentials( event );
+
+               $( document ).one( 'wp-delete-theme-success', function( event, response ) {
+                       _this.$el.find( '.close' ).trigger( 'click' );
+                       $( '[data-slug="' + response.slug + '"' ).css( { backgroundColor:'#faafaa' } ).fadeOut( 350, function() {
+                               $( this ).remove();
+                               _themes.data.themes = _.without( _themes.data.themes, _.findWhere( _themes.data.themes, { id: response.slug } ) );
+
+                               $( '.wp-filter-search' ).val( '' );
+                               _collection.doSearch( '' );
+                               _collection.remove( _this.model );
+                               _collection.trigger( 'themes:update' );
+                       } );
+               } );
+
+               wp.updates.deleteTheme( {
+                       slug: this.model.get( 'id' )
+               } );
+       },
+
</ins><span class="cx" style="display: block; padding: 0 10px">         nextTheme: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                var self = this;
</span><span class="cx" style="display: block; padding: 0 10px">                self.trigger( 'theme:next', self.model.cid );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -759,7 +857,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'click .devices button': 'previewDevice',
</span><span class="cx" style="display: block; padding: 0 10px">                'click .previous-theme': 'previousTheme',
</span><span class="cx" style="display: block; padding: 0 10px">                'click .next-theme': 'nextTheme',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'keyup': 'keyEvent'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'keyup': 'keyEvent',
+               'click .theme-install': 'installTheme'
</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">        // The HTML template for the theme preview
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -859,6 +958,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( event.keyCode === 37 ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.previousTheme();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        },
+
+       installTheme: function( event ) {
+               var _this   = this,
+                   $target = $( event.target );
+               event.preventDefault();
+
+               if ( $target.hasClass( 'disabled' ) ) {
+                       return;
+               }
+
+               wp.updates.maybeRequestFilesystemCredentials( event );
+
+               $( document ).on( 'wp-install-theme-success', function() {
+                       _this.model.set( { 'installed': true } );
+               } );
+
+               wp.updates.installTheme( {
+                       slug: $target.data( 'slug' )
+               } );
</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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -929,6 +1048,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                return;
</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">+                        // Bail if the filesystem credentials dialog is shown.
+                       if ( $( '#request-filesystem-credentials-dialog' ).is( ':visible' ) ) {
+                               return;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Pressing the right arrow key fires a theme:next event
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( event.keyCode === 39 ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                self.overlay.nextTheme();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1048,7 +1172,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        // Renders the overlay with the ThemeDetails view
</span><span class="cx" style="display: block; padding: 0 10px">        // Uses the current model data
</span><span class="cx" style="display: block; padding: 0 10px">        expand: function( id ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                var self = this;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         var self = this, $card, $modal;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Set the current theme model
</span><span class="cx" style="display: block; padding: 0 10px">                this.model = self.collection.get( id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1066,6 +1190,22 @@
</span><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">                this.overlay.render();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( this.model.get( 'hasUpdate' ) ) {
+                       $card  = $( '[data-slug="' + this.model.id + '"]' );
+                       $modal = $( this.overlay.el );
+
+                       if ( $card.find( '.updating-message' ).length ) {
+                               $modal.find( '.notice-warning h3' ).remove();
+                               $modal.find( '.notice-warning' )
+                                       .removeClass( 'notice-large' )
+                                       .addClass( 'updating-message' )
+                                       .find( 'p' ).text( wp.updates.l10n.updating );
+                       } else if ( $card.find( '.notice-error' ).length ) {
+                               $modal.find( '.notice-warning' ).remove();
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 this.$overlay.html( this.overlay.el );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Bind to theme:next and theme:previous
</span></span></pre></div>
<a id="trunksrcwpadminjsupdatesjs"></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/js/updates.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/updates.js  2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/js/updates.js    2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,7 +1,43 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/* global tb_remove */
-window.wp = window.wp || {};
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Functions for ajaxified updates, deletions and installs inside the WordPress admin.
+ *
+ * @version 4.2.0
+ *
+ * @package WordPress
+ * @subpackage Administration
+ */
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-(function( $, wp, pagenow ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* global pagenow */
+
+/**
+ * @param {jQuery}  $                                   jQuery object.
+ * @param {object}  wp                                  WP object.
+ * @param {object}  settings                            WP Updates settings.
+ * @param {string}  settings.ajax_nonce                 AJAX nonce.
+ * @param {object}  settings.l10n                       Translation strings.
+ * @param {object=} settings.plugins                    Base names of plugins in their different states.
+ * @param {Array}   settings.plugins.all                Base names of all plugins.
+ * @param {Array}   settings.plugins.active             Base names of active plugins.
+ * @param {Array}   settings.plugins.inactive           Base names of inactive plugins.
+ * @param {Array}   settings.plugins.upgrade            Base names of plugins with updates available.
+ * @param {Array}   settings.plugins.recently_activated Base names of recently activated plugins.
+ * @param {object=} settings.totals                     Plugin/theme status information or null.
+ * @param {number}  settings.totals.all                 Amount of all plugins or themes.
+ * @param {number}  settings.totals.upgrade             Amount of plugins or themes with updates available.
+ * @param {number}  settings.totals.disabled            Amount of disabled themes.
+ */
+(function( $, wp, settings ) {
+       var $document = $( document );
+
+       wp = wp || {};
+
+       /**
+        * The WP Updates object.
+        *
+        * @since 4.2.0
+        *
+        * @type {object}
+        */
</ins><span class="cx" style="display: block; padding: 0 10px">         wp.updates = {};
</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">@@ -9,591 +45,2144 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var string
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {string}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.ajaxNonce = window._wpUpdatesSettings.ajax_nonce;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.ajaxNonce = settings.ajax_nonce;
</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">         * Localized strings.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var object
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {object}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.l10n = window._wpUpdatesSettings.l10n;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.l10n = settings.l10n;
</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">+         * Current search term.
+        *
+        * @since 4.6.0
+        *
+        * @type {string}
+        */
+       wp.updates.searchTerm = '';
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Whether filesystem credentials need to be requested from the user.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var bool
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {bool}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.shouldRequestFilesystemCredentials = null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.shouldRequestFilesystemCredentials = false;
</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">         * Filesystem credentials to be packaged along with the request.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 Added `available` property to indicate whether credentials have been provided.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var object
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {object} filesystemCredentials                    Holds filesystem credentials.
+        * @type {object} filesystemCredentials.ftp                Holds FTP credentials.
+        * @type {string} filesystemCredentials.ftp.host           FTP host. Default empty string.
+        * @type {string} filesystemCredentials.ftp.username       FTP user name. Default empty string.
+        * @type {string} filesystemCredentials.ftp.password       FTP password. Default empty string.
+        * @type {string} filesystemCredentials.ftp.connectionType Type of FTP connection. 'ssh', 'ftp', or 'ftps'.
+        *                                                         Default empty string.
+        * @type {object} filesystemCredentials.ssh                Holds SSH credentials.
+        * @type {string} filesystemCredentials.ssh.publicKey      The public key. Default empty string.
+        * @type {string} filesystemCredentials.ssh.privateKey     The private key. Default empty string.
+        * @type {bool}   filesystemCredentials.available          Whether filesystem credentials have been provided.
+        *                                                         Default 'false'.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.filesystemCredentials = {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                ftp: {
-                       host: null,
-                       username: null,
-                       password: null,
-                       connectionType: null
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         ftp:       {
+                       host:           '',
+                       username:       '',
+                       password:       '',
+                       connectionType: ''
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                ssh: {
-                       publicKey: null,
-                       privateKey: null
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         ssh:       {
+                       publicKey:  '',
+                       privateKey: ''
+               },
+               available: false
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Flag if we're waiting for an update to complete.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Whether we're waiting for an Ajax request to complete.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 More accurately named `ajaxLocked`.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var bool
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {bool}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updateLock = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.ajaxLocked = false;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * * Flag if we've done an update successfully.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Admin notice template.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 4.2.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 4.6.0
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var bool
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {function} A function that lazily-compiles the template requested.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updateDoneSuccessfully = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' );
</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">+         * Update queue.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * If the user tries to update a plugin while an update is
</span><span class="cx" style="display: block; padding: 0 10px">         * already happening, it can be placed in this queue to perform later.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 More accurately named `queue`.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var array
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {Array.object}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updateQueue = [];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.queue = [];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Store a jQuery reference to return focus to when exiting the request credentials modal.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Holds a jQuery reference to return focus to when exiting the request credentials modal.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var jQuery object
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @type {jQuery}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.$elToReturnFocusToFromCredentialsModal = null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.$elToReturnFocusToFromCredentialsModal = undefined;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Decrement update counts throughout the various menus.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Adds or updates an admin notice.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0
+        *
+        * @param {object}  data
+        * @param {*=}      data.selector      Optional. Selector of an element to be replaced with the admin notice.
+        * @param {string=} data.id            Optional. Unique id that will be used as the notice's id attribute.
+        * @param {string=} data.className     Optional. Class names that will be used in the admin notice.
+        * @param {string=} data.message       Optional. The message displayed in the notice.
+        * @param {number=} data.successes     Optional. The amount of successful operations.
+        * @param {number=} data.errors        Optional. The amount of failed operations.
+        * @param {Array=}  data.errorMessages Optional. Error messages of failed operations.
+        *
+        */
+       wp.updates.addAdminNotice = function( data ) {
+               var $notice = $( data.selector ), $adminNotice;
+
+               delete data.selector;
+               $adminNotice = wp.updates.adminNotice( data );
+
+               // Check if this admin notice already exists.
+               if ( ! $notice.length ) {
+                       $notice = $( '#' + data.id );
+               }
+
+               if ( $notice.length ) {
+                       $notice.replaceWith( $adminNotice );
+               } else {
+                       $( '.wrap' ).find( '> h1' ).after( $adminNotice );
+               }
+
+               $document.trigger( 'wp-updates-notice-added' );
+       };
+
+       /**
+        * Handles Ajax requests to WordPress.
+        *
+        * @since 4.6.0
+        *
+        * @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc).
+        * @param {object} data   Data that needs to be passed to the ajax callback.
+        * @return {$.promise}    A jQuery promise that represents the request,
+        *                        decorated with an abort() method.
+        */
+       wp.updates.ajax = function( action, data ) {
+               var options = {};
+
+               if ( wp.updates.ajaxLocked ) {
+                       wp.updates.queue.push( {
+                               action: action,
+                               data:   data
+                       } );
+
+                       // Return a Deferred object so callbacks can always be registered.
+                       return $.Deferred();
+               }
+
+               wp.updates.ajaxLocked = true;
+
+               if ( data.success ) {
+                       options.success = data.success;
+                       delete data.success;
+               }
+
+               if ( data.error ) {
+                       options.error = data.error;
+                       delete data.error;
+               }
+
+               options.data = _.extend( data, {
+                       action:          action,
+                       _ajax_nonce:     wp.updates.ajaxNonce,
+                       username:        wp.updates.filesystemCredentials.ftp.username,
+                       password:        wp.updates.filesystemCredentials.ftp.password,
+                       hostname:        wp.updates.filesystemCredentials.ftp.hostname,
+                       connection_type: wp.updates.filesystemCredentials.ftp.connectionType,
+                       public_key:      wp.updates.filesystemCredentials.ssh.publicKey,
+                       private_key:     wp.updates.filesystemCredentials.ssh.privateKey
+               } );
+
+               return wp.ajax.send( options ).always( wp.updates.ajaxAlways );
+       };
+
+       /**
+        * Actions performed after every Ajax request.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}  response
+        * @param {array=}  response.debug     Optional. Debug information.
+        * @param {string=} response.errorCode Optional. Error code for an error that occurred.
+        */
+       wp.updates.ajaxAlways = function( response ) {
+               if ( ! response.errorCode && 'unable_to_connect_to_filesystem' !== response.errorCode ) {
+                       wp.updates.ajaxLocked = false;
+                       wp.updates.queueChecker();
+               }
+
+               if ( 'undefined' !== typeof response.debug ) {
+                       _.map( response.debug, function( message ) {
+                               window.console.log( $( '<p />' ).html( message ).text() );
+                       } );
+               }
+       };
+
+       /**
+        * Decrements the update counts throughout the various menus.
+        *
+        * This includes the toolbar, the "Updates" menu item and the menu items
+        * for plugins and themes.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 3.9.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {string} upgradeType
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param {string} type The type of item that was updated or deleted.
+        *                      Can be 'plugin', 'theme'.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.decrementCount = function( upgradeType ) {
-               var count,
-                       pluginCount,
-                       $adminBarUpdateCount = $( '#wp-admin-bar-updates .ab-label' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.decrementCount = function( type ) {
+               var $adminBarUpdates             = $( '#wp-admin-bar-updates' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         $dashboardNavMenuUpdateCount = $( 'a[href="update-core.php"] .update-plugins' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $pluginsMenuItem = $( '#menu-plugins' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 count                        = $adminBarUpdates.find( '.ab-label' ).text(),
+                       $menuItem, $itemCount, itemCount;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                count = parseInt( count, 10 ) - 1;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                count = $adminBarUpdateCount.text();
-               count = parseInt( count, 10 ) - 1;
</del><span class="cx" style="display: block; padding: 0 10px">                 if ( count < 0 || isNaN( count ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $( '#wp-admin-bar-updates .ab-item' ).removeAttr( 'title' );
-               $adminBarUpdateCount.text( count );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $adminBarUpdates.find( '.ab-item' ).removeAttr( 'title' );
+               $adminBarUpdates.find( '.ab-label' ).text( count );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $dashboardNavMenuUpdateCount.each( function( index, elem ) {
-                       elem.className = elem.className.replace( /count-\d+/, 'count-' + count );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Remove the update count from the toolbar if it's zero.
+               if ( ! count ) {
+                       $adminBarUpdates.find( '.ab-label' ).parents( 'li' ).remove();
+               }
+
+               // Update the "Updates" menu item.
+               $dashboardNavMenuUpdateCount.each( function( index, element ) {
+                       element.className = element.className.replace( /count-\d+/, 'count-' + count );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $dashboardNavMenuUpdateCount.removeAttr( 'title' );
</span><span class="cx" style="display: block; padding: 0 10px">                $dashboardNavMenuUpdateCount.find( '.update-count' ).text( count );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'plugin' === upgradeType ) {
-                       pluginCount = $pluginsMenuItem.find( '.plugin-count' ).eq(0).text();
-                       pluginCount = parseInt( pluginCount, 10 ) - 1;
-                       if ( pluginCount < 0 || isNaN( pluginCount ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         switch ( type ) {
+                       case 'plugin':
+                               $menuItem  = $( '#menu-plugins' );
+                               $itemCount = $menuItem.find( '.plugin-count' );
+                               break;
+
+                       case 'theme':
+                               $menuItem  = $( '#menu-appearance' );
+                               $itemCount = $menuItem.find( '.theme-count' );
+                               break;
+
+                       default:
+                               window.console.error( '"%s" is not white-listed to have its count decremented.', type );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        }
-                       $pluginsMenuItem.find( '.plugin-count' ).text( pluginCount );
-                       $pluginsMenuItem.find( '.update-plugins' ).each( function( index, elem ) {
-                               elem.className = elem.className.replace( /count-\d+/, 'count-' + pluginCount );
-                       } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if (pluginCount > 0 ) {
-                               $( '.subsubsub .upgrade .count' ).text( '(' + pluginCount + ')' );
-                       } else {
-                               $( '.subsubsub .upgrade' ).remove();
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Decrement the counter of the other menu items.
+               if ( $itemCount ) {
+                       itemCount = $itemCount.eq( 0 ).text();
+                       itemCount = parseInt( itemCount, 10 ) - 1;
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( itemCount < 0 || isNaN( itemCount ) ) {
+                       return;
+               }
+
+               if ( itemCount > 0 ) {
+                       $( '.subsubsub .upgrade .count' ).text( '(' + itemCount + ')' );
+
+                       $itemCount.text( itemCount );
+                       $menuItem.find( '.update-plugins' ).each( function( index, element ) {
+                               element.className = element.className.replace( /count-\d+/, 'count-' + itemCount );
+                       } );
+               } else {
+                       $( '.subsubsub .upgrade' ).remove();
+                       $menuItem.find( '.update-plugins' ).remove();
+               }
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Send an Ajax request to the server to update a plugin.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Sends an Ajax request to the server to update a plugin.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 More accurately named `updatePlugin`.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {string} plugin
-        * @param {string} slug
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param {object}               args         Arguments.
+        * @param {string}               args.plugin  Plugin basename.
+        * @param {string}               args.slug    Plugin slug.
+        * @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess
+        * @param {updatePluginError=}   args.error   Optional. Error callback. Default: wp.updates.updatePluginError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updatePlugin = function( plugin, slug ) {
-               var $message, name,
-                       $card = $( '.plugin-card-' + slug );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.updatePlugin = function( args ) {
+               var $updateRow, $card, $message, message;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                args = _.extend( {
+                       success: wp.updates.updatePluginSuccess,
+                       error: wp.updates.updatePluginError
+               }, args );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $message = $( '[data-plugin="' + plugin + '"]' ).next().find( '.update-message' );
-               } else if ( 'plugin-install' === pagenow ) {
-                       $message = $card.find( '.update-now' );
-                       name = $message.data( 'name' );
-                       $message.attr( 'aria-label', wp.updates.l10n.updatingLabel.replace( '%s', name ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' );
+                       $message   = $updateRow.find( '.update-message' ).addClass( 'updating-message' ).find( 'p' );
+                       message    = wp.updates.l10n.updatingLabel.replace( '%s', $updateRow.find( '.plugin-title strong' ).text() );
+               } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
+                       $card    = $( '.plugin-card-' + args.slug );
+                       $message = $card.find( '.update-now' ).addClass( 'updating-message' );
+                       message  = wp.updates.l10n.updatingLabel.replace( '%s', $message.data( 'name' ) );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Remove previous error messages, if any.
</span><span class="cx" style="display: block; padding: 0 10px">                        $card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $message.addClass( 'updating-message' );
-               if ( $message.html() !== wp.updates.l10n.updating ){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $message.html() !== wp.updates.l10n.updating ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $message.data( 'originaltext', $message.html() );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $message.text( wp.updates.l10n.updating );
-               wp.a11y.speak( wp.updates.l10n.updatingMsg );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $message
+                       .attr( 'aria-label', message )
+                       .text( wp.updates.l10n.updating );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( wp.updates.updateLock ) {
-                       wp.updates.updateQueue.push( {
-                               type: 'update-plugin',
-                               data: {
-                                       plugin: plugin,
-                                       slug: slug
-                               }
-                       } );
-                       return;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $document.trigger( 'wp-plugin-updating' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.updateLock = true;
-
-               var data = {
-                       _ajax_nonce:     wp.updates.ajaxNonce,
-                       plugin:          plugin,
-                       slug:            slug,
-                       username:        wp.updates.filesystemCredentials.ftp.username,
-                       password:        wp.updates.filesystemCredentials.ftp.password,
-                       hostname:        wp.updates.filesystemCredentials.ftp.hostname,
-                       connection_type: wp.updates.filesystemCredentials.ftp.connectionType,
-                       public_key:      wp.updates.filesystemCredentials.ssh.publicKey,
-                       private_key:     wp.updates.filesystemCredentials.ssh.privateKey
-               };
-
-               wp.ajax.post( 'update-plugin', data )
-                       .done( wp.updates.updateSuccess )
-                       .fail( wp.updates.updateError );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return wp.updates.ajax( 'update-plugin', args );
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * On a successful plugin update, update the UI with the result.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Updates the UI appropriately after a successful plugin update.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 More accurately named `updatePluginSuccess`.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {object} response
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @typedef {object} updatePluginSuccess
+        * @param {object} response            Response from the server.
+        * @param {string} response.slug       Slug of the plugin to be updated.
+        * @param {string} response.plugin     Basename of the plugin to be updated.
+        * @param {string} response.pluginName Name of the plugin to be updated.
+        * @param {string} response.oldVersion Old version of the plugin.
+        * @param {string} response.newVersion New version of the plugin.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updateSuccess = function( response ) {
-               var $updateMessage, name, $pluginRow, newText;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.updatePluginSuccess = function( response ) {
+               var $pluginRow, $updateMessage, newText;
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $pluginRow = $( '[data-plugin="' + response.plugin + '"]' ).first();
-                       $updateMessage = $pluginRow.next().find( '.update-message' );
-                       $pluginRow.addClass( 'updated' ).removeClass( 'update' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $pluginRow     = $( 'tr[data-plugin="' + response.plugin + '"]' )
+                               .removeClass( 'update' )
+                               .addClass( 'updated' );
+                       $updateMessage = $pluginRow.find( '.update-message' )
+                               .removeClass( 'updating-message notice-warning' )
+                               .addClass( 'updated-message notice-success' ).find( 'p' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Update the version number in the row.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        newText = $pluginRow.find('.plugin-version-author-uri').html().replace( response.oldVersion, response.newVersion );
-                       $pluginRow.find('.plugin-version-author-uri').html( newText );
-
-                       // Add updated class to update message parent tr
-                       $pluginRow.next().addClass( 'updated' );
-               } else if ( 'plugin-install' === pagenow ) {
-                       $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' );
-                       $updateMessage.addClass( 'button-disabled' );
-                       name = $updateMessage.data( 'name' );
-                       $updateMessage.attr( 'aria-label', wp.updates.l10n.updatedLabel.replace( '%s', name ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
+                       $pluginRow.find( '.plugin-version-author-uri' ).html( newText );
+               } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
+                       $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )
+                               .removeClass( 'updating-message' )
+                               .addClass( 'button-disabled updated-message' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $updateMessage.removeClass( 'updating-message' ).addClass( 'updated-message' );
-               $updateMessage.text( wp.updates.l10n.updated );
-               wp.a11y.speak( wp.updates.l10n.updatedMsg );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $updateMessage
+                       .attr( 'aria-label', wp.updates.l10n.updatedLabel.replace( '%s', response.pluginName ) )
+                       .text( wp.updates.l10n.updated );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp.updates.decrementCount( 'plugin' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.updateDoneSuccessfully = true;
-
-               /*
-                * The lock can be released since the update was successful,
-                * and any other updates can commence.
-                */
-               wp.updates.updateLock = false;
-
-               $(document).trigger( 'wp-plugin-update-success', response );
-
-               wp.updates.queueChecker();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $document.trigger( 'wp-plugin-update-success', response );
</ins><span class="cx" style="display: block; padding: 0 10px">         };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">         /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * On a plugin update error, update the UI appropriately.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Updates the UI appropriately after a failed plugin update.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 More accurately named `updatePluginError`.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {object} response
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @typedef {object} updatePluginError
+        * @param {object}  response              Response from the server.
+        * @param {string}  response.slug         Slug of the plugin to be updated.
+        * @param {string}  response.plugin       Basename of the plugin to be updated.
+        * @param {string=} response.pluginName   Optional. Name of the plugin to be updated.
+        * @param {string}  response.errorCode    Error code for the error that occurred.
+        * @param {string}  response.errorMessage The error that occurred.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.updateError = function( response ) {
-               var $card = $( '.plugin-card-' + response.slug ),
-                       $message,
-                       $button,
-                       name,
-                       error_message;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.updatePluginError = function( response ) {
+               var $card, $message, errorMessage;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.updateDoneSuccessfully = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
+                       return;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( response.errorCode && response.errorCode == 'unable_to_connect_to_filesystem' && wp.updates.shouldRequestFilesystemCredentials ) {
-                       wp.updates.credentialError( response, 'update-plugin' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                error_message = wp.updates.l10n.updateFailed.replace( '%s', response.error );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $message = $( '[data-plugin="' + response.plugin + '"]' ).next().find( '.update-message' );
-                       $message.html( error_message ).removeClass( 'updating-message' );
-               } else if ( 'plugin-install' === pagenow ) {
-                       $button = $card.find( '.update-now' );
-                       name = $button.data( 'name' );
-
-                       $card
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $message = $( 'tr[data-plugin="' + response.plugin + '"]' ).find( '.update-message' );
+                       $message.removeClass( 'updating-message notice-warning' ).addClass( 'notice-error' ).find( 'p' ).html( errorMessage );
+               } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
+                       $card = $( '.plugin-card-' + response.slug )
</ins><span class="cx" style="display: block; padding: 0 10px">                                 .addClass( 'plugin-card-update-failed' )
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                .append( '<div class="notice notice-error is-dismissible"><p>' + error_message + '</p></div>' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         .append( wp.updates.adminNotice( {
+                                       className: 'update-message notice-error notice-alt is-dismissible',
+                                       message:   errorMessage
+                               } ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $button
-                               .attr( 'aria-label', wp.updates.l10n.updateFailedLabel.replace( '%s', name ) )
-                               .html( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $card.find( '.update-now' )
+                               .attr( 'aria-label', wp.updates.l10n.updateFailedLabel.replace( '%s', response.pluginName ) )
+                               .text( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 // Use same delay as the total duration of the notice fadeTo + slideUp animation.
</span><span class="cx" style="display: block; padding: 0 10px">                                setTimeout( function() {
</span><span class="cx" style="display: block; padding: 0 10px">                                        $card
</span><span class="cx" style="display: block; padding: 0 10px">                                                .removeClass( 'plugin-card-update-failed' )
</span><span class="cx" style="display: block; padding: 0 10px">                                                .find( '.column-name a' ).focus();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                                       $card.find( '.update-now' )
+                                               .attr( 'aria-label', false )
+                                               .text( wp.updates.l10n.updateNow );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }, 200 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 } );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.a11y.speak( error_message, 'assertive' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp.a11y.speak( errorMessage, 'assertive' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /*
-                * The lock can be released since this failure was
-                * after the credentials form.
-                */
-               wp.updates.updateLock = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $document.trigger( 'wp-plugin-update-error', response );
+       };
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $(document).trigger( 'wp-plugin-update-error', response );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Sends an Ajax request to the server to install a plugin.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}                args         Arguments.
+        * @param {string}                args.slug    Plugin identifier in the WordPress.org Plugin repository.
+        * @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess
+        * @param {installPluginError=}   args.error   Optional. Error callback. Default: wp.updates.installPluginError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
+        */
+       wp.updates.installPlugin = function( args ) {
+               var $card    = $( '.plugin-card-' + args.slug ),
+                       $message = $card.find( '.install-now' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.queueChecker();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         args = _.extend( {
+                       success: wp.updates.installPluginSuccess,
+                       error: wp.updates.installPluginError
+               }, args );
+
+               if ( 'import' === pagenow ) {
+                       $message = $( 'a[href*="' + args.slug + '"]' );
+               } else {
+                       $message.text( wp.updates.l10n.installing );
+               }
+
+               $message.addClass( 'updating-message' );
+
+               wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
+
+               // Remove previous error messages, if any.
+               $card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
+
+               return wp.updates.ajax( 'install-plugin', args );
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Show an error message in the request for credentials form.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Updates the UI appropriately after a successful plugin install.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {string} message
-        * @since 4.2.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 4.6.0
+        *
+        * @typedef {object} installPluginSuccess
+        * @param {object} response             Response from the server.
+        * @param {string} response.slug        Slug of the installed plugin.
+        * @param {string} response.pluginName  Name of the installed plugin.
+        * @param {string} response.activateUrl URL to activate the just installed plugin.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.showErrorInCredentialsForm = function( message ) {
-               var $modal = $( '.notification-dialog' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.installPluginSuccess = function( response ) {
+               var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Remove any existing error.
-               $modal.find( '.error' ).remove();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $message
+                       .removeClass( 'updating-message' )
+                       .addClass( 'updated-message installed button-disabled' )
+                       .text( wp.updates.l10n.installed );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $modal.find( 'h3' ).after( '<div class="error">' + message + '</div>' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+
+               $document.trigger( 'wp-plugin-install-success', response );
+
+               if ( response.activateUrl ) {
+                       setTimeout( function() {
+
+                               // Transform the 'Install' button into an 'Activate' button.
+                               $message.removeClass( 'install-now installed button-disabled updated-message' ).addClass( 'activate-now button-primary' )
+                                       .attr( 'href', response.activateUrl )
+                                       .text( wp.updates.l10n.activatePlugin );
+                       }, 1000 );
+               }
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Events that need to happen when there is a credential error
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Updates the UI appropriately after a failed plugin install.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 4.2.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 4.6.0
+        *
+        * @typedef {object} installPluginError
+        * @param {object}  response              Response from the server.
+        * @param {string}  response.slug         Slug of the plugin to be installed.
+        * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
+        * @param {string}  response.errorCode    Error code for the error that occurred.
+        * @param {string}  response.errorMessage The error that occurred.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp.updates.credentialError = function( response, type ) {
-               wp.updates.updateQueue.push( {
-                       'type': type,
-                       'data': {
-                               // Not cool that we're depending on response for this data.
-                               // This would feel more whole in a view all tied together.
-                               plugin: response.plugin,
-                               slug: response.slug
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.updates.installPluginError = function( response ) {
+               var $card   = $( '.plugin-card-' + response.slug ),
+                       $button = $card.find( '.install-now' ),
+                       errorMessage;
+
+               if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
+                       return;
+               }
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
+                       return;
+               }
+
+               errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage );
+
+               $card
+                       .addClass( 'plugin-card-update-failed' )
+                       .append( '<div class="notice notice-error notice-alt is-dismissible"><p>' + errorMessage + '</p></div>' );
+
+               $card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
+
+                       // Use same delay as the total duration of the notice fadeTo + slideUp animation.
+                       setTimeout( function() {
+                               $card
+                                       .removeClass( 'plugin-card-update-failed' )
+                                       .find( '.column-name a' ).focus();
+                       }, 200 );
+               } );
+
+               $button
+                       .removeClass( 'updating-message' ).addClass( 'button-disabled' )
+                       .attr( 'aria-label', wp.updates.l10n.installFailedLabel.replace( '%s', response.pluginName ) )
+                       .text( wp.updates.l10n.installFailedShort );
+
+               wp.a11y.speak( errorMessage, 'assertive' );
+
+               $document.trigger( 'wp-plugin-install-error', response );
+       };
+
+       /**
+        * Updates the UI appropriately after a successful importer install.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} installImporterSuccess
+        * @param {object} response             Response from the server.
+        * @param {string} response.slug        Slug of the installed plugin.
+        * @param {string} response.pluginName  Name of the installed plugin.
+        * @param {string} response.activateUrl URL to activate the just installed plugin.
+        */
+       wp.updates.installImporterSuccess = function( response ) {
+               wp.updates.addAdminNotice( {
+                       id:        'install-success',
+                       className: 'notice-success is-dismissible',
+                       message:   wp.updates.l10n.importerInstalledMsg.replace( '%s', response.activateUrl + '&from=import' )
+               } );
+
+               $( 'a[href*="' + response.slug + '"]' )
+                       .removeClass( 'thickbox open-plugin-details-modal updating-message' )
+                       .off( 'click' )
+                       .attr( 'href', response.activateUrl + '&from=import' )
+                       .attr( 'title', wp.updates.l10n.activateImporter );
+
+               wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+
+               $document.trigger( 'wp-installer-install-success', response );
+       };
+
+       /**
+        * Updates the UI appropriately after a failed importer install.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} installImporterError
+        * @param {object}  response              Response from the server.
+        * @param {string}  response.slug         Slug of the plugin to be installed.
+        * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
+        * @param {string}  response.errorCode    Error code for the error that occurred.
+        * @param {string}  response.errorMessage The error that occurred.
+        */
+       wp.updates.installImporterError = function( response ) {
+               var errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage );
+
+               if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
+                       return;
+               }
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
+                       return;
+               }
+
+               wp.updates.addAdminNotice( {
+                       id:        response.errorCode,
+                       className: 'notice-error is-dismissible',
+                       message:   errorMessage
+               } );
+
+               $( 'a[href*="' + response.slug + '"]' ).removeClass( 'updating-message' );
+
+               wp.a11y.speak( errorMessage, 'assertive' );
+
+               $document.trigger( 'wp-importer-install-error', response );
+       };
+
+       /**
+        * Sends an Ajax request to the server to delete a plugin.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}               args         Arguments.
+        * @param {string}               args.plugin  Basename of the plugin to be deleted.
+        * @param {string}               args.slug    Slug of the plugin to be deleted.
+        * @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess
+        * @param {deletePluginError=}   args.error   Optional. Error callback. Default: wp.updates.deletePluginError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
+        */
+       wp.updates.deletePlugin = function( args ) {
+               var $message = $( '[data-plugin="' + args.plugin + '"]' ).find( '.update-message p' );
+
+               args = _.extend( {
+                       success: wp.updates.deletePluginSuccess,
+                       error: wp.updates.deletePluginError
+               }, args );
+
+               if ( $message.html() !== wp.updates.l10n.updating ) {
+                       $message.data( 'originaltext', $message.html() );
+               }
+
+               wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
+
+               return wp.updates.ajax( 'delete-plugin', args );
+       };
+
+       /**
+        * Updates the UI appropriately after a successful plugin deletion.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} deletePluginSuccess
+        * @param {object} response            Response from the server.
+        * @param {string} response.slug       Slug of the plugin that was deleted.
+        * @param {string} response.plugin     Base name of the plugin that was deleted.
+        * @param {string} response.pluginName Name of the plugin that was deleted.
+        */
+       wp.updates.deletePluginSuccess = function( response ) {
+
+               // Removes the plugin and updates rows.
+               $( '[data-plugin="' + response.plugin + '"]' ).css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() {
+                       var $form            = $( '#bulk-action-form' ),
+                               $views           = $( '.subsubsub' ),
+                               $pluginRow       = $( this ),
+                               columnCount      = $form.find( 'thead th:not(.hidden), thead td' ).length,
+                               pluginDeletedRow = wp.template( 'item-deleted-row' ),
+                               /** @type {object} plugins Base names of plugins in their different states. */
+                               plugins          = settings.plugins;
+
+                       // Add a success message after deleting a plugin.
+                       if ( ! $pluginRow.hasClass( 'plugin-update-tr' ) ) {
+                               $pluginRow.after(
+                                       pluginDeletedRow( {
+                                               slug:    response.slug,
+                                               plugin:  response.plugin,
+                                               colspan: columnCount,
+                                               name:    response.pluginName
+                                       } )
+                               );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       $pluginRow.remove();
+
+                       // Remove plugin from update count.
+                       if ( -1 !== _.indexOf( plugins.upgrade, response.plugin ) ) {
+                               plugins.upgrade = _.without( plugins.upgrade, response.plugin );
+                               wp.updates.decrementCount( 'plugin' );
+                       }
+
+                       // Remove from views.
+                       if ( -1 !== _.indexOf( plugins.inactive, response.plugin ) ) {
+                               plugins.inactive = _.without( plugins.inactive, response.plugin );
+                               if ( plugins.inactive.length ) {
+                                       $views.find( '.inactive .count' ).text( '(' + plugins.inactive.length + ')' );
+                               } else {
+                                       $views.find( '.inactive' ).remove();
+                               }
+                       }
+
+                       if ( -1 !== _.indexOf( plugins.active, response.plugin ) ) {
+                               plugins.active = _.without( plugins.active, response.plugin );
+                               if ( plugins.active.length ) {
+                                       $views.find( '.active .count' ).text( '(' + plugins.active.length + ')' );
+                               } else {
+                                       $views.find( '.active' ).remove();
+                               }
+                       }
+
+                       if ( -1 !== _.indexOf( plugins.recently_activated, response.plugin ) ) {
+                               plugins.recently_activated = _.without( plugins.recently_activated, response.plugin );
+                               if ( plugins.recently_activated.length ) {
+                                       $views.find( '.recently_activated .count' ).text( '(' + plugins.recently_activated.length + ')' );
+                               } else {
+                                       $views.find( '.recently_activated' ).remove();
+                               }
+                       }
+
+                       plugins.all = _.without( plugins.all, response.plugin );
+
+                       if ( plugins.all.length ) {
+                               $views.find( '.all .count' ).text( '(' + plugins.all.length + ')' );
+                       } else {
+                               $form.find( '.tablenav' ).css( { visibility: 'hidden' } );
+                               $views.find( '.all' ).remove();
+
+                               if ( ! $form.find( 'tr.no-items' ).length ) {
+                                       $form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + wp.updates.l10n.noPlugins + '</td></tr>' );
+                               }
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.showErrorInCredentialsForm( response.error );
-               wp.updates.requestFilesystemCredentials();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               wp.a11y.speak( wp.updates.l10n.deleted, 'polite' );
+
+               $document.trigger( 'wp-plugin-delete-success', response );
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * If an update job has been placed in the queue, queueChecker pulls it out and runs it.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Updates the UI appropriately after a failed plugin deletion.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0
+        *
+        * @typedef {object} deletePluginError
+        * @param {object}  response              Response from the server.
+        * @param {string}  response.slug         Slug of the plugin to be deleted.
+        * @param {string}  response.plugin       Base name of the plugin to be deleted
+        * @param {string=} response.pluginName   Optional. Name of the plugin to be deleted.
+        * @param {string}  response.errorCode    Error code for the error that occurred.
+        * @param {string}  response.errorMessage The error that occurred.
+        */
+       wp.updates.deletePluginError = function( response ) {
+               var $plugin          = $( 'tr.inactive[data-plugin="' + response.plugin + '"]' ),
+                       pluginUpdateRow  = wp.template( 'item-update-row' ),
+                       $pluginUpdateRow = $plugin.siblings( '[data-plugin="' + response.plugin + '"]' ),
+                       noticeContent    = wp.updates.adminNotice( {
+                               className: 'update-message notice-error notice-alt',
+                               message:   response.errorMessage
+                       } );
+
+               if ( ! wp.updates.isValidResponse( response, 'delete' ) ) {
+                       return;
+               }
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'delete-plugin' ) ) {
+                       return;
+               }
+
+               // Add a plugin update row if it doesn't exist yet.
+               if ( ! $pluginUpdateRow.length ) {
+                       $plugin.addClass( 'update' ).after(
+                               pluginUpdateRow( {
+                                       slug:    response.slug,
+                                       plugin:  response.plugin,
+                                       colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
+                                       content: noticeContent
+                               } )
+                       );
+               } else {
+
+                       // Remove previous error messages, if any.
+                       $pluginUpdateRow.find( '.notice-error' ).remove();
+
+                       $pluginUpdateRow.find( '.plugin-update' ).append( noticeContent );
+               }
+
+               $document.trigger( 'wp-plugin-delete-error', response );
+       };
+
+       /**
+        * Sends an Ajax request to the server to update a theme.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}              args         Arguments.
+        * @param {string}              args.slug    Theme stylesheet.
+        * @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess
+        * @param {updateThemeError=}   args.error   Optional. Error callback. Default: wp.updates.updateThemeError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
+        */
+       wp.updates.updateTheme = function( args ) {
+               var $notice;
+
+               args = _.extend( {
+                       success: wp.updates.updateThemeSuccess,
+                       error: wp.updates.updateThemeError
+               }, args );
+
+               if ( 'themes-network' === pagenow ) {
+                       $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).addClass( 'updating-message' ).find( 'p' );
+
+               } else {
+                       $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' );
+
+                       $notice.find( 'h3' ).remove();
+
+                       $notice = $notice.add( $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ) );
+                       $notice = $notice.addClass( 'updating-message' ).find( 'p' );
+               }
+
+               if ( $notice.html() !== wp.updates.l10n.updating ) {
+                       $notice.data( 'originaltext', $notice.html() );
+               }
+
+               wp.a11y.speak( wp.updates.l10n.updatingMsg, 'polite' );
+               $notice.text( wp.updates.l10n.updating );
+
+               return wp.updates.ajax( 'update-theme', args );
+       };
+
+       /**
+        * Updates the UI appropriately after a successful theme update.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} updateThemeSuccess
+        * @param {object} response
+        * @param {string} response.slug       Slug of the theme to be updated.
+        * @param {object} response.theme      Updated theme.
+        * @param {string} response.oldVersion Old version of the theme.
+        * @param {string} response.newVersion New version of the theme.
+        */
+       wp.updates.updateThemeSuccess = function( response ) {
+               var isModalOpen    = $( 'body.modal-open' ).length,
+                       $theme         = $( '[data-slug="' + response.slug + '"]' ),
+                       updatedMessage = {
+                               className: 'updated-message notice-success notice-alt',
+                               message:   wp.updates.l10n.updated
+                       },
+                       $notice, newText;
+
+               if ( 'themes-network' === pagenow ) {
+                       $notice = $theme.find( '.update-message' );
+
+                       // Update the version number in the row.
+                       newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
+                       $theme.find( '.theme-version-author-uri' ).html( newText );
+               } else {
+                       $notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
+
+                       // Focus on Customize button after updating.
+                       if ( isModalOpen ) {
+                               $( '.load-customize:visible' ).focus();
+                       } else {
+                               $theme.find( '.load-customize' ).focus();
+                       }
+               }
+
+               wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
+               wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
+
+               wp.updates.decrementCount( 'theme' );
+
+               $document.trigger( 'wp-theme-update-success', response );
+
+               // Show updated message after modal re-rendered.
+               if ( isModalOpen ) {
+                       $( '.theme-info .theme-author' ).after( wp.updates.adminNotice( updatedMessage ) );
+               }
+       };
+
+       /**
+        * Updates the UI appropriately after a failed theme update.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} updateThemeError
+        * @param {object} response              Response from the server.
+        * @param {string} response.slug         Slug of the theme to be updated.
+        * @param {string} response.errorCode    Error code for the error that occurred.
+        * @param {string} response.errorMessage The error that occurred.
+        */
+       wp.updates.updateThemeError = function( response ) {
+               var $theme       = $( '[data-slug="' + response.slug + '"]' ),
+                       errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ),
+                       $notice;
+
+               if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
+                       return;
+               }
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) {
+                       return;
+               }
+
+               if ( 'themes-network' === pagenow ) {
+                       $notice = $theme.find( '.update-message ' );
+               } else {
+                       $notice = $( '.theme-info .notice' ).add( $theme.find( '.notice' ) );
+
+                       $( 'body.modal-open' ).length ? $( '.load-customize:visible' ).focus() : $theme.find( '.load-customize' ).focus();
+               }
+
+               wp.updates.addAdminNotice( {
+                       selector:  $notice,
+                       className: 'update-message notice-error notice-alt is-dismissible',
+                       message:   errorMessage
+               } );
+
+               wp.a11y.speak( errorMessage, 'polite' );
+
+               $document.trigger( 'wp-theme-update-error', response );
+       };
+
+       /**
+        * Sends an Ajax request to the server to install a theme.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}               args
+        * @param {string}               args.slug    Theme stylesheet.
+        * @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess
+        * @param {installThemeError=}   args.error   Optional. Error callback. Default: wp.updates.installThemeError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
+        */
+       wp.updates.installTheme = function( args ) {
+               var $message = $( '.theme-install[data-slug="' + args.slug + '"]' );
+
+               args = _.extend( {
+                       success: wp.updates.installThemeSuccess,
+                       error: wp.updates.installThemeError
+               }, args );
+
+               $message.addClass( 'updating-message' );
+               $message.parents( '.theme' ).addClass( 'focus' );
+               if ( $message.html() !== wp.updates.l10n.installing ) {
+                       $message.data( 'originaltext', $message.html() );
+               }
+
+               $message.text( wp.updates.l10n.installing );
+               wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
+
+               // Remove previous error messages, if any.
+               $( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove();
+
+               return wp.updates.ajax( 'install-theme', args );
+       };
+
+       /**
+        * Updates the UI appropriately after a successful theme install.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} installThemeSuccess
+        * @param {object} response              Response from the server.
+        * @param {string} response.slug         Slug of the theme to be installed.
+        * @param {string} response.customizeUrl URL to the Customizer for the just installed theme.
+        * @param {string} response.activateUrl  URL to activate the just installed theme.
+        */
+       wp.updates.installThemeSuccess = function( response ) {
+               var $card = $( '.wp-full-overlay-header, [data-slug=' + response.slug + ']' ),
+                       $message;
+
+               $document.trigger( 'wp-install-theme-success', response );
+
+               $message = $card.find( '.button-primary' )
+                       .removeClass( 'updating-message' )
+                       .addClass( 'updated-message disabled' )
+                       .text( wp.updates.l10n.installed );
+
+               wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+
+               setTimeout( function() {
+
+                       if ( response.activateUrl ) {
+
+                               // Transform the 'Install' button into an 'Activate' button.
+                               $message
+                                       .attr( 'href', response.activateUrl )
+                                       .removeClass( 'theme-install updated-message disabled' )
+                                       .addClass( 'activate' )
+                                       .text( wp.updates.l10n.activateTheme );
+                       }
+
+                       if ( response.customizeUrl ) {
+
+                               // Transform the 'Preview' button into a 'Live Preview' button.
+                               $message.siblings( '.preview' ).replaceWith( function () {
+                                       return $( '<a>' )
+                                               .attr( 'href', response.customizeUrl )
+                                               .addClass( 'button button-secondary load-customize' )
+                                               .text( wp.updates.l10n.livePreview );
+                               } );
+                       }
+               }, 1000 );
+       };
+
+       /**
+        * Updates the UI appropriately after a failed theme install.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} installThemeError
+        * @param {object} response              Response from the server.
+        * @param {string} response.slug         Slug of the theme to be installed.
+        * @param {string} response.errorCode    Error code for the error that occurred.
+        * @param {string} response.errorMessage The error that occurred.
+        */
+       wp.updates.installThemeError = function( response ) {
+               var $card, $button,
+                       errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ),
+                       $message     = wp.updates.adminNotice( {
+                               className: 'update-message notice-error notice-alt',
+                               message:   errorMessage
+                       } );
+
+               if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
+                       return;
+               }
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'install-theme' ) ) {
+                       return;
+               }
+
+               if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) {
+                       $button = $( '.theme-install[data-slug="' + response.slug + '"]' );
+                       $card   = $( '.install-theme-info' ).prepend( $message );
+               } else {
+                       $card   = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message );
+                       $button = $card.find( '.theme-install' );
+               }
+
+               $button
+                       .removeClass( 'updating-message' )
+                       .attr( 'aria-label', wp.updates.l10n.installFailedLabel.replace( '%s', $card.find( '.theme-name' ).text() ) )
+                       .text( wp.updates.l10n.installFailedShort );
+
+               wp.a11y.speak( errorMessage, 'assertive' );
+
+               $document.trigger( 'wp-theme-install-error', response );
+       };
+
+       /**
+        * Sends an Ajax request to the server to install a theme.
+        *
+        * @since 4.6.0
+        *
+        * @param {object}              args
+        * @param {string}              args.slug    Theme stylesheet.
+        * @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess
+        * @param {deleteThemeError=}   args.error   Optional. Error callback. Default: wp.updates.deleteThemeError
+        * @return {$.promise} A jQuery promise that represents the request,
+        *                     decorated with an abort() method.
+        */
+       wp.updates.deleteTheme = function( args ) {
+               var $button = $( '.theme-actions .delete-theme' );
+
+               args = _.extend( {
+                       success: wp.updates.deleteThemeSuccess,
+                       error: wp.updates.deleteThemeError
+               }, args );
+
+               if ( $button.html() !== wp.updates.l10n.deleting ) {
+                       $button.data( 'originaltext', $button.html() );
+               }
+
+               $button.text( wp.updates.l10n.deleting );
+               wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
+
+               // Remove previous error messages, if any.
+               $( '.theme-info .update-message' ).remove();
+
+               return wp.updates.ajax( 'delete-theme', args );
+       };
+
+       /**
+        * Updates the UI appropriately after a successful theme deletion.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} deleteThemeSuccess
+        * @param {object} response      Response from the server.
+        * @param {string} response.slug Slug of the theme that was deleted.
+        */
+       wp.updates.deleteThemeSuccess = function( response ) {
+               var $themeRows = $( '[data-slug="' + response.slug + '"]' );
+
+               if ( 'themes-network' === pagenow ) {
+
+                       // Removes the theme and updates rows.
+                       $themeRows.css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() {
+                               var $views     = $( '.subsubsub' ),
+                                       $themeRow  = $( this ),
+                                       totals     = settings.totals,
+                                       deletedRow = wp.template( 'item-deleted-row' );
+
+                               if ( ! $themeRow.hasClass( 'plugin-update-tr' ) ) {
+                                       $themeRow.after(
+                                               deletedRow( {
+                                                       slug:    response.slug,
+                                                       colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
+                                                       name:    $themeRow.find( '.theme-title strong' ).text()
+                                               } )
+                                       );
+                               }
+
+                               $themeRow.remove();
+
+                               // Remove theme from update count.
+                               if ( $themeRow.hasClass( 'update' ) ) {
+                                       totals.upgrade--;
+                                       wp.updates.decrementCount( 'theme' );
+                               }
+
+                               // Remove from views.
+                               if ( $themeRow.hasClass( 'inactive' ) ) {
+                                       totals.disabled--;
+                                       if ( totals.disabled ) {
+                                               $views.find( '.disabled .count' ).text( '(' + totals.disabled + ')' );
+                                       } else {
+                                               $views.find( '.disabled' ).remove();
+                                       }
+                               }
+
+                               // There is always at least one theme available.
+                               $views.find( '.all .count' ).text( '(' + --totals.all + ')' );
+                       } );
+               }
+
+               wp.a11y.speak( wp.updates.l10n.deleted, 'polite' );
+
+               $document.trigger( 'wp-delete-theme-success', response );
+       };
+
+       /**
+        * Updates the UI appropriately after a failed theme deletion.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} deleteThemeError
+        * @param {object} response              Response from the server.
+        * @param {string} response.slug         Slug of the theme to be deleted.
+        * @param {string} response.errorCode    Error code for the error that occurred.
+        * @param {string} response.errorMessage The error that occurred.
+        */
+       wp.updates.deleteThemeError = function( response ) {
+               var $themeRow    = $( 'tr.inactive[data-slug="' + response.slug + '"]' ),
+                       $button      = $( '.theme-actions .delete-theme' ),
+                       updateRow    = wp.template( 'item-update-row' ),
+                       $updateRow   = $themeRow.siblings( '#' + response.slug + '-update' ),
+                       errorMessage = wp.updates.l10n.deleteFailed.replace( '%s', response.errorMessage ),
+                       $message     = wp.updates.adminNotice( {
+                               className: 'update-message notice-error notice-alt',
+                               message:   errorMessage
+                       } );
+
+               if ( wp.updates.maybeHandleCredentialError( response, 'delete-theme' ) ) {
+                       return;
+               }
+
+               if ( 'themes-network' === pagenow ) {
+                       if ( ! $updateRow.length ) {
+                               $themeRow.addClass( 'update' ).after(
+                                       updateRow( {
+                                               slug: response.slug,
+                                               colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
+                                               content: $message
+                                       } )
+                               );
+                       } else {
+                               // Remove previous error messages, if any.
+                               $updateRow.find( '.notice-error' ).remove();
+                               $updateRow.find( '.plugin-update' ).append( $message );
+                       }
+               } else {
+                       $( '.theme-info .theme-description' ).before( $message );
+               }
+
+               $button.html( $button.data( 'originaltext' ) );
+
+               wp.a11y.speak( errorMessage, 'assertive' );
+
+               $document.trigger( 'wp-theme-delete-error', response );
+       };
+
+       /**
+        * Adds the appropriate callback based on the type of action and the current page.
+        *
+        * @since 4.6.0
+        * @private
+        *
+        * @param {object} data   AJAX payload.
+        * @param {string} action The type of request to perform.
+        * @return {object} The AJAX payload with the appropriate callbacks.
+        */
+       wp.updates._addCallbacks = function( data, action ) {
+               if ( 'import' === pagenow && 'install-plugin' === action ) {
+                       data.success = wp.updates.installImporterSuccess;
+                       data.error   = wp.updates.installImporterError;
+               }
+
+               return data;
+       };
+
+       /**
+        * Pulls available jobs from the queue and runs them.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 Can handle multiple job types.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.queueChecker = function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( wp.updates.updateLock || wp.updates.updateQueue.length <= 0 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         var job;
+
+               if ( wp.updates.ajaxLocked || ! wp.updates.queue.length ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                var job = wp.updates.updateQueue.shift();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         job = wp.updates.queue.shift();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.updatePlugin( job.data.plugin, job.data.slug );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Handle a queue job.
+               switch ( job.action ) {
+                       case 'install-plugin':
+                               wp.updates.installPlugin( job.data );
+                               break;
+
+                       case 'update-plugin':
+                               wp.updates.updatePlugin( job.data );
+                               break;
+
+                       case 'delete-plugin':
+                               wp.updates.deletePlugin( job.data );
+                               break;
+
+                       case 'install-theme':
+                               wp.updates.installTheme( job.data );
+                               break;
+
+                       case 'update-theme':
+                               wp.updates.updateTheme( job.data );
+                               break;
+
+                       case 'delete-theme':
+                               wp.updates.deleteTheme( job.data );
+                               break;
+
+                       default:
+                               window.console.error( 'Failed to execute queued update job.', job );
+                               break;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">         /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Request the users filesystem credentials if we don't have them already.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Requests the users filesystem credentials if they aren't already known.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         *
+        * @param {Event=} event Optional. Event interface.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.requestFilesystemCredentials = function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( wp.updates.updateDoneSuccessfully === false ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( false === wp.updates.filesystemCredentials.available ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                         * For the plugin install screen, return the focus to the install button
-                        * after exiting the credentials request modal.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                  * After exiting the credentials request modal,
+                        * return the focus to the element triggering the request.
</ins><span class="cx" style="display: block; padding: 0 10px">                          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( 'plugin-install' === pagenow && event ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( event && ! wp.updates.$elToReturnFocusToFromCredentialsModal ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 wp.updates.$elToReturnFocusToFromCredentialsModal = $( event.target );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.updateLock = true;
-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.ajaxLocked = true;
</ins><span class="cx" style="display: block; padding: 0 10px">                         wp.updates.requestForCredentialsModalOpen();
</span><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><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Requests the users filesystem credentials if needed and there is no lock.
+        *
+        * @since 4.6.0
+        *
+        * @param {Event=} event Optional. Event interface.
+        */
+       wp.updates.maybeRequestFilesystemCredentials = function( event ) {
+               if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
+                       wp.updates.requestFilesystemCredentials( event );
+               }
+       };
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Keydown handler for the request for credentials modal.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Close the modal when the escape key is pressed.
-        * Constrain keyboard navigation to inside the modal.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Closes the modal when the escape key is pressed and
+        * constrains keyboard navigation to inside the modal.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         *
+        * @param {Event} event Event interface.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.keydown = function( event ) {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( 27 === event.keyCode ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        wp.updates.requestForCredentialsModalCancel();
</span><span class="cx" style="display: block; padding: 0 10px">                } else if ( 9 === event.keyCode ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // #upgrade button must always be the last focusable element in the dialog.
-                       if ( event.target.id === 'upgrade' && ! event.shiftKey ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       // #upgrade button must always be the last focus-able element in the dialog.
+                       if ( 'upgrade' === event.target.id && ! event.shiftKey ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $( '#hostname' ).focus();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 event.preventDefault();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        } else if ( event.target.id === 'hostname' && event.shiftKey ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 } else if ( 'hostname' === event.target.id && event.shiftKey ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $( '#upgrade' ).focus();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 event.preventDefault();
</span><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><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Open the request for credentials modal.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Opens the request for credentials modal.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.requestForCredentialsModalOpen = function() {
</span><span class="cx" style="display: block; padding: 0 10px">                var $modal = $( '#request-filesystem-credentials-dialog' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $( 'body' ).addClass( 'modal-open' );
</span><span class="cx" style="display: block; padding: 0 10px">                $modal.show();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 $modal.find( 'input:enabled:first' ).focus();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $modal.keydown( wp.updates.keydown );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $modal.on( 'keydown', wp.updates.keydown );
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Close the request for credentials modal.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Closes the request for credentials modal.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.requestForCredentialsModalClose = function() {
</span><span class="cx" style="display: block; padding: 0 10px">                $( '#request-filesystem-credentials-dialog' ).hide();
</span><span class="cx" style="display: block; padding: 0 10px">                $( 'body' ).removeClass( 'modal-open' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.$elToReturnFocusToFromCredentialsModal.focus();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( wp.updates.$elToReturnFocusToFromCredentialsModal ) {
+                       wp.updates.$elToReturnFocusToFromCredentialsModal.focus();
+               }
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * The steps that need to happen when the modal is canceled out
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Takes care of the steps that need to happen when the modal is canceled out.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.6.0 Triggers an event for callbacks to listen to and add their actions.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        wp.updates.requestForCredentialsModalCancel = function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // no updateLock and no updateQueue means we already have cleared things up
-               var data, $message;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if( wp.updates.updateLock === false && wp.updates.updateQueue.length === 0 ){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Not ajaxLocked and no queue means we already have cleared things up.
+               if ( ! wp.updates.ajaxLocked && ! wp.updates.queue.length ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                data = wp.updates.updateQueue[0].data;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         _.each( wp.updates.queue, function( job ) {
+                       $document.trigger( 'credential-modal-cancel', job );
+               } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // remove the lock, and clear the queue
-               wp.updates.updateLock = false;
-               wp.updates.updateQueue = [];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Remove the lock, and clear the queue.
+               wp.updates.ajaxLocked = false;
+               wp.updates.queue = [];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                wp.updates.requestForCredentialsModalClose();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
-                       $message = $( '[data-plugin="' + data.plugin + '"]' ).next().find( '.update-message' );
-               } else if ( 'plugin-install' === pagenow ) {
-                       $message = $( '.plugin-card-' + data.slug ).find( '.update-now' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ };
+
+       /**
+        * Displays an error message in the request for credentials form.
+        *
+        * @since 4.2.0
+        *
+        * @param {string} message Error message.
+        */
+       wp.updates.showErrorInCredentialsForm = function( message ) {
+               var $modal = $( '#request-filesystem-credentials-form' );
+
+               // Remove any existing error.
+               $modal.find( '.notice' ).remove();
+               $modal.find( '#request-filesystem-credentials-title' ).after( '<div class="notice notice-alt notice-error"><p>' + message + '</p></div>' );
+       };
+
+       /**
+        * Handles credential errors and runs events that need to happen in that case.
+        *
+        * @since 4.2.0
+        *
+        * @param {object} response Ajax response.
+        * @param {string} action   The type of request to perform.
+        */
+       wp.updates.credentialError = function( response, action ) {
+
+               // Restore callbacks.
+               response = wp.updates._addCallbacks( response, action );
+
+               wp.updates.queue.push( {
+                       action: action,
+
+                       /*
+                        * Not cool that we're depending on response for this data.
+                        * This would feel more whole in a view all tied together.
+                        */
+                       data: response
+               } );
+
+               wp.updates.filesystemCredentials.available = false;
+               wp.updates.showErrorInCredentialsForm( response.errorMessage );
+               wp.updates.requestFilesystemCredentials();
+       };
+
+       /**
+        * Handles credentials errors if it could not connect to the filesystem.
+        *
+        * @since 4.6.0
+        *
+        * @typedef {object} maybeHandleCredentialError
+        * @param {object} response              Response from the server.
+        * @param {string} response.errorCode    Error code for the error that occurred.
+        * @param {string} response.errorMessage The error that occurred.
+        * @param {string} action                The type of request to perform.
+        * @returns {boolean} Whether there is an error that needs to be handled or not.
+        */
+       wp.updates.maybeHandleCredentialError = function( response, action ) {
+               if ( response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) {
+                       wp.updates.credentialError( response, action );
+                       return true;
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $message.removeClass( 'updating-message' );
-               $message.html( $message.data( 'originaltext' ) );
-               wp.a11y.speak( wp.updates.l10n.updateCancel );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return false;
</ins><span class="cx" style="display: block; padding: 0 10px">         };
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Potentially add an AYS to a user attempting to leave the page
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Validates an AJAX response to ensure it's a proper object.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * If the response deems to be invalid, an admin notice is being displayed.
+        *
+        * @param {(object|string)} response              Response from the server.
+        * @param {function=}       response.always       Optional. Callback for when the Deferred is resolved or rejected.
+        * @param {string=}         response.statusText   Optional. Status message corresponding to the status code.
+        * @param {string=}         response.responseText Optional. Request response as text.
+        * @param {string}          action                Type of action the response is referring to. Can be 'delete',
+        *                                                'update' or 'install'.
+        */
+       wp.updates.isValidResponse = function( response, action ) {
+               var error = wp.updates.l10n.unknownError,
+                   errorMessage;
+
+               // Make sure the response is a valid data object and not a Promise object.
+               if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
+                       return true;
+               }
+
+               if ( _.isString( response ) ) {
+                       error = response;
+               } else if ( _.isString( response.responseText ) && '' !== response.responseText ) {
+                       error = response.responseText;
+               } else if ( _.isString( response.statusText ) ) {
+                       error = response.statusText;
+               }
+
+               switch ( action ) {
+                       case 'update':
+                               errorMessage = wp.updates.l10n.updateFailed;
+                               break;
+
+                       case 'install':
+                               errorMessage = wp.updates.l10n.installFailed;
+                               break;
+
+                       case 'delete':
+                               errorMessage = wp.updates.l10n.deleteFailed;
+                               break;
+               }
+
+               errorMessage = errorMessage.replace( '%s', error );
+
+               // Add admin notice.
+               wp.updates.addAdminNotice( {
+                       id:        'unknown_error',
+                       className: 'notice-error is-dismissible',
+                       message:   errorMessage
+               } );
+
+               // Remove the lock, and clear the queue.
+               wp.updates.ajaxLocked = false;
+               wp.updates.queue      = [];
+
+               // Change buttons of all running updates.
+               $( '.button.updating-message' )
+                       .removeClass( 'updating-message' )
+                       .attr( 'aria-label', wp.updates.l10n.updateFailedShort )
+                       .prop( 'disabled', true )
+                       .text( wp.updates.l10n.updateFailedShort );
+
+               $( '.updating-message:not(.button):not(.thickbox)' )
+                       .removeClass( 'updating-message notice-warning' )
+                       .addClass( 'notice-error' )
+                       .find( 'p' ).text( errorMessage );
+
+               wp.a11y.speak( errorMessage, 'assertive' );
+
+               return false;
+       };
+
+       /**
+        * Potentially adds an AYS to a user attempting to leave the page.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * If an update is on-going and a user attempts to leave the page,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * open an "Are you sure?" alert.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * opens an "Are you sure?" alert.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">         wp.updates.beforeunload = function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( wp.updates.updateLock ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( wp.updates.ajaxLocked ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return wp.updates.l10n.beforeunload;
</span><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">+        $( function() {
+               var $pluginFilter    = $( '#plugin-filter' ),
+                       $bulkActionForm  = $( '#bulk-action-form' ),
+                       $filesystemModal = $( '#request-filesystem-credentials-dialog' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $( document ).ready( function() {
-               // Set initial focus on the first empty form field.
-               $( '#request-filesystem-credentials-form input[value=""]:first' ).focus();
-
</del><span class="cx" style="display: block; padding: 0 10px">                 /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Check whether a user needs to submit filesystem credentials based on whether
-                * the form was output on the page server-side.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * Whether a user needs to submit filesystem credentials.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * This is based on whether the form was output on the page server-side.
+                *
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @see {wp_print_request_filesystem_credentials_modal() in PHP}
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp.updates.shouldRequestFilesystemCredentials = ( $( '#request-filesystem-credentials-dialog' ).length <= 0 ) ? false : true;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp.updates.shouldRequestFilesystemCredentials = $filesystemModal.length > 0;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // File system credentials form submit noop-er / handler.
-               $( '#request-filesystem-credentials-dialog form' ).on( 'submit', function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * File system credentials form submit noop-er / handler.
+                *
+                * @since 4.2.0
+                */
+               $filesystemModal.on( 'submit', 'form', function( event ) {
+                       event.preventDefault();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Persist the credentials input by the user for the duration of the page load.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.filesystemCredentials.ftp.hostname = $('#hostname').val();
-                       wp.updates.filesystemCredentials.ftp.username = $('#username').val();
-                       wp.updates.filesystemCredentials.ftp.password = $('#password').val();
-                       wp.updates.filesystemCredentials.ftp.connectionType = $('input[name="connection_type"]:checked').val();
-                       wp.updates.filesystemCredentials.ssh.publicKey = $('#public_key').val();
-                       wp.updates.filesystemCredentials.ssh.privateKey = $('#private_key').val();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.filesystemCredentials.ftp.hostname       = $( '#hostname' ).val();
+                       wp.updates.filesystemCredentials.ftp.username       = $( '#username' ).val();
+                       wp.updates.filesystemCredentials.ftp.password       = $( '#password' ).val();
+                       wp.updates.filesystemCredentials.ftp.connectionType = $( 'input[name="connection_type"]:checked' ).val();
+                       wp.updates.filesystemCredentials.ssh.publicKey      = $( '#public_key' ).val();
+                       wp.updates.filesystemCredentials.ssh.privateKey     = $( '#private_key' ).val();
+                       wp.updates.filesystemCredentials.available          = true;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.requestForCredentialsModalClose();
-
</del><span class="cx" style="display: block; padding: 0 10px">                         // Unlock and invoke the queue.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.updateLock = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.ajaxLocked = false;
</ins><span class="cx" style="display: block; padding: 0 10px">                         wp.updates.queueChecker();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return false;
-               });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.requestForCredentialsModalClose();
+               } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Close the request credentials modal when
-               $( '#request-filesystem-credentials-dialog [data-js-action="close"], .notification-dialog-background' ).on( 'click', function() {
-                       wp.updates.requestForCredentialsModalCancel();
-               });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Closes the request credentials modal when clicking the 'Cancel' button or outside of the modal.
+                *
+                * @since 4.2.0
+                */
+               $filesystemModal.on( 'click', '[data-js-action="close"], .notification-dialog-background', wp.updates.requestForCredentialsModalCancel );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Hide SSH fields when not selected.
-               $( '#request-filesystem-credentials-form input[name="connection_type"]' ).on( 'change', function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Hide SSH fields when not selected.
+                *
+                * @since 4.2.0
+                */
+               $filesystemModal.on( 'change', 'input[name="connection_type"]', function() {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $( '#ssh-keys' ).toggleClass( 'hidden', ( 'ssh' !== $( this ).val() ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         } ).change();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Click handler for plugin updates in List Table view.
-               $( '.plugin-update-tr' ).on( 'click', '.update-link', function( e ) {
-                       e.preventDefault();
-                       if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) {
-                               wp.updates.requestFilesystemCredentials( e );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Handles events after the credential modal was closed.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event}  event Event interface.
+                * @param {string} job   The install/update.delete request.
+                */
+               $document.on( 'credential-modal-cancel', function( event, job ) {
+                       var $updatingMessage = $( '.updating-message' ),
+                               $message, originalText;
+
+                       if ( 'import' === pagenow ) {
+                               $updatingMessage.removeClass( 'updating-message' );
+                       } else if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
+                               $message = $( 'tr[data-plugin="' + job.data.plugin + '"]' ).find( '.update-message' );
+                       } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
+                               $message = $( '.update-now.updating-message' );
+                       } else {
+                               $message = $updatingMessage;
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var updateRow = $( e.target ).parents( '.plugin-update-tr' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       if ( $message ) {
+                               originalText = $message.data( 'originaltext' );
+
+                               if ( 'undefined' === typeof originalText ) {
+                                       originalText = $( '<p>' ).html( $message.find( 'p' ).data( 'originaltext' ) );
+                               }
+
+                               $message
+                                       .removeClass( 'updating-message' )
+                                       .html( originalText );
+                       }
+
+                       wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
+               } );
+
+               /**
+                * Click handler for plugin updates in List Table view.
+                *
+                * @since 4.2.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $bulkActionForm.on( 'click', '[data-plugin] .update-link', function( event ) {
+                       var $message   = $( event.target ),
+                               $pluginRow = $message.parents( 'tr' );
+
+                       event.preventDefault();
+
+                       if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
+                               return;
+                       }
+
+                       wp.updates.maybeRequestFilesystemCredentials( event );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Return the user to the input box of the plugin's table row after closing the modal.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.$elToReturnFocusToFromCredentialsModal = updateRow.prev().find( '.check-column input' );
-                       wp.updates.updatePlugin( updateRow.data( 'plugin' ), updateRow.data( 'slug' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.$elToReturnFocusToFromCredentialsModal = $pluginRow.find( '.check-column input' );
+                       wp.updates.updatePlugin( {
+                               plugin: $pluginRow.data( 'plugin' ),
+                               slug:   $pluginRow.data( 'slug' )
+                       } );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $( '.plugin-card' ).on( 'click', '.update-now', function( e ) {
-                       e.preventDefault();
-                       var $button = $( e.target );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Click handler for plugin updates in plugin install view.
+                *
+                * @since 4.2.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $pluginFilter.on( 'click', '.update-now', function( event ) {
+                       var $button = $( event.target );
+                       event.preventDefault();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // Do nothing while updating and when the button is disabled.
</del><span class="cx" style="display: block; padding: 0 10px">                         if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                return;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) {
-                               wp.updates.requestFilesystemCredentials( e );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 wp.updates.maybeRequestFilesystemCredentials( event );
+
+                       wp.updates.updatePlugin( {
+                               plugin: $button.data( 'plugin' ),
+                               slug:   $button.data( 'slug' )
+                       } );
+               } );
+
+               /**
+                * Click handler for plugin installs in plugin install view.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $pluginFilter.on( 'click', '.install-now', function( event ) {
+                       var $button = $( event.target );
+                       event.preventDefault();
+
+                       if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
+                               return;
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        wp.updates.updatePlugin( $button.data( 'plugin' ), $button.data( 'slug' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
+                               wp.updates.requestFilesystemCredentials( event );
+
+                               $document.on( 'credential-modal-cancel', function() {
+                                       var $message = $( '.install-now.updating-message' );
+
+                                       $message
+                                               .removeClass( 'updating-message' )
+                                               .text( wp.updates.l10n.installNow );
+
+                                       wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
+                               } );
+                       }
+
+                       wp.updates.installPlugin( {
+                               slug: $button.data( 'slug' )
+                       } );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $( '#plugin_update_from_iframe' ).on( 'click' , function( e ) {
-                       var target, job;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Click handler for plugin deletions.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
+                       var $pluginRow = $( event.target ).parents( 'tr' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        target = window.parent == window ? null : window.parent,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 event.preventDefault();
+
+                       if ( ! window.confirm( wp.updates.l10n.aysDeleteUninstall.replace( '%s', $pluginRow.find( '.plugin-title strong' ).text() ) ) ) {
+                               return;
+                       }
+
+                       wp.updates.maybeRequestFilesystemCredentials( event );
+
+                       wp.updates.deletePlugin( {
+                               plugin: $pluginRow.data( 'plugin' ),
+                               slug:   $pluginRow.data( 'slug' )
+                       } );
+
+               } );
+
+               /**
+                * Click handler for theme updates.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $document.on( 'click', '.themes-php.network-admin .update-link', function( event ) {
+                       var $message  = $( event.target ),
+                               $themeRow = $message.parents( 'tr' );
+
+                       event.preventDefault();
+
+                       if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
+                               return;
+                       }
+
+                       wp.updates.maybeRequestFilesystemCredentials( event );
+
+                       // Return the user to the input box of the theme's table row after closing the modal.
+                       wp.updates.$elToReturnFocusToFromCredentialsModal = $themeRow.find( '.check-column input' );
+                       wp.updates.updateTheme( {
+                               slug: $themeRow.data( 'slug' )
+                       } );
+               } );
+
+               /**
+                * Click handler for theme deletions.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
+                       var $themeRow = $( event.target ).parents( 'tr' );
+
+                       event.preventDefault();
+
+                       if ( ! window.confirm( wp.updates.l10n.aysDelete.replace( '%s', $themeRow.find( '.theme-title strong' ).text() ) ) ) {
+                               return;
+                       }
+
+                       wp.updates.maybeRequestFilesystemCredentials( event );
+
+                       wp.updates.deleteTheme( {
+                               slug: $themeRow.data( 'slug' )
+                       } );
+               } );
+
+               /**
+                * Bulk action handler for plugins and themes.
+                *
+                * Handles both deletions and updates.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $bulkActionForm.on( 'click', '[type="submit"]', function( event ) {
+                       var bulkAction    = $( event.target ).siblings( 'select' ).val(),
+                               itemsSelected = $bulkActionForm.find( 'input[name="checked[]"]:checked' ),
+                               success       = 0,
+                               error         = 0,
+                               errorMessages = [],
+                               type, action;
+
+                       // Determine which type of item we're dealing with.
+                       switch ( pagenow ) {
+                               case 'plugins':
+                               case 'plugins-network':
+                                       type = 'plugin';
+                                       break;
+
+                               case 'themes-network':
+                                       type = 'theme';
+                                       break;
+
+                               default:
+                                       window.console.error( 'The page "%s" is not white-listed for bulk action handling.', pagenow );
+                                       return;
+                       }
+
+                       // Bail if there were no items selected.
+                       if ( ! itemsSelected.length ) {
+                               event.preventDefault();
+                               $( 'html, body' ).animate( { scrollTop: 0 } );
+
+                               return wp.updates.addAdminNotice( {
+                                       id:        'no-items-selected',
+                                       className: 'notice-error is-dismissible',
+                                       message:   wp.updates.l10n.noItemsSelected
+                               } );
+                       }
+
+                       // Determine the type of request we're dealing with.
+                       switch ( bulkAction ) {
+                               case 'update-selected':
+                                       action = bulkAction.replace( 'selected', type );
+                                       break;
+
+                               case 'delete-selected':
+                                       if ( ! window.confirm( 'plugin' === type ? wp.updates.l10n.aysBulkDelete : wp.updates.l10n.aysBulkDeleteThemes ) ) {
+                                               event.preventDefault();
+                                               return;
+                                       }
+
+                                       action = bulkAction.replace( 'selected', type );
+                                       break;
+
+                               default:
+                                       window.console.error( 'Failed to identify bulk action: %s', bulkAction );
+                                       return;
+                       }
+
+                       wp.updates.maybeRequestFilesystemCredentials( event );
+
+                       event.preventDefault();
+
+                       // Un-check the bulk checkboxes.
+                       $bulkActionForm.find( '.manage-column [type="checkbox"]' ).prop( 'checked', false );
+
+                       // Find all the checkboxes which have been checked.
+                       itemsSelected.each( function( index, element ) {
+                               var $checkbox  = $( element ),
+                                       $itemRow = $checkbox.parents( 'tr' );
+
+                               // Un-check the box.
+                               $checkbox.prop( 'checked', false );
+
+                               // Only add update-able items to the update queue.
+                               if ( 'update-selected' === bulkAction && ( ! $itemRow.hasClass( 'update' ) || $itemRow.find( 'notice-error' ).length ) ) {
+                                       return;
+                               }
+
+                               // Add it to the queue.
+                               wp.updates.queue.push( {
+                                       action: action,
+                                       data:   {
+                                               plugin: $itemRow.data( 'plugin' ),
+                                               slug:   $itemRow.data( 'slug' )
+                                       }
+                               } );
+                       } );
+
+                       // Display bulk notification for updates of any kind.
+                       $document.on( 'wp-plugin-update-success wp-plugin-update-error wp-theme-update-success wp-theme-update-error', function( event, response ) {
+                               var $bulkActionNotice, itemName;
+
+                               if ( 'wp-' + response.update + '-update-success' === event.type ) {
+                                       success++;
+                               } else {
+                                       itemName = response.pluginName ? response.pluginName : $( '[data-slug="' + response.slug + '"]' ).find( '.theme-title strong' ).text();
+
+                                       error++;
+                                       errorMessages.push( itemName + ': ' + response.errorMessage );
+                               }
+
+                               wp.updates.adminNotice = wp.template( 'wp-bulk-updates-admin-notice' );
+
+                               wp.updates.addAdminNotice( {
+                                       id:            'bulk-action-notice',
+                                       successes:     success,
+                                       errors:        error,
+                                       errorMessages: errorMessages,
+                                       type:          response.update
+                               } );
+
+                               $bulkActionNotice = $( '#bulk-action-notice' ).on( 'click', 'button', function() {
+                                       $bulkActionNotice.find( 'ul' ).toggleClass( 'hidden' );
+                               } );
+
+                               if ( error > 0 && ! wp.updates.queue.length ) {
+                                       $( 'html, body' ).animate( { scrollTop: 0 } );
+                               }
+                       } );
+
+                       // Reset admin notice template after #bulk-action-notice was added.
+                       $document.on( 'wp-updates-notice-added', function() {
+                               wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' );
+                       } );
+
+                       // Check the queue, now that the event handlers have been added.
+                       wp.updates.queueChecker();
+               } );
+
+               /**
+                * Handles changes to the plugin search box on the new-plugin page,
+                * searching the repository dynamically.
+                *
+                * @since 4.6.0
+                */
+               $( 'input.wp-filter-search, .wp-filter input[name="s"]' ).on( 'keyup search', _.debounce( function() {
+                       var $form = $( '#plugin-filter' ).empty(),
+                               data  = _.extend( {
+                                       _ajax_nonce: wp.updates.ajaxNonce,
+                                       s:           $( '<p />' ).html( $( this ).val() ).text(),
+                                       tab:         'search',
+                                       type:        $( '#typeselector' ).val()
+                               }, { type: 'term' } );
+
+                       if ( wp.updates.searchTerm === data.s ) {
+                               return;
+                       } else {
+                               wp.updates.searchTerm = data.s;
+                       }
+
+                       history.pushState( null, '', location.href.split( '?' )[0] + '?' + $.param( _.omit( data, '_ajax_nonce' ) ) );
+
+                       if ( 'undefined' !== typeof wp.updates.searchRequest ) {
+                               wp.updates.searchRequest.abort();
+                       }
+                       $( 'body' ).addClass( 'loading-content' );
+
+                       wp.updates.searchRequest = wp.ajax.post( 'search-install-plugins', data ).done( function( response ) {
+                               $( 'body' ).removeClass( 'loading-content' );
+                               $form.append( response.items );
+                               delete wp.updates.searchRequest;
+                       } );
+               }, 500 ) );
+
+               /**
+                * Handles changes to the plugin search box on the Installed Plugins screen,
+                * searching the plugin list dynamically.
+                *
+                * @since 4.6.0
+                */
+               $( '#plugin-search-input' ).on( 'keyup search', _.debounce( function() {
+                       var data = {
+                               _ajax_nonce: wp.updates.ajaxNonce,
+                               s:           $( '<p />' ).html( $( this ).val() ).text()
+                       };
+
+                       if ( wp.updates.searchTerm === data.s ) {
+                               return;
+                       } else {
+                               wp.updates.searchTerm = data.s;
+                       }
+
+                       history.pushState( null, '', location.href.split( '?' )[0] + '?s=' + data.s );
+
+                       if ( 'undefined' !== typeof wp.updates.searchRequest ) {
+                               wp.updates.searchRequest.abort();
+                       }
+
+                       $bulkActionForm.empty();
+                       $( 'body' ).addClass( 'loading-content' );
+
+                       wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
+
+                               // Can we just ditch this whole subtitle business?
+                               var $subTitle    = $( '<span />' ).addClass( 'subtitle' ).html( wp.updates.l10n.searchResults.replace( '%s', data.s ) ),
+                                       $oldSubTitle = $( '.wrap .subtitle' );
+
+                               if ( ! data.s.length ) {
+                                       $oldSubTitle.remove();
+                               } else if ( $oldSubTitle.length ) {
+                                       $oldSubTitle.replaceWith( $subTitle );
+                               } else {
+                                       $( '.wrap h1' ).append( $subTitle );
+                               }
+
+                               $( 'body' ).removeClass( 'loading-content' );
+                               $bulkActionForm.append( response.items );
+                               delete wp.updates.searchRequest;
+                       } );
+               }, 500 ) );
+
+               /**
+                * Trigger a search event when the search form gets submitted.
+                *
+                * @since 4.6.0
+                */
+               $document.on( 'submit', '.search-plugins', function( event ) {
+                       event.preventDefault();
+
+                       $( 'input.wp-filter-search' ).trigger( 'search' );
+               } );
+
+               /**
+                * Trigger a search event when the search type gets changed.
+                *
+                * @since 4.6.0
+                */
+               $( '#typeselector' ).on( 'change', function() {
+                       $( 'input[name="s"]' ).trigger( 'search' );
+               } );
+
+               /**
+                * Click handler for updating a plugin from the details modal on `plugin-install.php`.
+                *
+                * @since 4.2.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $( '#plugin_update_from_iframe' ).on( 'click', function( event ) {
+                       var target = window.parent === window ? null : window.parent,
+                               update;
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $.support.postMessage = !! window.postMessage;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( $.support.postMessage === false || target === null || window.parent.location.pathname.indexOf( 'update-core.php' ) !== -1 )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( false === $.support.postMessage || null === target ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        e.preventDefault();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 event.preventDefault();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        job = {
-                               action: 'updatePlugin',
-                               type: 'update-plugin',
-                               data: {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 update = {
+                               action: 'update-plugin',
+                               data:   {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         plugin: $( this ).data( 'plugin' ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        slug:   $( this ).data( 'slug' )
+                               }
+                       };
+
+                       target.postMessage( JSON.stringify( update ), window.location.origin );
+               } );
+
+               /**
+                * Click handler for installing a plugin from the details modal on `plugin-install.php`.
+                *
+                * @since 4.6.0
+                *
+                * @param {Event} event Event interface.
+                */
+               $( '#plugin_install_from_iframe' ).on( 'click', function( event ) {
+                       var target = window.parent === window ? null : window.parent,
+                               install;
+
+                       $.support.postMessage = !! window.postMessage;
+
+                       if ( false === $.support.postMessage || null === target ) {
+                               return;
+                       }
+
+                       event.preventDefault();
+
+                       install = {
+                               action: 'install-plugin',
+                               data:   {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         slug: $( this ).data( 'slug' )
</span><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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        target.postMessage( JSON.stringify( job ), window.location.origin );
-               });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 target.postMessage( JSON.stringify( install ), window.location.origin );
+               } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Handles postMessage events.
+                *
+                * @since 4.2.0
+                * @since 4.6.0 Switched `update-plugin` action to use the queue.
+                *
+                * @param {Event} event Event interface.
+                */
+               $( window ).on( 'message', function( event ) {
+                       var originalEvent  = event.originalEvent,
+                               expectedOrigin = document.location.protocol + '//' + document.location.hostname,
+                               message;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $( window ).on( 'message', function( e ) {
-               var event = e.originalEvent,
-                       message,
-                       loc = document.location,
-                       expectedOrigin = loc.protocol + '//' + loc.hostname;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( originalEvent.origin !== expectedOrigin ) {
+                               return;
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( event.origin !== expectedOrigin ) {
-                       return;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 message = $.parseJSON( originalEvent.data );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                message = $.parseJSON( event.data );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( 'undefined' === typeof message.action ) {
+                               return;
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( typeof message.action === 'undefined' ) {
-                       return;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 switch ( message.action ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                switch (message.action){
-                       case 'decrementUpdateCount' :
-                               wp.updates.decrementCount( message.upgradeType );
-                               break;
-                       case 'updatePlugin' :
-                               tb_remove();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         // Called from `wp-admin/includes/class-wp-upgrader-skins.php`.
+                               case 'decrementUpdateCount':
+                                       /** @property {string} message.upgradeType */
+                                       wp.updates.decrementCount( message.upgradeType );
+                                       break;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                wp.updates.updateQueue.push( message );
-                               wp.updates.queueChecker();
-                               break;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         case 'install-plugin':
+                               case 'update-plugin':
+                                       /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
+                                       window.tb_remove();
+                                       /* jscs:enable */
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 message.data = wp.updates._addCallbacks( message.data, message.action );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $( window ).on( 'beforeunload', wp.updates.beforeunload );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 wp.updates.queue.push( message );
+                                       wp.updates.queueChecker();
+                                       break;
+                       }
+               } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-})( jQuery, window.wp, window.pagenow, window.ajaxurl );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Adds a callback to display a warning before leaving the page.
+                *
+                * @since 4.2.0
+                */
+               $( window ).on( 'beforeunload', wp.updates.beforeunload );
+       } );
+})( jQuery, window.wp, _.extend( window._wpUpdatesSettings, window._wpUpdatesItemCounts || {} ) );
</ins></span></pre></div>
<a id="trunksrcwpadminnetworkthemesphp"></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/network/themes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/network/themes.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/network/themes.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -228,6 +228,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $title = __('Themes');
</span><span class="cx" style="display: block; padding: 0 10px"> $parent_file = 'themes.php';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_enqueue_script( 'updates' );
</ins><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'theme-preview' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once(ABSPATH . 'wp-admin/admin-header.php');
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -287,7 +288,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        echo '<p class="clear">' . __( 'The following themes are installed but incomplete.' ) . '</p>';
</span><span class="cx" style="display: block; padding: 0 10px"> ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<form method="post">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<form id="bulk-action-form" method="post">
</ins><span class="cx" style="display: block; padding: 0 10px"> <input type="hidden" name="theme_status" value="<?php echo esc_attr($status) ?>" />
</span><span class="cx" style="display: block; padding: 0 10px"> <input type="hidden" name="paged" value="<?php echo esc_attr($page) ?>" />
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -297,4 +298,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_print_request_filesystem_credentials_modal();
+wp_print_admin_notice_templates();
+wp_print_update_row_templates();
+
</ins><span class="cx" style="display: block; padding: 0 10px"> include(ABSPATH . 'wp-admin/admin-footer.php');
</span></span></pre></div>
<a id="trunksrcwpadminplugininstallphp"></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/plugin-install.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/plugin-install.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/plugin-install.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -148,10 +148,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param int $paged The current page number of the plugins list table.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> do_action( "install_plugins_$tab", $paged ); ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       <span class="spinner"></span>
</ins><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px"> wp_print_request_filesystem_credentials_modal();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_print_admin_notice_templates();
</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">  * WordPress Administration Template Footer.
</span></span></pre></div>
<a id="trunksrcwpadminpluginsphp"></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/plugins.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/plugins.php    2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/plugins.php      2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -507,7 +507,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php $wp_list_table->views(); ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<form method="get">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<form class="search-form search-plugins" method="get">
</ins><span class="cx" style="display: block; padding: 0 10px"> <?php $wp_list_table->search_box( __( 'Search Installed Plugins' ), 'plugin' ); ?>
</span><span class="cx" style="display: block; padding: 0 10px"> </form>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -519,9 +519,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php $wp_list_table->display(); ?>
</span><span class="cx" style="display: block; padding: 0 10px"> </form>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <span class="spinner"></span>
</ins><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px"> wp_print_request_filesystem_credentials_modal();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_print_admin_notice_templates();
+wp_print_update_row_templates();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> include(ABSPATH . 'wp-admin/admin-footer.php');
</span></span></pre></div>
<a id="trunksrcwpadminthemeinstallphp"></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/theme-install.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/theme-install.php      2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/theme-install.php        2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -58,6 +58,7 @@
</span><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"> wp_enqueue_script( 'theme' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_enqueue_script( 'updates' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> if ( $tab ) {
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -234,68 +235,92 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="theme-screenshot blank"></div>
</span><span class="cx" style="display: block; padding: 0 10px">        <# } #>
</span><span class="cx" style="display: block; padding: 0 10px">        <span class="more-details"><?php _ex( 'Details &amp; Preview', 'theme' ); ?></span>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <div class="theme-author"><?php printf( __( 'By %s' ), '{{ data.author }}' ); ?></div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <div class="theme-author">
+               <?php
+               /* translators: %s: Theme author name */
+               printf( __( 'By %s' ), '{{ data.author }}' );
+               ?>
+       </div>
</ins><span class="cx" style="display: block; padding: 0 10px">         <h3 class="theme-name">{{ data.name }}</h3>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="theme-actions">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <a class="button button-primary" href="{{ data.install_url }}"><?php esc_html_e( 'Install' ); ?></a>
-               <a class="button button-secondary preview install-theme-preview" href="#"><?php esc_html_e( 'Preview' ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <# if ( data.installed ) { #>
+                       <# if ( data.activate_url ) { #>
+                               <a class="button button-primary activate" href="{{ data.activate_url }}"><?php esc_html_e( 'Activate' ); ?></a>
+                       <# } #>
+                       <# if ( data.customize_url ) { #>
+                               <a class="button button-secondary load-customize" href="{{ data.customize_url }}"><?php esc_html_e( 'Live Preview' ); ?></a>
+                       <# } else { #>
+                               <button class="button-secondary preview install-theme-preview"><?php esc_html_e( 'Preview' ); ?></button>
+                       <# } #>
+               <# } else { #>
+                       <a class="button button-primary theme-install" data-slug="{{ data.id }}" href="{{ data.install_url }}"><?php esc_html_e( 'Install' ); ?></a>
+                       <button class="button-secondary preview install-theme-preview"><?php esc_html_e( 'Preview' ); ?></button>
+               <# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">         </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <# if ( data.installed ) { #>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <div class="theme-installed"><?php _ex( 'Already Installed', 'theme' ); ?></div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <div class="notice notice-success notice-alt"><p><?php _ex( 'Installed', 'theme' ); ?></p></div>
</ins><span class="cx" style="display: block; padding: 0 10px">         <# } #>
</span><span class="cx" style="display: block; padding: 0 10px"> </script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <script id="tmpl-theme-preview" type="text/template">
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="wp-full-overlay-sidebar">
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="wp-full-overlay-header">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <a href="#" class="close-full-overlay"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></a>
-                       <a href="#" class="previous-theme"><span class="screen-reader-text"><?php _ex( 'Previous', 'Button label for a theme' ); ?></span></a>
-                       <a href="#" class="next-theme"><span class="screen-reader-text"><?php _ex( 'Next', 'Button label for a theme' ); ?></span></a>
-               <# if ( data.installed ) { #>
-                       <a href="#" class="button button-primary theme-install disabled"><?php _ex( 'Installed', 'theme' ); ?></a>
-               <# } else { #>
-                       <a href="{{ data.install_url }}" class="button button-primary theme-install"><?php _e( 'Install' ); ?></a>
-               <# } #>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <button class="close-full-overlay"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
+                       <button class="previous-theme"><span class="screen-reader-text"><?php _ex( 'Previous', 'Button label for a theme' ); ?></span></button>
+                       <button class="next-theme"><span class="screen-reader-text"><?php _ex( 'Next', 'Button label for a theme' ); ?></span></button>
+                       <# if ( data.installed ) { #>
+                               <a class="button button-primary activate" href="{{ data.activate_url }}"><?php esc_html_e( 'Activate' ); ?></a>
+                       <# } else { #>
+                               <a href="{{ data.install_url }}" class="button button-primary theme-install" data-slug="{{ data.id }}"><?php _e( 'Install' ); ?></a>
+                       <# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="wp-full-overlay-sidebar-content">
</span><span class="cx" style="display: block; padding: 0 10px">                        <div class="install-theme-info">
</span><span class="cx" style="display: block; padding: 0 10px">                                <h3 class="theme-name">{{ data.name }}</h3>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <span class="theme-by"><?php printf( __( 'By %s' ), '{{ data.author }}' ); ?></span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <span class="theme-by">
+                                               <?php
+                                               /* translators: %s: Theme author name */
+                                               printf( __( 'By %s' ), '{{ data.author }}' );
+                                               ?>
+                                       </span>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <img class="theme-screenshot" src="{{ data.screenshot_url }}" alt="" />
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <img class="theme-screenshot" src="{{ data.screenshot_url }}" alt="" />
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <div class="theme-details">
-                                       <# if ( data.rating ) { #>
-                                               <div class="theme-rating">
-                                                       {{{ data.stars }}}
-                                                       <span class="num-ratings" aria-hidden="true">({{ data.num_ratings }})</span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <div class="theme-details">
+                                               <# if ( data.rating ) { #>
+                                                       <div class="theme-rating">
+                                                               {{{ data.stars }}}
+                                                               <span class="num-ratings">({{ data.num_ratings }})</span>
+                                                       </div>
+                                               <# } else { #>
+                                                       <span class="no-rating"><?php _e( 'This theme has not been rated yet.' ); ?></span>
+                                               <# } #>
+                                               <div class="theme-version">
+                                                       <?php
+                                                       /* translators: %s: Theme version */
+                                                       printf( __( 'Version: %s' ), '{{ data.version }}' );
+                                                       ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <# } else { #>
-                                               <span class="no-rating"><?php _e( 'This theme has not been rated yet.' ); ?></span>
-                                       <# } #>
-                                       <div class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></div>
-                                       <div class="theme-description">{{{ data.description }}}</div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <div class="theme-description">{{{ data.description }}}</div>
+                                       </div>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                </div>
-               <div class="wp-full-overlay-footer">
-                       <div class="devices">
-                               <button type="button" class="preview-desktop active" aria-pressed="true" data-device="desktop"><span class="screen-reader-text"><?php _e( 'Enter desktop preview mode' ); ?></span></button>
-                               <button type="button" class="preview-tablet" aria-pressed="false" data-device="tablet"><span class="screen-reader-text"><?php _e( 'Enter tablet preview mode' ); ?></span></button>
-                               <button type="button" class="preview-mobile" aria-pressed="false" data-device="mobile"><span class="screen-reader-text"><?php _e( 'Enter mobile preview mode' ); ?></span></button>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <div class="wp-full-overlay-footer">
+                               <button type="button" class="collapse-sidebar button-secondary" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>">
+                                       <span class="collapse-sidebar-arrow"></span>
+                                       <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
+                               </button>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <button type="button" class="collapse-sidebar button-secondary" aria-expanded="true" aria-label="<?php esc_attr_e( 'Collapse Sidebar' ); ?>">
-                               <span class="collapse-sidebar-arrow"></span>
-                               <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
-                       </button>
</del><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <div class="wp-full-overlay-main">
+               <iframe src="{{ data.preview_url }}" title="<?php esc_attr_e( 'Preview' ); ?>"></iframe>
</ins><span class="cx" style="display: block; padding: 0 10px">         </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <div class="wp-full-overlay-main">
-               <iframe src="{{ data.preview_url }}" title="<?php esc_attr_e( 'Preview' ); ?>" />
-       </div>
</del><span class="cx" style="display: block; padding: 0 10px"> </script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_print_request_filesystem_credentials_modal();
+wp_print_admin_notice_templates();
+
</ins><span class="cx" style="display: block; padding: 0 10px"> include(ABSPATH . 'wp-admin/admin-footer.php');
</span></span></pre></div>
<a id="trunksrcwpadminthemesphp"></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/themes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/themes.php     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-admin/themes.php       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -145,6 +145,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> add_thickbox();
</span><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'theme' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_enqueue_script( 'updates' );
</ins><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'customize-loader' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once( ABSPATH . 'wp-admin/admin-header.php' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -248,6 +249,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <?php } else { ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="theme-screenshot blank"></div>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php } ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       <?php if ( $theme['hasUpdate'] ) : ?>
+               <div class="update-message notice inline notice-warning notice-alt">
+                       <p><?php _e( 'New version available. <button class="button-link" type="button">Update now</button>' ); ?></p>
+               </div>
+       <?php endif; ?>
+
</ins><span class="cx" style="display: block; padding: 0 10px">         <span class="more-details" id="<?php echo $aria_action; ?>"><?php _e( 'Theme Details' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="theme-author"><?php printf( __( 'By %s' ), $theme['author'] ); ?></div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -276,10 +284,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <?php } ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-       <?php if ( $theme['hasUpdate'] ) { ?>
-               <div class="theme-update"><?php _e( 'Update Available' ); ?></div>
-       <?php } ?>
</del><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -368,13 +372,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <# } else { #>
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="theme-screenshot blank"></div>
</span><span class="cx" style="display: block; padding: 0 10px">        <# } #>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       <# if ( data.hasUpdate ) { #>
+               <div class="update-message notice inline notice-warning notice-alt"><p><?php _e( 'New version available. <button class="button-link" type="button">Update now</button>' ); ?></p></div>
+       <# } #>
+
</ins><span class="cx" style="display: block; padding: 0 10px">         <span class="more-details" id="{{ data.id }}-action"><?php _e( 'Theme Details' ); ?></span>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <div class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.author }}}' ); ?></div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <div class="theme-author">
+               <?php
+               /* translators: %s: Theme author name */
+               printf( __( 'By %s' ), '{{{ data.author }}}' );
+               ?>
+       </div>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <# if ( data.active ) { #>
</span><span class="cx" style="display: block; padding: 0 10px">                <h2 class="theme-name" id="{{ data.id }}-name">
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        /* translators: %s: theme name */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 /* translators: %s: Theme name */
</ins><span class="cx" style="display: block; padding: 0 10px">                         printf( __( '<span>Active:</span> %s' ), '{{{ data.name }}}' );
</span><span class="cx" style="display: block; padding: 0 10px">                        ?>
</span><span class="cx" style="display: block; padding: 0 10px">                </h2>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -383,21 +397,15 @@
</span><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">        <div class="theme-actions">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-       <# if ( data.active ) { #>
-               <# if ( data.actions.customize ) { #>
-                       <a class="button button-primary customize load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Customize' ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <# if ( data.active ) { #>
+                       <# if ( data.actions.customize ) { #>
+                               <a class="button button-primary customize load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Customize' ); ?></a>
+                       <# } #>
+               <# } else { #>
+                       <a class="button button-secondary activate" href="{{{ data.actions.activate }}}"><?php _e( 'Activate' ); ?></a>
+                       <a class="button button-primary load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Live Preview' ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <# } #>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <# } else { #>
-               <a class="button button-secondary activate" href="{{{ data.actions.activate }}}"><?php _e( 'Activate' ); ?></a>
-               <a class="button button-primary load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Live Preview' ); ?></a>
-       <# } #>
-
</del><span class="cx" style="display: block; padding: 0 10px">         </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-       <# if ( data.hasUpdate ) { #>
-               <div class="theme-update"><?php _e( 'Update Available' ); ?></div>
-       <# } #>
</del><span class="cx" style="display: block; padding: 0 10px"> </script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <script id="tmpl-theme-single" type="text/template">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -461,4 +469,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px"> </script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php require( ABSPATH . 'wp-admin/admin-footer.php' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+wp_print_request_filesystem_credentials_modal();
+wp_print_admin_notice_templates();
+wp_print_update_row_templates();
+
+require( ABSPATH . 'wp-admin/admin-footer.php' );
</ins></span></pre></div>
<a id="trunksrcwpcontentthemestwentytenlanguagestwentytenpot"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-content/themes/twentyten/languages/twentyten.pot</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-content/themes/twentyten/languages/twentyten.pot     2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-content/themes/twentyten/languages/twentyten.pot       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,14 +1,14 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-# Copyright (C) 2016 the WordPress team
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+# Copyright (C) 2015 the WordPress team
</ins><span class="cx" style="display: block; padding: 0 10px"> # This file is distributed under the GNU General Public License v2 or later.
</span><span class="cx" style="display: block; padding: 0 10px"> msgid ""
</span><span class="cx" style="display: block; padding: 0 10px"> msgstr ""
</span><span class="cx" style="display: block; padding: 0 10px"> "Project-Id-Version: Twenty Ten 2.1\n"
</span><span class="cx" style="display: block; padding: 0 10px"> "Report-Msgid-Bugs-To: https://wordpress.org/support/theme/twentyten\n"
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-"POT-Creation-Date: 2016-04-05 09:48:39+00:00\n"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+"POT-Creation-Date: 2015-12-08 15:15:12+00:00\n"
</ins><span class="cx" style="display: block; padding: 0 10px"> "MIME-Version: 1.0\n"
</span><span class="cx" style="display: block; padding: 0 10px"> "Content-Type: text/plain; charset=UTF-8\n"
</span><span class="cx" style="display: block; padding: 0 10px"> "Content-Transfer-Encoding: 8bit\n"
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+"PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
</ins><span class="cx" style="display: block; padding: 0 10px"> "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
</span><span class="cx" style="display: block; padding: 0 10px"> "Language-Team: LANGUAGE <LL@li.org>\n"
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesjswputiljs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/wp-util.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/wp-util.js       2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-includes/js/wp-util.js 2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -48,8 +48,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * Sends a POST request to WordPress.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * @param  {string} action The slug of the action to fire in WordPress.
-                * @param  {object} data   The data to populate $_POST with.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * @param  {(string|object)} action  The slug of the action to fire in WordPress or options passed
+                *                                   to jQuery.ajax.
+                * @param  {object=}         data    Optional. The data to populate $_POST with.
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @return {$.promise}     A jQuery promise that represents the request,
</span><span class="cx" style="display: block; padding: 0 10px">                 *                         decorated with an abort() method.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -64,8 +65,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * Sends a POST request to WordPress.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * @param  {string} action  The slug of the action to fire in WordPress.
-                * @param  {object} options The options passed to jQuery.ajax.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * @param  {(string|object)} action  The slug of the action to fire in WordPress or options passed
+                *                                   to jQuery.ajax.
+                * @param  {object=}         options Optional. The options passed to jQuery.ajax.
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @return {$.promise}      A jQuery promise that represents the request,
</span><span class="cx" style="display: block; padding: 0 10px">                 *                          decorated with an abort() method.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/src/wp-includes/script-loader.php     2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -595,25 +595,62 @@
</span><span class="cx" style="display: block; padding: 0 10px">                did_action( 'init' ) && $scripts->localize( 'updates', '_wpUpdatesSettings', array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'ajax_nonce' => wp_create_nonce( 'updates' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'l10n'       => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updating'          => __( 'Updating...' ), // no ellipsis
-                               'updated'           => __( 'Updated!' ),
-                               'updateFailedShort' => __( 'Update Failed!' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         /* translators: %s: Search string */
+                               'searchResults'              => __( 'Search results for &#8220;%s&#8221;' ),
+                               'noPlugins'                  => __( 'You do not appear to have any plugins available at this time.' ),
+                               'noItemsSelected'            => __( 'Please select at least one item to perform this action on.' ),
+                               'updating'                   => __( 'Updating...' ), // No ellipsis.
+                               'updated'                    => __( 'Updated!' ),
+                               'update'                     => __( 'Update' ),
+                               'updateNow'                  => __( 'Update Now' ),
+                               'updateFailedShort'          => __( 'Update Failed!' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: Error string for a failed update */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updateFailed'      => __( 'Update Failed: %s' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updateFailed'               => __( 'Update Failed: %s' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: Plugin name and version */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updatingLabel'     => __( 'Updating %s...' ), // no ellipsis
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updatingLabel'              => __( 'Updating %s...' ), // No ellipsis.
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: Plugin name and version */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updatedLabel'      => __( '%s updated!' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updatedLabel'               => __( '%s updated!' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: Plugin name and version */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updateFailedLabel' => __( '%s update failed' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updateFailedLabel'          => __( '%s update failed' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: JavaScript accessible string */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updatingMsg'       => __( 'Updating... please wait.' ), // no ellipsis
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updatingMsg'                => __( 'Updating... please wait.' ), // No ellipsis.
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: JavaScript accessible string */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updatedMsg'        => __( 'Update completed successfully.' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updatedMsg'                 => __( 'Update completed successfully.' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 /* translators: JavaScript accessible string */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'updateCancel'      => __( 'Update canceled.' ),
-                               'beforeunload'      => __( 'Plugin updates may not complete if you navigate away from this page.' ),
-                       )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'updateCancel'               => __( 'Update canceled.' ),
+                               'beforeunload'               => __( 'Updates may not complete if you navigate away from this page.' ),
+                               'installNow'                 => __( 'Install Now' ),
+                               'installing'                 => __( 'Installing...' ),
+                               'installed'                  => __( 'Installed!' ),
+                               'installFailedShort'         => __( 'Install Failed!' ),
+                               /* translators: Error string for a failed installation */
+                               'installFailed'              => __( 'Installation failed: %s' ),
+                               /* translators: Plugin/Theme name and version */
+                               'installingLabel'            => __( 'Installing %s...' ), // no ellipsis
+                               /* translators: Plugin/Theme name and version */
+                               'installedLabel'             => __( '%s installed!' ),
+                               /* translators: Plugin/Theme name and version */
+                               'installFailedLabel'         => __( '%s installation failed' ),
+                               'installingMsg'              => __( 'Installing... please wait.' ),
+                               'installedMsg'               => __( 'Installation completed successfully.' ),
+                               /* translators: Activation URL */
+                               'importerInstalledMsg'       => __( 'Importer installed successfully. <a href="%s">Activate plugin &#38; run importer</a>' ),
+                               /* translators: %s: Theme name */
+                               'aysDelete'                  => __( 'Are you sure you want to delete %s?' ),
+                               /* translators: %s: Plugin name */
+                               'aysDeleteUninstall'         => __( 'Are you sure you want to delete %s and its data?' ),
+                               'aysBulkDelete'              => __( 'Are you sure you want to delete the selected plugins and their data?' ),
+                               'aysBulkDeleteThemes'        => __( 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?' ),
+                               'deleting'                   => __( 'Deleting...' ),
+                               /* translators: %s: Error string for a failed deletion */
+                               'deleteFailed'               => __( 'Deletion failed: %s' ),
+                               'deleted'                    => __( 'Deleted!' ),
+                               'livePreview'                => __( 'Live Preview' ),
+                               'activatePlugin'             => is_network_admin() ? __( 'Network Activate' ) : __( 'Activate' ),
+                               'activateTheme'              => is_network_admin() ? __( 'Network Enable' ) : __( 'Activate' ),
+                               'activateImporter'           => __( 'Activate importer' ),
+                               'unknownError'               => __( 'An unknown error occured' ),
+                       ),
</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">                $scripts->add( 'farbtastic', '/wp-admin/js/farbtastic.js', array('jquery'), '1.2' );
</span></span></pre></div>
<a id="trunktestsqunitfixturesupdatesjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/fixtures/updates.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/fixtures/updates.js                             (rev 0)
+++ trunk/tests/qunit/fixtures/updates.js       2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,48 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+window._wpUpdatesSettings = {
+       'ajax_nonce': '719b10f05d',
+       'l10n': {
+               'searchResults': 'Search results for &#8220;%s&#8221;',
+               'noPlugins': 'You do not appear to have any plugins available at this time.',
+               'noItemsSelected': 'Please select at least one item to perform this action on.',
+               'updating': 'Updating...',
+               'updated': 'Updated!',
+               'update': 'Update',
+               'updateNow': 'Update Now',
+               'updateFailedShort': 'Update Failed!',
+               'updateFailed': 'Update Failed: %s',
+               'updatingLabel': 'Updating %s...',
+               'updatedLabel': '%s updated!',
+               'updateFailedLabel': '%s update failed',
+               'updatingMsg': 'Updating... please wait.',
+               'updatedMsg': 'Update completed successfully.',
+               'updateCancel': 'Update canceled.',
+               'beforeunload': 'Updates may not complete if you navigate away from this page.',
+               'installNow': 'Install Now',
+               'installing': 'Installing...',
+               'installed': 'Installed!',
+               'installFailedShort': 'Install Failed!',
+               'installFailed': 'Installation failed: %s',
+               'installingLabel': 'Installing %s...', // No ellipsis
+               'installedLabel': '%s installed!',
+               'installFailedLabel': '%s installation failed',
+               'installingMsg': 'Installing... please wait.',
+               'installedMsg': 'Installation completed successfully.',
+               'importerInstalledMsg': 'Importer installed successfully. <a href="%s">Activate plugin &#38; run importer</a>',
+               'aysDelete': 'Are you sure you want to delete %s?',
+               'aysDeleteUninstall': 'Are you sure you want to delete %s and its data?',
+               'aysBulkDelete': 'Are you sure you want to delete the selected plugins and their data?',
+               'aysBulkDeleteThemes': 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?',
+               'deleting': 'Deleting...',
+               'deleteFailed': 'Deletion failed: %s',
+               'deleted': 'Deleted!',
+               'livePreview': 'Live Preview',
+               'activatePlugin': 'Activate',
+               'activateTheme': 'Activate',
+               'activateImporter': 'Activate importer',
+               'unknownError': 'An unknown error occured'
+       }
+};
+window._wpUpdatesItemCounts = {
+       plugins: {},
+       totals: {}
+};
</ins></span></pre></div>
<a id="trunktestsqunitindexhtml"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/index.html</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/index.html      2016-06-15 14:50:38 UTC (rev 37713)
+++ trunk/tests/qunit/index.html        2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -10,6 +10,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-includes/js/backbone.min.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-includes/js/wp-backbone.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-includes/js/zxcvbn.min.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <script>
+                       window._wpUtilSettings = {
+                               'ajax': {
+                                       'url': '\/wp-admin\/admin-ajax.php'
+                               }
+                       };
+               </script>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <script src="../../src/wp-includes/js/wp-util.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-includes/js/wp-a11y.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -482,5 +489,42 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-includes/js/tinymce/tinymce.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="editor/js/utils.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="wp-includes/js/tinymce/plugins/wptextpattern/plugin.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               <!-- Updates templates and HTML fixtures -->
+               <script id="tmpl-wp-updates-admin-notice" type="text/html">
+                       <div <# if ( data.id ) { #>id="{{ data.id }}"<# } #> class="notice {{ data.className }}"><p>{{{ data.message }}}</p></div>
+               </script>
+               <div hidden>
+                       <li id="wp-admin-bar-updates">
+                               <a class="ab-item" href="wp-admin/update-core.php" title="2 Plugin Updates">
+                                       <span class="ab-icon"></span>
+                                       <span class="ab-label">2</span>
+                                       <span class="screen-reader-text">2 Plugin Updates</span>
+                               </a>
+                       </li>
+                       <li class="wp-has-submenu wp-not-current-submenu menu-top menu-icon-plugins" id="menu-plugins">
+                               <a href="plugins.php" class="wp-has-submenu wp-not-current-submenu menu-top menu-icon-plugins" aria-haspopup="true">
+                                       <div class="wp-menu-arrow"><div></div></div>
+                                       <div class="wp-menu-image dashicons-before dashicons-admin-plugins"><br></div>
+                                       <div class="wp-menu-name">Plugins
+                               <span class="update-plugins count-2">
+                                       <span class="plugin-count">2</span>
+                               </span>
+                                       </div>
+                               </a>
+                               <ul class="wp-submenu wp-submenu-wrap">
+                                       <li class="wp-submenu-head" aria-hidden="true">Plugins
+                               <span class="update-plugins count-2">
+                                       <span class="plugin-count">2</span>
+                               </span>
+                                       </li>
+                                       <li class="wp-first-item">
+                                               <a href="plugins.php" class="wp-first-item">Installed Plugins</a></li><li><a href="plugin-install.php">Add New</a>
+                               </li><li>
+                                       <a href="plugin-editor.php">Editor</a>
+                               </li>
+                               </ul>
+                       </li>
+               </div>
</ins><span class="cx" style="display: block; padding: 0 10px">         </body>
</span><span class="cx" style="display: block; padding: 0 10px"> </html>
</span></span></pre></div>
<a id="trunktestsqunitwpadminjsupdatesjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/wp-admin/js/updates.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/updates.js                          (rev 0)
+++ trunk/tests/qunit/wp-admin/js/updates.js    2016-06-15 16:36:07 UTC (rev 37714)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,179 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*global QUnit, wp, sinon */
+jQuery( function( $ ) {
+
+       QUnit.module( 'wp.updates' );
+
+       QUnit.test( 'Initially, the update lock should be false', function( assert ) {
+               assert.strictEqual( wp.updates.ajaxLocked, false );
+       });
+
+       QUnit.test( 'The nonce should be set correctly', function( assert ) {
+               assert.equal( wp.updates.ajaxNonce, window._wpUpdatesSettings.ajax_nonce );
+       });
+
+       QUnit.test( 'decrementCount correctly decreases the update number', function( assert ) {
+               var menuItemCount  = $( '#menu-plugins' ).find( '.plugin-count' ).eq( 0 ).text();
+               var screenReaderItemCount = $( '#wp-admin-bar-updates' ).find( '.screen-reader-text' ).text();
+               var adminItemCount = $( '#wp-admin-bar-updates' ).find( '.ab-label' ).text();
+               assert.equal( menuItemCount, 2, 'Intial value is correct' );
+               assert.equal( screenReaderItemCount, '2 Plugin Updates', 'Intial value is correct' );
+               assert.equal( adminItemCount, 2, 'Intial value is correct' );
+
+               wp.updates.decrementCount( 'plugin' );
+
+               // Re-read these values
+               menuItemCount  = $( '#menu-plugins' ).find( '.plugin-count' ).eq( 0 ).text();
+               screenReaderItemCount = $( '#wp-admin-bar-updates' ).find( '.screen-reader-text' ).text();
+               adminItemCount = $( '#wp-admin-bar-updates' ).find( '.ab-label' ).text();
+               assert.equal( menuItemCount, 1 );
+
+               // @todo: Update screen reader count.
+               // Should the screenReader count change? Is that announced to the user?
+               // assert.equal( screenReaderItemCount, '1 Plugin Update' );
+               assert.equal( adminItemCount, 1 );
+       });
+
+       QUnit.test( '`beforeunload` should only fire when locked', function( assert ) {
+               wp.updates.ajaxLocked = false;
+               assert.notOk( wp.updates.beforeunload(), '`beforeunload` should not fire.' );
+               wp.updates.ajaxLocked = true;
+               assert.equal( wp.updates.beforeunload(), window._wpUpdatesSettings.l10n.beforeunload, '`beforeunload` should equal the localized `beforeunload` string.' );
+               wp.updates.ajaxLocked = false;
+       });
+
+       // FTP creds... exist?
+       // Admin notice?
+
+       QUnit.module( 'wp.updates.plugins', {
+               beforeEach: function() {
+                       this.oldPagenow = window.pagenow;
+                       window.pagenow = 'plugins';
+                       sinon.spy( jQuery, 'ajax' );
+               },
+               afterEach: function() {
+                       window.pagenow = this.oldPagenow;
+                       wp.updates.ajaxLocked = false;
+                       wp.updates.queue = [];
+                       jQuery.ajax.restore();
+               }
+       } );
+
+       QUnit.test( 'Update lock is set when plugins are updating', function( assert ) {
+               wp.updates.updatePlugin( {
+                       plugin: 'test/test.php',
+                       slug: 'test'
+               } );
+               assert.strictEqual( wp.updates.ajaxLocked, true );
+       });
+
+       QUnit.test( 'Plugins are queued when the lock is set', function( assert ) {
+               var value = [
+                       {
+                               action: 'update-plugin',
+                               data: {
+                                       plugin: 'test/test.php',
+                                       slug: 'test',
+                                       success: null,
+                                       error: null
+                               }
+                       }
+               ];
+
+               wp.updates.ajaxLocked = true;
+               wp.updates.updatePlugin( {
+                       plugin: 'test/test.php',
+                       slug: 'test',
+                       success: null,
+                       error: null
+               } );
+
+               assert.deepEqual( wp.updates.queue, value );
+       });
+
+       QUnit.test( 'If plugins are installing (lock is set), the beforeUnload function should fire', function( assert ) {
+               wp.updates.updatePlugin( {
+                       plugin: 'test/test.php',
+                       slug: 'test'
+               } );
+               assert.equal( wp.updates.beforeunload(), window._wpUpdatesSettings.l10n.beforeunload );
+       } );
+
+       QUnit.test( 'Starting a plugin update should call the update API', function( assert ) {
+               wp.updates.updatePlugin( {
+                       plugin: 'test/test.php',
+                       slug: 'test'
+               } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'update-plugin' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'test' );
+       } );
+       QUnit.test( 'Installing a plugin should call the API', function( assert ) {
+               wp.updates.installPlugin( { slug: 'jetpack' } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'install-plugin' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'jetpack' );
+       } );
+       QUnit.test( 'Deleting a plugin should call the API', function( assert ) {
+               wp.updates.deletePlugin( { slug: 'jetpack', plugin: 'jetpack/jetpack.php' } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'delete-plugin' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'jetpack' );
+       } );
+
+       // QUnit.test( 'A successful update changes the message?', function( assert ) {} );
+       // QUnit.test( 'A failed update changes the message?', function( assert ) {} );
+
+       QUnit.module( 'wp.updates.themes', {
+               beforeEach: function() {
+                       this.oldPagenow = window.pagenow;
+                       window.pagenow = 'themes';
+                       sinon.spy( jQuery, 'ajax' );
+               },
+               afterEach: function() {
+                       window.pagenow = this.oldPagenow;
+                       wp.updates.ajaxLocked = false;
+                       wp.updates.queue = [];
+                       jQuery.ajax.restore();
+               }
+       } );
+
+       QUnit.test( 'Update lock is set when themes are updating', function( assert ) {
+               wp.updates.updateTheme( 'twentyeleven' );
+               assert.strictEqual( wp.updates.ajaxLocked, true );
+       });
+
+       QUnit.test( 'If themes are installing (lock is set), the beforeUnload function should fire', function( assert ) {
+               wp.updates.updateTheme( { slug: 'twentyeleven' } );
+               assert.equal( wp.updates.beforeunload(), window._wpUpdatesSettings.l10n.beforeunload );
+       } );
+
+       QUnit.test( 'Starting a theme update should call the update API', function( assert ) {
+               wp.updates.updateTheme( { slug: 'twentyeleven' } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'update-theme' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'twentyeleven' );
+       } );
+
+       QUnit.test( 'Installing a theme should call the API', function( assert ) {
+               wp.updates.installTheme( { slug: 'twentyeleven' } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'install-theme' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'twentyeleven' );
+       } );
+
+       QUnit.test( 'Deleting a theme should call the API', function( assert ) {
+               wp.updates.deleteTheme( { slug: 'twentyeleven' } );
+               assert.ok( jQuery.ajax.calledOnce );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].url, '/wp-admin/admin-ajax.php' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.action, 'delete-theme' );
+               assert.equal( jQuery.ajax.getCall( 0 ).args[0].data.slug, 'twentyeleven' );
+       } );
+
+       // QUnit.test( 'A successful update changes the message?', function( assert ) {} );
+       // QUnit.test( 'A failed update changes the message?', function( assert ) {} );
+});
</ins></span></pre>
</div>
</div>

</body>
</html>