<!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>[26726] trunk/src/wp-admin: No-JavaScript and no-Customizer support for the new Themes screen.</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/26726">26726</a></dd>
<dt>Author</dt> <dd>nacin</dd>
<dt>Date</dt> <dd>2013-12-06 16:10:25 +0000 (Fri, 06 Dec 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>No-JavaScript and no-Customizer support for the new Themes screen.

JavaScript is rarely disabled, but graceful degradation is still important. For example, syntax errors can occur, usually with major WP updates that overhaul entire experiences and update external libraries combined with themes or plugins doing weird or old things. If this error is due to their current theme, a user needs to be able to access the themes screen to switch away from the theme. A more subtle issue could make things painful to diagnose.

This commit renders the grid in PHP (the template is duplicated, but it lightweight, fairly mundane, and easy to sync). On Backbone render, the grid is then re-rendered from JavaScript so searches can occur. Customize and Live Preview is disabled if JS fails to kick in. If JS is disabled, old-school "Preview" links are displayed.

No-Customizer support: The customizer is only supported when the browser supports postMessage (IE8+), and if the frontend is a different domain, CORS (IE10+). We use the .hide-if-no-customize class for this. Pre-customize "Preview" links should use .hide-if-customize.

The .load-customize class should be used to declare a link that opens the customizer. This enables customize-loader.js to intercept this link and load the customizer on top of the current window, making for a smoother experience.

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincsswpadmincss">trunk/src/wp-admin/css/wp-admin.css</a></li>
<li><a href="#trunksrcwpadminincludesthemephp">trunk/src/wp-admin/includes/theme.php</a></li>
<li><a href="#trunksrcwpadminjsthemejs">trunk/src/wp-admin/js/theme.js</a></li>
<li><a href="#trunksrcwpadminnetworkthemesphp">trunk/src/wp-admin/network/themes.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>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincsswpadmincss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/css/wp-admin.css (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/css/wp-admin.css      2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/css/wp-admin.css 2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -6454,7 +6454,7 @@
</span><span class="cx">  background: #fff;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.theme-browser .theme:hover .theme-screenshot img {
</del><ins>+.theme-browser.rendered .theme:hover .theme-screenshot img {
</ins><span class="cx">   opacity: 0.4;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -6477,7 +6477,7 @@
</span><span class="cx">  transition:         opacity 0.1s ease-in-out;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.theme-browser .theme:hover .more-details {
</del><ins>+.theme-browser.rendered .theme:hover .more-details {
</ins><span class="cx">   opacity: 1;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpadminincludesthemephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/includes/theme.php (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/includes/theme.php    2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/includes/theme.php       2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -378,9 +378,11 @@
</span><span class="cx">  * @return array An associative array of theme data, sorted by name.
</span><span class="cx">  */
</span><span class="cx"> function wp_prepare_themes_for_js( $themes = null ) {
</span><del>-       $prepared_themes = array();
</del><span class="cx">   $current_theme = get_stylesheet();
</span><span class="cx"> 
</span><ins>+       // Make sure the current theme is listed first.
+       $prepared_themes = array( $current_theme => array() );
+
</ins><span class="cx">   if ( null === $themes ) {
</span><span class="cx">          $themes = wp_get_themes( array( 'allowed' => true ) );
</span><span class="cx">          if ( ! isset( $themes[ $current_theme ] ) ) {
</span><span class="lines">@@ -406,7 +408,7 @@
</span><span class="cx">          $slug = $theme->get_stylesheet();
</span><span class="cx">          $encoded_slug = urlencode( $slug );
</span><span class="cx"> 
</span><del>-               $prepared_themes[] = array(
</del><ins>+                $prepared_themes[ $slug ] = array(
</ins><span class="cx">                   'id'           => $slug,
</span><span class="cx">                  'name'         => $theme->display( 'Name' ),
</span><span class="cx">                  'screenshot'   => array( $theme->get_screenshot() ), // @todo multiple
</span><span class="lines">@@ -422,6 +424,13 @@
</span><span class="cx">                  'actions'      => array(
</span><span class="cx">                          'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
</span><span class="cx">                          'customize'=> current_user_can( 'edit_theme_options' ) ? wp_customize_url( $slug ) : null,
</span><ins>+                               'preview'   => add_query_arg( array(
+                                       'preview'        => 1,
+                                       'template'       => urlencode( $theme->get_template() ),
+                                       'stylesheet'     => urlencode( $slug ),
+                                       'preview_iframe' => true,
+                                       'TB_iframe'      => true,
+                               ), home_url( '/' ) ),
</ins><span class="cx">                           'delete'   => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
</span><span class="cx">                  ),
</span><span class="cx">          );
</span><span class="lines">@@ -436,5 +445,6 @@
</span><span class="cx">   *
</span><span class="cx">   * @param array $prepared_themes Array of themes.
</span><span class="cx">   */
</span><del>-       return apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
</del><ins>+        $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
+       return array_values( $prepared_themes );
</ins><span class="cx"> }
</span><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunksrcwpadminjsthemejs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/js/theme.js (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/js/theme.js   2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/js/theme.js      2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -51,7 +51,7 @@
</span><span class="cx"> 
</span><span class="cx">          // Render and append
</span><span class="cx">          this.view.render();
</span><del>-               this.$el.append( this.view.el );
</del><ins>+                this.$el.empty().append( this.view.el ).addClass('rendered');
</ins><span class="cx">           this.$el.append( '<br class="clear"/>' );
</span><span class="cx">  },
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpadminnetworkthemesphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/network/themes.php (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/network/themes.php    2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/network/themes.php       2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -218,7 +218,7 @@
</span><span class="cx"> $title = __('Themes');
</span><span class="cx"> $parent_file = 'themes.php';
</span><span class="cx"> 
</span><del>-wp_enqueue_script( 'theme' );
</del><ins>+wp_enqueue_script( 'theme-preview' );
</ins><span class="cx"> 
</span><span class="cx"> require_once(ABSPATH . 'wp-admin/admin-header.php');
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpadminthemeinstallphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/theme-install.php (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/theme-install.php     2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/theme-install.php        2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx">  $submenu_file = 'themes.php';
</span><span class="cx"> 
</span><span class="cx"> wp_enqueue_script( 'theme-install' );
</span><del>-wp_enqueue_script( 'theme' );
</del><ins>+wp_enqueue_script( 'theme-preview' );
</ins><span class="cx"> 
</span><span class="cx"> $body_id = $tab;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpadminthemesphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/themes.php (26725 => 26726)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/themes.php    2013-12-06 14:36:07 UTC (rev 26725)
+++ trunk/src/wp-admin/themes.php       2013-12-06 16:10:25 UTC (rev 26726)
</span><span class="lines">@@ -116,7 +116,7 @@
</span><span class="cx"> 
</span><span class="cx"> <div class="wrap">
</span><span class="cx">  <h2><?php esc_html_e( 'Themes' ); ?>
</span><del>-               <span class="theme-count"></span>
</del><ins>+                <span class="theme-count"><?php echo count( $themes ); ?></span>
</ins><span class="cx">   <?php if ( ! is_multisite() && current_user_can( 'install_themes' ) ) : ?>
</span><span class="cx">          <a href="<?php echo admin_url( 'theme-install.php' ); ?>" class="add-new-h2"><?php echo esc_html( _x( 'Add New', 'Add new theme' ) ); ?></a>
</span><span class="cx">  <?php endif; ?>
</span><span class="lines">@@ -179,9 +179,56 @@
</span><span class="cx"> 
</span><span class="cx"> ?>
</span><span class="cx"> 
</span><del>-<div class="theme-browser"></div>
</del><ins>+<div class="theme-browser">
+       <div class="themes">
</ins><span class="cx"> 
</span><span class="cx"> <?php
</span><ins>+/*
+ * This PHP is synchronized with the tmpl-theme template below!
+ */
+
+foreach ( $themes as $theme ) : ?>
+<div class="theme<?php if ( $theme['active'] ) echo ' active'; ?>">
+       <?php if ( ! empty( $theme['screenshot'][0] ) ) { ?>
+               <div class="theme-screenshot">
+                       <img src="<?php echo $theme['screenshot'][0]; ?>" alt="" />
+               </div>
+       <?php } else { ?>
+               <div class="theme-screenshot blank"></div>
+       <?php } ?>
+       <span class="more-details"><?php _e( 'Theme Details' ); ?></span>
+       <div class="theme-author"><?php printf( __( 'By %s' ), $theme['author'] ); ?></div>
+
+       <?php if ( $theme['active'] ) { ?>
+               <h3 class="theme-name"><span><?php _ex( 'Active:', 'theme' ); ?></span> <?php echo $theme['name']; ?></h3>
+       <?php } else { ?>
+               <h3 class="theme-name"><?php echo $theme['name']; ?></h3>
+       <?php } ?>
+
+       <div class="theme-actions">
+
+       <?php if ( $theme['active'] ) { ?>
+               <?php if ( $theme['actions']['customize'] ) { ?>
+                       <a class="button button-primary customize load-customize hide-if-no-customize" href="<?php echo $theme['actions']['customize']; ?>"><?php _e( 'Customize' ); ?></a>
+               <?php } ?>
+       <?php } else { ?>
+               <a class="button button-primary activate" href="<?php echo $theme['actions']['activate']; ?>"><?php _e( 'Activate' ); ?></a>
+               <a class="button button-secondary load-customize hide-if-no-customize" href="<?php echo $theme['actions']['customize']; ?>"><?php _e( 'Live Preview' ); ?></a>
+               <a class="button button-secondary hide-if-customize" href="<?php echo $theme['actions']['preview']; ?>"><?php _e( 'Preview' ); ?></a>
+       <?php } ?>
+
+       </div>
+
+       <?php if ( $theme['hasUpdate'] ) { ?>
+               <div class="theme-update"><?php _e( 'Update Available' ); ?></div>
+       <?php } ?>
+</div>
+<?php endforeach; ?>
+       <br class="clear" />
+       </div>
+</div>
+
+<?php
</ins><span class="cx"> // List broken themes, if any.
</span><span class="cx"> if ( ! is_multisite() && current_user_can('edit_themes') && $broken_themes = wp_get_themes( array( 'errors' => true ) ) ) {
</span><span class="cx"> ?>
</span><span class="lines">@@ -212,6 +259,11 @@
</span><span class="cx"> ?>
</span><span class="cx"> </div><!-- .wrap -->
</span><span class="cx"> 
</span><ins>+<?php
+/*
+ * The tmpl-theme template is synchronized with PHP above!
+ */
+?>
</ins><span class="cx"> <script id="tmpl-theme" type="text/template">
</span><span class="cx">  <# if ( data.screenshot[0] ) { #>
</span><span class="cx">          <div class="theme-screenshot">
</span><span class="lines">@@ -233,11 +285,12 @@
</span><span class="cx"> 
</span><span class="cx">  <# if ( data.active ) { #>
</span><span class="cx">          <# if ( data.actions.customize ) { #>
</span><del>-                       <a class="button button-primary hide-if-no-customize" href="{{ data.actions.customize }}"><?php _e( 'Customize' ); ?></a>
</del><ins>+                        <a class="button button-primary customize load-customize hide-if-no-customize" href="{{ data.actions.customize }}"><?php _e( 'Customize' ); ?></a>
</ins><span class="cx">           <# } #>
</span><span class="cx">  <# } else { #>
</span><span class="cx">          <a class="button button-primary activate" href="{{{ data.actions.activate }}}"><?php _e( 'Activate' ); ?></a>
</span><del>-               <a class="button button-secondary preview" href="{{{ data.actions.customize }}}"><?php _e( 'Live Preview' ); ?></a>
</del><ins>+                <a class="button button-secondary load-customize hide-if-no-customize" href="{{{ data.actions.customize }}}"><?php _e( 'Live Preview' ); ?></a>
+               <a class="button button-secondary hide-if-customize" href="{{{ data.actions.preview }}}"><?php _e( 'Preview' ); ?></a>
</ins><span class="cx">   <# } #>
</span><span class="cx"> 
</span><span class="cx">  </div>
</span><span class="lines">@@ -296,14 +349,15 @@
</span><span class="cx"> 
</span><span class="cx">          <div class="theme-actions">
</span><span class="cx">                  <div class="active-theme">
</span><del>-                               <a href="{{{ data.actions.customize }}}" class="button button-primary hide-if-no-customize"><?php _e( 'Customize' ); ?></a>
</del><ins>+                                <a href="{{{ data.actions.customize }}}" class="button button-primary customize load-customize hide-if-no-customize"><?php _e( 'Customize' ); ?></a>
</ins><span class="cx">                           <?php echo implode( ' ', $current_theme_actions ); ?>
</span><span class="cx">                  </div>
</span><span class="cx">                  <div class="inactive-theme">
</span><span class="cx">                          <# if ( data.actions.activate ) { #>
</span><del>-                                       <a href="{{{ data.actions.activate }}}" class="button button-primary"><?php _e( 'Activate' ); ?></a>
</del><ins>+                                        <a href="{{{ data.actions.activate }}}" class="button button-primary activate"><?php _e( 'Activate' ); ?></a>
</ins><span class="cx">                           <# } #>
</span><del>-                               <a href="{{{ data.actions.customize }}}" class="button button-secondary"><?php _e( 'Live Preview' ); ?></a>
</del><ins>+                                <a href="{{{ data.actions.customize }}}" class="button button-secondary load-customize hide-if-no-customize"><?php _e( 'Live Preview' ); ?></a>
+                               <a href="{{{ data.actions.preview }}}" class="button button-secondary hide-if-customize"><?php _e( 'Preview' ); ?></a>
</ins><span class="cx">                   </div>
</span><span class="cx"> 
</span><span class="cx">                  <# if ( ! data.active && data.actions.delete ) { #>
</span></span></pre>
</div>
</div>

</body>
</html>