WordPress.org

Make WordPress Core

Ticket #27055: 27055.9.diff

File 27055.9.diff, 31.5 KB (added by gcorne, 5 years ago)
  • src/wp-admin/css/themes.css

    diff --git src/wp-admin/css/themes.css src/wp-admin/css/themes.css
    index 329d546..54d00a0 100644
     
    2525        margin-left: 20px;
    2626}
    2727
    28 .themes-php .wrap .theme-count {
     28.themes-php .wrap .theme-count,
     29.theme-navigation .theme-count {
    2930        color: #fff;
    3031        -webkit-border-radius: 30px;
    3132        border-radius: 30px;
    body.folded .theme-overlay .theme-wrap { 
    10641065  16.2 - Install Themes
    10651066------------------------------------------------------------------------------*/
    10661067
    1067 .theme-install-php h4 {
    1068         margin: 2.5em 0 8px;
    1069 }
    1070 
    1071 .theme-install-php .tablenav {
    1072         height: auto;
    1073 }
    1074 
    1075 .theme-install-php .spinner {
    1076         margin-top: 9px;
     1068.theme-install-php h2 .upload {
     1069        margin-left: 10px;
    10771070}
    1078 
    1079 .available-theme {
     1071.theme-navigation {
     1072        background: #fff;
     1073        box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
     1074        -moz-box-sizing: border-box;
     1075        box-sizing: border-box;
     1076        color: #555;
    10801077        display: inline-block;
    1081         margin-right: 10px;
    1082         overflow: hidden;
    1083         padding: 20px 20px 20px 0;
    1084         vertical-align: top;
    1085         width: 300px;
     1078        font-size: 13px;
     1079        margin: 20px 0 30px;
     1080        padding: 0 20px;
     1081        position: relative;
     1082        width: 100%;
    10861083}
    1087 
    1088 .available-theme .screenshot {
    1089         width: 300px;
    1090         height: 225px;
    1091         display: block;
    1092         border: 1px solid #ccc;
    1093         margin-bottom: 10px;
     1084.upload-theme {
     1085        -moz-box-sizing: border-box;
     1086        box-sizing: border-box;
     1087        display: none;
     1088        margin: 0px 0 0;
     1089        padding: 0;
     1090        width: 100%;
    10941091        overflow: hidden;
    1095         background-color: #fff;
     1092        position: relative;
     1093                top: 10px;
    10961094}
    1097 
    1098 .available-theme img {
    1099         width: 300px;
     1095.upload-theme.opened {
     1096        display: block;
    11001097}
    1101 
    1102 .available-theme h3 {
    1103         margin: 15px 0 0;
     1098.upload-theme .wp-upload-form {
     1099        background: #fafafa;
     1100        border: 1px solid #e5e5e5;
     1101        padding: 30px;
     1102        margin: 30px auto;
     1103        max-width: 380px;
    11041104}
    1105 
    1106 .available-theme .theme-author {
    1107         line-height: 18px;
     1105.upload-theme .install-help {
     1106        color: #999;
     1107        font-size: 18px;
     1108        font-style: normal;
     1109        margin: 0;
     1110        padding: 40px 0 0;
     1111        text-align: center;
    11081112}
    1109 
    1110 .available-theme .action-links {
    1111         margin-top: 10px;
    1112         overflow: hidden;
     1113.upload-theme.opened + .theme-navigation,
     1114.upload-theme.opened + .theme-navigation + .theme-browser {
     1115        display: none;
    11131116}
    1114 
    1115 .available-theme a.screenshot:focus {
    1116         border-color: #777;
     1117.theme-navigation .theme-count {
     1118        top: 3px;
     1119        margin-left: 0;
    11171120}
    1118 
    1119 .available-theme .action-links li {
    1120         float: left;
    1121         padding-right: 10px;
    1122         margin-right: 10px;
    1123         border-right: 1px solid #dfdfdf;
     1121.theme-section,
     1122.theme-filter {
     1123        border-bottom: 4px solid #fff;
     1124        color: #666;
     1125        cursor: pointer;
     1126        display: inline-block;
     1127        margin: 0 10px;
     1128        padding: 15px 0;
     1129        -moz-transition: border-color .1s ease-in;
     1130        -webkit-transition: border-color .1s ease-in;
    11241131}
    1125 
    1126 .available-theme .action-links li {
    1127         padding-right: 8px;
    1128         margin-right: 8px;
     1132.theme-section.current,
     1133.theme-filter.current {
     1134        border-bottom: 4px solid #666;
     1135        color: #222;
    11291136}
    1130 
    1131 .ie8 .available-theme .action-links li {
    1132         padding-right: 7px;
    1133         margin-right: 7px;
     1137.theme-top-filters {
     1138        display: inline-block;
    11341139}
    1135 
    1136 .available-theme .action-links li:last-child {
    1137         padding-right: 0;
    1138         margin-right: 0;
    1139         border-right: 0;
     1140.theme-navigation .more-filters {
     1141        color: #666;
     1142        cursor: pointer;
     1143        display: inline-block;
     1144        margin: 0 10px;
     1145        padding: 4px 5px;
     1146        -moz-transition: color .1s ease-in, background .1s ease-in;
     1147        -webkit-transition: color .1s ease-in, background .1s ease-in;
     1148        transition: color .1s ease-in, background .1s ease-in;
     1149}
     1150body.more-filters-opened .more-filters,
     1151.theme-navigation .more-filters.current {
     1152        background: rgb(46, 162, 204);
     1153        border-radius: 2px;
     1154        border: none;
     1155        color: #fff;
    11401156}
    1141 
    1142 .available-theme .action-links .delete-theme {
    1143         float: right;
    1144         margin-left: 8px;
    1145         margin-right: 0;
     1157.theme-install-php .theme-search {
     1158        position: absolute;
     1159                right: 10px;
     1160                top: 9px;
     1161        font-size: 16px;
     1162        font-weight: 300;
     1163        line-height: 1.5;
     1164        width: 280px;
    11461165}
    1147 
    1148 .available-theme .action-links .delete-theme a {
    1149         color: red;
    1150         padding: 2px;
     1166.more-filters:before {
     1167        color: #777;
     1168        text-align: center;
     1169        margin: 0 5px 0 0;
     1170        content: "\f111";
     1171        display: inline-block;
     1172        width: 16px;
     1173        height: 16px;
     1174        -webkit-font-smoothing: antialiased;
     1175        font-size: 16px;
     1176        line-height: 1;
     1177        font-family: "dashicons";
     1178        text-decoration: inherit;
     1179        font-weight: normal;
     1180        font-style: normal;
     1181        vertical-align: top;
     1182        -moz-transition: color .1s ease-in 0;
     1183        -webkit-transition: color .1s ease-in 0;
     1184        transition: color .1s ease-in 0;
     1185        text-align: center;
    11511186}
    1152 
    1153 .available-theme .action-links .delete-theme a:hover {
    1154         background: red;
     1187.more-filters.current:before {
    11551188        color: #fff;
    1156         text-decoration: none;
    1157 }
    1158 
    1159 .available-theme .action-links p {
    1160         float: left;
    1161 }
    1162 
    1163 /* Allow for three-up in small windows when sidebar is collapsed */
    1164 @media only screen and (max-width: 1200px) {
    1165         .folded .available-theme,
    1166         .folded .available-theme .screenshot {
    1167                 width: 300px;
    1168         }
    1169 
    1170         .folded .available-theme .screenshot {
    1171                 height: 225px;
    1172         }
    11731189}
    1174 
    1175 /* Adjust three-up display in smaller windows when sidebar is collapsed */
    1176 @media only screen and (max-width: 1079px) {
    1177         .folded .available-theme,
    1178         .folded .available-theme .screenshot {
    1179                 width: 270px;
    1180         }
    1181 
    1182         .folded .available-theme .screenshot {
    1183                 height: 203px;
    1184         }
     1190.more-filters-container {
     1191        display: none;
     1192        padding: 30px;
     1193        border-top: 1px solid #eee;
     1194        margin: 0 -20px;
     1195        background: #fafafa;
    11851196}
    1186 
    1187 /* Allow for three-up on 1024px wide screens, e.g. tablets */
    1188 @media only screen and (max-width: 1200px) {
    1189         .available-theme,
    1190         .available-theme .screenshot {
    1191                 width: 240px;
    1192         }
    1193 
    1194         .available-theme .screenshot {
    1195                 height: 180px;
    1196         }
    1197 
    1198         .available-theme img {
    1199                 width: 100%;
    1200         }
     1197body.more-filters-opened .more-filters-container {
     1198        display: block;
    12011199}
    1202 
    1203 .feature-filter {
    1204         padding: 8px 12px 0;
     1200.theme-install-php .add-new-theme {
     1201        display: none !important;
    12051202}
    12061203
    1207 .feature-filter .feature-group {
    1208         float: left;
    1209         margin: 5px 10px 10px;
     1204.rating {
     1205        margin: 30px 0;
    12101206}
    1211 
    1212 .feature-filter .feature-group li {
     1207.rating span:before {
     1208        color: #e6b800;
     1209        content: "\f154";
    12131210        display: inline-block;
     1211        -webkit-font-smoothing: antialiased;
     1212        font: normal 20px/1 'dashicons';
    12141213        vertical-align: top;
    1215         list-style-type: none;
    1216         padding-right: 25px;
    1217         width: 150px;
     1214}
     1215/* Half stars */
     1216.rating-10 span.one:before,
     1217.rating-30 span.two:before,
     1218.rating-50 span.three:before,
     1219.rating-70 span.four:before,
     1220.rating-90 span.five:before {
     1221        content: "\f459";
     1222}
     1223/* Full stars */
     1224.rating-20 span.one:before {
     1225        content: "\f155";
     1226}
     1227.rating-30 span.one:before,
     1228.rating-40 span.one:before,
     1229.rating-40 span.two:before {
     1230        content: "\f155";
     1231}
     1232.rating-50 span.one:before,
     1233.rating-50 span.two:before,
     1234.rating-60 span.one:before,
     1235.rating-60 span.two:before,
     1236.rating-60 span.three:before {
     1237        content: "\f155";
     1238}
     1239.rating-70 span.one:before,
     1240.rating-70 span.two:before,
     1241.rating-70 span.three:before,
     1242.rating-80 span.one:before,
     1243.rating-80 span.two:before,
     1244.rating-80 span.three:before,
     1245.rating-80 span.four:before {
     1246        content: "\f155";
     1247}
     1248.rating-90 span.one:before,
     1249.rating-90 span.two:before,
     1250.rating-90 span.three:before,
     1251.rating-90 span.four:before,
     1252.rating-100 span.one:before,
     1253.rating-100 span.two:before,
     1254.rating-100 span.three:before,
     1255.rating-100 span.four:before,
     1256.rating-100 span.five:before {
     1257        content: "\f155";
     1258}
     1259.rating .votes {
     1260        display: inline;
     1261        margin-left: 10px;
     1262        line-height: 20px;
     1263}
     1264.loading-themes .theme-browser,
     1265.error .theme-browser {
     1266        display: none;
     1267}
     1268.loading-themes .spinner {
     1269        display: block;
     1270        margin: 40px auto 0;
     1271        float: none;
    12181272}
    12191273
    12201274/*------------------------------------------------------------------------------
  • src/wp-admin/includes/theme.php

    diff --git src/wp-admin/includes/theme.php src/wp-admin/includes/theme.php
    index 9e06596..e5bf43a 100644
    function wp_prepare_themes_for_js( $themes = null ) { 
    453453         */
    454454        $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes );
    455455        return array_values( $prepared_themes );
     456}
     457
     458
     459/**
     460 * Get public themes from the themes directory
     461 * and prepare them for JavaScript
     462 *
     463 * @since 3.8.0
     464 *
     465 * @uses themes_api themes_directory_sections
     466 * @return array with $theme objects
     467 */
     468function prepare_public_themes_for_js( $themes = array() ) {
     469
     470        $sections = array(
     471                'featured' => __( 'Featured Themes' ),
     472                'popular'  => __( 'Popular Themes' ),
     473                'new'      => __( 'Newest Themes' ),
     474        );
     475
     476        $sections = array_keys( $sections );
     477
     478        $args = array(
     479                'page' => 1,
     480                'per_page' => 24,
     481        );
     482
     483        foreach ( $sections as $section ) {
     484                $args['browse'] = $section;
     485                $themes[ $section ] = themes_api( 'query_themes', $args );
     486        }
     487
     488        return $themes;
    456489}
     490 No newline at end of file
  • src/wp-admin/js/theme.js

    diff --git src/wp-admin/js/theme.js src/wp-admin/js/theme.js
    index 7e12958..28339cf 100644
    themes = wp.themes = wp.themes || {}; 
    1212themes.data = _wpThemeSettings;
    1313l10n = themes.data.l10n;
    1414
     15// Shortcut for (bool) isBrowsing check
     16themes.isInstall = themes.data.settings.isBrowsing;
     17
    1518// Setup app structure
    16 _.extend( themes, { model: {}, view: {}, routes: {}, router: {}, template: wp.template });
     19_.extend( themes, { model: {}, view: {}, routes: {}, router: {}, template: wp.template, orgAPI: {} });
    1720
    18 themes.model = Backbone.Model.extend({});
     21themes.Model = Backbone.Model.extend({
     22        // Adds attributes to the defaul data coming through the .org themes api
     23        // Map `id` to `slug` for shared code
     24        initialize: function() {
     25                var install, preview;
     26
     27                // Install url for the theme
     28                // using the install nonce
     29                install = {
     30                        action: 'install-theme',
     31                        theme: this.get( 'slug' ),
     32                        _wpnonce: themes.data.settings._nonceInstall
     33                };
     34
     35                // Build the url query
     36                install = themes.data.settings.updateURI + '?' + $.param( install );
     37
     38                // Preview url for the theme
     39                preview = {
     40                        tab: 'theme-information',
     41                        theme: this.get( 'slug' )
     42                };
     43
     44                preview = themes.data.settings.installURI + '?' + $.param( preview );
     45
     46                // Set the attributes
     47                this.set({
     48                        installURI: install,
     49                        previewURI: preview,
     50                        id: this.get( 'slug' )
     51                });
     52        }
     53});
    1954
    2055// Main view controller for themes.php
    2156// Unifies and renders all available views
    themes.view.Appearance = wp.Backbone.View.extend({ 
    2863        page: 0,
    2964
    3065        // Sets up a throttler for binding to 'scroll'
    31         initialize: function() {
     66        initialize: function( options ) {
    3267                // Scroller checks how far the scroll position is
    3368                _.bindAll( this, 'scroller' );
    3469
     70                this.SearchView = options.SearchView ? options.SearchView : themes.view.Search;
    3571                // Bind to the scroll event and throttle
    3672                // the results from this.scroller
    3773                this.window.bind( 'scroll', _.throttle( this.scroller, 300 ) );
    themes.view.Appearance = wp.Backbone.View.extend({ 
    5591                this.$el.append( '<br class="clear"/>' );
    5692        },
    5793
     94        // Defines search element container
     95        searchContainer: $( '#wpbody h2:first' ),
     96
    5897        // Search input and view
    5998        // for current theme collection
    6099        search: function() {
    themes.view.Appearance = wp.Backbone.View.extend({ 
    66105                        return;
    67106                }
    68107
    69                 view = new themes.view.Search({ collection: self.collection, parent: this });
     108                view = new this.SearchView({
     109                        collection: self.collection,
     110                        parent: this
     111                });
    70112
    71113                // Render and append after screen title
    72114                view.render();
    73                 $('#wpbody h2:first')
     115                this.searchContainer
    74116                        .append( $.parseHTML( '<label class="screen-reader-text" for="theme-search-input">' + l10n.search + '</label>' ) )
    75117                        .append( view.el );
    76118        },
    themes.view.Appearance = wp.Backbone.View.extend({ 
    95137// @has 'id' 'name' 'screenshot' 'author' 'authorURI' 'version' 'active' ...
    96138themes.Collection = Backbone.Collection.extend({
    97139
    98         model: themes.model,
     140        model: themes.Model,
    99141
    100142        // Search terms
    101143        terms: '',
    themes.view.Theme = wp.Backbone.View.extend({ 
    188230
    189231        events: {
    190232                'click': 'expand',
     233                'click .preview': 'preview',
    191234                'keydown': 'expand',
    192235                'touchend': 'expand',
    193236                'touchmove': 'preventExpand'
    themes.view.Theme = wp.Backbone.View.extend({ 
    250293
    251294        preventExpand: function() {
    252295                this.touchDrag = true;
     296        },
     297
     298        preview: function( event ) {
     299                event.preventDefault();
     300
     301                event = event || window.event;
     302
     303                var preview = new themes.view.Preview({
     304                        model: this.model
     305                });
     306
     307                preview.render();
     308                $( '#appearance' ).append( preview.el );
    253309        }
    254310});
    255311
    themes.view.Details = wp.Backbone.View.extend({ 
    415471        }
    416472});
    417473
     474// Theme Preview view
     475// Set ups a modal overlay with the expanded theme data
     476themes.view.Preview = wp.Backbone.View.extend({
     477
     478        className: 'wp-full-overlay expanded',
     479        el: '#theme-installer',
     480
     481        events: {
     482                'click .close-full-overlay': 'close',
     483                'click .collapse-sidebar': 'collapse'
     484        },
     485
     486        // The HTML template for the theme preview
     487        html: themes.template( 'theme-preview' ),
     488
     489        render: function() {
     490                var data = this.model.toJSON();
     491                this.$el.html( this.html( data ) );
     492
     493                themes.router.navigate( themes.router.baseUrl( '?preview=' + this.model.get( 'id' ) ), { replace: true } );
     494
     495                this.$el.fadeIn( 200, function() {
     496                        $( 'body' ).addClass( 'theme-installer-active full-overlay-active' );
     497                });
     498        },
     499
     500        close: function() {
     501                this.$el.fadeOut( 200, function() {
     502                        $( 'body' ).removeClass( 'theme-installer-active full-overlay-active' );
     503                });
     504
     505                themes.router.navigate( themes.router.baseUrl( '' ) );
     506        },
     507
     508        collapse: function() {
     509                this.$el.toggleClass( 'collapsed' ).toggleClass( 'expanded' );
     510        }
     511});
     512
    418513// Controls the rendering of div.themes,
    419514// a wrapper that will hold all the theme elements
    420515themes.view.Themes = wp.Backbone.View.extend({
    themes.view.Search = wp.Backbone.View.extend({ 
    732827
    733828// Sets up the routes events for relevant url queries
    734829// Listens to [theme] and [search] params
    735 themes.routes = Backbone.Router.extend({
     830themes.Router = Backbone.Router.extend({
    736831
    737832        routes: {
    738833                'themes.php?theme=:slug': 'theme',
    themes.Run = { 
    788883                var self = this;
    789884                // Bind to our global thx object
    790885                // so that the object is available to sub-views
    791                 themes.router = new themes.routes();
     886                themes.router = new themes.Router();
    792887
    793888                // Handles theme details route event
    794889                themes.router.on( 'route:theme', function( slug ) {
    themes.Run = { 
    805900                        self.view.trigger( 'theme:close' );
    806901                        self.themes.doSearch( query );
    807902                });
     903
     904                this.extraRoutes();
     905        },
     906
     907        extraRoutes: function() {
     908                return false;
    808909        }
    809910};
    810911
    811 // Ready...
    812 jQuery( document ).ready(
     912// Extend the main Search view
     913themes.view.InstallerSearch =  themes.view.Search.extend({
     914
     915        events: {
     916                'keyup': 'search'
     917        },
     918
     919        // Handles Ajax request for searching through themes in public repo
     920        search: function( event ) {
     921                var data,
     922                        self = this;
    813923
    814         // Bring on the themes
    815         _.bind( themes.Run.init, themes.Run )
     924                this.collection = this.options.parent.view.collection;
     925
     926                // Clear on escape.
     927                if ( event.type === 'keyup' && event.which === 27 ) {
     928                        event.target.value = '';
     929                }
     930
     931                // Construct the main `search` request
     932                // using the default values
     933                data = _.defaults( {}, themes.orgAPI.data );
     934
     935                data.request.search = event.target.value;
     936
     937                // Intercept an [author] search.
     938                //
     939                // If input value starts with `author:` send a request
     940                // for `author` instead of a regular `search`
     941                if ( event.target.value.substring( 0, 7 ) === 'author:' ) {
     942                        data.request.search = '';
     943                        data.request.author = event.target.value.slice( 7 );
     944                }
     945
     946                // Intercept a [tag] search.
     947                //
     948                // If input value starts with `tag:` send a request
     949                // for `tag` instead of a regular `search`
     950                if ( event.target.value.substring( 0, 4 ) === 'tag:' ) {
     951                        data.request.search = '';
     952                        data.request.tag = [ event.target.value.slice( 4 ) ];
     953                }
     954
     955                // Send Ajax POST request to api.wordpress.org/themes
     956                $.ajax({
     957                        url: themes.orgAPI.url,
     958
     959                        // We want JSON data
     960                        dataType: 'json',
     961                        type: 'POST',
     962                        crossDomain: true,
     963
     964                        // Request data
     965                        data: data,
     966
     967                        beforeSend: function() {
     968                                // Spin it
     969                                $( 'body' ).addClass( 'loading-themes' );
     970                        }
     971                })
     972                        .done( function( data ) {
     973                                // Update the collection with the queried data
     974                                self.collection.reset( data.themes );
     975                                // Trigger a collection refresh event to render the views
     976                                self.collection.trigger( 'update' );
     977
     978                                // Un-spin it
     979                                $( 'body' ).removeClass( 'loading-themes' );
     980                                $( '#appearance' ).find( 'div.error' ).remove();
     981                })
     982                        .fail( function() {
     983                                $( '#appearance' ).find( 'div.error' ).remove();
     984                                $( '#appearance' ).append( '<div class="error"><p>' + l10n.error + '</p></div>' );
     985                });
     986
     987                return false;
     988        }
     989});
     990
     991// Store api.wordpress.org/themes values and structure
     992// for internal access
     993themes.orgAPI = {
     994
     995        // Default data request
     996        data: {
     997                action: 'query_themes',
     998                request: {}
     999        },
     1000
     1001        url: 'https://api.wordpress.org/themes/info/1.1/?action=query_themes'
     1002};
     1003
     1004themes.view.Installer = themes.view.Appearance.extend({
     1005
     1006        el: '#wpbody-content .wrap',
     1007
     1008        // Register events for sorting and filters in theme-navigation
     1009        events: {
     1010                'click .theme-section': 'onSort',
     1011                'click .theme-filter': 'onFilter',
     1012                'click .more-filters': 'moreFilters'
     1013        },
     1014
     1015        // Handles all the rendering of the public theme directory
     1016        browse: function( section ) {
     1017                // Create a new collection with the proper theme data
     1018                // for each section
     1019                this.collection = new themes.Collection( themes.data.browse.publicThemes[ section ].themes );
     1020
     1021                if ( this.view ) {
     1022                        this.view.remove();
     1023                }
     1024
     1025                // Set ups the view and passes the section argument
     1026                this.view = new themes.view.Themes({
     1027                        collection: this.collection,
     1028                        section: section,
     1029                        parent: this
     1030                });
     1031
     1032                // Reset pagination every time the install view handler is run
     1033                this.page = 0;
     1034
     1035                // Render and append
     1036                this.$el.find( '.themes' ).remove();
     1037                this.view.render();
     1038                this.$el.find( '.theme-browser' ).append( this.view.el );
     1039
     1040                this.uploader();
     1041        },
    8161042
    817 );
     1043        // Initial render method
     1044        render: function() {
     1045                this.search();
     1046                return this.browse( this.options.section );
     1047        },
     1048
     1049        // Sorting navigation
     1050        onSort: function( event ) {
     1051                var $el = $( event.target ),
     1052                        sort = $el.data( 'sort' );
     1053
     1054                // Bail if this is already active
     1055                if ( $el.hasClass( this.activeClass ) ) {
     1056                        return;
     1057                }
     1058
     1059                this.sort( sort );
     1060
     1061                // Trigger a router.naviagte update
     1062                themes.router.navigate( themes.router.baseUrl( '?sort=' + sort ) );
     1063        },
     1064
     1065        sort: function( sort ) {
     1066                $( '#theme-search-input' ).val( '' );
     1067
     1068                $( '.theme-section, .theme-filter' ).removeClass( this.activeClass );
     1069                $( '[data-sort="' + sort + '"]' ).addClass( this.activeClass );
     1070
     1071                this.browse( sort );
     1072        },
     1073
     1074        // Filters and Tags
     1075        onFilter: function( event ) {
     1076                var $el = $( event.target ),
     1077                        filter = $el.data( 'filter' ),
     1078                        self = this,
     1079                        data;
     1080
     1081                // Bail if this is already active
     1082                if ( $el.hasClass( this.activeClass ) ) {
     1083                        return;
     1084                }
     1085
     1086                $( '.theme-filter, .theme-section' ).removeClass( this.activeClass );
     1087                $el.addClass( this.activeClass );
     1088
     1089                if ( ! filter ) {
     1090                        return;
     1091                }
     1092
     1093                // Construct the filter request
     1094                // using the default values
     1095                data = _.defaults( {}, themes.orgAPI.data );
     1096
     1097                data.request = _.defaults({
     1098                        tag: [ filter ]
     1099                }, themes.orgAPI.data.request );
     1100
     1101                // Send Ajax POST request to api.wordpress.org/themes
     1102                $.ajax({
     1103                        url: themes.orgAPI.url,
     1104
     1105                        // We want JSON data
     1106                        dataType: 'json',
     1107                        type: 'POST',
     1108                        crossDomain: true,
     1109
     1110                        // Request data
     1111                        data: data,
     1112
     1113                        beforeSend: function() {
     1114                                // Spin it
     1115                                $( 'body' ).addClass( 'loading-themes' );
     1116                        }
     1117                })
     1118                        .done( function( data ) {
     1119                                // Update the collection with the queried data
     1120                                self.collection.reset( data.themes );
     1121                                // Trigger a collection refresh event to render the views
     1122                                self.collection.trigger( 'update' );
     1123
     1124                                // Un-spin it
     1125                                $( 'body' ).removeClass( 'loading-themes' );
     1126                                $( '#appearance' ).find( 'div.error' ).remove();
     1127                })
     1128                        .fail( function() {
     1129                                $( '#appearance' ).find( 'div.error' ).remove();
     1130                                $( '#appearance' ).append( '<div class="error"><p>' + l10n.error + '</p></div>' );
     1131                });
     1132
     1133                return false;
     1134        },
     1135
     1136        activeClass: 'current',
     1137
     1138        // Overwrite search container class to append search
     1139        // in new location
     1140        searchContainer: $( '.theme-navigation' ),
     1141
     1142        uploader: function() {
     1143                $( 'a.upload.button' ).on( 'click', function() {
     1144                        $( '.upload-theme' )
     1145                                .toggleClass( 'opened' )
     1146                                .hasClass( 'opened' ) ? $( this ).text( l10n.back ) : $( this ).text( l10n.upload );
     1147                });
     1148        },
     1149
     1150        moreFilters: function() {
     1151                $( 'body' ).toggleClass( 'more-filters-opened' );
     1152        }
     1153});
     1154
     1155themes.InstallerRouter = Backbone.Router.extend({
     1156        routes: {
     1157                'theme-install.php?theme=:slug': 'theme',
     1158                '': 'sort',
     1159                'theme-install.php(?sort=:sort)': 'sort',
     1160                'theme-install.php?preview=:slug': 'preview'
     1161        },
     1162
     1163        baseUrl: function( url ) {
     1164                return 'theme-install.php' + url;
     1165        }
     1166});
     1167
     1168
     1169themes.RunInstaller = {
     1170
     1171        init: function() {
     1172                // Set up the view
     1173                // Passes the default 'section' as an option
     1174                this.view = new themes.view.Installer({
     1175                        el: $( '#appearance' ),
     1176                        section: 'featured',
     1177                        SearchView: themes.view.InstallerSearch
     1178                });
     1179
     1180                // Render results
     1181                this.render();
     1182
     1183        },
     1184
     1185        render: function() {
     1186
     1187                // Render results
     1188                this.view.render();
     1189                this.routes();
     1190
     1191                Backbone.history.start({
     1192                        root: themes.data.settings.adminUrl,
     1193                        pushState: true,
     1194                        hashChange: false
     1195                });
     1196        },
     1197
     1198        routes: function() {
     1199                var self = this;
     1200                // Bind to our global thx object
     1201                // so that the object is available to sub-views
     1202                themes.router = new themes.InstallerRouter();
     1203
     1204                // Handles theme details route event
     1205                themes.router.on( 'route:theme', function( slug ) {
     1206                        self.view.view.expand( slug );
     1207                });
     1208
     1209                themes.router.on( 'route:sort', function( sort ) {
     1210                        if ( ! sort ) {
     1211                                sort = 'featured';
     1212                        }
     1213                        self.view.sort( sort );
     1214                        self.view.trigger( 'theme:close' );
     1215                });
     1216
     1217                this.extraRoutes();
     1218        },
     1219
     1220        extraRoutes: function() {
     1221                return false;
     1222        }
     1223};
     1224
     1225// Ready...
     1226$( document ).ready(function() {
     1227        if ( themes.isInstall ) {
     1228                themes.RunInstaller.init();
     1229        } else {
     1230                themes.Run.init();
     1231        }
     1232});
    8181233
    8191234})( jQuery );
    8201235
  • src/wp-admin/theme-install.php

    diff --git src/wp-admin/theme-install.php src/wp-admin/theme-install.php
    index 7cb2a9a..28a2e71 100644
    if ( is_multisite() && ! is_network_admin() ) { 
    2020        exit();
    2121}
    2222
    23 $wp_list_table = _get_list_table('WP_Theme_Install_List_Table');
    24 $pagenum = $wp_list_table->get_pagenum();
    25 $wp_list_table->prepare_items();
     23// $wp_list_table = _get_list_table('WP_Theme_Install_List_Table');
     24// $pagenum = $wp_list_table->get_pagenum();
     25// $wp_list_table->prepare_items();
    2626
    27 $title = __('Install Themes');
     27$title = __( 'Add Themes' );
    2828$parent_file = 'themes.php';
    29 if ( !is_network_admin() )
     29
     30if ( ! is_network_admin() ) {
    3031        $submenu_file = 'themes.php';
     32}
    3133
    32 wp_enqueue_script( 'theme-install' );
     34// wp_enqueue_script( 'theme-install' );
    3335wp_enqueue_script( 'theme-preview' );
    3436
    35 $body_id = $tab;
     37wp_localize_script( 'theme', '_wpThemeSettings', array(
     38        'themes'   => false,
     39        'settings' => array(
     40                'isBrowsing'    => (bool) ( get_current_screen()->id == 'theme-install' ),
     41                'canInstall'    => ( ! is_multisite() && current_user_can( 'install_themes' ) ),
     42                'installURI'    => ( ! is_multisite() && current_user_can( 'install_themes' ) ) ? admin_url( 'theme-install.php' ) : null,
     43                'adminUrl'      => parse_url( admin_url(), PHP_URL_PATH ),
     44                'updateURI'     => admin_url( 'update.php' ),
     45                '_nonceInstall' => wp_create_nonce( 'install-theme' )
     46        ),
     47        'l10n' => array(
     48                'addNew' => __( 'Add New Theme' ),
     49                'search'  => __( 'Search Themes' ),
     50                'searchPlaceholder' => __( 'Search themes...' ),
     51                'upload' => __( 'Upload Theme' ),
     52                'back'   => __( 'Back' ),
     53                'error'  => __( 'There was a problem trying to load the themes. Please, try again.' ),
     54        ),
     55        'browse' => array(
     56                'sections' => apply_filters( 'install_theme_sections', array(
     57                        'featured' => __( 'Featured Themes' ),
     58                        'popular'  => __( 'Popular Themes' ),
     59                        'new'      => __( 'Newest Themes' ),
     60                ) ),
     61                'publicThemes' => ( get_current_screen()->id == 'theme-install' ) ? prepare_public_themes_for_js() : null,
     62        ),
     63) );
     64
     65wp_enqueue_script( 'theme' );
     66
     67// $body_id = $tab;
    3668
    3769/**
    3870 * Fires before each of the tabs are rendered on the Install Themes page.
    $body_id = $tab; 
    4375 *
    4476 * @since 2.8.0
    4577 */
    46 do_action( "install_themes_pre_{$tab}" );
     78// do_action( "install_themes_pre_{$tab}" );
    4779
    4880$help_overview =
    4981        '<p>' . sprintf(__('You can find additional themes for your site by using the Theme Browser/Installer on this screen, which will display themes from the <a href="%s" target="_blank">WordPress.org Theme Directory</a>. These themes are designed and developed by third parties, are available free of charge, and are compatible with the license WordPress uses.'), 'https://wordpress.org/themes/') . '</p>' .
    get_current_screen()->set_help_sidebar( 
    73105);
    74106
    75107include(ABSPATH . 'wp-admin/admin-header.php');
     108
     109/**
     110 * Uploader
     111 */
     112function install_themes_upload() {
     113        ?>
     114        <div class="upload-theme">
     115                <p class="install-help"><?php _e( 'If you have a theme in a .zip format, you may install it by uploading it here.' ); ?></p>
     116                <form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url('update.php?action=upload-theme'); ?>">
     117                        <?php wp_nonce_field( 'theme-upload'); ?>
     118                        <input type="file" name="themezip" />
     119                        <?php submit_button( __( 'Install Now' ), 'button', 'install-theme-submit', false ); ?>
     120                </form>
     121        </div>
     122        <?php
     123}
     124add_action( 'install_themes_upload', 'install_themes_upload', 10, 1 );
     125
    76126?>
    77 <div class="wrap">
    78 <h2><?php echo esc_html( $title ); ?></h2>
    79 <?php
     127<div id="appearance" class="wrap">
     128        <h2>
     129                <?php echo esc_html( $title ); ?>
     130                <a class="upload button button-secondary"><?php esc_html_e( 'Upload Theme' ); ?></a>
     131        </h2>
     132
     133        <?php install_themes_upload(); ?>
     134
     135        <div class="theme-navigation">
     136                <span class="theme-count"></span>
     137                <span class="theme-section current" data-sort="featured"><?php esc_html_e( 'Featured' ); ?></span>
     138                <span class="theme-section" data-sort="popular"><?php esc_html_e( 'Popular' ); ?></span>
     139                <span class="theme-section" data-sort="new"><?php esc_html_e( 'Latest' ); ?></span>
     140                <div class="theme-top-filters">
     141                        <span class="theme-filter" data-filter="photoblogging">Photography</span>
     142                        <span class="theme-filter" data-filter="responsive-layout">Responsive</span>
     143                        <span class="theme-filter more-filters">More</span>
     144                </div>
     145                <div class="more-filters-container">
     146                        Display more filters.
     147                </div>
     148        </div>
     149        <div class="theme-browser"></div>
     150        <div class="theme-overlay"></div>
     151        <div id="theme-installer" class="wp-full-overlay expanded"></div>
    80152
    81 $wp_list_table->views(); ?>
     153        <span class="spinner"></span>
    82154
    83 <br class="clear" />
     155        <br class="clear" />
    84156<?php
    85157/**
    86158 * Fires at the top of each of the tabs on the Install Themes page.
    $wp_list_table->views(); ?> 
    93165 *
    94166 * @param int $paged Number of the current page of results being viewed.
    95167 */
    96 do_action( "install_themes_{$tab}", $paged );
     168// do_action( "install_themes_{$tab}", $paged );
    97169?>
    98170</div>
     171
     172
     173<script id="tmpl-theme" type="text/template">
     174        <# if ( data.screenshot_url ) { #>
     175                <div class="theme-screenshot">
     176                        <img src="{{ data.screenshot_url }}" alt="" />
     177                </div>
     178        <# } else { #>
     179                <div class="theme-screenshot blank"></div>
     180        <# } #>
     181        <span class="more-details"><?php _e( 'Theme Details' ); ?></span>
     182        <div class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.author }}}' ); ?></div>
     183        <h3 class="theme-name">{{{ data.name }}}</h3>
     184
     185        <div class="theme-actions">
     186                <a class="button button-primary" href="{{{ data.installURI }}}"><?php esc_html_e( 'Install' ); ?></a>
     187                <a class="button button-secondary preview install-theme-preview" href=""><?php esc_html_e( 'Preview' ); ?></a>
     188        </div>
     189</script>
     190
     191<script id="tmpl-theme-single" type="text/template">
     192        <div class="theme-backdrop"></div>
     193        <div class="theme-wrap">
     194                <div class="theme-header">
     195                        <button alt="<?php _e( 'Show previous theme' ); ?>" class="left dashicons dashicons-no"></button>
     196                        <button alt="<?php _e( 'Show next theme' ); ?>" class="right dashicons dashicons-no"></button>
     197                        <button alt="<?php _e( 'Close overlay' ); ?>" class="close dashicons dashicons-no"></button>
     198                </div>
     199                <div class="theme-about">
     200                        <div class="theme-screenshots">
     201                        <# if ( data.screenshot_url ) { #>
     202                                <div class="screenshot"><img src="{{ data.screenshot_url }}" alt="" /></div>
     203                        <# } else { #>
     204                                <div class="screenshot blank"></div>
     205                        <# } #>
     206                        </div>
     207
     208                        <div class="theme-info">
     209                                <h3 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{{ data.version }}}' ); ?></span></h3>
     210                                <h4 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.author }}}' ); ?></h4>
     211                                <p class="theme-description">{{{ data.description }}}</p>
     212
     213                                <div class="rating rating-{{ Math.round( data.rating / 10 ) * 10 }}">
     214                                        <span class="one"></span>
     215                                        <span class="two"></span>
     216                                        <span class="three"></span>
     217                                        <span class="four"></span>
     218                                        <span class="five"></span>
     219                                        <p class="votes"><?php printf( __( 'Based on %s ratings.' ), '{{{ data.num_ratings }}}' ); ?></p>
     220                                </div>
     221
     222                                <# if ( data.tags ) { #>
     223                                        <p class="theme-tags">
     224                                                <span><?php _e( 'Tags:' ); ?></span>
     225                                                <# _.each( data.tags, function( tag ) { #>
     226                                                        {{{ tag }}}
     227                                                <# }); #>
     228                                        </p>
     229                                <# } #>
     230                        </div>
     231                </div>
     232
     233                <div class="theme-actions">
     234                        <a href="{{{ data.installURI }}}" class="button button-primary"><?php _e( 'Install' ); ?></a>
     235                        <a href="" class="button button-secondary"><?php _e( 'Preview' ); ?></a>
     236                        <a href="{{{ data.homepage }}}" class="button button-secondary"><?php _e( 'More Info' ); ?></a>
     237                </div>
     238        </div>
     239</script>
     240
     241<script id="tmpl-theme-preview" type="text/template">
     242        <div class="wp-full-overlay-sidebar">
     243                <div class="wp-full-overlay-header">
     244                        <a href="" class="close-full-overlay button-secondary"><?php _e( 'Close' ); ?></a>
     245                        <a href="{{{ data.installURI }}}" class="button button-primary theme-install"><?php _e( 'Install' ); ?></a>
     246                </div>
     247                <div class="wp-full-overlay-sidebar-content">
     248                        <div class="install-theme-info">
     249                                <h3 class="theme-name">{{{ data.name }}}</h3>
     250                                <span class="theme-by"><?php printf( __( 'By %s' ), '{{{ data.author }}}' ); ?></span>
     251
     252                                <img class="theme-screenshot" src="{{ data.screenshot_url }}" alt="" />
     253
     254                                <div class="theme-details">
     255                                        <div class="star-rating" title="4.5 rating based on 25 ratings"></div>
     256                                        <div class="theme-version"><?php printf( __( 'Version: %s' ), '{{{ data.version }}}' ); ?></div>
     257                                        <div class="theme-description">{{{ data.description }}}</div>
     258                                </div>
     259                        </div>
     260                </div>
     261                <div class="wp-full-overlay-footer">
     262                        <a href="" class="collapse-sidebar" title="<?php esc_attr_e( 'Collapse Sidebar' ); ?>">
     263                                <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
     264                                <span class="collapse-sidebar-arrow"></span>
     265                        </a>
     266                </div>
     267        </div>
     268        <div class="wp-full-overlay-main">
     269                <iframe src="{{{ data.preview_url }}}" />
     270        </div>
     271</script>
     272
    99273<?php
    100274include(ABSPATH . 'wp-admin/admin-footer.php');
  • src/wp-admin/update.php

    diff --git src/wp-admin/update.php src/wp-admin/update.php
    index d698e26..16145b8 100644
    if ( isset($_GET['action']) ) { 
    202202
    203203                include_once ABSPATH . 'wp-admin/includes/theme-install.php'; //for themes_api..
    204204
    205                 check_admin_referer('install-theme_' . $theme);
     205                check_admin_referer( 'install-theme' );
    206206                $api = themes_api('theme_information', array('slug' => $theme, 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth.
    207207
    208208                if ( is_wp_error($api) )