Making WordPress.org

Ticket #1101: 1101.patch

File 1101.patch, 13.8 KB (added by ocean90, 10 years ago)
  • trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/css/rosetta-roles.css

     
     1.projects-list {
     2        position: relative;
     3        display: inline-block;
     4        border: 1px solid #dfdfdf;
     5        border-right: 0;
     6}
     7
     8.projects-list > li {
     9        width: 200px;
     10        background: #f5f5f5;
     11        margin: 0;
     12        padding: 10px;
     13        box-sizing: border-box;
     14        border-right: 1px solid #dfdfdf;
     15        border-bottom: 1px solid #dfdfdf;
     16}
     17
     18.projects-list > li:last-child {
     19        border-bottom: 0;
     20}
     21
     22#projects-list label {
     23        margin: 0 !important;
     24}
     25
     26.sub-projects-wrapper {
     27        position: absolute;
     28        top: -1px;
     29        bottom: -1px;
     30        left: 200px;
     31        width: 300px;
     32        padding: 10px;
     33        border: 1px solid #dfdfdf;
     34        border-left: 0;
     35        background: #fff;
     36        display: none;
     37        overflow: scroll;
     38        box-sizing: border-box;
     39}
     40
     41.projects-list > li:nth-child(6) {
     42        background: #fff;
     43        border-right: 1px solid #fff;
     44}
     45
     46.projects-list > li:nth-child(6) .sub-projects-wrapper {
     47        display: block;
     48}
     49
     50.sub-projects-search {
     51        width: 100%;
     52        margin-bottom: 10px;
     53}
     54
     55.sub-projects-list {
     56        margin: 0 0 0 2px;
     57}
  • trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/js/rosetta-roles.js

     
    11( function( $ ) {
    22
    3         $(function() {
    4                 var $projects = $( 'input.project' );
     3        var projects, l10n;
    54
    6                 if ( $projects.length ) {
    7                         var $allProjects = $( '#project-all' ),
    8                                 checked = [];
     5        projects = { model: {}, view: {}, controller: {} };
    96
    10                         // Deselect "All" if a project is checked.
    11                         $projects.on( 'change', function() {
    12                                 $allProjects.prop( 'checked', false );
    13                                 checked = [];
    14                         } );
     7        projects.settings = window._rosettaProjectsSettings || {};
     8        l10n = projects.settings.l10n || {};
     9        delete projects.settings.l10n;
    1510
    16                         // (De)select projects if "All" is (de)selected.
    17                         $allProjects.on( 'change', function() {
    18                                 if ( this.checked ) {
    19                                         $projects.each( function( index, checkbox ) {
    20                                                 var $cb = $( checkbox );
    21                                                 if ( $cb.prop( 'checked' ) ) {
    22                                                         checked.push( $cb.attr( 'id' ) );
    23                                                         $cb.prop( 'checked', false );
    24                                                 }
    25                                         } );
    26                                 } else {
    27                                         for ( i = 0; i < checked.length; i++ ) {
    28                                                 $( '#' +  checked[ i ] ).prop( 'checked', true );
    29                                         }
    30                                         checked = [];
    31                                 }
    32                         } );
     11        /**
     12         * MODELS
     13         */
     14        projects.model.Project = Backbone.Model.extend({
     15                defaults: {
     16                        id: 0,
     17                        name: '',
     18                        checked: false
     19                },
    3320
    34                         // Deselect all checkboxes.
    35                         $( '#clear-all' ).on( 'click', function( event ) {
     21                initialize: function() {
     22                        var subProjects = this.get( 'sub_projects' );
     23                        this.unset( 'sub_projects' );
     24                        this.set( 'subProjects', new projects.model.subProjects( subProjects ) );
     25                }
     26        });
     27
     28        projects.model.Projects = Backbone.Collection.extend({
     29                model: projects.model.Project,
     30        });
     31
     32        projects.model.subProject = Backbone.Model.extend({
     33                defaults: {
     34                        id: 0,
     35                        name: '',
     36                        checked: false,
     37                        isVisible: true
     38                },
     39        });
     40
     41        projects.model.subProjects = Backbone.Collection.extend({
     42                model: projects.model.subProject,
     43
     44                // Search terms
     45                terms: '',
     46
     47                initialize: function( options ) {
     48                        this.originalCol = options;
     49
     50                        this.on( 'state-change', this.triggerStateChange );
     51                },
     52
     53                triggerStateChange: function() {
     54                        // Each model checked => false/true
     55                },
     56
     57                doSearch: function( value ) {
     58                        // Don't do anything if we've already done this search
     59                        if ( this.terms === value ) {
     60                                return;
     61                        }
     62
     63                        this.terms = value;
     64
     65                        if ( this.terms.length > 0 ) {
     66                                this.search( this.terms );
     67                        }
     68
     69                        if ( this.terms === '' ) {
     70                                this.map( function( project ) {
     71                                        project.set( { 'isVisible': true } );
     72                                });
     73                        }
     74                },
     75
     76                // Performs a search within the collection
     77                // @uses RegExp
     78                search: function( term ) {
     79                        var match, name;
     80
     81                        // Escape the term string for RegExp meta characters
     82                        term = term.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
     83
     84                        // Consider spaces as word delimiters and match the whole string
     85                        // so matching terms can be combined
     86                        term = term.replace( / /g, ')(?=.*' );
     87                        match = new RegExp( '^(?=.*' + term + ').+', 'i' );
     88
     89                        // Find results
     90                        this.map( function( project ) {
     91                                name = project.get( 'name' );
     92                                project.set( { 'isVisible': match.test( name ) } );
     93                        });
     94                },
     95        });
     96
     97        /**
     98         * VIEWS
     99         */
     100        projects.view.Frame = wp.Backbone.View.extend({
     101                className: 'projects-frame',
     102                template: wp.template( 'projects-frame' ),
     103
     104                render: function() {
     105                        var view = this;
     106                        wp.Backbone.View.prototype.render.apply( this, arguments );
     107
     108                        $( '#projects' ).append( this.el );
     109
     110                        this.collection.each( function( project ) {
     111                                var projectView = new projects.view.Project({
     112                                        model: project
     113                                });
     114
     115                                projectView.render();
     116                                view.$el.find( '#projects-list' ).append( projectView.el );
     117                        });
     118
     119                        this.views.ready();
     120
     121                        return this;
     122                }
     123        });
     124
     125        projects.view.Checkbox = wp.Backbone.View.extend({
     126                className: 'project-checkbox',
     127                template: wp.template( 'project-checkbox' ),
     128
     129                initialize: function() {
     130                        this.listenTo( this.model, 'change:checked', this.updateChecked );
     131                },
     132
     133                events: {
     134                        'click input': 'updateModel'
     135                },
     136
     137                prepare: function() {
     138                        return _.pick( this.model.toJSON(), 'id', 'name', 'checked' );
     139                },
     140
     141                updateChecked: function() {
     142                        this.$el.find( 'input' ).prop( 'checked', this.model.get( 'checked' ) );
     143                },
     144
     145                updateModel: function() {
     146                        this.model.set({ checked: this.$el.find( 'input' ).prop( 'checked' ) });
     147                }
     148        });
     149
     150        projects.view.Project = wp.Backbone.View.extend({
     151                tagName: 'li',
     152
     153                initialize: function() {
     154                        this.listenTo( this.model, 'change:checked', this.propagateChange );
     155                        this.listenTo( this.model.get( 'subProjects' ), 'change', this.updateModel );
     156
     157                        this.views.add( new projects.view.Checkbox( {
     158                                model: this.model
     159                        }) );
     160
     161                        this.views.add( new projects.view.SubProjects({
     162                                model: this.model
     163                        }) );
     164                },
     165
     166                propagateChange: function() {
     167                        this.model.get( 'subProjects' ).trigger( 'state-change', this.model.get('checked') );
     168                },
     169
     170                updateModel: function() {
     171                        this.model.set({ checked: false });
     172                }
     173        });
     174
     175        projects.view.SubProjects = wp.Backbone.View.extend({
     176                tagName: 'div',
     177                className: 'sub-projects-wrapper',
     178
     179                initialize: function() {
     180                        this.views.add( new projects.view.Search({
     181                                collection: this.model.get( 'subProjects' )
     182                        }) );
     183
     184                        this.views.add( new projects.view.SubProjectsList({
     185                                collection: this.model.get( 'subProjects' )
     186                        }) );
     187                },
     188        });
     189
     190        projects.view.SubProjectsList = wp.Backbone.View.extend({
     191                tagName: 'ul',
     192
     193                initialize: function() {
     194                        var view = this;
     195                        this.collection.each( function( project ) {
     196                                view.views.add( new projects.view.SubProject({
     197                                        model: project
     198                                }) );
     199                        });
     200                }
     201        });
     202
     203        projects.view.SubProject = wp.Backbone.View.extend({
     204                tagName: 'li',
     205
     206                initialize: function() {
     207                        this.views.add( new projects.view.Checkbox({
     208                                model: this.model
     209                        }) );
     210
     211                        this.listenTo( this.model, 'change:isVisible', this.changeVisibility );
     212                },
     213
     214                changeVisibility: function() {
     215                        if (  this.model.get( 'isVisible' ) ) {
     216                                this.$el.removeClass( 'hidden' );
     217                        } else {
     218                                this.$el.addClass( 'hidden' );
     219                        }
     220                }
     221        });
     222
     223        projects.view.Search = wp.Backbone.View.extend({
     224                tagName: 'input',
     225                className: 'sub-projects-search',
     226
     227                attributes: {
     228                        placeholder: l10n.searchPlaceholder,
     229                        type: 'search',
     230                },
     231
     232                events: {
     233                        'input': 'search',
     234                        'keyup': 'search',
     235                        'keydown': 'search',
     236                },
     237
     238                search: function( event ) {
     239                        if ( event.type === 'keydown' && event.which === 13 ) {
    36240                                event.preventDefault();
     241                        } else if ( event.type === 'keydown' ) {
     242                                return;
     243                        }
    37244
    38                                 checked = [];
    39                                 $allProjects.prop( 'checked', false );
    40                                 $projects.prop( 'checked', false );
    41                         } );
    42                 }
    43         } );
     245                        // Clear on escape.
     246                        if ( event.type === 'keyup' && event.which === 27 ) {
     247                                event.target.value = '';
     248                        }
     249
     250                        this.doSearch( event );
     251                },
     252
     253                doSearch: _.debounce( function( event ) {
     254                        this.collection.doSearch( event.target.value );
     255                }, 250 )
     256        });
     257
     258        projects.init = function() {
     259                projects.view.frame = new projects.view.Frame({
     260                        collection: new projects.model.Projects( projects.settings.data )
     261                }).render();
     262        };
     263
     264        $( projects.init );
    44265} )( jQuery );
  • trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/rosetta-roles.php

     
    210210
    211211                add_action( 'load-' . $this->translation_editors_page, array( $this, 'load_translation_editors_page' ) );
    212212                add_action( 'admin_print_scripts-' . $this->translation_editors_page, array( $this, 'enqueue_scripts' ) );
     213                add_action( 'admin_footer-' . $this->translation_editors_page, array( $this, 'print_js_templates' ) );
     214                add_action( 'admin_print_styles-' . $this->translation_editors_page, array( $this, 'enqueue_styles' ) );
    213215        }
    214216
    215217        /**
    216218         * Enqueues scripts.
    217219         */
    218220        public function enqueue_scripts() {
    219                 wp_enqueue_script( 'rosetta-roles', plugins_url( '/js/rosetta-roles.js', __FILE__ ), array( 'jquery' ), '1', true );
     221                wp_enqueue_script( 'rosetta-roles', plugins_url( '/js/rosetta-roles.js', __FILE__ ), array( 'jquery', 'wp-backbone' ), '1', true );
     222
     223                $projects = $this->get_translate_projects();
     224                $project_tree = $this->get_project_tree( $projects, 0, 1 ); // This removes items from $projects.
     225
     226                usort( $project_tree, array( $this, '_sort_name_callback' ) );
     227                foreach ( $project_tree as $key => $project ) {
     228                        if ( ! $project->active ) {
     229                                unset( $project_tree[ $key ] );
     230                        }
     231                        if ( $project->sub_projects ) {
     232                                usort( $project->sub_projects, array( $this, '_sort_name_callback' ) );
     233                        }
     234                        $project->sub_projects = array_values( $project->sub_projects );
     235                }
     236                $project_tree = array_values( $project_tree );
     237
     238                wp_localize_script( 'rosetta-roles', '_rosettaProjectsSettings', array(
     239                        'l10n' => array(
     240                                'searchPlaceholder' => esc_attr__( 'Search...', 'rosetta' )
     241                        ),
     242                        'data' => $project_tree
     243                ) );
    220244        }
    221245
    222246        /**
     247         * Enqueues styles.
     248         */
     249        public function enqueue_styles() {
     250                wp_enqueue_style( 'rosetta-roles', plugins_url( '/css/rosetta-roles.css', __FILE__ ), array(), '1' );
     251        }
     252
     253        public function print_js_templates() {
     254                ?>
     255                <script id="tmpl-projects-frame" type="text/html">
     256                        <ul id="projects-list" class="projects-list"></ul>
     257                </script>
     258
     259                <script id="tmpl-project-checkbox" type="text/html">
     260                        <label>
     261                                <input type="checkbox" value="{{data.id}}"
     262                                <#
     263                                if ( 'undefined' !== typeof data && data.checked ) {
     264                                        #> checked="checked"<#
     265                                }
     266                                #>
     267                                />
     268                                {{data.name}}
     269                        </label>
     270                </script>
     271                <?php
     272        }
     273
     274        /**
    223275         * Loads either the overview or the edit handler.
    224276         */
    225277        public function load_translation_editors_page() {
     
    430482        private function render_edit_translation_editor( $user_id ) {
    431483                global $wpdb;
    432484
    433                 $projects = $this->get_translate_top_level_projects();
     485                /*$projects = $this->get_translate_projects();
     486                $project_tree = $this->get_project_tree( $projects, 0, 1 ); // This removes items from $projects.
    434487
     488                uasort( $project_tree, array( $this, '_sort_name_callback' ) );
     489                foreach ( $project_tree as $key => $project ) {
     490                        if ( ! $project->active ) {
     491                                unset( $project_tree[ $key ] );
     492                        }
     493                        if ( $project->sub_projects ) {
     494                                uasort( $project->sub_projects, array( $this, '_sort_name_callback' ) );
     495                        }
     496                }*/
     497
    435498                $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key;
    436499                $project_access_list = get_user_meta( $user_id, $meta_key, true );
    437500                if ( ! $project_access_list ) {
     
    534597        }
    535598
    536599        /**
     600         * Fetches all projects from translate.wordpress.org.
     601         *
     602         * @return array List of projects.
     603         */
     604        private function get_translate_projects() {
     605                global $wpdb;
     606
     607                $_projects = $wpdb->get_results( "
     608                        SELECT id, name, parent_project_id, active
     609                        FROM translate_projects
     610                        ORDER BY parent_project_id DESC, id DESC
     611                " );
     612
     613                $projects = array();
     614                foreach ( $_projects as $project ) {
     615                        $projects[ $project->id ] = $project;
     616                }
     617
     618                return $projects;
     619        }
     620
     621        /**
     622         * Transforms a flat array to a hierarchy tree.
     623         *
     624         * @param array $projects  The projects.
     625         * @param int   $parent_id Optional. Parent ID. Default 0.
     626         * @param int   $max_depth Optional. Max depth to avoid endless recursion. Default 5.
     627         * @return array The project tree.
     628         */
     629        public function get_project_tree( &$projects, $parent_id = 0, $max_depth = 5 ) {
     630                if ( $max_depth < 0 ) { // Avoid an endless recursion.
     631                        return;
     632                }
     633
     634                $tree = array();
     635                foreach ( $projects as $project ) {
     636                        if ( $project->parent_project_id == $parent_id ) {
     637                                unset( $projects[ $project->id ] ); // No need to check this project twice.
     638                                $sub_projects = $this->get_project_tree( $projects, $project->id, $max_depth - 1 );
     639                                if ( $sub_projects ) {
     640                                        $project->sub_projects = $sub_projects;
     641                                }
     642
     643                                $tree[ $project->id ] = $project;
     644                        }
     645                }
     646                return $tree;
     647        }
     648
     649        /**
    537650         * Fetches all top level projects from translate.wordpress.org.
    538651         *
    539652         * @return array List of projects.
     
    562675
    563676                return $projects;
    564677        }
     678
     679        private function _sort_name_callback( $a, $b ) {
     680                return strnatcasecmp( $a->name, $b->name );
     681        }
    565682}