From 567c1fb55e6e53760adebe16bb55d60db9700342 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 22 Dec 2014 18:06:43 +0100 Subject: [PATCH] SONAR-5820 Add quality profiles --- .../coding-rules-rule-details.hbs | 149 ++---------------- .../rule/coding-rules-profile-activation.hbs | 76 +++++++++ .../rule/coding-rules-rule-description.hbs | 48 ++++++ .../rule/coding-rules-rule-meta.hbs | 54 +++++++ .../rule/coding-rules-rule-parameters.hbs | 25 +++ .../rule/coding-rules-rule-profile.hbs | 72 +++++++++ .../rule/coding-rules-rule-profiles.hbs | 19 +++ .../src/main/js/coding-rules/controller.js | 15 +- .../src/main/js/coding-rules/models/rule.js | 15 +- .../src/main/js/coding-rules/models/rules.js | 6 + .../main/js/coding-rules/rule-details-view.js | 70 ++++++-- .../rule/profile-activation-view.js | 131 +++++++++++++++ .../rule/rule-description-view.js | 87 ++++++++++ .../js/coding-rules/rule/rule-meta-view.js | 86 ++++++++++ .../coding-rules/rule/rule-parameters-view.js | 27 ++++ .../js/coding-rules/rule/rule-profile-view.js | 135 ++++++++++++++++ .../coding-rules/rule/rule-profiles-view.js | 43 +++++ .../sonar-web/src/main/less/coding-rules.less | 57 +++---- .../src/main/less/components/modals.less | 4 +- 19 files changed, 931 insertions(+), 188 deletions(-) create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-profile-activation.hbs create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-description.hbs create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-parameters.hbs create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profile.hbs create mode 100644 server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profiles.hbs create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/rule-parameters-view.js create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js create mode 100644 server/sonar-web/src/main/js/coding-rules/rule/rule-profiles-view.js diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-rule-details.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-rule-details.hbs index 935dd66e8cb..4414fd197d8 100644 --- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-rule-details.hbs +++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-rule-details.hbs @@ -1,125 +1,6 @@ -

- {{name}} - -

