From ed29dd10b36dbc71c1b71a0127fba2b779646dcd Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Thu, 13 Mar 2014 16:49:51 +0600 Subject: [PATCH] SONAR-5007 Bulk action on found rules --- .../app/views/coding_rules/index.html.erb | 1 + ..._coding_rules_bulk_change_template.hbs.erb | 64 ++++++++ .../javascripts/coding-rules/app.coffee | 12 ++ .../webapp/javascripts/coding-rules/app.js | 16 +- .../javascripts/coding-rules/mockjax.coffee | 15 +- .../javascripts/coding-rules/mockjax.js | 23 ++- .../coding-rules/views/actions-view.coffee | 5 + .../coding-rules/views/actions-view.js | 7 +- .../coding-rules-bulk-change-view.coffee | 109 +++++++++++++ .../views/coding-rules-bulk-change-view.js | 154 ++++++++++++++++++ .../src/main/webapp/stylesheets/style.css | 4 + 11 files changed, 400 insertions(+), 10 deletions(-) create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_bulk_change_template.hbs.erb create mode 100644 sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.coffee create mode 100644 sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.js diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/index.html.erb index 8c33c7b7a96..66438a28820 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/index.html.erb @@ -17,3 +17,4 @@ <%= render :partial => '/coding_rules/templates/coding_rules_list_empty_template.hbs' -%> <%= render :partial => '/coding_rules/templates/coding_rules_detail_template.hbs' -%> <%= render :partial => '/coding_rules/templates/coding_rules_facets_item_template.hbs' -%> +<%= render :partial => '/coding_rules/templates/coding_rules_bulk_change_template.hbs' -%> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_bulk_change_template.hbs.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_bulk_change_template.hbs.erb new file mode 100644 index 00000000000..7e086f284ed --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_bulk_change_template.hbs.erb @@ -0,0 +1,64 @@ + diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/app.coffee b/sonar-server/src/main/webapp/javascripts/coding-rules/app.coffee index 0b69e542905..0915eb9dfa4 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/app.coffee +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/app.coffee @@ -28,6 +28,7 @@ requirejs [ 'coding-rules/views/actions-view', 'coding-rules/views/filter-bar-view', 'coding-rules/views/coding-rules-list-view', + 'coding-rules/views/coding-rules-bulk-change-view', # filters 'navigator/filters/base-filters', @@ -49,6 +50,7 @@ requirejs [ CodingRulesActionsView, CodingRulesFilterBarView, CodingRulesListView, + CodingRulesBulkChangeView, # filters BaseFilters, @@ -143,6 +145,11 @@ requirejs [ if value? && value.length == 1 then value[0] else null + App.getInactiveQualityProfile = -> + value = @inactiveInFilter.get('value') + if value? && value.length == 1 then value[0] else null + + # Construct layout App.addInitializer -> @layout = new CodingRulesLayout app: @ @@ -169,6 +176,11 @@ requirejs [ @layout.actionsRegion.show @codingRulesActionsView + # Construct bulk change view + App.addInitializer -> + @codingRulesBulkChangeView = new CodingRulesBulkChangeView app: @ + + # Define filters App.addInitializer -> @filters = new BaseFilters.Filters diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/app.js b/sonar-server/src/main/webapp/javascripts/coding-rules/app.js index ff1c02cbb00..4ca54b9f583 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/app.js +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/app.js @@ -22,7 +22,7 @@ } }); - requirejs(['backbone', 'backbone.marionette', 'coding-rules/layout', 'coding-rules/router', 'coding-rules/views/header-view', 'coding-rules/views/actions-view', 'coding-rules/views/filter-bar-view', 'coding-rules/views/coding-rules-list-view', 'navigator/filters/base-filters', 'navigator/filters/choice-filters', 'navigator/filters/string-filters', 'navigator/filters/date-filter-view', 'coding-rules/views/filters/quality-profile-filter-view', 'coding-rules/views/filters/inheritance-filter-view', 'coding-rules/mockjax'], function(Backbone, Marionette, CodingRulesLayout, CodingRulesRouter, CodingRulesHeaderView, CodingRulesActionsView, CodingRulesFilterBarView, CodingRulesListView, BaseFilters, ChoiceFilters, StringFilterView, DateFilterView, QualityProfileFilterView, InheritanceFilterView) { + requirejs(['backbone', 'backbone.marionette', 'coding-rules/layout', 'coding-rules/router', 'coding-rules/views/header-view', 'coding-rules/views/actions-view', 'coding-rules/views/filter-bar-view', 'coding-rules/views/coding-rules-list-view', 'coding-rules/views/coding-rules-bulk-change-view', 'navigator/filters/base-filters', 'navigator/filters/choice-filters', 'navigator/filters/string-filters', 'navigator/filters/date-filter-view', 'coding-rules/views/filters/quality-profile-filter-view', 'coding-rules/views/filters/inheritance-filter-view', 'coding-rules/mockjax'], function(Backbone, Marionette, CodingRulesLayout, CodingRulesRouter, CodingRulesHeaderView, CodingRulesActionsView, CodingRulesFilterBarView, CodingRulesListView, CodingRulesBulkChangeView, BaseFilters, ChoiceFilters, StringFilterView, DateFilterView, QualityProfileFilterView, InheritanceFilterView) { var App, appXHR; jQuery.ajaxSetup({ error: function(jqXHR) { @@ -112,6 +112,15 @@ return null; } }; + App.getInactiveQualityProfile = function() { + var value; + value = this.inactiveInFilter.get('value'); + if ((value != null) && value.length === 1) { + return value[0]; + } else { + return null; + } + }; App.addInitializer(function() { this.layout = new CodingRulesLayout({ app: this @@ -138,6 +147,11 @@ }); return this.layout.actionsRegion.show(this.codingRulesActionsView); }); + App.addInitializer(function() { + return this.codingRulesBulkChangeView = new CodingRulesBulkChangeView({ + app: this + }); + }); App.addInitializer(function() { this.filters = new BaseFilters.Filters; this.filters.add(new BaseFilters.Filter({ diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.coffee b/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.coffee index 72f71095b65..42cf2cdd25c 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.coffee +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.coffee @@ -8,9 +8,10 @@ define ['jquery.mockjax'], -> url: "#{baseUrl}/api/codingrules/app" responseText: JSON.stringify qualityprofiles: [ - { name: 'Sonar Way', lang: 'Java' } - { name: 'Sonar Way', lang: 'JavaScript' } - { name: 'Quality Profile 1', lang: 'Java' } + { key: 'sonarway', name: 'Sonar Way', parent: null }, + { key: 'qp1', name: 'Quality Profile 1', parent: 'sonarway' }, + { key: 'qp2', name: 'Quality Profile 2', parent: 'sonarway' }, + { key: 'qp3', name: 'Quality Profile 3', parent: null }, ] languages: java: 'Java' @@ -44,7 +45,9 @@ define ['jquery.mockjax'], -> messages: 'all': 'All' 'any': 'Any' + 'apply': 'Apply' 'bulk_change': 'Bulk Change' + 'cancel': 'Cancel' 'moreCriteria': '+ More Criteria' 'search_verb': 'Search' 'update': 'Update' @@ -56,6 +59,7 @@ define ['jquery.mockjax'], -> 'severity.INFO': 'Info' 'coding_rules.activate_quality_profile': 'Activate Quality Profile' + 'coding_rules.bulk_change': 'Bulk Change' 'coding_rules.deactivate_quality_profile': 'Deactivate' 'coding_rules.found': 'Found' 'coding_rules.new_search': 'New Search' @@ -178,6 +182,11 @@ define ['jquery.mockjax'], -> + # POST /api/codingrules/bulk_change + jQuery.mockjax + url: "#{baseUrl}/api/codingrules/bulk_change" + + # GET /api/qualityprofiles/list jQuery.mockjax url: "#{baseUrl}/api/qualityprofiles/list" diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.js b/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.js index 6c65ea7c283..6eaba0cb23f 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.js +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.js @@ -8,14 +8,21 @@ responseText: JSON.stringify({ qualityprofiles: [ { + key: 'sonarway', name: 'Sonar Way', - lang: 'Java' - }, { - name: 'Sonar Way', - lang: 'JavaScript' + parent: null }, { + key: 'qp1', name: 'Quality Profile 1', - lang: 'Java' + parent: 'sonarway' + }, { + key: 'qp2', + name: 'Quality Profile 2', + parent: 'sonarway' + }, { + key: 'qp3', + name: 'Quality Profile 3', + parent: null } ], languages: { @@ -54,7 +61,9 @@ messages: { 'all': 'All', 'any': 'Any', + 'apply': 'Apply', 'bulk_change': 'Bulk Change', + 'cancel': 'Cancel', 'moreCriteria': '+ More Criteria', 'search_verb': 'Search', 'update': 'Update', @@ -64,6 +73,7 @@ 'severity.MINOR': 'Minor', 'severity.INFO': 'Info', 'coding_rules.activate_quality_profile': 'Activate Quality Profile', + 'coding_rules.bulk_change': 'Bulk Change', 'coding_rules.deactivate_quality_profile': 'Deactivate', 'coding_rules.found': 'Found', 'coding_rules.new_search': 'New Search', @@ -161,6 +171,9 @@ } }) }); + jQuery.mockjax({ + url: "" + baseUrl + "/api/codingrules/bulk_change" + }); jQuery.mockjax({ url: "" + baseUrl + "/api/qualityprofiles/list", responseText: JSON.stringify({ diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.coffee b/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.coffee index b9472b9a23c..5371f70b54b 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.coffee +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.coffee @@ -20,6 +20,7 @@ define [ events: 'click .navigator-actions-order': 'toggleOrderChoices' 'click @ui.orderChoices': 'sort' + 'click .navigator-actions-bulk': 'bulkChange' onRender: -> @@ -48,6 +49,10 @@ define [ @options.app.fetchFirstPage() + bulkChange: -> + @options.app.codingRulesBulkChangeView.show() + + serializeData: -> _.extend super, paging: @collection.paging diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.js b/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.js index 18a3cb256bd..f0ae6110f52 100644 --- a/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.js +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/views/actions-view.js @@ -25,7 +25,8 @@ CodingRulesStatusView.prototype.events = { 'click .navigator-actions-order': 'toggleOrderChoices', - 'click @ui.orderChoices': 'sort' + 'click @ui.orderChoices': 'sort', + 'click .navigator-actions-bulk': 'bulkChange' }; CodingRulesStatusView.prototype.onRender = function() { @@ -64,6 +65,10 @@ } }; + CodingRulesStatusView.prototype.bulkChange = function() { + return this.options.app.codingRulesBulkChangeView.show(); + }; + CodingRulesStatusView.prototype.serializeData = function() { return _.extend(CodingRulesStatusView.__super__.serializeData.apply(this, arguments), { paging: this.collection.paging, diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.coffee b/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.coffee new file mode 100644 index 00000000000..d1155ce6a47 --- /dev/null +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.coffee @@ -0,0 +1,109 @@ +define [ + 'backbone.marionette', + 'common/handlebars-extensions' +], ( + Marionette +) -> + + class CodingRulesBulkChangeView extends Marionette.ItemView + className: 'modal' + template: getTemplate '#coding-rules-bulk-change-template' + + + events: + 'submit form': 'onSubmit' + 'click #coding-rules-cancel-bulk-change': 'hide' + 'click label': 'enableAction' + 'change select': 'enableAction' + + + onRender: -> + @$el.dialog + dialogClass: 'no-close', + width: '600px', + draggable: false, + autoOpen: false, + modal: true, + minHeight: 50, + resizable: false, + title: null + + @$('#coding-rules-bulk-change-activate-on, #coding-rules-bulk-change-deactivate-on').select2 + width: '250px' + minimumResultsForSearch: 1 + + format = (state) -> + return state.text unless state.id + " #{state.text}" + @$('#coding-rules-bulk-change-severity').select2 + width: '250px' + minimumResultsForSearch: 999 + formatResult: format + formatSelection: format + escapeMarkup: (m) -> m + + + show: -> + @render() + @$el.dialog 'open' + + + hide: -> + @$el.dialog 'close' + + + prepareQuery: -> + query = @options.app.getQuery() + activateIn = [] + deactivateIn = [] + severity = null + if @$('#coding-rules-bulk-change-activate-qp').is(':checked') + activateIn.push @options.app.getInactiveQualityProfile() + if @$('#coding-rules-bulk-change-activate').is(':checked') + activateIn.push @$('#coding-rules-bulk-change-activate-on').val() + if @$('#coding-rules-bulk-change-deactivate-qp').is(':checked') + deactivateIn.push @options.app.getActiveQualityProfile() + if @$('#coding-rules-bulk-change-deactivate').is(':checked') + deactivateIn.push @$('#coding-rules-bulk-change-deactivate-on').val() + if @$('#coding-rules-bulk-change-set-severity').is(':checked') + severity = @$('#coding-rules-bulk-change-severity').val() + actions = [] + if activateIn.length > 0 + actions.push 'bulk_activate' + _.extend query, bulk_activate_in: activateIn.join(',') + if deactivateIn.length > 0 + actions.push 'bulk_deactivate' + _.extend query, bulk_deactivate_in: deactivateIn.join(',') + if severity + actions.push 'bulk_set_severity' + _.extend query, bulk_severity: severity + _.extend query, bulk_actions: actions + + onSubmit: (e) -> + e.preventDefault() + jQuery.ajax + type: 'POST' + url: "#{baseUrl}/api/codingrules/bulk_change" + data: @prepareQuery() + .done => + @options.app.fetchFirstPage() + @hide() + + + + enableAction: (e) -> + jQuery(e.target).siblings('input[type=checkbox]').prop 'checked', true + + + serializeData: -> + qualityProfiles: @options.app.qualityProfiles + + activeQualityProfile: @options.app.getActiveQualityProfile() + activeQualityProfileName: @options.app.activeInFilter.view.renderValue() + activateOnQualityProfiles: _.reject @options.app.qualityProfiles, (q) => q.key == @options.app.getInactiveQualityProfile() + + inactiveQualityProfile: @options.app.getInactiveQualityProfile() + inactiveQualityProfileName: @options.app.inactiveInFilter.view.renderValue() + deactivateOnQualityProfiles: _.reject @options.app.qualityProfiles, (q) => q.key == @options.app.getActiveQualityProfile() + + severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'] diff --git a/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.js b/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.js new file mode 100644 index 00000000000..e3e3113000c --- /dev/null +++ b/sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-bulk-change-view.js @@ -0,0 +1,154 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + define(['backbone.marionette', 'common/handlebars-extensions'], function(Marionette) { + var CodingRulesBulkChangeView, _ref; + return CodingRulesBulkChangeView = (function(_super) { + __extends(CodingRulesBulkChangeView, _super); + + function CodingRulesBulkChangeView() { + _ref = CodingRulesBulkChangeView.__super__.constructor.apply(this, arguments); + return _ref; + } + + CodingRulesBulkChangeView.prototype.className = 'modal'; + + CodingRulesBulkChangeView.prototype.template = getTemplate('#coding-rules-bulk-change-template'); + + CodingRulesBulkChangeView.prototype.events = { + 'submit form': 'onSubmit', + 'click #coding-rules-cancel-bulk-change': 'hide', + 'click label': 'enableAction', + 'change select': 'enableAction' + }; + + CodingRulesBulkChangeView.prototype.onRender = function() { + var format; + this.$el.dialog({ + dialogClass: 'no-close', + width: '600px', + draggable: false, + autoOpen: false, + modal: true, + minHeight: 50, + resizable: false, + title: null + }); + this.$('#coding-rules-bulk-change-activate-on, #coding-rules-bulk-change-deactivate-on').select2({ + width: '250px', + minimumResultsForSearch: 1 + }); + format = function(state) { + if (!state.id) { + return state.text; + } + return " " + state.text; + }; + return this.$('#coding-rules-bulk-change-severity').select2({ + width: '250px', + minimumResultsForSearch: 999, + formatResult: format, + formatSelection: format, + escapeMarkup: function(m) { + return m; + } + }); + }; + + CodingRulesBulkChangeView.prototype.show = function() { + this.render(); + return this.$el.dialog('open'); + }; + + CodingRulesBulkChangeView.prototype.hide = function() { + return this.$el.dialog('close'); + }; + + CodingRulesBulkChangeView.prototype.prepareQuery = function() { + var actions, activateIn, deactivateIn, query, severity; + query = this.options.app.getQuery(); + activateIn = []; + deactivateIn = []; + severity = null; + if (this.$('#coding-rules-bulk-change-activate-qp').is(':checked')) { + activateIn.push(this.options.app.getInactiveQualityProfile()); + } + if (this.$('#coding-rules-bulk-change-activate').is(':checked')) { + activateIn.push(this.$('#coding-rules-bulk-change-activate-on').val()); + } + if (this.$('#coding-rules-bulk-change-deactivate-qp').is(':checked')) { + deactivateIn.push(this.options.app.getActiveQualityProfile()); + } + if (this.$('#coding-rules-bulk-change-deactivate').is(':checked')) { + deactivateIn.push(this.$('#coding-rules-bulk-change-deactivate-on').val()); + } + if (this.$('#coding-rules-bulk-change-set-severity').is(':checked')) { + severity = this.$('#coding-rules-bulk-change-severity').val(); + } + actions = []; + if (activateIn.length > 0) { + actions.push('bulk_activate'); + _.extend(query, { + bulk_activate_in: activateIn.join(',') + }); + } + if (deactivateIn.length > 0) { + actions.push('bulk_deactivate'); + _.extend(query, { + bulk_deactivate_in: deactivateIn.join(',') + }); + } + if (severity) { + actions.push('bulk_set_severity'); + _.extend(query, { + bulk_severity: severity + }); + } + return _.extend(query, { + bulk_actions: actions + }); + }; + + CodingRulesBulkChangeView.prototype.onSubmit = function(e) { + var _this = this; + e.preventDefault(); + return jQuery.ajax({ + type: 'POST', + url: "" + baseUrl + "/api/codingrules/bulk_change", + data: this.prepareQuery() + }).done(function() { + _this.options.app.fetchFirstPage(); + return _this.hide(); + }); + }; + + CodingRulesBulkChangeView.prototype.enableAction = function(e) { + return jQuery(e.target).siblings('input[type=checkbox]').prop('checked', true); + }; + + CodingRulesBulkChangeView.prototype.serializeData = function() { + var _this = this; + return { + qualityProfiles: this.options.app.qualityProfiles, + activeQualityProfile: this.options.app.getActiveQualityProfile(), + activeQualityProfileName: this.options.app.activeInFilter.view.renderValue(), + activateOnQualityProfiles: _.reject(this.options.app.qualityProfiles, function(q) { + return q.key === _this.options.app.getInactiveQualityProfile(); + }), + inactiveQualityProfile: this.options.app.getInactiveQualityProfile(), + inactiveQualityProfileName: this.options.app.inactiveInFilter.view.renderValue(), + deactivateOnQualityProfiles: _.reject(this.options.app.qualityProfiles, function(q) { + return q.key === _this.options.app.getActiveQualityProfile(); + }), + severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'] + }; + }; + + return CodingRulesBulkChangeView; + + })(Marionette.ItemView); + }); + +}).call(this); diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css index 3085bc78154..c838716737f 100644 --- a/sonar-server/src/main/webapp/stylesheets/style.css +++ b/sonar-server/src/main/webapp/stylesheets/style.css @@ -2466,6 +2466,10 @@ ul.modal-head-metadata li { width: 250px; } +.modal-field .text { + line-height: 20px; +} + .modal-foot { text-align: right; padding: 2px 10px; -- 2.39.5