aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2014-12-26 17:43:39 +0100
committerStas Vilchik <vilchiks@gmail.com>2015-01-05 11:27:17 +0100
commit1fc88c2c9114999f7635df19d6d33e04df95b0f6 (patch)
tree95afcf506125d7a037848ac81caa54cb265e1c16 /server/sonar-web
parente87e06f2789cc3c42c30de31f62084552f6295d3 (diff)
downloadsonarqube-1fc88c2c9114999f7635df19d6d33e04df95b0f6.tar.gz
sonarqube-1fc88c2c9114999f7635df19d6d33e04df95b0f6.zip
SONAR-5820 Ability to create, edit and delete a "Manual" rule
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-filters.hbs3
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-rule-details.hbs6
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-manual-rule-creation.hbs50
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs6
-rw-r--r--server/sonar-web/src/main/js/coding-rules/app.js2
-rw-r--r--server/sonar-web/src/main/js/coding-rules/filters-view.js20
-rw-r--r--server/sonar-web/src/main/js/coding-rules/models/rule.js3
-rw-r--r--server/sonar-web/src/main/js/coding-rules/rule-details-view.js284
-rw-r--r--server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js115
-rw-r--r--server/sonar-web/src/main/js/coding-rules/rule/rule-profiles-view.js2
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules.js50
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/app.json168
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/search.json7
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/show.json17
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules.js50
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/app.json168
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-after.json7
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-before.json25
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/show.json17
19 files changed, 866 insertions, 134 deletions
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-filters.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-filters.hbs
index 45ce48c26c6..dc97d68a6fe 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-filters.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-filters.hbs
@@ -5,5 +5,8 @@
<div class="search-navigator-filters-actions">
<div class="button-group">
<button class="js-new-search" id="coding-rules-new-search">{{t 'issue_filter.new_search'}}</button>
+ {{#if canWrite}}
+ <button class="js-create-manual-rule">{{t 'coding_rules.create'}}</button>
+ {{/if}}
</div>
</div>
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 91acf126305..930576b5285 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
@@ -6,11 +6,11 @@
<div class="coding-rules-detail-description">
<div class="button-group">
{{#if isManual}}
- <button id="coding-rules-detail-manual-rule-change">{{t 'edit'}}</button>
+ <button class="js-edit-manual" id="coding-rules-detail-manual-rule-change">{{t 'edit'}}</button>
{{else}}
- <button id="coding-rules-detail-custom-rule-change">{{t 'edit'}}</button>
+ <button class="js-edit-custom" id="coding-rules-detail-custom-rule-change">{{t 'edit'}}</button>
{{/if}}
- <button id="coding-rules-detail-rule-delete" class="button-red">{{t 'delete'}}</button>
+ <button class="js-delete" id="coding-rules-detail-rule-delete" class="button-red">{{t 'delete'}}</button>
</div>
</div>
{{/if}}
diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-manual-rule-creation.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-manual-rule-creation.hbs
new file mode 100644
index 00000000000..5a6e62a6c46
--- /dev/null
+++ b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-manual-rule-creation.hbs
@@ -0,0 +1,50 @@
+<form>
+ <div class="modal-head">
+ {{#if change}}
+ <h2>{{t 'coding_rules.update_manual_rule'}}</h2>
+ {{else}}
+ <h2>{{t 'coding_rules.create_manual_rule'}}</h2>
+ {{/if}}
+ </div>
+
+ <div class="modal-body">
+ <div class="modal-error"></div>
+ <div class="modal-warning">{{t 'coding_rules.reactivate.help'}}</div>
+
+ <table>
+ <tr class="property">
+ <th><h3>{{t 'name'}} <em class="mandatory">*</em></h3></th>
+ <td>
+ <input type="text" name="name" id="coding-rules-manual-rule-creation-name"
+ class="coding-rules-name-key" value="{{name}}"/>
+ </td>
+ </tr>
+ <tr class="property">
+ <th><h3>{{t 'key'}}{{#unless change}} <em class="mandatory">*</em>{{/unless}}</h3></th>
+ <td>
+ {{#if change}}
+ {{key}}
+ {{else}}
+ <input type="text" name="key" id="coding-rules-manual-rule-creation-key"
+ class="coding-rules-name-key" value="{{internalKey}}"/>
+ {{/if}}
+ </td>
+ </tr>
+ <tr class="property">
+ <th><h3>{{t 'description'}} <em class="mandatory">*</em></h3></th>
+ <td>
+ <textarea name="markdown_description" id="coding-rules-manual-rule-creation-html-description"
+ class="coding-rules-markdown-description" rows="15">{{{mdDesc}}}</textarea>
+ <span class="right">{{> '_markdown-tips' }}</span>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="modal-foot">
+ <button id="coding-rules-manual-rule-creation-create">
+ {{#if change}}{{t 'save'}}{{else}}{{t 'create'}}{{/if}}
+ </button>
+ <a id="coding-rules-manual-rule-creation-cancel" class="action">{{t 'cancel'}}</a>
+ </div>
+</form>
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
index fff167bbcf3..af307386b1d 100644
--- 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
@@ -38,13 +38,11 @@
</li>
{{/if}}
-
-
<li class="coding-rules-detail-property">{{t 'coding_rules.available_since'}} {{d createdAt}}</li>
<li class="coding-rules-detail-property"
- data-toggle="tooltip" data-placement="bottom" title="Rule repository (language)">
- {{default repo.name repo}}{{#unless isManual}} ({{langName}}){{/unless}}
+ data-toggle="tooltip" data-placement="bottom" title="Rule repository{{#unless isManual}} (language){{/unless}}">
+ {{repoName}}{{#unless isManual}} ({{langName}}){{/unless}}
</li>
{{#if isTemplate}}
diff --git a/server/sonar-web/src/main/js/coding-rules/app.js b/server/sonar-web/src/main/js/coding-rules/app.js
index c42f4a41a8b..4dfb34661a7 100644
--- a/server/sonar-web/src/main/js/coding-rules/app.js
+++ b/server/sonar-web/src/main/js/coding-rules/app.js
@@ -114,7 +114,7 @@ requirejs([
App.manualRepository = function () {
return {
key: 'manual',
- name: t('coding_rules.manual_rules'),
+ name: t('coding_rules.manual_rule'),
language: 'none'
};
};
diff --git a/server/sonar-web/src/main/js/coding-rules/filters-view.js b/server/sonar-web/src/main/js/coding-rules/filters-view.js
index 9c2de652b4a..d46f4f68e12 100644
--- a/server/sonar-web/src/main/js/coding-rules/filters-view.js
+++ b/server/sonar-web/src/main/js/coding-rules/filters-view.js
@@ -1,17 +1,31 @@
define([
'backbone.marionette',
- 'templates/coding-rules'
-], function (Marionette, Templates) {
+ 'templates/coding-rules',
+ 'coding-rules/rule/manual-rule-creation-view'
+], function (Marionette, Templates, ManualRuleCreationView) {
return Marionette.ItemView.extend({
template: Templates['coding-rules-filters'],
events: {
- 'click .js-new-search': 'newSearch'
+ 'click .js-new-search': 'newSearch',
+ 'click .js-create-manual-rule': 'createManualRule'
},
newSearch: function () {
this.options.app.controller.newSearch();
+ },
+
+ createManualRule: function() {
+ new ManualRuleCreationView({
+ 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/js/coding-rules/models/rule.js b/server/sonar-web/src/main/js/coding-rules/models/rule.js
index 0b20c8eab0d..9363ff689d8 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
@@ -8,11 +8,12 @@ define([
addExtraAttributes: function (languages, repositories) {
var langName = languages[this.get('lang')] || this.get('lang'),
repo = _.findWhere(repositories, { key: this.get('repo') }) || this.get('repo'),
+ repoName = repo != null ? repo.name : repo,
isManual = this.get('repo') === 'manual',
isCustom = this.has('templateKey');
this.set({
langName: langName,
- repo: repo,
+ repoName: repoName,
isManual: isManual,
isCustom: isCustom
});
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 627d3165311..d9b76e526d2 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
@@ -7,130 +7,172 @@ define([
'coding-rules/rule/rule-description-view',
'coding-rules/rule/rule-parameters-view',
'coding-rules/rule/rule-profiles-view',
- 'coding-rules/rule/custom-rules-view'
-], function (Backbone, Marionette, Templates, Rules, MetaView, DescView, ParamView, ProfilesView, CustomRulesView) {
-
- var $ = jQuery;
-
- return Marionette.Layout.extend({
- className: 'coding-rule-details',
- template: Templates['coding-rules-rule-details'],
-
- regions: {
- metaRegion: '.js-rule-meta',
- descRegion: '.js-rule-description',
- paramRegion: '.js-rule-parameters',
- profilesRegion: '.js-rule-profiles',
- customRulesRegion: '.js-rule-custom-rules'
- },
-
- initialize: function () {
- this.bindShortcuts();
- this.customRules = new Rules();
- if (this.model.get('isTemplate')) {
- this.fetchCustomRules();
- }
- },
-
- 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())
- }));
- this.customRulesRegion.show(new CustomRulesView({
- app: this.options.app,
- model: this.model,
- collection: this.customRules
- }));
- this.$el.scrollParent().scrollTop(30);
- },
-
- onClose: function () {
- this.unbindShortcuts();
- },
-
- fetchCustomRules: function () {
- var that = this,
- url = baseUrl + '/api/rules/search',
- options = {
- template_key: this.model.get('key'),
- f: 'name,severity,params'
- };
- return $.get(url, options).done(function (data) {
- that.customRules.reset(data.rules);
- });
- },
-
- 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 () {
- that.options.app.controller.selectPrev();
- that.options.app.controller.showDetailsForSelected();
- return false;
- });
- key('down', 'details', function () {
- that.options.app.controller.selectNext();
- that.options.app.controller.showDetailsForSelected();
- return false;
- });
- key('left', 'details', function () {
- that.options.app.controller.hideDetails();
- return false;
- });
- },
+ 'coding-rules/rule/custom-rules-view',
+ 'coding-rules/rule/manual-rule-creation-view'
+],
+ function (Backbone,
+ Marionette,
+ Templates,
+ Rules,
+ MetaView,
+ DescView,
+ ParamView,
+ ProfilesView,
+ CustomRulesView,
+ ManualRuleCreationView) {
- unbindShortcuts: function () {
- key.deleteScope('details');
- },
+ var $ = jQuery;
- serializeData: function () {
- 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;
+ return Marionette.Layout.extend({
+ className: 'coding-rule-details',
+ template: Templates['coding-rules-rule-details'],
- if (qualityProfilesVisible) {
- if (this.model.get('isTemplate')) {
- qualityProfilesVisible = !_.isEmpty(this.options.actives);
- }
- else {
- qualityProfilesVisible = (this.options.app.canWrite || !_.isEmpty(this.options.actives));
+ regions: {
+ metaRegion: '.js-rule-meta',
+ descRegion: '.js-rule-description',
+ paramRegion: '.js-rule-parameters',
+ profilesRegion: '.js-rule-profiles',
+ customRulesRegion: '.js-rule-custom-rules'
+ },
+
+ events: {
+ 'click .js-edit-manual': 'editManualRule',
+ 'click .js-delete': 'deleteRule'
+ },
+
+ initialize: function () {
+ this.bindShortcuts();
+ this.customRules = new Rules();
+ if (this.model.get('isTemplate')) {
+ this.fetchCustomRules();
+ }
+ },
+
+ 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())
+ }));
+ this.customRulesRegion.show(new CustomRulesView({
+ app: this.options.app,
+ model: this.model,
+ collection: this.customRules
+ }));
+ this.$el.scrollParent().scrollTop(30);
+ },
+
+ onClose: function () {
+ this.unbindShortcuts();
+ },
+
+ fetchCustomRules: function () {
+ var that = this,
+ url = baseUrl + '/api/rules/search',
+ options = {
+ template_key: this.model.get('key'),
+ f: 'name,severity,params'
+ };
+ return $.get(url, options).done(function (data) {
+ that.customRules.reset(data.rules);
+ });
+ },
+
+ 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 () {
+ that.options.app.controller.selectPrev();
+ that.options.app.controller.showDetailsForSelected();
+ return false;
+ });
+ key('down', 'details', function () {
+ that.options.app.controller.selectNext();
+ that.options.app.controller.showDetailsForSelected();
+ return false;
+ });
+ key('left', 'details', function () {
+ that.options.app.controller.hideDetails();
+ return false;
+ });
+ },
+
+ unbindShortcuts: function () {
+ key.deleteScope('details');
+ },
+
+ editManualRule: function () {
+ new ManualRuleCreationView({
+ app: this.options.app,
+ model: this.model
+ }).render();
+ },
+
+ deleteRule: function () {
+ var that = this,
+ ruleType = this.model.has('templateKey') ? 'custom' : 'manual';
+ window.confirmDialog({
+ title: t('delete'),
+ html: tp('coding_rules.delete.' + ruleType + '.confirm', this.model.get('name')),
+ yesHandler: function () {
+ var p = window.process.addBackgroundProcess(),
+ url = baseUrl + '/api/rules/delete',
+ options = { key: that.model.id };
+ $.post(url, options).done(function () {
+ that.options.app.controller.fetchList();
+ window.process.finishBackgroundProcess(p);
+ }).fail(function () {
+ window.process.failBackgroundProcess(p);
+ });
+ }
+ });
+ },
+
+ serializeData: function () {
+ var isManual = this.model.get('isManual'),
+ 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), {
+ 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'))
+ });
}
- }
-
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- 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/manual-rule-creation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js
new file mode 100644
index 00000000000..53f58f0b2aa
--- /dev/null
+++ b/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js
@@ -0,0 +1,115 @@
+define([
+ 'common/modal-form',
+ 'templates/coding-rules'
+], function (ModalFormView, Templates) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['coding-rules-manual-rule-creation'],
+
+ ui: function () {
+ return _.extend(ModalFormView.prototype.ui.apply(this.arguments), {
+ manualRuleCreationKey: '#coding-rules-manual-rule-creation-key',
+ manualRuleCreationName: '#coding-rules-manual-rule-creation-name',
+ manualRuleCreationHtmlDescription: '#coding-rules-manual-rule-creation-html-description',
+ manualRuleCreationSeverity: '#coding-rules-manual-rule-creation-severity',
+ manualRuleCreationStatus: '#coding-rules-manual-rule-creation-status',
+ manualRuleCreationParameters: '[name]',
+ manualRuleCreationCreate: '#coding-rules-manual-rule-creation-create',
+ manualRuleCreationReactivate: '#coding-rules-manual-rule-creation-reactivate',
+ modalFoot: '.modal-foot'
+ });
+ },
+
+ events: function () {
+ return _.extend(ModalFormView.prototype.events.apply(this.arguments), {
+ 'input @ui.manualRuleCreationName': 'generateKey',
+ 'keydown @ui.manualRuleCreationName': 'generateKey',
+ 'keyup @ui.manualRuleCreationName': 'generateKey',
+
+ 'input @ui.manualRuleCreationKey': 'flagKey',
+ 'keydown @ui.manualRuleCreationKey': 'flagKey',
+ 'keyup @ui.manualRuleCreationKey': 'flagKey',
+
+ 'click #coding-rules-manual-rule-creation-cancel': 'hide',
+ 'click @ui.manualRuleCreationCreate': 'create',
+ 'click @ui.manualRuleCreationReactivate': 'reactivate'
+ });
+ },
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+ this.keyModifiedByUser = false;
+ },
+
+ generateKey: function () {
+ if (!this.keyModifiedByUser && this.ui.manualRuleCreationKey) {
+ var generatedKey = this.ui.manualRuleCreationName.val().latinize().replace(/[^A-Za-z0-9]/g, '_');
+ this.ui.manualRuleCreationKey.val(generatedKey);
+ }
+ },
+
+ flagKey: function () {
+ this.keyModifiedByUser = true;
+ // Cannot use @ui.manualRuleCreationReactivate.hide() directly since it was not there at initial render
+ $(this.ui.manualRuleCreationReactivate.selector).hide();
+ },
+
+ create: function () {
+ var action = (this.model && this.model.has('key')) ? 'update' : 'create',
+ options = {
+ name: this.ui.manualRuleCreationName.val(),
+ markdown_description: this.ui.manualRuleCreationHtmlDescription.val()
+ };
+ if (action === 'update') {
+ options.key = this.model.get('key');
+ } else {
+ options.manual_key = this.ui.manualRuleCreationKey.val();
+ options.prevent_reactivation = true;
+ }
+ this.sendRequest(action, options);
+ },
+
+ reactivate: function () {
+ var options = {
+ name: this.existingRule.name,
+ markdown_description: this.existingRule.mdDesc,
+ manual_key: this.ui.manualRuleCreationKey.val(),
+ prevent_reactivation: false
+ };
+ this.sendRequest('create', options);
+ },
+
+ sendRequest: function (action, options) {
+ this.$('.modal-error').hide();
+ this.$('.modal-warning').hide();
+ var that = this,
+ p = window.process.addBackgroundProcess(),
+ url = baseUrl + '/api/rules/' + action;
+ return $.post(url, options).done(function (r) {
+ if (typeof r === 'string') {
+ r = JSON.parse(r);
+ }
+ that.options.app.controller.showDetails(r.rule.key);
+ that.close();
+ }).fail(function (jqXHR) {
+ if (jqXHR.status === 409) {
+ that.existingRule = jqXHR.responseJSON.rule;
+ that.$('.modal-warning').show();
+ } else {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ }
+ }).always(function () {
+ window.process.finishBackgroundProcess(p);
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(ModalFormView.prototype.serializeData.apply(this, arguments), {
+ change: this.model && this.model.has('key')
+ });
+ }
+ });
+
+});
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
index 0aedc97918c..5a7be4748e0 100644
--- 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
@@ -26,7 +26,7 @@ define([
},
onRender: function () {
- var isManual = (this.options.app.manualRepository().key === this.model.get('repo')),
+ var isManual = this.model.get('isManual'),
qualityProfilesVisible = !isManual;
if (qualityProfilesVisible) {
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules.js
new file mode 100644
index 00000000000..a128ffa277c
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules.js
@@ -0,0 +1,50 @@
+/* global casper:false */
+
+var lib = require('../lib');
+
+lib.initMessages();
+lib.changeWorkingDirectory('coding-rules-page-should-create-manual-rules');
+
+
+casper.test.begin('coding-rules-page-should-delete-manual-rules', 3, function (test) {
+ casper
+ .start(lib.buildUrl('coding-rules'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/rules/app', 'app.json');
+ lib.mockRequestFromFile('/api/rules/search', 'search.json');
+ lib.mockRequestFromFile('/api/rules/create', 'show.json');
+ lib.mockRequestFromFile('/api/rules/show', 'show.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.js-create-manual-rule', function () {
+ casper.click('.js-create-manual-rule');
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('.modal');
+ })
+
+ .then(function () {
+ casper.evaluate(function () {
+ jQuery('.modal [name="name"]').val('Manual Rule');
+ jQuery('.modal [name="key"]').val('manual:Manual_Rule');
+ jQuery('.modal [name="markdown_description"]').val('Manual Rule Description');
+ jQuery('.modal #coding-rules-manual-rule-creation-create').click();
+ });
+ casper.waitForSelector('.coding-rules-detail-header');
+ })
+
+ .then(function () {
+ test.assertSelectorContains('.coding-rules-detail-header', 'Manual Rule');
+ test.assertSelectorContains('.coding-rule-details .subtitle', 'manual:Manual_Rule');
+ test.assertSelectorContains('.coding-rules-detail-description', 'Manual Rule Description');
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/app.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/app.json
new file mode 100644
index 00000000000..f66b07ba5a1
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/app.json
@@ -0,0 +1,168 @@
+{
+ "canWrite": true,
+ "qualityprofiles": [
+ {
+ "key": "java-default-with-mojo-conventions-49307",
+ "name": "Default - Maven Conventions",
+ "lang": "java",
+ "parentKey": "java-top-profile-without-formatting-conventions-50037"
+ },
+ {
+ "key": "java-default-with-sonarsource-conventions-27339",
+ "name": "Default - SonarSource conventions",
+ "lang": "java",
+ "parentKey": "java-top-profile-without-formatting-conventions-50037"
+ },
+ {
+ "key": "java-top-profile-without-formatting-conventions-50037",
+ "name": "Default - Top",
+ "lang": "java"
+ },
+ {
+ "key": "java-findbugs-14954",
+ "name": "FindBugs",
+ "lang": "java"
+ },
+ {
+ "key": "java-for-sq-java-plugin-only-92289",
+ "name": "For SQ Java Plugin Only",
+ "lang": "java",
+ "parentKey": "java-default-with-sonarsource-conventions-27339"
+ },
+ {
+ "key": "java-for-sq-only-95381",
+ "name": "For SQ Only",
+ "lang": "java",
+ "parentKey": "java-default-with-sonarsource-conventions-27339"
+ },
+ {
+ "key": "php-psr-2-06315",
+ "name": "PSR-2",
+ "lang": "php"
+ },
+ {
+ "key": "java-sonar-way-80423",
+ "name": "Sonar way",
+ "lang": "java"
+ },
+ {
+ "key": "js-sonar-way",
+ "name": "Sonar way",
+ "lang": "js"
+ },
+ {
+ "key": "php-sonar-way-05548",
+ "name": "Sonar way",
+ "lang": "php"
+ },
+ {
+ "key": "py-sonar-way-80265",
+ "name": "Sonar way",
+ "lang": "py"
+ },
+ {
+ "key": "java-without-findbugs",
+ "name": "Without Findbugs",
+ "lang": "java"
+ }
+ ],
+ "languages": {
+ "py": "Python",
+ "js": "JavaScript",
+ "php": "PHP",
+ "java": "Java"
+ },
+ "repositories": [
+ {
+ "key": "common-java",
+ "name": "Common SonarQube",
+ "language": "java"
+ },
+ {
+ "key": "common-js",
+ "name": "Common SonarQube",
+ "language": "js"
+ },
+ {
+ "key": "common-php",
+ "name": "Common SonarQube",
+ "language": "php"
+ },
+ {
+ "key": "common-py",
+ "name": "Common SonarQube",
+ "language": "py"
+ },
+ {
+ "key": "Pylint",
+ "name": "Pylint",
+ "language": "py"
+ },
+ {
+ "key": "javascript",
+ "name": "SonarQube",
+ "language": "js"
+ },
+ {
+ "key": "php",
+ "name": "SonarQube",
+ "language": "php"
+ },
+ {
+ "key": "python",
+ "name": "SonarQube",
+ "language": "py"
+ },
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "java"
+ }
+ ],
+ "statuses": {
+ "BETA": "Beta",
+ "DEPRECATED": "Deprecated",
+ "READY": "Ready"
+ },
+ "characteristics": {
+ "UNDERSTANDABILITY": "Maintainability: Understandability",
+ "MAINTAINABILITY": "Maintainability",
+ "TIME_ZONE_RELATED_PORTABILITY": "Portability: Time zone related portability",
+ "READABILITY": "Maintainability: Readability",
+ "SECURITY_FEATURES": "Security: Security features",
+ "ARCHITECTURE_RELIABILITY": "Reliability: Architecture related reliability",
+ "OS_RELATED_PORTABILITY": "Portability: OS related portability",
+ "EXCEPTION_HANDLING": "Reliability: Exception handling",
+ "LOGIC_CHANGEABILITY": "Changeability: Logic related changeability",
+ "SOFTWARE_RELATED_PORTABILITY": "Portability: Software related portability",
+ "INPUT_VALIDATION_AND_REPRESENTATION": "Security: Input validation and representation",
+ "LANGUAGE_RELATED_PORTABILITY": "Portability: Language related portability",
+ "ERRORS": "Security: Errors",
+ "SECURITY": "Security",
+ "RELIABILITY": "Reliability",
+ "PORTABILITY": "Portability",
+ "HARDWARE_RELATED_PORTABILITY": "Portability: Hardware related portability",
+ "SYNCHRONIZATION_RELIABILITY": "Reliability: Synchronization related reliability",
+ "TRANSPORTABILITY": "Reusability: Transportability",
+ "COMPILER_RELATED_PORTABILITY": "Portability: Compiler related portability",
+ "RESOURCE_RELIABILITY": "Reliability: Resource",
+ "CPU_EFFICIENCY": "Efficiency: Processor use",
+ "EFFICIENCY": "Efficiency",
+ "CHANGEABILITY": "Changeability",
+ "DATA_CHANGEABILITY": "Changeability: Data related changeability",
+ "API_ABUSE": "Security: API abuse",
+ "ARCHITECTURE_CHANGEABILITY": "Changeability: Architecture related changeability",
+ "UNIT_TESTS": "Reliability: Unit tests",
+ "INSTRUCTION_RELIABILITY": "Reliability: Instruction related reliability",
+ "REUSABILITY": "Reusability",
+ "MODULARITY": "Reusability: Modularity",
+ "UNIT_TESTABILITY": "Testability: Unit level testability",
+ "TESTABILITY": "Testability",
+ "INTEGRATION_TESTABILITY": "Testability: Integration level testability",
+ "NETWORK_USE": "Efficiency: Network use",
+ "MEMORY_EFFICIENCY": "Efficiency: Memory use",
+ "DATA_RELIABILITY": "Reliability: Data related reliability",
+ "FAULT_TOLERANCE": "Reliability: Fault tolerance",
+ "LOGIC_RELIABILITY": "Reliability: Logic related reliability"
+ }
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/search.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/search.json
new file mode 100644
index 00000000000..f43d117c569
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/search.json
@@ -0,0 +1,7 @@
+{
+ "total": 0,
+ "p": 1,
+ "ps": 200,
+ "rules": [],
+ "facets": []
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/show.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/show.json
new file mode 100644
index 00000000000..38190c28abb
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-manual-rules/show.json
@@ -0,0 +1,17 @@
+{
+ "rule": {
+ "key": "manual:Manual_Rule",
+ "repo": "manual",
+ "name": "Manual Rule",
+ "createdAt": "2015-01-05T10:02:11+0100",
+ "status": "READY",
+ "isTemplate": false,
+ "tags": [],
+ "sysTags": [],
+ "htmlDesc": "Manual Rule Description",
+ "mdDesc": "Manual Rule Description",
+ "debtOverloaded": false,
+ "params": []
+ },
+ "actives": []
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules.js
new file mode 100644
index 00000000000..06fcd20739a
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules.js
@@ -0,0 +1,50 @@
+/* global casper:false */
+
+var lib = require('../lib');
+
+lib.initMessages();
+lib.changeWorkingDirectory('coding-rules-page-should-delete-manual-rules');
+
+
+casper.test.begin('coding-rules-page-should-delete-manual-rules', 1, function (test) {
+ casper
+ .start(lib.buildUrl('coding-rules'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/rules/app', 'app.json');
+ this.searchMock = lib.mockRequestFromFile('/api/rules/search', 'search-before.json');
+ lib.mockRequestFromFile('/api/rules/show', 'show.json');
+ lib.mockRequest('/api/rules/delete', '{}');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.coding-rule.selected', function () {
+ casper.click('.coding-rule.selected .js-rule');
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('.js-delete');
+ })
+
+ .then(function () {
+ casper.click('.js-delete');
+ casper.waitForSelector('[data-confirm="yes"]');
+ })
+
+ .then(function () {
+ lib.clearRequestMock(this.searchMock);
+ lib.mockRequestFromFile('/api/rules/search', 'search-after.json');
+ casper.click('[data-confirm="yes"]');
+ casper.waitForSelectorTextChange('#coding-rules-total');
+ })
+
+ .then(function () {
+ test.assertSelectorContains('#coding-rules-total', 0);
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/app.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/app.json
new file mode 100644
index 00000000000..f66b07ba5a1
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/app.json
@@ -0,0 +1,168 @@
+{
+ "canWrite": true,
+ "qualityprofiles": [
+ {
+ "key": "java-default-with-mojo-conventions-49307",
+ "name": "Default - Maven Conventions",
+ "lang": "java",
+ "parentKey": "java-top-profile-without-formatting-conventions-50037"
+ },
+ {
+ "key": "java-default-with-sonarsource-conventions-27339",
+ "name": "Default - SonarSource conventions",
+ "lang": "java",
+ "parentKey": "java-top-profile-without-formatting-conventions-50037"
+ },
+ {
+ "key": "java-top-profile-without-formatting-conventions-50037",
+ "name": "Default - Top",
+ "lang": "java"
+ },
+ {
+ "key": "java-findbugs-14954",
+ "name": "FindBugs",
+ "lang": "java"
+ },
+ {
+ "key": "java-for-sq-java-plugin-only-92289",
+ "name": "For SQ Java Plugin Only",
+ "lang": "java",
+ "parentKey": "java-default-with-sonarsource-conventions-27339"
+ },
+ {
+ "key": "java-for-sq-only-95381",
+ "name": "For SQ Only",
+ "lang": "java",
+ "parentKey": "java-default-with-sonarsource-conventions-27339"
+ },
+ {
+ "key": "php-psr-2-06315",
+ "name": "PSR-2",
+ "lang": "php"
+ },
+ {
+ "key": "java-sonar-way-80423",
+ "name": "Sonar way",
+ "lang": "java"
+ },
+ {
+ "key": "js-sonar-way",
+ "name": "Sonar way",
+ "lang": "js"
+ },
+ {
+ "key": "php-sonar-way-05548",
+ "name": "Sonar way",
+ "lang": "php"
+ },
+ {
+ "key": "py-sonar-way-80265",
+ "name": "Sonar way",
+ "lang": "py"
+ },
+ {
+ "key": "java-without-findbugs",
+ "name": "Without Findbugs",
+ "lang": "java"
+ }
+ ],
+ "languages": {
+ "py": "Python",
+ "js": "JavaScript",
+ "php": "PHP",
+ "java": "Java"
+ },
+ "repositories": [
+ {
+ "key": "common-java",
+ "name": "Common SonarQube",
+ "language": "java"
+ },
+ {
+ "key": "common-js",
+ "name": "Common SonarQube",
+ "language": "js"
+ },
+ {
+ "key": "common-php",
+ "name": "Common SonarQube",
+ "language": "php"
+ },
+ {
+ "key": "common-py",
+ "name": "Common SonarQube",
+ "language": "py"
+ },
+ {
+ "key": "Pylint",
+ "name": "Pylint",
+ "language": "py"
+ },
+ {
+ "key": "javascript",
+ "name": "SonarQube",
+ "language": "js"
+ },
+ {
+ "key": "php",
+ "name": "SonarQube",
+ "language": "php"
+ },
+ {
+ "key": "python",
+ "name": "SonarQube",
+ "language": "py"
+ },
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "java"
+ }
+ ],
+ "statuses": {
+ "BETA": "Beta",
+ "DEPRECATED": "Deprecated",
+ "READY": "Ready"
+ },
+ "characteristics": {
+ "UNDERSTANDABILITY": "Maintainability: Understandability",
+ "MAINTAINABILITY": "Maintainability",
+ "TIME_ZONE_RELATED_PORTABILITY": "Portability: Time zone related portability",
+ "READABILITY": "Maintainability: Readability",
+ "SECURITY_FEATURES": "Security: Security features",
+ "ARCHITECTURE_RELIABILITY": "Reliability: Architecture related reliability",
+ "OS_RELATED_PORTABILITY": "Portability: OS related portability",
+ "EXCEPTION_HANDLING": "Reliability: Exception handling",
+ "LOGIC_CHANGEABILITY": "Changeability: Logic related changeability",
+ "SOFTWARE_RELATED_PORTABILITY": "Portability: Software related portability",
+ "INPUT_VALIDATION_AND_REPRESENTATION": "Security: Input validation and representation",
+ "LANGUAGE_RELATED_PORTABILITY": "Portability: Language related portability",
+ "ERRORS": "Security: Errors",
+ "SECURITY": "Security",
+ "RELIABILITY": "Reliability",
+ "PORTABILITY": "Portability",
+ "HARDWARE_RELATED_PORTABILITY": "Portability: Hardware related portability",
+ "SYNCHRONIZATION_RELIABILITY": "Reliability: Synchronization related reliability",
+ "TRANSPORTABILITY": "Reusability: Transportability",
+ "COMPILER_RELATED_PORTABILITY": "Portability: Compiler related portability",
+ "RESOURCE_RELIABILITY": "Reliability: Resource",
+ "CPU_EFFICIENCY": "Efficiency: Processor use",
+ "EFFICIENCY": "Efficiency",
+ "CHANGEABILITY": "Changeability",
+ "DATA_CHANGEABILITY": "Changeability: Data related changeability",
+ "API_ABUSE": "Security: API abuse",
+ "ARCHITECTURE_CHANGEABILITY": "Changeability: Architecture related changeability",
+ "UNIT_TESTS": "Reliability: Unit tests",
+ "INSTRUCTION_RELIABILITY": "Reliability: Instruction related reliability",
+ "REUSABILITY": "Reusability",
+ "MODULARITY": "Reusability: Modularity",
+ "UNIT_TESTABILITY": "Testability: Unit level testability",
+ "TESTABILITY": "Testability",
+ "INTEGRATION_TESTABILITY": "Testability: Integration level testability",
+ "NETWORK_USE": "Efficiency: Network use",
+ "MEMORY_EFFICIENCY": "Efficiency: Memory use",
+ "DATA_RELIABILITY": "Reliability: Data related reliability",
+ "FAULT_TOLERANCE": "Reliability: Fault tolerance",
+ "LOGIC_RELIABILITY": "Reliability: Logic related reliability"
+ }
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-after.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-after.json
new file mode 100644
index 00000000000..f43d117c569
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-after.json
@@ -0,0 +1,7 @@
+{
+ "total": 0,
+ "p": 1,
+ "ps": 200,
+ "rules": [],
+ "facets": []
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-before.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-before.json
new file mode 100644
index 00000000000..1f19e1d27c2
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/search-before.json
@@ -0,0 +1,25 @@
+{
+ "total": 1,
+ "p": 1,
+ "ps": 200,
+ "rules": [
+ {
+ "key": "manual:Manual_Rule",
+ "name": "Manual Rule",
+ "sysTags": [],
+ "tags": [],
+ "status": "READY"
+ }
+ ],
+ "facets": [
+ {
+ "property": "repositories",
+ "values": [
+ {
+ "val": "manual",
+ "count": 1
+ }
+ ]
+ }
+ ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/show.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/show.json
new file mode 100644
index 00000000000..38190c28abb
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-delete-manual-rules/show.json
@@ -0,0 +1,17 @@
+{
+ "rule": {
+ "key": "manual:Manual_Rule",
+ "repo": "manual",
+ "name": "Manual Rule",
+ "createdAt": "2015-01-05T10:02:11+0100",
+ "status": "READY",
+ "isTemplate": false,
+ "tags": [],
+ "sysTags": [],
+ "htmlDesc": "Manual Rule Description",
+ "mdDesc": "Manual Rule Description",
+ "debtOverloaded": false,
+ "params": []
+ },
+ "actives": []
+}