-{{key}} - - - -
{{{htmlDesc}}}
- -{{#unless isEditable}} - {{#unless isManual}} -
-
- {{#if htmlNote}} -
{{{htmlNote}}}
{{/if}} - {{#if canWrite}}
- -
{{/if}} -
- - {{#if canWrite}}
- - - - - - - - - - -
- -
-
- - {{#if mdNote}} - - {{/if}} -
- {{t 'cancel'}} -
- {{> '_markdown-tips' }} -
-
- -
- -
{{/if}} -
- {{/unless}} -{{/unless}} - - -{{#if params}} -

{{t 'coding_rules.parameters'}}

-
- {{#each params}} -
-
{{key}}
-
-

{{{htmlDesc}}}

- {{#if ../../templateKey}} -
- {{#if defaultValue }} - {{defaultValue}} - {{else}} - {{t 'coding_rules.parameter.empty'}} - {{/if}} -
- {{else}} - {{#if defaultValue}} -
{{t 'coding_rules.parameters.default_value'}} {{defaultValue}}
- {{/if}} - {{/if}} -
-
- {{/each}} -
-{{/if}} +
+
+
{{#if isEditable}}
@@ -134,29 +15,17 @@
{{/if}} - {{#if isTemplate}}

{{t 'coding_rules.custom_rules'}}

- {{#if canWrite}}
- -
{{/if}} + {{#if canWrite}} +
+ +
+ {{/if}}
{{/if}} - -{{#if qualityProfilesVisible}} -
-

{{t 'coding_rules.quality_profiles'}}

- - {{#if canWrite}}{{#unless isTemplate}}
- -
{{/unless}}{{/if}} - {{#if isTemplate}} -
{{t 'coding_rules.quality_profiles.template_caption'}}
- {{/if}} -
-
-{{/if}} +
diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-profile-activation.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-profile-activation.hbs new file mode 100644 index 00000000000..dc3374d8d46 --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-profile-activation.hbs @@ -0,0 +1,76 @@ +
+ + + + + +
diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-description.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-description.hbs new file mode 100644 index 00000000000..69a31cbbccc --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-description.hbs @@ -0,0 +1,48 @@ +
{{{htmlDesc}}}
+ +{{#unless isEditable}} + {{#unless isManual}} +
+
+ {{#if htmlNote}} +
{{{htmlNote}}}
+ {{/if}} + {{#if canWrite}} +
+ +
+ {{/if}} +
+ + {{#if canWrite}} + + {{/if}} +
+ {{/unless}} +{{/unless}} diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs new file mode 100644 index 00000000000..829003f8ed4 --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs @@ -0,0 +1,54 @@ +

+ {{name}} + +

+ +{{key}} + + diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-parameters.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-parameters.hbs new file mode 100644 index 00000000000..f0dcb3d8e11 --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-parameters.hbs @@ -0,0 +1,25 @@ +

{{t 'coding_rules.parameters'}}

+ + {{#each params}} + + + + + {{/each}} +
{{key}} +

{{{htmlDesc}}}

+ {{#if ../../templateKey}} +
+ {{#if defaultValue }} + {{defaultValue}} + {{else}} + {{t 'coding_rules.parameter.empty'}} + {{/if}} +
+ {{else}} + {{#if defaultValue}} +
{{t 'coding_rules.parameters.default_value'}} {{defaultValue}}
+ {{/if}} + {{/if}} +
diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profile.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profile.hbs new file mode 100644 index 00000000000..99f6f5f33c4 --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profile.hbs @@ -0,0 +1,72 @@ + + {{name}} + {{#if parent}} +
+ {{#eq inherit 'OVERRIDES'}} + + {{/eq}} + {{#eq inherit 'INHERITED'}} + + {{/eq}} + {{parent.name}} +
+ {{/if}} + + +{{#if severity}} + + {{severityIcon severity}} {{t "severity" severity}} + {{#if parent}}{{#notEq severity parent.severity}} +
+ {{t 'coding_rules.original'}} {{t 'severity' parent.severity}} +
+ {{/notEq}}{{/if}} + + + {{#unless templateKey}} + + {{#each parameters}} +
+ {{key}}{{value}} + {{#if ../parent}}{{#notEq value original}} +
+ {{t 'coding_rules.original'}} {{original}} +
+ {{/notEq}}{{/if}} +
+ {{/each}} +   + + {{/unless}} + + {{#if canWrite}} + +
+ {{#unless isTemplate}} + + {{/unless}} + {{#if parent}} + {{#eq inherit 'OVERRIDES'}} + + {{/eq}} + {{else}} + + {{/if}} +
+ + {{/if}} + +{{else}} + {{#if canWrite}}{{#unless isTemplate}} + +
+ +
+ + {{/unless}}{{/if}} +{{/if}} diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profiles.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profiles.hbs new file mode 100644 index 00000000000..629206a9162 --- /dev/null +++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-profiles.hbs @@ -0,0 +1,19 @@ +
+

{{t 'coding_rules.quality_profiles'}}

+ + {{#if canWrite}} + {{#unless isTemplate}} +
+ +
+ {{/unless}} + {{/if}} + + {{#if isTemplate}} +
+ {{t 'coding_rules.quality_profiles.template_caption'}} +
+ {{/if}} + +
+
diff --git a/server/sonar-web/src/main/js/coding-rules/controller.js b/server/sonar-web/src/main/js/coding-rules/controller.js index 3eec23d697c..3d391182d74 100644 --- a/server/sonar-web/src/main/js/coding-rules/controller.js +++ b/server/sonar-web/src/main/js/coding-rules/controller.js @@ -39,6 +39,7 @@ define([ that.app.list.add(rules); } that.app.list.setIndex(); + that.app.list.addExtraAttributes(that.app.languages, that.app.repositories); that.app.facets.reset(that._allFacets()); that.app.facets.add(r.facets, { merge: true }); that.enableFacets(that._enabledFacets()); @@ -74,26 +75,30 @@ define([ }, getRuleDetails: function (rule) { - var url = baseUrl + '/api/rules/show', + var that = this, + url = baseUrl + '/api/rules/show', options = { key: rule.id, actives: true }; return $.get(url, options).done(function (data) { rule.set(data.rule); + rule.addExtraAttributes(that.app.languages, that.app.repositories); }); }, showDetails: function (rule) { - var that = this; + var that = this, + ruleModel = typeof rule === 'string' ? new Backbone.Model({ key: rule }) : rule; this.app.layout.workspaceDetailsRegion.reset(); - this.getRuleDetails(rule).done(function () { + this.getRuleDetails(ruleModel).done(function (data) { key.setScope('details'); that.app.workspaceListView.unbindScrollEvents(); - that.app.state.set({ rule: rule }); + that.app.state.set({ rule: ruleModel }); that.app.workspaceDetailsView = new RuleDetailsView({ app: that.app, - model: rule + model: ruleModel, + actives: data.actives }); that.app.layout.workspaceDetailsRegion.show(that.app.workspaceDetailsView); that.app.layout.showDetails(); diff --git a/server/sonar-web/src/main/js/coding-rules/models/rule.js b/server/sonar-web/src/main/js/coding-rules/models/rule.js index 78c1804a21d..0b20c8eab0d 100644 --- a/server/sonar-web/src/main/js/coding-rules/models/rule.js +++ b/server/sonar-web/src/main/js/coding-rules/models/rule.js @@ -3,7 +3,20 @@ define([ ], function (Backbone) { return Backbone.Model.extend({ - idAttribute: 'key' + idAttribute: 'key', + + addExtraAttributes: function (languages, repositories) { + var langName = languages[this.get('lang')] || this.get('lang'), + repo = _.findWhere(repositories, { key: this.get('repo') }) || this.get('repo'), + isManual = this.get('repo') === 'manual', + isCustom = this.has('templateKey'); + this.set({ + langName: langName, + repo: repo, + isManual: isManual, + isCustom: isCustom + }); + } }); }); diff --git a/server/sonar-web/src/main/js/coding-rules/models/rules.js b/server/sonar-web/src/main/js/coding-rules/models/rules.js index ea0c3577a2d..752576ee2f8 100644 --- a/server/sonar-web/src/main/js/coding-rules/models/rules.js +++ b/server/sonar-web/src/main/js/coding-rules/models/rules.js @@ -14,6 +14,12 @@ define([ this.forEach(function (rule, index) { rule.set({ index: index }); }); + }, + + addExtraAttributes: function (languages, repositories) { + this.models.forEach(function (model) { + model.addExtraAttributes(languages, repositories); + }); } }); diff --git a/server/sonar-web/src/main/js/coding-rules/rule-details-view.js b/server/sonar-web/src/main/js/coding-rules/rule-details-view.js index 202e1698dfd..2fa6378a677 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule-details-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule-details-view.js @@ -1,23 +1,62 @@ define([ - 'backbone.marionette', - 'templates/coding-rules' -], function (Marionette, Templates) { + 'backbone', + 'backbone.marionette', + 'templates/coding-rules', + 'coding-rules/rule/rule-meta-view', + 'coding-rules/rule/rule-description-view', + 'coding-rules/rule/rule-parameters-view', + 'coding-rules/rule/rule-profiles-view' +], function (Backbone, Marionette, Templates, MetaView, DescView, ParamView, ProfilesView) { - return Marionette.ItemView.extend({ + return Marionette.Layout.extend({ template: Templates['coding-rules-rule-details'], - modelEvents: { - 'change': 'render' + regions: { + metaRegion: '.js-rule-meta', + descRegion: '.js-rule-description', + paramRegion: '.js-rule-parameters', + profilesRegion: '.js-rule-profiles' }, initialize: function () { this.bindShortcuts(); }, + onRender: function () { + this.metaRegion.show(new MetaView({ + app: this.options.app, + model: this.model + })); + this.descRegion.show(new DescView({ + app: this.options.app, + model: this.model + })); + this.paramRegion.show(new ParamView({ + app: this.options.app, + model: this.model + })); + this.profilesRegion.show(new ProfilesView({ + app: this.options.app, + model: this.model, + collection: new Backbone.Collection(this.getQualityProfiles()) + })); + }, + onClose: function () { this.unbindShortcuts(); }, + getQualityProfiles: function () { + var that = this; + return this.options.actives.map(function (profile) { + var profileBase = _.findWhere(that.options.app.qualityProfiles, { key: profile.qProfile }); + if (profileBase != null) { + _.extend(profile, profileBase); + } + return profile; + }); + }, + bindShortcuts: function () { var that = this; key('up', 'details', function () { @@ -41,12 +80,25 @@ define([ }, serializeData: function () { - var isManual = (this.options.app.manualRepository().key === this.model.get('repo')); + var isManual = (this.options.app.manualRepository().key === this.model.get('repo')), + isCustom = this.model.has('templateKey'), + isEditable = this.options.app.canWrite && (isManual || isCustom), + qualityProfilesVisible = !isManual; + + if (qualityProfilesVisible) { + if (this.model.get('isTemplate')) { + qualityProfilesVisible = !_.isEmpty(this.options.actives); + } + else { + qualityProfilesVisible = (this.options.app.canWrite || !_.isEmpty(this.options.actives)); + } + } return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { - language: this.options.app.languages[this.model.get('lang')], - repository: _.findWhere(this.options.app.repositories, { key: this.model.get('repo') }).name, isManual: isManual, + isEditable: isEditable, + canWrite: this.options.app.canWrite, + qualityProfilesVisible: qualityProfilesVisible, subCharacteristic: this.options.app.getSubCharacteristicName(this.model.get('debtSubChar')), allTags: _.union(this.model.get('sysTags'), this.model.get('tags')) }); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js new file mode 100644 index 00000000000..3bd8855bddb --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js @@ -0,0 +1,131 @@ +define([ + 'common/modals', + 'templates/coding-rules' +], function (Modal, Templates) { + + var $ = jQuery; + + return Modal.extend({ + template: Templates['coding-rules-profile-activation'], + + ui: { + qualityProfileSelect: '#coding-rules-quality-profile-activation-select', + qualityProfileSeverity: '#coding-rules-quality-profile-activation-severity', + qualityProfileActivate: '#coding-rules-quality-profile-activation-activate', + qualityProfileParameters: '[name]' + }, + + events: function () { + return _.extend(Modal.prototype.events.apply(this, arguments), { + 'click @ui.qualityProfileActivate': 'activate' + }); + }, + + onRender: function () { + Modal.prototype.onRender.apply(this, arguments); + + this.ui.qualityProfileSelect.select2({ + width: '250px', + minimumResultsForSearch: 5 + }); + + var format = function (state) { + if (!state.id) { + return state.text; + } else { + return ' ' + state.text; + } + }, + severity = (this.model && this.model.get('severity')) || this.options.rule.get('severity'); + this.ui.qualityProfileSeverity.val(severity); + this.ui.qualityProfileSeverity.select2({ + width: '250px', + minimumResultsForSearch: 999, + formatResult: format, + formatSelection: format + }); + }, + + activate: function () { + var that = this, + p = window.process.addBackgroundProcess(), + profileKey = this.ui.qualityProfileSelect.val(), + params = this.ui.qualityProfileParameters.map(function () { + return { + key: $(this).prop('name'), + value: $(this).val() || $(this).prop('placeholder') || '' + }; + }).get(), + paramsHash = (params.map(function (param) { + return param.key + '=' + window.csvEscape(param.value); + })).join(';'); + + if (this.model) { + profileKey = this.model.get('qProfile'); + if (!profileKey) { + profileKey = this.model.get('key'); + } + } + + var severity = this.ui.qualityProfileSeverity.val(), + ruleKey = this.options.rule.get('key'); + + this.close(); + + return jQuery.ajax({ + type: 'POST', + url: baseUrl + '/api/qualityprofiles/activate_rule', + data: { + profile_key: profileKey, + rule_key: ruleKey, + severity: severity, + params: paramsHash + } + }).done(function () { + that.options.app.controller.showDetails(that.options.rule); + window.process.finishBackgroundProcess(p); + }).fail(function () { + that.options.app.controller.showDetails(that.options.rule); + window.process.failBackgroundProcess(p); + }); + }, + + getAvailableQualityProfiles: function (lang) { + var activeQualityProfiles = this.collection, + inactiveProfiles = _.reject(this.options.app.qualityProfiles, function (profile) { + return activeQualityProfiles.findWhere({ key: profile.key }); + }); + return _.filter(inactiveProfiles, function (profile) { + return profile.lang === lang; + }); + }, + + serializeData: function () { + var params = this.options.rule.get('params'); + if (this.model != null) { + var modelParams = this.model.get('params'); + if (_.isArray(modelParams)) { + params = params.map(function (p) { + var parentParam = _.findWhere(modelParams, { key: p.key }); + if (parentParam != null) { + _.extend(p, { value: parentParam.value }); + } + return p; + }); + } + } + + var availableProfiles = this.getAvailableQualityProfiles(this.options.rule.get('lang')); + + return _.extend(Modal.prototype.serializeData.apply(this, arguments), { + change: this.model && this.model.has('severity'), + params: params, + qualityProfiles: availableProfiles, + severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'], + saveEnabled: !_.isEmpty(availableProfiles) || (this.model && this.model.get('qProfile')), + isCustomRule: (this.model && this.model.has('templateKey')) || this.options.rule.has('templateKey') + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js new file mode 100644 index 00000000000..dfbe5e156d4 --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js @@ -0,0 +1,87 @@ +define([ + 'backbone.marionette', + 'templates/coding-rules' +], function (Marionette, Templates) { + + return Marionette.ItemView.extend({ + template: Templates['coding-rules-rule-description'], + + modelEvents: { + 'change': 'render' + }, + + ui: { + descriptionExtra: '#coding-rules-detail-description-extra', + extendDescriptionLink: '#coding-rules-detail-extend-description', + extendDescriptionForm: '.coding-rules-detail-extend-description-form', + extendDescriptionSubmit: '#coding-rules-detail-extend-description-submit', + extendDescriptionRemove: '#coding-rules-detail-extend-description-remove', + extendDescriptionText: '#coding-rules-detail-extend-description-text', + cancelExtendDescription: '#coding-rules-detail-extend-description-cancel' + }, + + events: { + 'click @ui.extendDescriptionLink': 'showExtendDescriptionForm', + 'click @ui.cancelExtendDescription': 'hideExtendDescriptionForm', + 'click @ui.extendDescriptionSubmit': 'submitExtendDescription', + 'click @ui.extendDescriptionRemove': 'removeExtendedDescription' + }, + + showExtendDescriptionForm: function () { + this.ui.descriptionExtra.addClass('hidden'); + this.ui.extendDescriptionForm.removeClass('hidden'); + this.ui.extendDescriptionText.focus(); + }, + + hideExtendDescriptionForm: function () { + this.ui.descriptionExtra.removeClass('hidden'); + this.ui.extendDescriptionForm.addClass('hidden'); + }, + + submitExtendDescription: function () { + var that = this, + p = window.process.addBackgroundProcess(); + this.ui.extendDescriptionForm.addClass('hidden'); + return jQuery.ajax({ + type: 'POST', + url: baseUrl + '/api/rules/update', + dataType: 'json', + data: { + key: this.model.get('key'), + markdown_note: this.ui.extendDescriptionText.val() + } + }).done(function (r) { + that.model.set({ + htmlNote: r.rule.htmlNote, + mdNote: r.rule.mdNote + }); + that.render(); + window.process.finishBackgroundProcess(p); + }).fail(function () { + that.render(); + window.process.failBackgroundProcess(p); + }); + }, + + removeExtendedDescription: function () { + var that = this; + window.confirmDialog({ + html: t('coding_rules.remove_extended_description.confirm'), + yesHandler: function () { + that.ui.extendDescriptionText.val(''); + that.submitExtendDescription(); + } + }); + }, + + serializeData: function () { + var isEditable = this.options.app.canWrite && (this.model.get('isManual') || this.model.get('isCustom')); + + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + isEditable: isEditable, + canWrite: this.options.app.canWrite + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js new file mode 100644 index 00000000000..6f3c3c36700 --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js @@ -0,0 +1,86 @@ +define([ + 'backbone.marionette', + 'templates/coding-rules' +], function (Marionette, Templates) { + + return Marionette.ItemView.extend({ + template: Templates['coding-rules-rule-meta'], + + modelEvents: { + 'change': 'render' + }, + + ui: { + tagsChange: '.coding-rules-detail-tags-change', + tagInput: '.coding-rules-detail-tag-input', + tagsEdit: '.coding-rules-detail-tag-edit', + tagsEditDone: '.coding-rules-detail-tag-edit-done', + tagsEditCancel: '.coding-rules-details-tag-edit-cancel', + tagsList: '.coding-rules-detail-tag-list' + }, + + events: { + 'click @ui.tagsChange': 'changeTags', + 'click @ui.tagsEditDone': 'editDone', + 'click @ui.tagsEditCancel': 'cancelEdit' + }, + + requestTags: function () { + var url = baseUrl + '/api/rules/tags'; + return jQuery.get(url); + }, + + changeTags: function () { + var that = this; + this.requestTags().done(function (r) { + that.ui.tagInput.select2({ + tags: _.difference(_.difference(r.tags, that.model.get('tags')), that.model.get('sysTags')), + width: '300px' + }); + + that.ui.tagsEdit.removeClass('hidden'); + that.ui.tagsList.addClass('hidden'); + that.tagsBuffer = that.ui.tagInput.select2('val'); + }); + }, + + cancelEdit: function () { + this.ui.tagsList.removeClass('hidden'); + this.ui.tagsEdit.addClass('hidden'); + if (this.ui.tagInput.select2) { + this.ui.tagInput.select2('val', this.tagsBuffer); + this.ui.tagInput.select2('close'); + } + }, + + editDone: function () { + var that = this, + p = window.process.addBackgroundProcess(), + tags = this.ui.tagInput.val(); + return jQuery.ajax({ + type: 'POST', + url: baseUrl + '/api/rules/update', + data: { + key: this.model.get('key'), + tags: tags + } + }).done(function (r) { + that.model.set('tags', r.rule.tags); + that.cancelEdit(); + window.process.finishBackgroundProcess(p); + }).always(function () { + that.cancelEdit(); + window.process.failBackgroundProcess(p); + }); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + canWrite: this.options.app.canWrite, + subCharacteristic: this.options.app.getSubCharacteristicName(this.model.get('debtSubChar')), + allTags: _.union(this.model.get('sysTags'), this.model.get('tags')) + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-parameters-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-parameters-view.js new file mode 100644 index 00000000000..f2572460033 --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-parameters-view.js @@ -0,0 +1,27 @@ +define([ + 'backbone.marionette', + 'templates/coding-rules' +], function (Marionette, Templates) { + + return Marionette.ItemView.extend({ + template: Templates['coding-rules-rule-parameters'], + + modelEvents: { + 'change': 'render' + }, + + onRender: function () { + this.$el.toggleClass('hidden', _.isEmpty(this.model.get('params'))); + }, + + serializeData: function () { + var isEditable = this.options.app.canWrite && (this.model.get('isManual') || this.model.get('isCustom')); + + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + isEditable: isEditable, + canWrite: this.options.app.canWrite + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js new file mode 100644 index 00000000000..6e90f6ff947 --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js @@ -0,0 +1,135 @@ +define([ + 'backbone.marionette', + 'templates/coding-rules', + 'coding-rules/rule/profile-activation-view' +], function (Marionette, Templates, ProfileActivationView) { + + return Marionette.ItemView.extend({ + tagName: 'tr', + template: Templates['coding-rules-rule-profile'], + + modelEvents: { + 'change': 'render' + }, + + ui: { + change: '.coding-rules-detail-quality-profile-change', + revert: '.coding-rules-detail-quality-profile-revert', + deactivate: '.coding-rules-detail-quality-profile-deactivate' + }, + + events: { + 'click @ui.change': 'change', + 'click @ui.revert': 'revert', + 'click @ui.deactivate': 'deactivate' + }, + + change: function () { + new ProfileActivationView({ + model: this.model, + collection: this.model.collection, + rule: this.options.rule, + app: this.options.app + }).render(); + }, + + revert: function () { + var that = this, + ruleKey = this.options.rule.get('key'); + window.confirmDialog({ + title: t('coding_rules.revert_to_parent_definition'), + html: tp('coding_rules.revert_to_parent_definition.confirm', this.getParent().name), + yesHandler: function () { + var p = window.process.addBackgroundProcess(); + return jQuery.ajax({ + type: 'POST', + url: baseUrl + '/api/qualityprofiles/activate_rule', + data: { + profile_key: that.model.get('qProfile'), + rule_key: ruleKey, + reset: true + } + }).done(function () { + window.process.finishBackgroundProcess(p); + that.options.app.controller.showDetails(that.options.rule); + }); + } + }); + }, + + deactivate: function () { + var that = this, + ruleKey = this.options.rule.get('key'), + myProfile = _.findWhere(this.options.app.qualityProfiles, { + key: this.model.get('qProfile') + }); + window.confirmDialog({ + title: t('coding_rules.deactivate'), + html: tp('coding_rules.deactivate.confirm', myProfile.name), + yesHandler: function () { + var p = window.process.addBackgroundProcess(); + return jQuery.ajax({ + type: 'POST', + url: baseUrl + '/api/qualityprofiles/deactivate_rule', + data: { + profile_key: that.model.get('qProfile'), + rule_key: ruleKey + } + }).done(function () { + window.process.finishBackgroundProcess(p); + that.options.app.controller.showDetails(that.options.rule); + }); + } + }); + }, + + enableUpdate: function () { + return this.ui.update.prop('disabled', false); + }, + + getParent: function () { + if (!(this.model.get('inherit') && this.model.get('inherit') !== 'NONE')) { + return null; + } + var myProfile = _.findWhere(this.options.app.qualityProfiles, { + key: this.model.get('qProfile') + }), + parentKey = myProfile.parentKey, + parent = _.extend({}, _.findWhere(this.options.app.qualityProfiles, { + key: parentKey + })), + parentActiveInfo = this.model.collection.findWhere({ qProfile: parentKey }) || new Backbone.Model(); + _.extend(parent, parentActiveInfo.toJSON()); + return parent; + }, + + enhanceParameters: function () { + var parent = this.getParent(), + params = _.sortBy(this.model.get('params'), 'key'); + if (!parent) { + return params; + } + return params.map(function (p) { + var parentParam = _.findWhere(parent.params, { key: p.key }); + if (parentParam != null) { + return _.extend(p, { + original: _.findWhere(parent.params, { key: p.key }).value + }); + } else { + return p; + } + }); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + canWrite: this.options.app.canWrite, + parent: this.getParent(), + parameters: this.enhanceParameters(), + templateKey: this.options.rule.get('templateKey'), + isTemplate: this.options.rule.get('isTemplate') + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-profiles-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-profiles-view.js new file mode 100644 index 00000000000..b9e36555201 --- /dev/null +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-profiles-view.js @@ -0,0 +1,43 @@ +define([ + 'backbone.marionette', + 'templates/coding-rules', + 'coding-rules/rule/rule-profile-view', + 'coding-rules/rule/profile-activation-view' +], function (Marionette, Templates, ProfileView, ProfileActivationView) { + + return Marionette.CompositeView.extend({ + template: Templates['coding-rules-rule-profiles'], + itemView: ProfileView, + itemViewContainer: '#coding-rules-detail-quality-profiles', + + itemViewOptions: function () { + return { + app: this.options.app, + rule: this.model + }; + }, + + modelEvents: { + 'change': 'render' + }, + + events: { + 'click #coding-rules-quality-profile-activate': 'activate' + }, + + activate: function () { + new ProfileActivationView({ + rule: this.model, + collection: this.collection, + app: this.options.app + }).render(); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + canWrite: this.options.app.canWrite + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/less/coding-rules.less b/server/sonar-web/src/main/less/coding-rules.less index f8bd0a43811..86badc92f7a 100644 --- a/server/sonar-web/src/main/less/coding-rules.less +++ b/server/sonar-web/src/main/less/coding-rules.less @@ -57,9 +57,10 @@ .coding-rules-detail-header, .coding-rules-detail-title { position: relative; - margin-bottom: @navigatorPadding; + margin: 1em 0; line-height: 1.5; - font-weight: bold; + font-size: @bigFontSize; + font-weight: 400; } .coding-rules-detail-header { @@ -70,8 +71,7 @@ .coding-rules-detail-title { display: inline-block; - margin-top: 3 * @navigatorPadding; - text-transform: uppercase; + margin-top: 2em; } .coding-rules-detail-permalink { @@ -227,28 +227,24 @@ } .coding-rules-detail-parameters { + width: 100%; margin: @navigatorPadding 0 @navigatorPadding * 2; } .coding-rules-detail-parameter { - margin: @navigatorPadding 0; + } .coding-rules-detail-parameter-name { - display: block; - margin-left: 2 * @navigatorPadding; + width: 1px; + vertical-align: top; + padding: 5px 10px 5px 0; font-weight: bold; - cursor: pointer; } .coding-rules-detail-parameter-description { - display: inline-block; - text-overflow: ellipsis; vertical-align: top; - max-width: 75%; - margin-left: 2 * @navigatorPadding; - padding: @navigatorPadding; - .box-sizing(border-box); + padding: 5px 5px; .subtitle { margin-top: @navigatorPadding; @@ -278,44 +274,43 @@ } .coding-rules-detail-quality-profiles { - font-size: 0; -} + line-height: 22px; -.coding-rules-detail-quality-profile { - margin-left: 2 * @navigatorPadding; -} + td { + border-top: 1px solid @barBorderColor; + } -.coding-rules-detail-quality-profile + .coding-rules-detail-quality-profile { - margin-top: @navigatorPadding; - padding-top: @navigatorPadding; - border-top: 1px solid @navigatorBorderLightColor; + tr:first-child td { + border-top: none; + } } .coding-rules-detail-quality-profile-name { vertical-align: top; - width: 15%; + width: 1px; + padding: 8px 5px 8px 0; font-weight: bold; white-space: nowrap; - padding-right: 5px; } .coding-rules-detail-quality-profile-severity { vertical-align: top; - width: 10%; + width: 1px; + padding: 8px 5px; + white-space: nowrap; } .coding-rules-detail-quality-profile-parameters { vertical-align: top; -} - -.coding-rules-detail-quality-profile-parameter + .coding-rules-detail-quality-profile-parameter { - margin-top: 8px; + padding: 8px 5px; } .coding-rules-detail-quality-profile-actions { vertical-align: top; - width: 25%; + width: 1px; + padding: 8px 0 8px 5px; text-align: right; + white-space: nowrap; } .coding-rules-detail-quality-profile-inheritance { diff --git a/server/sonar-web/src/main/less/components/modals.less b/server/sonar-web/src/main/less/components/modals.less index 97059162852..6581e6691c6 100644 --- a/server/sonar-web/src/main/less/components/modals.less +++ b/server/sonar-web/src/main/less/components/modals.less @@ -1,6 +1,6 @@ .modal { position: fixed; - z-index: 9100; + z-index: 9000; top: 15%; left: 50%; margin-left: -270px; @@ -10,7 +10,7 @@ .modal-overlay { position: fixed; - z-index: 9099; + z-index: 8999; top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(0, 0, 0, 0.5); } -- 2.39.5