<!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>[29634] trunk/src: Plugin/Theme Uploads: New capabilities; unify UIs; ensure compatibility with old filters.</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">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/29634">29634</a></dd>
<dt>Author</dt> <dd>nacin</dd>
<dt>Date</dt> <dd>2014-08-27 01:31:05 +0000 (Wed, 27 Aug 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Plugin/Theme Uploads: New capabilities; unify UIs; ensure compatibility with old filters.

Introduce upload_plugins and upload_themes capabilities to allow blocking of plugin and theme uploads, versus the old hacky (and not secure) ways of just hiding UI tabs. These are simply meta capabilities that map to install_plugins and install_themes.
 
Also:
 * Use the same nice design for the plugin upload screen as the theme upload screen.
 * Better compatibility for the old install_themes_tabs filter added in <a href="http://core.trac.wordpress.org/changeset/29002">[29002]</a>. see <a href="http://core.trac.wordpress.org/ticket/28578">#28578</a>.
 * Ensure using the install_plugins_tabs filter to remove the upload tab removes the new button.
 * Use 'Add Plugins' instead of 'Install Plugins' to match 'Add Themes'.

fixes <a href="http://core.trac.wordpress.org/ticket/29236">#29236</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincssthemescss">trunk/src/wp-admin/css/themes.css</a></li>
<li><a href="#trunksrcwpadminincludesclasswpplugininstalllisttablephp">trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php</a></li>
<li><a href="#trunksrcwpadminincludesplugininstallphp">trunk/src/wp-admin/includes/plugin-install.php</a></li>
<li><a href="#trunksrcwpadminplugininstallphp">trunk/src/wp-admin/plugin-install.php</a></li>
<li><a href="#trunksrcwpadminthemeinstallphp">trunk/src/wp-admin/theme-install.php</a></li>
<li><a href="#trunksrcwpadminupdatephp">trunk/src/wp-admin/update.php</a></li>
<li><a href="#trunksrcwpincludescapabilitiesphp">trunk/src/wp-includes/capabilities.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincssthemescss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/css/themes.css (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/css/themes.css        2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/css/themes.css   2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -1091,7 +1091,8 @@
</span><span class="cx"> .theme-install-php.show-upload-theme a.browse-themes {
</span><span class="cx">  display: inline;
</span><span class="cx"> }
</span><del>-.upload-theme {
</del><ins>+.upload-theme,
+.upload-plugin {
</ins><span class="cx">   -webkit-box-sizing: border-box;
</span><span class="cx">  -moz-box-sizing: border-box;
</span><span class="cx">  box-sizing: border-box;
</span><span class="lines">@@ -1103,17 +1104,20 @@
</span><span class="cx">  position: relative;
</span><span class="cx">  top: 10px;
</span><span class="cx"> }
</span><del>-body.show-upload-theme .upload-theme {
</del><ins>+body.show-upload-theme .upload-theme,
+.upload-plugin {
</ins><span class="cx">   display: block;
</span><span class="cx"> }
</span><del>-.upload-theme .wp-upload-form {
</del><ins>+.upload-theme .wp-upload-form,
+.upload-plugin .wp-upload-form {
</ins><span class="cx">   background: #fafafa;
</span><span class="cx">  border: 1px solid #e5e5e5;
</span><span class="cx">  padding: 30px;
</span><span class="cx">  margin: 30px auto;
</span><span class="cx">  max-width: 380px;
</span><span class="cx"> }
</span><del>-.upload-theme .install-help {
</del><ins>+.upload-theme .install-help,
+.upload-plugin .install-help {
</ins><span class="cx">   color: #999;
</span><span class="cx">  font-size: 18px;
</span><span class="cx">  font-style: normal;
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpplugininstalllisttablephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php       2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php  2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -69,8 +69,13 @@
</span><span class="cx">          if ( $tab === 'beta' || false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
</span><span class="cx">                  $tabs['beta']      = _x( 'Beta Testing', 'Plugin Installer' );
</span><span class="cx">          }
</span><ins>+               if ( current_user_can( 'upload_plugins' ) ) {
+                       // No longer a real tab. Here for filter compatibility.
+                       // Gets juggled into $nonmenu_tabs below.
+                       $tabs['upload'] = __( 'Upload Plugin' );
+               }
</ins><span class="cx"> 
</span><del>-               $nonmenu_tabs = array( 'upload', 'plugin-information' ); //Valid actions to perform which do not have a Menu item.
</del><ins>+                $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item.
</ins><span class="cx"> 
</span><span class="cx">          /**
</span><span class="cx">           * Filter the tabs shown on the Plugin Install screen.
</span><span class="lines">@@ -91,6 +96,11 @@
</span><span class="cx">           */
</span><span class="cx">          $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs );
</span><span class="cx"> 
</span><ins>+               if ( isset( $tabs['upload'] ) ) {
+                       unset( $tabs['upload'] );
+                       $nonmenu_tabs[] = 'upload';
+               }
+
</ins><span class="cx">           // If a non-valid menu tab has been selected, And it's not a non-menu action.
</span><span class="cx">          if ( empty( $tab ) || ( !isset( $tabs[ $tab ] ) && !in_array( $tab, (array) $nonmenu_tabs ) ) )
</span><span class="cx">                  $tab = key( $tabs );
</span></span></pre></div>
<a id="trunksrcwpadminincludesplugininstallphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/includes/plugin-install.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/includes/plugin-install.php   2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/includes/plugin-install.php      2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -195,7 +195,7 @@
</span><span class="cx">  */
</span><span class="cx"> function install_plugins_upload( $page = 1 ) {
</span><span class="cx"> ?>
</span><del>-       <h4><?php _e('Install a plugin in .zip format'); ?></h4>
</del><ins>+<div class="upload-plugin">
</ins><span class="cx">   <p class="install-help"><?php _e('If you have a plugin in a .zip format, you may install it by uploading it here.'); ?></p>
</span><span class="cx">  <form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url('update.php?action=upload-plugin'); ?>">
</span><span class="cx">          <?php wp_nonce_field( 'plugin-upload'); ?>
</span><span class="lines">@@ -203,6 +203,7 @@
</span><span class="cx">          <input type="file" id="pluginzip" name="pluginzip" />
</span><span class="cx">          <?php submit_button( __( 'Install Now' ), 'button', 'install-plugin-submit', false ); ?>
</span><span class="cx">  </form>
</span><ins>+</div>
</ins><span class="cx"> <?php
</span><span class="cx"> }
</span><span class="cx"> add_action('install_plugins_upload', 'install_plugins_upload', 10, 1);
</span></span></pre></div>
<a id="trunksrcwpadminplugininstallphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/plugin-install.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/plugin-install.php    2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/plugin-install.php       2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx"> $pagenum = $wp_list_table->get_pagenum();
</span><span class="cx"> $wp_list_table->prepare_items();
</span><span class="cx"> 
</span><del>-$title = __('Install Plugins');
</del><ins>+$title = __( 'Add Plugins' );
</ins><span class="cx"> $parent_file = 'plugins.php';
</span><span class="cx"> 
</span><span class="cx"> wp_enqueue_script( 'plugin-install' );
</span><span class="lines">@@ -75,16 +75,26 @@
</span><span class="cx"> <div class="wrap">
</span><span class="cx"> <h2>
</span><span class="cx">  <?php
</span><del>-               echo esc_html( $title );
-               $href = self_admin_url( 'plugin-install.php?tab=upload' );
</del><ins>+        echo esc_html( $title );
+       if ( ! empty( $tabs['upload'] ) && current_user_can( 'upload_plugins' ) ) {
+               if ( $tab === 'upload' ) {
+                       $href = self_admin_url( 'plugin-install.php' );
+                       $text = _x( 'Browse', 'plugins' );
+               } else {
+                       $href = self_admin_url( 'plugin-install.php?tab=upload' );
+                       $text = __( 'Upload Plugin' );
+               }
+               echo ' <a href="' . $href . '" class="upload add-new-h2">' . $text . '</a>';
+       }
</ins><span class="cx">   ?>
</span><del>-       <a href="<?php echo $href; ?>" class="upload add-new-h2"><?php _e( 'Upload Plugin' ); ?></a>
</del><span class="cx"> </h2>
</span><span class="cx"> 
</span><del>-<?php $wp_list_table->views(); ?>
</del><ins>+<?php
+if ( $tab !== 'upload' ) {
+       $wp_list_table->views();
+       echo '<br class="clear" />';
+}
</ins><span class="cx"> 
</span><del>-<br class="clear" />
-<?php
</del><span class="cx"> /**
</span><span class="cx">  * Fires after the plugins list table in each tab of the Install Plugins screen.
</span><span class="cx">  *
</span></span></pre></div>
<a id="trunksrcwpadminthemeinstallphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/theme-install.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/theme-install.php     2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/theme-install.php        2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -27,11 +27,6 @@
</span><span class="cx">  $submenu_file = 'themes.php';
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-$tabs = array(
-       'upload'        => __( 'Upload Theme' ),
-       'browse-themes' => _x( 'Browse', 'themes' ),
-);
-
</del><span class="cx"> $sections = array(
</span><span class="cx">  'featured' => __( 'Featured Themes' ),
</span><span class="cx">  'popular'  => __( 'Popular Themes' ),
</span><span class="lines">@@ -113,23 +108,25 @@
</span><span class="cx"> 
</span><span class="cx"> ?>
</span><span class="cx"> <div class="wrap">
</span><del>-       <h2>
-               <?php echo esc_html( $title ); ?>
-               <?php
-               /**
-                * Filter the tabs shown on the Install Themes screen.
-                * 
-                * @since 2.8.0
-                * @param array $tabs The tabs shown on the Install Themes screen. Defaults are
-                *                    'upload' and 'browse-themes'.
-                */
-               $tabs = apply_filters( 'install_themes_tabs', $tabs );
-               foreach ( $tabs as $tab_slug => $tab_name ) {
-                       echo '<a href="#" class="' . esc_attr( $tab_slug ) . ' add-new-h2">' . $tab_name . '</a>';
-               }
-               ?>
-       </h2>
</del><ins>+        <h2><?php
+       echo esc_html( $title );
</ins><span class="cx"> 
</span><ins>+       /**
+        * Filter the tabs shown on the Add Themes screen.
+        *
+        * This filter is for backwards compatibility only,
+        * for the suppression of the upload tab.
+        *
+        * @since 2.8.0
+        * @param array $tabs The tabs shown on the Add Themes screen. Default is 'upload'.
+        */
+       $tabs = apply_filters( 'install_themes_tabs', array( 'upload' => __( 'Upload Theme' ) ) );
+       if ( ! empty( $tabs['upload'] ) && current_user_can( 'upload_themes' ) ) {
+               echo ' <a href="#" class="upload add-new-h2">' . __( 'Upload Theme' ) . '</a>';
+               echo ' <a href="#" class="browse-themes add-new-h2">' . _x( 'Browse', 'themes' ) . '</a>';
+       }
+       ?></h2>
+
</ins><span class="cx">   <div class="upload-theme">
</span><span class="cx">  <?php install_themes_upload(); ?>
</span><span class="cx">  </div>
</span></span></pre></div>
<a id="trunksrcwpadminupdatephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/update.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/update.php    2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-admin/update.php       2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -123,8 +123,9 @@
</span><span class="cx"> 
</span><span class="cx">  } elseif ( 'upload-plugin' == $action ) {
</span><span class="cx"> 
</span><del>-               if ( ! current_user_can('install_plugins') )
</del><ins>+                if ( ! current_user_can( 'upload_plugins' ) ) {
</ins><span class="cx">                   wp_die( __( 'You do not have sufficient permissions to install plugins on this site.' ) );
</span><ins>+               }
</ins><span class="cx"> 
</span><span class="cx">          check_admin_referer('plugin-upload');
</span><span class="cx"> 
</span><span class="lines">@@ -227,8 +228,9 @@
</span><span class="cx"> 
</span><span class="cx">  } elseif ( 'upload-theme' == $action ) {
</span><span class="cx"> 
</span><del>-               if ( ! current_user_can('install_themes') )
</del><ins>+                if ( ! current_user_can( 'upload_themes' ) ) {
</ins><span class="cx">                   wp_die( __( 'You do not have sufficient permissions to install themes on this site.' ) );
</span><ins>+               }
</ins><span class="cx"> 
</span><span class="cx">          check_admin_referer('theme-upload');
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpincludescapabilitiesphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/capabilities.php (29633 => 29634)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/capabilities.php   2014-08-26 22:26:43 UTC (rev 29633)
+++ trunk/src/wp-includes/capabilities.php      2014-08-27 01:31:05 UTC (rev 29634)
</span><span class="lines">@@ -1262,18 +1262,25 @@
</span><span class="cx">  case 'update_plugins':
</span><span class="cx">  case 'delete_plugins':
</span><span class="cx">  case 'install_plugins':
</span><ins>+       case 'upload_plugins':
</ins><span class="cx">   case 'update_themes':
</span><span class="cx">  case 'delete_themes':
</span><span class="cx">  case 'install_themes':
</span><ins>+       case 'upload_themes':
</ins><span class="cx">   case 'update_core':
</span><span class="cx">          // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
</span><span class="cx">          // Files in uploads are excepted.
</span><del>-               if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
</del><ins>+                if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
</ins><span class="cx">                   $caps[] = 'do_not_allow';
</span><del>-               elseif ( is_multisite() && ! is_super_admin( $user_id ) )
</del><ins>+                } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
</ins><span class="cx">                   $caps[] = 'do_not_allow';
</span><del>-               else
</del><ins>+                } elseif ( 'upload_themes' === $cap ) {
+                       $caps[] = 'install_themes';
+               } elseif ( 'upload_plugins' === $cap ) {
+                       $caps[] = 'install_plugins';
+               } else {
</ins><span class="cx">                   $caps[] = $cap;
</span><ins>+               }
</ins><span class="cx">           break;
</span><span class="cx">  case 'activate_plugins':
</span><span class="cx">          $caps[] = $cap;
</span></span></pre>
</div>
</div>

</body>
</html>