Ticket #1101: 1101.patch
File 1101.patch, 13.8 KB (added by , 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
1 1 ( function( $ ) { 2 2 3 $(function() { 4 var $projects = $( 'input.project' ); 3 var projects, l10n; 5 4 6 if ( $projects.length ) { 7 var $allProjects = $( '#project-all' ), 8 checked = []; 5 projects = { model: {}, view: {}, controller: {} }; 9 6 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; 15 10 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 }, 33 20 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 ) { 36 240 event.preventDefault(); 241 } else if ( event.type === 'keydown' ) { 242 return; 243 } 37 244 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 ); 44 265 } )( jQuery ); -
trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/rosetta-roles.php
210 210 211 211 add_action( 'load-' . $this->translation_editors_page, array( $this, 'load_translation_editors_page' ) ); 212 212 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' ) ); 213 215 } 214 216 215 217 /** 216 218 * Enqueues scripts. 217 219 */ 218 220 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 ) ); 220 244 } 221 245 222 246 /** 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 /** 223 275 * Loads either the overview or the edit handler. 224 276 */ 225 277 public function load_translation_editors_page() { … … 430 482 private function render_edit_translation_editor( $user_id ) { 431 483 global $wpdb; 432 484 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. 434 487 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 435 498 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 436 499 $project_access_list = get_user_meta( $user_id, $meta_key, true ); 437 500 if ( ! $project_access_list ) { … … 534 597 } 535 598 536 599 /** 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 /** 537 650 * Fetches all top level projects from translate.wordpress.org. 538 651 * 539 652 * @return array List of projects. … … 562 675 563 676 return $projects; 564 677 } 678 679 private function _sort_name_callback( $a, $b ) { 680 return strnatcasecmp( $a->name, $b->name ); 681 } 565 682 }