aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/action-view.js58
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/actions-view.js62
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/app.js87
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/controller.js93
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/filters-view.js46
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/header-view.js32
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/item-view.js59
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/layout.js45
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/list-view.js42
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/list.js42
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/router.js37
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs73
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs9
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs7
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/app.js140
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js116
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js60
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/controller.js163
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets-view.js78
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/active-severity-facet.js65
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/available-since-facet.js61
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/base-facet.js30
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/characteristic-facet.js85
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/custom-labels-facet.js47
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/custom-values-facet.js89
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/inheritance-facet.js88
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/key-facet.js43
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/language-facet.js70
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js99
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/query-facet.js56
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/repository-facet.js74
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/severity-facet.js37
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/status-facet.js49
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/tag-facet.js50
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/facets/template-facet.js52
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/filters-view.js28
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/layout.js60
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/models/rule.js38
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/models/rules.js62
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/models/state.js41
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js209
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule-filter-view.js43
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-creation-view.js210
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js63
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js64
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/manual-rule-creation-view.js138
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js156
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js102
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-filter-mixin.js47
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js70
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js115
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js45
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js157
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js82
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs29
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-modal.hbs41
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-popup.hbs41
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-filters.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-layout.hbs10
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-details.hbs20
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-filter-form.hbs38
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs38
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs61
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/_coding-rules-facet-header.hbs4
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-available-since-facet.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs10
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-characteristic-facet.hbs19
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs14
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-key-facet.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-quality-profile-facet.hbs13
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-query-facet.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs10
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-template-facet.hbs12
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule-creation.hbs86
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs28
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs12
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-manual-rule-creation.hbs51
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs82
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs48
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs21
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs87
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs27
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs74
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs21
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js77
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js30
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js106
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js43
-rw-r--r--server/sonar-web/src/main/js/apps/drilldown/app.js52
-rw-r--r--server/sonar-web/src/main/js/apps/markdown/app.js30
-rw-r--r--server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js26
-rw-r--r--server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs100
-rw-r--r--server/sonar-web/src/main/js/apps/measures/app.js192
-rw-r--r--server/sonar-web/src/main/js/apps/measures/measures-filter-bar.js28
-rw-r--r--server/sonar-web/src/main/js/apps/nav/app.js79
-rw-r--r--server/sonar-web/src/main/js/apps/nav/context-navbar-view.js82
-rw-r--r--server/sonar-web/src/main/js/apps/nav/global-navbar-view.js97
-rw-r--r--server/sonar-web/src/main/js/apps/nav/search-view.js250
-rw-r--r--server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js32
-rw-r--r--server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js30
-rw-r--r--server/sonar-web/src/main/js/apps/nav/state.js51
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs124
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs105
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs22
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs8
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs86
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs57
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/actions-view.js116
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/app.js88
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/change-profile-parent-view.js82
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/controller.js128
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/copy-profile-view.js69
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/create-profile-view.js106
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/delete-profile-view.js62
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/helpers.js50
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/intro-view.js28
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/layout.js51
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile-changelog-view.js57
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile-comparison-view.js60
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile-details-view.js158
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile-header-view.js93
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile-view.js60
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profile.js133
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profiles-view.js83
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/profiles.js45
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/rename-profile-view.js60
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/restore-built-in-profiles-view.js75
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/restore-profile-view.js55
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/router.js56
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-changelog.hbs63
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-comparison.hbs85
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-actions.hbs39
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs32
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-layout.hbs9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-details.hbs132
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-header.hbs19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile.hbs21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles-language.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-built-in-profiles.hbs20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs17
-rw-r--r--server/sonar-web/src/main/js/apps/source-viewer/app.js54
153 files changed, 8888 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/action-view.js b/server/sonar-web/src/main/js/apps/api-documentation/action-view.js
new file mode 100644
index 00000000000..6df5db30ee5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/action-view.js
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ className: 'panel panel-vertical',
+ template: Templates['api-documentation-action'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click .js-show-response-example': 'onShowResponseExampleClick'
+ },
+
+ onRender: function () {
+ this.$el.attr('data-web-service', this.model.get('path'));
+ this.$el.attr('data-action', this.model.get('key'));
+ },
+
+ onShowResponseExampleClick: function (e) {
+ e.preventDefault();
+ this.fetchResponse();
+ },
+
+ fetchResponse: function () {
+ var that = this,
+ url = baseUrl + '/api/webservices/response_example',
+ options = { controller: this.model.get('path'), action: this.model.get('key') };
+ return $.get(url, options).done(function (r) {
+ that.model.set({ responseExample: r.example });
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js b/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js
new file mode 100644
index 00000000000..099e1ea73d0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './action-view'
+], function (ActionView) {
+
+ var $ = jQuery;
+
+ return Marionette.CollectionView.extend({
+ itemView: ActionView,
+
+ scrollToTop: function () {
+ var parent = this.$el.scrollParent();
+ if (parent.is(document)) {
+ parent = $(window);
+ }
+ parent.scrollTop(0);
+ },
+
+ scrollToAction: function (action) {
+ var model = this.collection.findWhere({ key: action });
+ if (model != null) {
+ var view = this.children.findByModel(model);
+ if (view != null) {
+ this.scrollToView(view);
+ }
+ }
+ },
+
+ scrollToView: function (view) {
+ var el = view.$el,
+ parent = el.scrollParent();
+ var elOffset = el.offset(),
+ parentOffset = parent.offset();
+ if (parent.is(document)) {
+ parentOffset = { top: 0 };
+ }
+ if (elOffset != null && parentOffset != null) {
+ var scrollTop = elOffset.top - parentOffset.top - 70;
+ parent.scrollTop(scrollTop);
+ }
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/app.js b/server/sonar-web/src/main/js/apps/api-documentation/app.js
new file mode 100644
index 00000000000..3e706bcc05f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/app.js
@@ -0,0 +1,87 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './router',
+ './controller',
+ './layout',
+ './list',
+ './list-view',
+ './filters-view'
+], function (Router, Controller, Layout, List, ListView, FiltersView) {
+
+ var $ = jQuery,
+ App = new Marionette.Application(),
+ init = function (options) {
+
+ // State
+ this.state = new Backbone.Model({ internal: false });
+
+ // Layout
+ this.layout = new Layout({ el: options.el });
+ this.layout.render();
+ $('#footer').addClass('search-navigator-footer');
+
+ // Web Services List
+ this.list = new List();
+
+ // Controller
+ this.controller = new Controller({
+ app: this,
+ state: this.state
+ });
+
+ // List View
+ this.listView = new ListView({
+ collection: this.list,
+ state: this.state
+ });
+ this.layout.resultsRegion.show(this.listView);
+
+ // Filters View
+ this.filtersView = new FiltersView({
+ collection: this.list,
+ state: this.state
+ });
+ this.layout.actionsRegion.show(this.filtersView);
+
+ // Router
+ this.router = new Router({ app: this });
+ Backbone.history.start({
+ pushState: true,
+ root: getRoot()
+ });
+ };
+
+ App.on('start', function (options) {
+ window.requestMessages().done(function () {
+ init.call(App, options);
+ });
+ });
+
+ function getRoot () {
+ var API_DOCUMENTATION = '/api_documentation',
+ path = window.location.pathname,
+ pos = path.indexOf(API_DOCUMENTATION);
+ return path.substr(0, pos + API_DOCUMENTATION.length);
+ }
+
+ return App;
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/controller.js b/server/sonar-web/src/main/js/apps/api-documentation/controller.js
new file mode 100644
index 00000000000..69ae9413bac
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/controller.js
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './actions-view',
+ './header-view'
+], function (ActionsView, HeaderView) {
+
+ return Marionette.Controller.extend({
+
+ initialize: function (options) {
+ this.list = options.app.list;
+ this.listenTo(this.list, 'select', this.onItemSelect);
+ },
+
+ show: function (path) {
+ var that = this;
+ this.fetchList().done(function () {
+ if (path) {
+ var item = that.list.findWhere({ path: path });
+ if (item != null) {
+ that.showWebService(path);
+ } else {
+ that.showAction(path);
+ }
+ }
+ });
+ },
+
+ showWebService: function (path) {
+ var item = this.list.findWhere({ path: path });
+ if (item != null) {
+ item.trigger('select', item);
+ }
+ },
+
+ showAction: function (path) {
+ var webService = this.list.find(function (item) {
+ return path.indexOf(item.get('path')) === 0;
+ });
+ if (webService != null) {
+ var action = path.substr(webService.get('path').length + 1);
+ webService.trigger('select', webService, { trigger: false, action: action });
+ }
+ },
+
+ onItemSelect: function (item, options) {
+ var path = item.get('path'),
+ opts = _.defaults(options || {}, { trigger: true });
+ if (opts.trigger) {
+ this.options.app.router.navigate(path);
+ }
+ this.options.app.listView.highlight(path);
+
+ if (item.get('internal')) {
+ this.options.state.set({ internal: true });
+ }
+
+ var actions = new Backbone.Collection(item.get('actions')),
+ actionsView = new ActionsView({ collection: actions });
+ this.options.app.layout.detailsRegion.show(actionsView);
+ this.options.app.layout.headerRegion.show(new HeaderView({ model: item }));
+
+ if (opts.action != null) {
+ actionsView.scrollToAction(opts.action);
+ } else {
+ actionsView.scrollToTop();
+ }
+ },
+
+ fetchList: function () {
+ return this.list.fetch({ data: { 'include_internals': true } });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js b/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js
new file mode 100644
index 00000000000..8465cfe9db3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['api-documentation-filters'],
+
+ events: {
+ 'change .js-toggle-internal': 'toggleInternal'
+ },
+
+ initialize: function () {
+ this.listenTo(this.options.state, 'change:internal', this.render);
+ },
+
+ toggleInternal: function () {
+ this.options.state.set({ internal: !this.options.state.get('internal')});
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ state: this.options.state.toJSON()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/header-view.js b/server/sonar-web/src/main/js/apps/api-documentation/header-view.js
new file mode 100644
index 00000000000..259fffae1c6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/header-view.js
@@ -0,0 +1,32 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['api-documentation-header'],
+
+ modelEvents: {
+ 'change': 'render'
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/item-view.js b/server/sonar-web/src/main/js/apps/api-documentation/item-view.js
new file mode 100644
index 00000000000..2c88eb9e13f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/item-view.js
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ tagName: 'a',
+ className: 'list-group-item',
+ template: Templates['api-documentation-web-service'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click': 'onClick'
+ },
+
+ initialize: function () {
+ this.listenTo(this.options.state, 'change:internal', this.toggleInternal);
+ },
+
+ onRender: function () {
+ this.$el.attr('data-path', this.model.get('path'));
+ this.$el.toggleClass('active', this.options.highlighted);
+ this.toggleInternal();
+ },
+
+ onClick: function (e) {
+ e.preventDefault();
+ this.model.trigger('select', this.model);
+ },
+
+ toggleInternal: function () {
+ var showInternal = this.options.state.get('internal'),
+ hideMe = this.model.get('internal') && !showInternal;
+ this.$el.toggleClass('hidden', hideMe);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/layout.js b/server/sonar-web/src/main/js/apps/api-documentation/layout.js
new file mode 100644
index 00000000000..64f17b220ed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/layout.js
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.Layout.extend({
+ template: Templates['api-documentation-layout'],
+
+ regions: {
+ headerRegion: '.search-navigator-workspace-header',
+ actionsRegion: '.search-navigator-filters',
+ resultsRegion: '.api-documentation-results',
+ detailsRegion: '.search-navigator-workspace-details'
+ },
+
+ onRender: function () {
+ var navigator = $('.search-navigator');
+ navigator.addClass('sticky search-navigator-extended-view');
+ var top = navigator.offset().top;
+ this.$('.search-navigator-workspace-header').css({ top: top });
+ this.$('.search-navigator-side').css({ top: top }).isolatedScroll();
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/list-view.js b/server/sonar-web/src/main/js/apps/api-documentation/list-view.js
new file mode 100644
index 00000000000..a833cf7b48b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/list-view.js
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './item-view'
+], function (ItemView) {
+
+ return Marionette.CollectionView.extend({
+ className: 'list-group',
+ itemView: ItemView,
+
+ itemViewOptions: function (model) {
+ return {
+ collectionView: this,
+ highlighted: model.get('path') === this.highlighted,
+ state: this.options.state
+ };
+ },
+
+ highlight: function (path) {
+ this.highlighted = path;
+ this.render();
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/list.js b/server/sonar-web/src/main/js/apps/api-documentation/list.js
new file mode 100644
index 00000000000..131b3352e8d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/list.js
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ return Backbone.Collection.extend({
+ url: baseUrl + '/api/webservices/list',
+ comparator: 'path',
+
+ parse: function (r) {
+ return r.webServices.map(function (webService) {
+ var internal = _.every(webService.actions, function (action) {
+ return action.internal;
+ }),
+ actions = webService.actions.map(function (action) {
+ return _.extend(action, { path: webService.path });
+ });
+ return _.extend(webService, {
+ internal: internal,
+ actions: actions
+ });
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/router.js b/server/sonar-web/src/main/js/apps/api-documentation/router.js
new file mode 100644
index 00000000000..fee3493fb18
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/router.js
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ return Backbone.Router.extend({
+
+ routes: {
+ '*path': 'show'
+ },
+
+ initialize: function (options) {
+ this.app = options.app;
+ },
+
+ show: function (path) {
+ this.app.controller.show(path);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs
new file mode 100644
index 00000000000..7e5c702e145
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs
@@ -0,0 +1,73 @@
+<header class="page-header">
+ <h3 class="page-title big">{{#if post}}POST{{else}}GET{{/if}} {{path}}/{{key}}</h3>
+
+ <div class="page-actions">
+ {{#if internal}}
+ <span class="badge spacer-right">internal</span>
+ {{/if}}
+ {{#if since}}
+ <span class="note spacer-right">Since {{since}}</span>
+ {{/if}}
+
+ <a class="js-permalink icon-link" href="{{link '/api_documentation/' path '/' key}}" target="_blank"></a>
+ </div>
+</header>
+
+<div class="markdown">{{{description}}}</div>
+
+{{#if params}}
+ <h4 class="spacer-top little-spacer-bottom">Parameters</h4>
+ <table class="width-100 data zebra">
+ {{#each params}}
+ <tr>
+ <td style="width: 10em;">
+ <code>{{key}}</code>
+ <div class="note">{{#if required}}required{{else}}optional{{/if}}</div>
+ </td>
+ <td>
+ <div class="markdown">{{{description}}}</div>
+
+ {{#if possibleValues}}
+ <p class="little-spacer-top">
+ <strong>Possible values:</strong>
+ </p>
+ <ul class="list-styled">
+ {{#each possibleValues}}
+ <li class="little-spacer-top"><code>{{this}}</code></li>
+ {{/each}}
+ </ul>
+ {{/if}}
+
+ {{#if defaultValue}}
+ <p class="little-spacer-top">
+ <strong>Default value:</strong> <code>{{defaultValue}}</code>
+ </p>
+ {{/if}}
+
+ {{#if exampleValue}}
+ <p class="little-spacer-top">
+ <strong>Example value:</strong> <code>{{exampleValue}}</code>
+ </p>
+ {{/if}}
+ </td>
+ </tr>
+ {{/each}}
+ </table>
+{{/if}}
+
+{{#if hasResponseExample}}
+ <h4 class="spacer-top">
+ Example Response
+ {{#unless responseExample}}
+ <a class="js-show-response-example little-spacer-left" href="#">Show</a>
+ {{/unless}}
+ </h4>
+
+ {{#if responseExample}}
+ <div class="little-spacer-top">
+ <pre>
+{{responseExample}}
+ </pre>
+ </div>
+ {{/if}}
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs
new file mode 100644
index 00000000000..5941110db0a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs
@@ -0,0 +1,6 @@
+<h1 class="page-title">Web Service API</h1>
+
+<div class="page-actions">
+ <input class="js-toggle-internal" type="checkbox" id="api-documentation-show-internal" {{#if state.internal}}checked{{/if}}>
+ <label for="api-documentation-show-internal">Show Internal</label>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs
new file mode 100644
index 00000000000..b652f85c9d4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs
@@ -0,0 +1 @@
+<h2 class="search-navigator-header-component">{{path}}</h2>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs
new file mode 100644
index 00000000000..4ef38c51b11
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs
@@ -0,0 +1,9 @@
+<div class="search-navigator-side search-navigator-side-light">
+ <div class="search-navigator-filters"></div>
+ <div class="api-documentation-results panel"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header"></div>
+ <div class="search-navigator-workspace-details"></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs
new file mode 100644
index 00000000000..2e47c41cc68
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs
@@ -0,0 +1,7 @@
+<h3 class="list-group-item-heading">
+ {{path}}
+ {{#if internal}}
+ <span class="badge">internal</span>
+ {{/if}}
+</h3>
+<p class="list-group-item-text">{{{description}}}</p>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/app.js b/server/sonar-web/src/main/js/apps/coding-rules/app.js
new file mode 100644
index 00000000000..c07d7513591
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/app.js
@@ -0,0 +1,140 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './models/state',
+ './layout',
+ './models/rules',
+ 'components/navigator/models/facets',
+
+ './controller',
+ 'components/navigator/router',
+
+ './workspace-list-view',
+ './workspace-header-view',
+
+ './facets-view',
+ './filters-view'
+],
+ function (State,
+ Layout,
+ Rules,
+ Facets,
+ Controller,
+ Router,
+ WorkspaceListView,
+ WorkspaceHeaderView,
+ FacetsView,
+ FiltersView) {
+
+ var $ = jQuery,
+ App = new Marionette.Application();
+
+ App.addInitializer(function () {
+ this.layout = new Layout();
+ $('.coding-rules').empty().append(this.layout.render().el);
+ $('#footer').addClass('search-navigator-footer');
+ });
+
+ App.addInitializer(function () {
+ this.state = new State();
+ this.list = new Rules();
+ this.facets = new Facets();
+ });
+
+ App.addInitializer(function () {
+ this.controller = new Controller({
+ app: this
+ });
+ });
+
+ App.addInitializer(function () {
+ this.workspaceListView = new WorkspaceListView({
+ app: this,
+ collection: this.list
+ });
+ this.layout.workspaceListRegion.show(this.workspaceListView);
+ this.workspaceListView.bindScrollEvents();
+
+ this.workspaceHeaderView = new WorkspaceHeaderView({
+ app: this,
+ collection: this.list
+ });
+ this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView);
+
+ this.facetsView = new FacetsView({
+ app: this,
+ collection: this.facets
+ });
+ this.layout.facetsRegion.show(this.facetsView);
+
+ this.filtersView = new FiltersView({
+ app: this
+ });
+ this.layout.filtersRegion.show(this.filtersView);
+ });
+
+ App.addInitializer(function () {
+ key.setScope('list');
+ this.router = new Router({
+ app: this
+ });
+ Backbone.history.start();
+ });
+
+ App.manualRepository = function () {
+ return {
+ key: 'manual',
+ name: t('coding_rules.manual_rule'),
+ language: 'none'
+ };
+ };
+
+ App.getSubCharacteristicName = function (key) {
+ if (key != null) {
+ var ch = _.findWhere(App.characteristics, { key: key }),
+ parent = _.findWhere(App.characteristics, { key: ch.parent });
+ return [parent.name, ch.name].join(' > ');
+ } else {
+ return null;
+ }
+ };
+
+ var appXHR = $.get(baseUrl + '/api/rules/app').done(function(r) {
+ App.canWrite = r.canWrite;
+ App.qualityProfiles = _.sortBy(r.qualityprofiles, ['name', 'lang']);
+ App.languages = _.extend(r.languages, {
+ none: 'None'
+ });
+ _.map(App.qualityProfiles, function(profile) {
+ profile.language = App.languages[profile.lang];
+ });
+ App.repositories = r.repositories;
+ App.repositories.push(App.manualRepository());
+ App.statuses = r.statuses;
+ App.characteristics = r.characteristics.map(function (item, index) {
+ return _.extend(item, { index: index });
+ });
+ });
+
+ $.when(window.requestMessages(), appXHR).done(function () {
+ App.start();
+ });
+
+ });
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js
new file mode 100644
index 00000000000..bdd2cbb5f34
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js
@@ -0,0 +1,116 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['coding-rules-bulk-change-modal'],
+
+ ui: function () {
+ return _.extend(ModalFormView.prototype.ui.apply(this, arguments), {
+ codingRulesSubmitBulkChange: '#coding-rules-submit-bulk-change'
+ });
+ },
+
+ showSuccessMessage: function (profile, succeeded) {
+ var profileBase = _.findWhere(this.options.app.qualityProfiles, { key: profile }),
+ profileName = profileBase != null ? profileBase.name : profile,
+ message = tp('coding_rules.bulk_change.success', profileName, profileBase.language, succeeded);
+ this.ui.messagesContainer.append('<div class="alert alert-success">' + message + '</div>');
+ },
+
+ showWarnMessage: function (profile, succeeded, failed) {
+ var profileBase = _.findWhere(this.options.app.qualityProfiles, { key: profile }),
+ profileName = profileBase != null ? profileBase.name : profile,
+ message = tp('coding_rules.bulk_change.warning', profileName, profileBase.language, succeeded, failed);
+ this.ui.messagesContainer.append('<div class="alert alert-warning">' + message + '</div>');
+ },
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+ this.$('#coding-rules-bulk-change-profile').select2({
+ width: '250px',
+ minimumResultsForSearch: 1,
+ openOnEnter: false
+ });
+ },
+
+ onFormSubmit: function () {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ var url = baseUrl + '/api/qualityprofiles/' + this.options.action + '_rules',
+ options = _.extend({}, this.options.app.state.get('query'), { wsAction: this.options.action }),
+ profiles = this.$('#coding-rules-bulk-change-profile').val() || [this.options.param];
+ this.ui.messagesContainer.empty();
+ this.sendRequests(url, options, profiles);
+ },
+
+ sendRequests: function (url, options, profiles) {
+ var that = this,
+ looper = $.Deferred().resolve();
+ profiles.forEach(function (profile) {
+ var opts = _.extend({}, options, { profile_key: profile });
+ looper = looper.then(function () {
+ return $.post(url, opts).done(function (r) {
+ if (r.failed) {
+ that.showWarnMessage(profile, r.succeeded, r.failed);
+ } else {
+ that.showSuccessMessage(profile, r.succeeded);
+ }
+ });
+ });
+ });
+ looper.done(function () {
+ that.options.app.controller.fetchList();
+ that.$(that.ui.codingRulesSubmitBulkChange.selector).hide();
+ that.$('.modal-field').hide();
+ that.$('.js-modal-close').focus();
+ });
+ },
+
+ getAvailableQualityProfiles: function () {
+ var queryLanguages = this.options.app.state.get('query').languages,
+ languages = queryLanguages && queryLanguages.length > 0 ? queryLanguages.split(',') : [],
+ profiles = this.options.app.qualityProfiles;
+ if (languages.length > 0) {
+ profiles = _.filter(profiles, function (profile) {
+ return languages.indexOf(profile.lang) !== -1;
+ });
+ }
+ return profiles;
+ },
+
+ serializeData: function () {
+ var profile = _.findWhere(this.options.app.qualityProfiles, { key: this.options.param });
+ return _.extend(ModalFormView.prototype.serializeData.apply(this, arguments), {
+ action: this.options.action,
+ state: this.options.app.state.toJSON(),
+ qualityProfile: this.options.param,
+ qualityProfileName: profile != null ? profile.name : null,
+ qualityProfiles: this.options.app.qualityProfiles,
+ availableQualityProfiles: this.getAvailableQualityProfiles()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js
new file mode 100644
index 00000000000..be418ea4540
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/popup',
+ './bulk-change-modal-view',
+ './templates'
+], function (PopupView, BulkChangeModalView) {
+
+ var $ = jQuery;
+
+ return PopupView.extend({
+ template: Templates['coding-rules-bulk-change-popup'],
+
+ events: {
+ 'click .js-bulk-change': 'doAction'
+ },
+
+ doAction: function (e) {
+ var action = $(e.currentTarget).data('action'),
+ param = $(e.currentTarget).data('param');
+ new BulkChangeModalView({
+ app: this.options.app,
+ action: action,
+ param: param
+ }).render();
+ },
+
+ serializeData: function () {
+ var query = this.options.app.state.get('query'),
+ profileKey = query.qprofile,
+ profile = _.findWhere(this.options.app.qualityProfiles, { key: profileKey }),
+ activation = '' + query.activation;
+
+ return {
+ qualityProfile: profileKey,
+ qualityProfileName: profile != null ? profile.name : null,
+ allowActivateOnProfile: profileKey != null && activation === 'false',
+ allowDeactivateOnProfile: profileKey != null && activation === 'true'
+ };
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/controller.js b/server/sonar-web/src/main/js/apps/coding-rules/controller.js
new file mode 100644
index 00000000000..5ee32475e99
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/controller.js
@@ -0,0 +1,163 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/controller',
+ './models/rule',
+ './rule-details-view'
+], function (Controller, Rule, RuleDetailsView) {
+
+ var $ = jQuery;
+
+ return Controller.extend({
+ pageSize: 200,
+ ruleFields: ['name', 'lang', 'langName', 'sysTags', 'tags', 'status', 'severity',
+ 'debtChar', 'debtCharName', 'debtSubChar', 'debtSubCharName'],
+
+
+ _searchParameters: function () {
+ var fields = this.ruleFields.slice(),
+ profile = this.app.state.get('query').qprofile;
+ if (profile != null) {
+ fields.push('actives');
+ fields.push('params');
+ fields.push('isTemplate');
+ fields.push('severity');
+ }
+ var params = {
+ p: this.app.state.get('page'),
+ ps: this.pageSize,
+ facets: this._facetsFromServer().join(),
+ f: fields.join()
+ };
+ if (this.app.state.get('query').q == null) {
+ _.extend(params, { s: 'name', asc: true });
+ }
+ return params;
+ },
+
+ fetchList: function (firstPage) {
+ firstPage = firstPage == null ? true : firstPage;
+ if (firstPage) {
+ this.app.state.set({ selectedIndex: 0, page: 1 }, { silent: true });
+ }
+
+ this.hideDetails(firstPage);
+
+ var that = this,
+ url = baseUrl + '/api/rules/search',
+ options = _.extend(this._searchParameters(), this.app.state.get('query'));
+ return $.get(url, options).done(function (r) {
+ var rules = that.app.list.parseRules(r);
+ if (firstPage) {
+ that.app.list.reset(rules);
+ } else {
+ 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());
+ that.app.state.set({
+ page: r.p,
+ pageSize: r.ps,
+ total: r.total,
+ maxResultsReached: r.p * r.ps >= r.total
+ });
+ if (firstPage && that.isRulePermalink()) {
+ that.showDetails(that.app.list.first());
+ }
+ });
+ },
+
+ isRulePermalink: function () {
+ var query = this.app.state.get('query');
+ return query.rule_key != null && this.app.list.length === 1;
+ },
+
+ requestFacet: function (id) {
+ var url = baseUrl + '/api/rules/search',
+ facet = this.app.facets.get(id),
+ options = _.extend({ facets: id, ps: 1 }, this.app.state.get('query'));
+ return $.get(url, options).done(function (r) {
+ var facetData = _.findWhere(r.facets, { property: id });
+ if (facetData) {
+ facet.set(facetData);
+ }
+ });
+ },
+
+ parseQuery: function () {
+ var q = Controller.prototype.parseQuery.apply(this, arguments);
+ delete q.asc;
+ delete q.s;
+ return q;
+ },
+
+ getRuleDetails: function (rule) {
+ 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.repositories);
+ });
+ },
+
+ showDetails: function (rule) {
+ var that = this,
+ ruleModel = typeof rule === 'string' ? new Rule({ key: rule }) : rule;
+ this.app.layout.workspaceDetailsRegion.reset();
+ this.getRuleDetails(ruleModel).done(function (data) {
+ key.setScope('details');
+ that.app.workspaceListView.unbindScrollEvents();
+ that.app.state.set({ rule: ruleModel });
+ that.app.workspaceDetailsView = new RuleDetailsView({
+ app: that.app,
+ model: ruleModel,
+ actives: data.actives
+ });
+ that.app.layout.showDetails();
+ that.app.layout.workspaceDetailsRegion.show(that.app.workspaceDetailsView);
+ });
+ },
+
+ showDetailsForSelected: function () {
+ var rule = this.app.list.at(this.app.state.get('selectedIndex'));
+ this.showDetails(rule);
+ },
+
+ hideDetails: function (firstPage) {
+ key.setScope('list');
+ this.app.state.unset('rule');
+ this.app.layout.workspaceDetailsRegion.reset();
+ this.app.layout.hideDetails();
+ this.app.workspaceListView.bindScrollEvents();
+ if (firstPage) {
+ this.app.workspaceListView.scrollTo();
+ }
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets-view.js b/server/sonar-web/src/main/js/apps/coding-rules/facets-view.js
new file mode 100644
index 00000000000..b9c2a7f4921
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets-view.js
@@ -0,0 +1,78 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/facets-view',
+ './facets/base-facet',
+ './facets/query-facet',
+ './facets/key-facet',
+ './facets/language-facet',
+ './facets/repository-facet',
+ './facets/tag-facet',
+ './facets/quality-profile-facet',
+ './facets/characteristic-facet',
+ './facets/severity-facet',
+ './facets/status-facet',
+ './facets/available-since-facet',
+ './facets/inheritance-facet',
+ './facets/active-severity-facet',
+ './facets/template-facet'
+ ],
+ function (FacetsView,
+ BaseFacet,
+ QueryFacet,
+ KeyFacet,
+ LanguageFacet,
+ RepositoryFacet,
+ TagFacet,
+ QualityProfileFacet,
+ CharacteristicFacet,
+ SeverityFacet,
+ StatusFacet,
+ AvailableSinceFacet,
+ InheritanceFacet,
+ ActiveSeverityFacet,
+ TemplateFacet) {
+
+ var viewsMapping = {
+ q: QueryFacet,
+ rule_key: KeyFacet,
+ languages: LanguageFacet,
+ repositories: RepositoryFacet,
+ tags: TagFacet,
+ qprofile: QualityProfileFacet,
+ debt_characteristics: CharacteristicFacet,
+ severities: SeverityFacet,
+ statuses: StatusFacet,
+ available_since: AvailableSinceFacet,
+ inheritance: InheritanceFacet,
+ active_severities: ActiveSeverityFacet,
+ is_template: TemplateFacet
+ };
+
+ return FacetsView.extend({
+
+ getItemView: function (model) {
+ var view = viewsMapping[model.get('property')];
+ return view ? view : BaseFacet;
+ }
+
+ });
+
+ });
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/active-severity-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/active-severity-facet.js
new file mode 100644
index 00000000000..6695943968d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/active-severity-facet.js
@@ -0,0 +1,65 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-severity-facet'],
+ severities: ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR'],
+
+ initialize: function (options) {
+ this.listenTo(options.app.state, 'change:query', this.onQueryChange);
+ },
+
+ onQueryChange: function () {
+ var query = this.options.app.state.get('query'),
+ isProfileSelected = query.qprofile != null,
+ isActiveShown = '' + query.activation === 'true';
+ if (!isProfileSelected || !isActiveShown) {
+ this.forbid();
+ }
+ },
+
+ onRender: function () {
+ BaseFacet.prototype.onRender.apply(this, arguments);
+ this.onQueryChange();
+ },
+
+ forbid: function () {
+ BaseFacet.prototype.forbid.apply(this, arguments);
+ this.$el.prop('title', t('coding_rules.filters.active_severity.inactive'));
+ },
+
+ allow: function () {
+ BaseFacet.prototype.allow.apply(this, arguments);
+ this.$el.prop('title', null);
+ },
+
+ sortValues: function (values) {
+ var order = this.severities;
+ return _.sortBy(values, function (v) {
+ return order.indexOf(v.val);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/available-since-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/available-since-facet.js
new file mode 100644
index 00000000000..b67e07d205b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/available-since-facet.js
@@ -0,0 +1,61 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-available-since-facet'],
+
+ events: function () {
+ return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+ 'change input': 'applyFacet'
+ });
+ },
+
+ onRender: function () {
+ this.$el.toggleClass('search-navigator-facet-box-collapsed', !this.model.get('enabled'));
+ this.$el.attr('data-property', this.model.get('property'));
+ this.$('input').datepicker({
+ dateFormat: 'yy-mm-dd',
+ changeMonth: true,
+ changeYear: true
+ });
+ var value = this.options.app.state.get('query').available_since;
+ if (value) {
+ this.$('input').val(value);
+ }
+ },
+
+ applyFacet: function() {
+ var obj = {},
+ property = this.model.get('property');
+ obj[property] = this.$('input').val();
+ this.options.app.state.updateFilter(obj);
+ },
+
+ getLabelsSource: function () {
+ return this.options.app.languages;
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/base-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/base-facet.js
new file mode 100644
index 00000000000..04da173a760
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/base-facet.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/facets/base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ className: 'search-navigator-facet-box',
+ template: Templates['coding-rules-base-facet']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/characteristic-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/characteristic-facet.js
new file mode 100644
index 00000000000..7cd9d884257
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/characteristic-facet.js
@@ -0,0 +1,85 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ var $ = jQuery;
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-characteristic-facet'],
+
+ onRender: function () {
+ BaseFacet.prototype.onRender.apply(this, arguments);
+ var value = this.options.app.state.get('query').has_debt_characteristic;
+ if (value != null && ('' + value === 'false')) {
+ this.$('.js-facet').filter('[data-empty-characteristic]').addClass('active');
+ }
+ },
+
+ toggleFacet: function (e) {
+ var noneCharacteristic = $(e.currentTarget).is('[data-empty-characteristic]'),
+ property = this.model.get('property'),
+ obj = {};
+ $(e.currentTarget).toggleClass('active');
+ if (noneCharacteristic) {
+ var checked = $(e.currentTarget).is('.active');
+ obj.has_debt_characteristic = checked ? 'false' : null;
+ obj[property] = null;
+ } else {
+ obj.has_debt_characteristic = null;
+ obj[property] = this.getValue();
+ }
+ this.options.app.state.updateFilter(obj);
+ },
+
+ disable: function () {
+ var property = this.model.get('property'),
+ obj = {};
+ obj.has_debt_characteristic = null;
+ obj[property] = null;
+ this.options.app.state.updateFilter(obj);
+ },
+
+ getValues: function () {
+ var values = this.model.getValues(),
+ characteristics = this.options.app.characteristics;
+ return values.map(function (value) {
+ var ch = _.findWhere(characteristics, { key: value.val });
+ if (ch != null) {
+ _.extend(value, ch, { label: ch.name });
+ }
+ return value;
+ });
+ },
+
+ sortValues: function (values) {
+ return _.sortBy(values, 'index');
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.sortValues(this.getValues())
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-labels-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-labels-facet.js
new file mode 100644
index 00000000000..da7d1949a18
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-labels-facet.js
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+
+ getLabelsSource: function () {
+ return [];
+ },
+
+ getValues: function () {
+ var that = this,
+ labels = that.getLabelsSource();
+ return this.model.getValues().map(function (item) {
+ return _.extend(item, {
+ label: labels[item.val]
+ });
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.getValues()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-values-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-values-facet.js
new file mode 100644
index 00000000000..7aad57ad48c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/custom-values-facet.js
@@ -0,0 +1,89 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-custom-values-facet'],
+
+ events: function () {
+ return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+ 'change .js-custom-value': 'addCustomValue'
+ });
+ },
+
+ getUrl: function () {
+ return baseUrl;
+ },
+
+ onRender: function () {
+ BaseFacet.prototype.onRender.apply(this, arguments);
+ this.prepareSearch();
+ },
+
+ prepareSearch: function () {
+ this.$('.js-custom-value').select2({
+ placeholder: t('search_verb'),
+ minimumInputLength: 1,
+ allowClear: false,
+ formatNoMatches: function () {
+ return t('select2.noMatches');
+ },
+ formatSearching: function () {
+ return t('select2.searching');
+ },
+ formatInputTooShort: function () {
+ return tp('select2.tooShort', 1);
+ },
+ width: '100%',
+ ajax: this.prepareAjaxSearch()
+ });
+ },
+
+ prepareAjaxSearch: function () {
+ return {
+ quietMillis: 300,
+ url: this.getUrl(),
+ data: function (term, page) {
+ return { s: term, p: page };
+ },
+ results: function (data) {
+ return { more: data.more, results: data.results };
+ }
+ };
+ },
+
+ addCustomValue: function () {
+ var property = this.model.get('property'),
+ customValue = this.$('.js-custom-value').select2('val'),
+ value = this.getValue();
+ if (value.length > 0) {
+ value += ',';
+ }
+ value += customValue;
+ var obj = {};
+ obj[property] = value;
+ this.options.app.state.updateFilter(obj);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/inheritance-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/inheritance-facet.js
new file mode 100644
index 00000000000..36f56294967
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/inheritance-facet.js
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet'
+], function (BaseFacet) {
+
+ var $ = jQuery;
+
+ return BaseFacet.extend({
+
+ initialize: function (options) {
+ this.listenTo(options.app.state, 'change:query', this.onQueryChange);
+ },
+
+ onQueryChange: function () {
+ var query = this.options.app.state.get('query'),
+ isProfileSelected = query.qprofile != null;
+ if (isProfileSelected) {
+ var profile = _.findWhere(this.options.app.qualityProfiles, { key: query.qprofile });
+ if (profile != null && profile.parentKey == null) {
+ this.forbid();
+ }
+ } else {
+ this.forbid();
+ }
+ },
+
+ onRender: function () {
+ BaseFacet.prototype.onRender.apply(this, arguments);
+ this.onQueryChange();
+ },
+
+ forbid: function () {
+ BaseFacet.prototype.forbid.apply(this, arguments);
+ this.$el.prop('title', t('coding_rules.filters.inheritance.inactive'));
+ },
+
+ allow: function () {
+ BaseFacet.prototype.allow.apply(this, arguments);
+ this.$el.prop('title', null);
+ },
+
+ getValues: function () {
+ var values = ['NONE', 'INHERITED', 'OVERRIDES'];
+ return values.map(function (key) {
+ return {
+ label: t('coding_rules.filters.inheritance', key.toLowerCase()),
+ val: key
+ };
+ });
+ },
+
+ toggleFacet: function (e) {
+ var obj = {},
+ property = this.model.get('property');
+ if ($(e.currentTarget).is('.active')) {
+ obj[property] = null;
+ } else {
+ obj[property] = $(e.currentTarget).data('value');
+ }
+ this.options.app.state.updateFilter(obj);
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.getValues()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/key-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/key-facet.js
new file mode 100644
index 00000000000..07542add062
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/key-facet.js
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-key-facet'],
+
+ onRender: function () {
+ this.$el.toggleClass('hidden', !this.options.app.state.get('query').rule_key);
+ },
+
+ disable: function () {
+ this.options.app.state.updateFilter({ rule_key: null });
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ key: this.options.app.state.get('query').rule_key
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/language-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/language-facet.js
new file mode 100644
index 00000000000..478c589032c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/language-facet.js
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './custom-values-facet'
+], function (CustomValuesFacet) {
+
+ return CustomValuesFacet.extend({
+
+ getUrl: function () {
+ return baseUrl + '/api/languages/list';
+ },
+
+ prepareAjaxSearch: function () {
+ return {
+ quietMillis: 300,
+ url: this.getUrl(),
+ data: function (term) {
+ return { q: term, ps: 10000 };
+ },
+ results: function (data) {
+ return {
+ more: false,
+ results: data.languages.map(function (lang) {
+ return { id: lang.key, text: lang.name };
+ })
+ };
+ }
+ };
+ },
+
+ getLabelsSource: function () {
+ return this.options.app.languages;
+ },
+
+ getValues: function () {
+ var that = this,
+ labels = that.getLabelsSource();
+ return this.model.getValues().map(function (item) {
+ return _.extend(item, {
+ label: labels[item.val]
+ });
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.getValues()
+ });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js
new file mode 100644
index 00000000000..b7af11147e4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js
@@ -0,0 +1,99 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ var $ = jQuery;
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-quality-profile-facet'],
+
+ events: function () {
+ return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+ 'click .js-active': 'setActivation',
+ 'click .js-inactive': 'unsetActivation'
+ });
+ },
+
+ getValues: function () {
+ var that = this,
+ languagesQuery = this.options.app.state.get('query').languages,
+ languages = languagesQuery != null ? languagesQuery.split(',') : [],
+ lang = languages.length === 1 ? languages[0] : null,
+ values = this.options.app.qualityProfiles
+ .filter(function (profile) {
+ return lang != null ? profile.lang === lang : true;
+ })
+ .map(function (profile) {
+ return {
+ label: profile.name,
+ extra: that.options.app.languages[profile.lang],
+ val: profile.key
+ };
+ });
+ return _.sortBy(values, 'label');
+ },
+
+ toggleFacet: function (e) {
+ var obj = {},
+ property = this.model.get('property');
+ if ($(e.currentTarget).is('.active')) {
+ obj.activation = null;
+ obj[property] = null;
+ } else {
+ obj.activation = true;
+ obj[property] = $(e.currentTarget).data('value');
+ }
+ this.options.app.state.updateFilter(obj);
+ },
+
+ setActivation: function (e) {
+ e.stopPropagation();
+ this.options.app.state.updateFilter({ activation: 'true' });
+ },
+
+ unsetActivation: function (e) {
+ e.stopPropagation();
+ this.options.app.state.updateFilter({ activation: 'false' });
+ },
+
+ getToggled: function () {
+ var activation = this.options.app.state.get('query').activation;
+ return activation === 'true' || activation === true;
+ },
+
+ disable: function () {
+ var obj = { activation: null },
+ property = this.model.get('property');
+ obj[property] = null;
+ this.options.app.state.updateFilter(obj);
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.getValues(),
+ toggled: this.getToggled()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/query-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/query-facet.js
new file mode 100644
index 00000000000..03431cd76cd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/query-facet.js
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-query-facet'],
+
+ events: function () {
+ return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+ 'submit form': 'onFormSubmit'
+ });
+ },
+
+ onRender: function () {
+ this.$el.attr('data-property', this.model.get('property'));
+ var query = this.options.app.state.get('query'),
+ value = query.q;
+ if (value != null) {
+ this.$('input').val(value);
+ }
+ },
+
+ onFormSubmit: function (e) {
+ e.preventDefault();
+ this.applyFacet();
+ },
+
+ applyFacet: function() {
+ var obj = {},
+ property = this.model.get('property');
+ obj[property] = this.$('input').val();
+ this.options.app.state.updateFilter(obj, { force: true });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/repository-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/repository-facet.js
new file mode 100644
index 00000000000..08283dc5b18
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/repository-facet.js
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './custom-values-facet'
+], function (CustomValuesFacet) {
+
+ return CustomValuesFacet.extend({
+
+ getUrl: function () {
+ return baseUrl + '/api/rules/repositories';
+ },
+
+ prepareAjaxSearch: function () {
+ return {
+ quietMillis: 300,
+ url: this.getUrl(),
+ data: function (term) {
+ return { q: term, ps: 10000 };
+ },
+ results: function (data) {
+ return {
+ more: false,
+ results: data.repositories.map(function (repo) {
+ return { id: repo.key, text: repo.name + ' (' + repo.language + ')' };
+ })
+ };
+ }
+ };
+ },
+
+ getLabelsSource: function () {
+ var repos = this.options.app.repositories;
+ return _.object(_.pluck(repos, 'key'), _.pluck(repos, 'name'));
+ },
+
+ getValues: function () {
+ var that = this,
+ labels = that.getLabelsSource();
+ return this.model.getValues().map(function (value) {
+ var repo = _.findWhere(that.options.app.repositories, { key: value.val });
+ if (repo != null) {
+ var langName = that.options.app.languages[repo.language];
+ _.extend(value, { extra: langName });
+ }
+ return _.extend(value, { label: labels[value.val] });
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.getValues()
+ });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/severity-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/severity-facet.js
new file mode 100644
index 00000000000..54f71f86691
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/severity-facet.js
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-severity-facet'],
+ severities: ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR'],
+
+ sortValues: function (values) {
+ var order = this.severities;
+ return _.sortBy(values, function (v) {
+ return order.indexOf(v.val);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/status-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/status-facet.js
new file mode 100644
index 00000000000..96854e47564
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/status-facet.js
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet'
+], function (BaseFacet) {
+
+ return BaseFacet.extend({
+ statuses: ['READY', 'DEPRECATED', 'BETA'],
+
+ getValues: function () {
+ var values = this.model.getValues();
+ var x = values.map(function (value) {
+ return _.extend(value, { label: t('rules.status', value.val.toLowerCase()) });
+ });
+ return x;
+ },
+
+ sortValues: function (values) {
+ var order = this.statuses;
+ return _.sortBy(values, function (v) {
+ return order.indexOf(v.val);
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+ values: this.sortValues(this.getValues())
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/tag-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/tag-facet.js
new file mode 100644
index 00000000000..9a5b0f21570
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/tag-facet.js
@@ -0,0 +1,50 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './custom-values-facet'
+], function (CustomValuesFacet) {
+
+ return CustomValuesFacet.extend({
+
+ getUrl: function () {
+ return baseUrl + '/api/rules/tags';
+ },
+
+ prepareAjaxSearch: function () {
+ return {
+ quietMillis: 300,
+ url: this.getUrl(),
+ data: function (term) {
+ return { q: term, ps: 10000 };
+ },
+ results: function (data) {
+ return {
+ more: false,
+ results: data.tags.map(function (tag) {
+ return { id: tag, text: tag };
+ })
+ };
+ }
+ };
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/template-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/template-facet.js
new file mode 100644
index 00000000000..118913333d2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/template-facet.js
@@ -0,0 +1,52 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './base-facet',
+ '../templates'
+], function (BaseFacet) {
+
+ var $ = jQuery;
+
+ return BaseFacet.extend({
+ template: Templates['coding-rules-template-facet'],
+
+ onRender: function () {
+ BaseFacet.prototype.onRender.apply(this, arguments);
+ var value = this.options.app.state.get('query').is_template;
+ if (value != null) {
+ this.$('.js-facet').filter('[data-value="' + value + '"]').addClass('active');
+ }
+ },
+
+ toggleFacet: function (e) {
+ $(e.currentTarget).toggleClass('active');
+ var property = this.model.get('property'),
+ obj = {};
+ if ($(e.currentTarget).hasClass('active')) {
+ obj[property] = '' + $(e.currentTarget).data('value');
+ } else {
+ obj[property] = null;
+ }
+ this.options.app.state.updateFilter(obj);
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/filters-view.js b/server/sonar-web/src/main/js/apps/coding-rules/filters-view.js
new file mode 100644
index 00000000000..835119e9a66
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/filters-view.js
@@ -0,0 +1,28 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['coding-rules-filters']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/layout.js b/server/sonar-web/src/main/js/apps/coding-rules/layout.js
new file mode 100644
index 00000000000..87ec56e9694
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/layout.js
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.Layout.extend({
+ template: Templates['coding-rules-layout'],
+
+ regions: {
+ filtersRegion: '.search-navigator-filters',
+ facetsRegion: '.search-navigator-facets',
+ workspaceHeaderRegion: '.search-navigator-workspace-header',
+ workspaceListRegion: '.search-navigator-workspace-list',
+ workspaceDetailsRegion: '.search-navigator-workspace-details'
+ },
+
+ onRender: function () {
+ var navigator = $('.search-navigator');
+ navigator.addClass('sticky');
+ var top = navigator.offset().top;
+ this.$('.search-navigator-workspace-header').css({ top: top });
+ this.$('.search-navigator-side').css({ top: top }).isolatedScroll();
+ },
+
+ showDetails: function () {
+ this.scroll = $(window).scrollTop();
+ $('.search-navigator').addClass('search-navigator-extended-view');
+ },
+
+
+ hideDetails: function () {
+ $('.search-navigator').removeClass('search-navigator-extended-view');
+ if (this.scroll != null) {
+ $(window).scrollTop(this.scroll);
+ }
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/models/rule.js b/server/sonar-web/src/main/js/apps/coding-rules/models/rule.js
new file mode 100644
index 00000000000..51993df4b98
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/models/rule.js
@@ -0,0 +1,38 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ return Backbone.Model.extend({
+ idAttribute: 'key',
+
+ addExtraAttributes: function (repositories) {
+ var 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({
+ repoName: repoName,
+ isManual: isManual,
+ isCustom: isCustom
+ }, { silent: true });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/models/rules.js b/server/sonar-web/src/main/js/apps/coding-rules/models/rules.js
new file mode 100644
index 00000000000..f1f83bcf1bd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/models/rules.js
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './rule'
+], function (Rule) {
+
+ return Backbone.Collection.extend({
+ model: Rule,
+
+ parseRules: function (r) {
+ var rules = r.rules,
+ profiles = r.qProfiles || [];
+
+ if (r.actives != null) {
+ rules = rules.map(function (rule) {
+ var activations = (r.actives[rule.key] || []).map(function (activation) {
+ var profile = profiles[activation.qProfile];
+ if (profile != null) {
+ _.extend(activation, { profile: profile });
+ if (profile.parent != null) {
+ _.extend(activation, { parentProfile: profiles[profile.parent] });
+ }
+ }
+ return activation;
+ });
+ return _.extend(rule, { activation: activations.length > 0 ? activations[0] : null });
+ });
+ }
+ return rules;
+ },
+
+ setIndex: function () {
+ this.forEach(function (rule, index) {
+ rule.set({ index: index });
+ });
+ },
+
+ addExtraAttributes: function (repositories) {
+ this.models.forEach(function (model) {
+ model.addExtraAttributes(repositories);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/models/state.js b/server/sonar-web/src/main/js/apps/coding-rules/models/state.js
new file mode 100644
index 00000000000..8c25312a2f1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/models/state.js
@@ -0,0 +1,41 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/models/state'
+], function (State) {
+
+ return State.extend({
+ defaults: {
+ page: 1,
+ maxResultsReached: false,
+ query: {},
+ facets: ['languages', 'tags'],
+ allFacets: ['q', 'rule_key', 'languages', 'tags', 'repositories', 'debt_characteristics', 'severities',
+ 'statuses', 'available_since', 'is_template', 'qprofile', 'inheritance', 'active_severities'],
+ facetsFromServer: ['languages', 'repositories', 'tags', 'severities', 'statuses', 'debt_characteristics',
+ 'active_severities'],
+ transform: {
+ 'has_debt_characteristic': 'debt_characteristics'
+ }
+ }
+ });
+
+});
+
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js
new file mode 100644
index 00000000000..941cfc42912
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js
@@ -0,0 +1,209 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './models/rules',
+ './rule/rule-meta-view',
+ './rule/rule-description-view',
+ './rule/rule-parameters-view',
+ './rule/rule-profiles-view',
+ './rule/custom-rules-view',
+ './rule/manual-rule-creation-view',
+ './rule/custom-rule-creation-view',
+ './rule/rule-issues-view',
+ './templates'
+ ],
+ function (Rules,
+ MetaView,
+ DescView,
+ ParamView,
+ ProfilesView,
+ CustomRulesView,
+ ManualRuleCreationView,
+ CustomRuleCreationView,
+ IssuesView) {
+
+ 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',
+ issuesRegion: '.js-rule-issues'
+ },
+
+ events: {
+ 'click .js-edit-manual': 'editManualRule',
+ 'click .js-edit-custom': 'editCustomRule',
+ 'click .js-delete': 'deleteRule'
+ },
+
+ initialize: function () {
+ this.bindShortcuts();
+ this.customRules = new Rules();
+ if (this.model.get('isTemplate')) {
+ this.fetchCustomRules();
+ }
+ this.listenTo(this.options.app.state, 'change:selectedIndex', this.select);
+ },
+
+ 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.issuesRegion.show(new IssuesView({
+ app: this.options.app,
+ model: this.model
+ }));
+ this.$el.scrollParent().scrollTop(0);
+ },
+
+ 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();
+ return false;
+ });
+ key('down', 'details', function () {
+ that.options.app.controller.selectNext();
+ return false;
+ });
+ key('left, backspace', '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();
+ },
+
+ editCustomRule: function () {
+ new CustomRuleCreationView({
+ 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 url = baseUrl + '/api/rules/delete',
+ options = { key: that.model.id };
+ $.post(url, options).done(function () {
+ that.options.app.controller.fetchList();
+ });
+ }
+ });
+ },
+
+ select: function () {
+ var selected = this.options.app.state.get('selectedIndex'),
+ selectedRule = this.options.app.list.at(selected);
+ this.options.app.controller.showDetails(selectedRule);
+ },
+
+ 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,
+ allTags: _.union(this.model.get('sysTags'), this.model.get('tags'))
+ });
+ }
+ });
+
+ });
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule-filter-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule-filter-view.js
new file mode 100644
index 00000000000..cbe6434e225
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule-filter-view.js
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/issue/views/action-options-view',
+ './templates'
+], function (ActionOptionsView) {
+ var $ = jQuery;
+
+ return ActionOptionsView.extend({
+ template: Templates['coding-rules-rule-filter-form'],
+
+ selectOption: function (e) {
+ var property = $(e.currentTarget).data('property'),
+ value = $(e.currentTarget).data('value');
+ this.trigger('select', property, value);
+ return ActionOptionsView.prototype.selectOption.apply(this, arguments);
+ },
+
+ serializeData: function () {
+ return _.extend(ActionOptionsView.prototype.serializeData.apply(this, arguments), {
+ tags: _.union(this.model.get('sysTags'), this.model.get('tags'))
+ });
+ }
+
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-creation-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-creation-view.js
new file mode 100644
index 00000000000..5c3829854a6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-creation-view.js
@@ -0,0 +1,210 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ '../templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['coding-rules-custom-rule-creation'],
+
+ ui: function () {
+ return _.extend(ModalFormView.prototype.ui.apply(this, arguments), {
+ customRuleCreationKey: '#coding-rules-custom-rule-creation-key',
+ customRuleCreationName: '#coding-rules-custom-rule-creation-name',
+ customRuleCreationHtmlDescription: '#coding-rules-custom-rule-creation-html-description',
+ customRuleCreationSeverity: '#coding-rules-custom-rule-creation-severity',
+ customRuleCreationStatus: '#coding-rules-custom-rule-creation-status',
+ customRuleCreationParameters: '[name]',
+ customRuleCreationCreate: '#coding-rules-custom-rule-creation-create',
+ customRuleCreationReactivate: '#coding-rules-custom-rule-creation-reactivate',
+ modalFoot: '.modal-foot'
+ });
+ },
+
+ events: function () {
+ return _.extend(ModalFormView.prototype.events.apply(this, arguments), {
+ 'input @ui.customRuleCreationName': 'generateKey',
+ 'keydown @ui.customRuleCreationName': 'generateKey',
+ 'keyup @ui.customRuleCreationName': 'generateKey',
+
+ 'input @ui.customRuleCreationKey': 'flagKey',
+ 'keydown @ui.customRuleCreationKey': 'flagKey',
+ 'keyup @ui.customRuleCreationKey': 'flagKey',
+
+ 'click #coding-rules-custom-rule-creation-cancel': 'close',
+ 'click @ui.customRuleCreationCreate': 'create',
+ 'click @ui.customRuleCreationReactivate': 'reactivate'
+ });
+ },
+
+ generateKey: function () {
+ if (!this.keyModifiedByUser && this.ui.customRuleCreationKey) {
+ var generatedKey = this.ui.customRuleCreationName.val().latinize().replace(/[^A-Za-z0-9]/g, '_');
+ this.ui.customRuleCreationKey.val(generatedKey);
+ }
+ },
+
+ flagKey: function () {
+ this.keyModifiedByUser = true;
+ },
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+
+ this.keyModifiedByUser = false;
+
+ var format = function (state) {
+ if (!state.id) {
+ return state.text;
+ } else {
+ return '<i class="icon-severity-' + state.id.toLowerCase() + '"></i> ' + state.text;
+ }
+ },
+ severity = (this.model && this.model.get('severity')) || this.options.templateRule.get('severity'),
+ status = (this.model && this.model.get('status')) || this.options.templateRule.get('status');
+
+ this.ui.customRuleCreationSeverity.val(severity);
+ this.ui.customRuleCreationSeverity.select2({
+ width: '250px',
+ minimumResultsForSearch: 999,
+ formatResult: format,
+ formatSelection: format
+ });
+
+ this.ui.customRuleCreationStatus.val(status);
+ this.ui.customRuleCreationStatus.select2({
+ width: '250px',
+ minimumResultsForSearch: 999
+ });
+ },
+
+ create: function (e) {
+ e.preventDefault();
+ var action = (this.model && this.model.has('key')) ? 'update' : 'create',
+ options = {
+ name: this.ui.customRuleCreationName.val(),
+ markdown_description: this.ui.customRuleCreationHtmlDescription.val(),
+ severity: this.ui.customRuleCreationSeverity.val(),
+ status: this.ui.customRuleCreationStatus.val()
+ };
+ if (this.model && this.model.has('key')) {
+ options.key = this.model.get('key');
+ } else {
+ _.extend(options, {
+ template_key: this.options.templateRule.get('key'),
+ custom_key: this.ui.customRuleCreationKey.val(),
+ prevent_reactivation: true
+ });
+ }
+ var params = this.ui.customRuleCreationParameters.map(function () {
+ var node = $(this),
+ value = node.val();
+ if (!value && action === 'create') {
+ value = node.prop('placeholder') || '';
+ }
+ return {
+ key: node.prop('name'),
+ value: value
+ };
+ }).get();
+ options.params = params.map(function (param) {
+ return param.key + '=' + window.csvEscape(param.value);
+ }).join(';');
+ this.sendRequest(action, options);
+ },
+
+ reactivate: function () {
+ var options = {
+ name: this.existingRule.name,
+ markdown_description: this.existingRule.mdDesc,
+ severity: this.existingRule.severity,
+ status: this.existingRule.status,
+ template_key: this.existingRule.templateKey,
+ custom_key: this.ui.customRuleCreationKey.val(),
+ prevent_reactivation: false
+ },
+ params = this.existingRule.params;
+ options.params = params.map(function (param) {
+ return param.key + '=' + param.defaultValue;
+ }).join(';');
+ this.sendRequest('create', options);
+ },
+
+ sendRequest: function (action, options) {
+ this.$('.alert').addClass('hidden');
+ var that = this,
+ url = baseUrl + '/api/rules/' + action;
+ return $.ajax({
+ url: url,
+ type: 'POST',
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ if (that.options.templateRule) {
+ that.options.app.controller.showDetails(that.options.templateRule);
+ } else {
+ that.options.app.controller.showDetails(that.model);
+ }
+ that.close();
+ }).fail(function (jqXHR) {
+ if (jqXHR.status === 409) {
+ that.existingRule = jqXHR.responseJSON.rule;
+ that.showErrors([], [{ msg: t('coding_rules.reactivate.help') }]);
+ that.ui.customRuleCreationCreate.addClass('hidden');
+ that.ui.customRuleCreationReactivate.removeClass('hidden');
+ } else {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ }
+ });
+ },
+
+ serializeData: function () {
+ var params = {};
+ if (this.options.templateRule) {
+ params = this.options.templateRule.get('params');
+ } else if (this.model && this.model.has('params')) {
+ params = this.model.get('params').map(function (p) {
+ return _.extend(p, { value: p.defaultValue });
+ });
+ }
+
+ var statuses = ['READY', 'BETA', 'DEPRECATED'].map(function (status) {
+ return {
+ id: status,
+ text: t('rules.status', status.toLowerCase())
+ };
+ });
+
+ return _.extend(ModalFormView.prototype.serializeData.apply(this, arguments), {
+ change: this.model && this.model.has('key'),
+ params: params,
+ severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'],
+ statuses: statuses
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js
new file mode 100644
index 00000000000..98d9634516e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ '../templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ tagName: 'tr',
+ template: Templates['coding-rules-custom-rule'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click .js-delete-custom-rule': 'deleteRule'
+ },
+
+ deleteRule: function () {
+ var that = this;
+ window.confirmDialog({
+ title: t('delete'),
+ html: t('are_you_sure'),
+ yesHandler: function () {
+ var url = baseUrl + '/api/rules/delete',
+ options = { key: that.model.id };
+ $.post(url, options).done(function () {
+ that.model.collection.remove(that.model);
+ that.close();
+ });
+ }
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canWrite: this.options.app.canWrite,
+ templateRule: this.options.templateRule,
+ permalink: baseUrl + '/coding_rules/#rule_key=' + encodeURIComponent(this.model.id)
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js
new file mode 100644
index 00000000000..3785b8ed6b6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './custom-rule-view',
+ './custom-rule-creation-view',
+ '../templates'
+], function (CustomRuleView, CustomRuleCreationView) {
+
+ return Marionette.CompositeView.extend({
+ template: Templates['coding-rules-custom-rules'],
+ itemView: CustomRuleView,
+ itemViewContainer: '#coding-rules-detail-custom-rules',
+
+ itemViewOptions: function () {
+ return {
+ app: this.options.app,
+ templateRule: this.model
+ };
+ },
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click .js-create-custom-rule': 'createCustomRule'
+ },
+
+ onRender: function () {
+ this.$el.toggleClass('hidden', !this.model.get('isTemplate'));
+ },
+
+ createCustomRule: function () {
+ new CustomRuleCreationView({
+ app: this.options.app,
+ templateRule: this.model
+ }).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/apps/coding-rules/rule/manual-rule-creation-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/manual-rule-creation-view.js
new file mode 100644
index 00000000000..6417beb3c8a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/manual-rule-creation-view.js
@@ -0,0 +1,138 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ '../templates'
+], function (ModalFormView) {
+
+ 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;
+ this.ui.manualRuleCreationReactivate.addClass('hidden');
+ },
+
+ 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;
+ },
+
+ 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) {
+ var that = this,
+ url = baseUrl + '/api/rules/' + action;
+ return $.ajax({
+ url: url,
+ type: 'POST',
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).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.showErrors([], [{ msg: t('coding_rules.reactivate.help') }]);
+ that.ui.manualRuleCreationCreate.addClass('hidden');
+ that.ui.manualRuleCreationReactivate.removeClass('hidden');
+ } else {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ }
+ });
+ },
+
+ 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/apps/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js
new file mode 100644
index 00000000000..5381c6299bc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js
@@ -0,0 +1,156 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modals',
+ '../templates'
+], function (Modal) {
+
+ 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 that = this,
+ format = function (state) {
+ if (!state.id) {
+ return state.text;
+ } else {
+ return '<i class="icon-severity-' + state.id.toLowerCase() + '"></i> ' + 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
+ });
+ setTimeout(function () {
+ that.$('a').first().focus();
+ }, 0);
+ },
+
+ activate: function (e) {
+ e.preventDefault();
+ var that = this,
+ 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
+ },
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ that.trigger('profileActivated', severity, params);
+ }).fail(function () {
+ that.trigger('profileActivationFailed');
+ });
+ },
+
+ getAvailableQualityProfiles: function (lang) {
+ var activeQualityProfiles = this.collection || new Backbone.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/apps/coding-rules/rule/rule-description-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js
new file mode 100644
index 00000000000..c84213c614d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ '../templates'
+], function () {
+
+ 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;
+ 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();
+ }).fail(function () {
+ that.render();
+ });
+ },
+
+ 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/apps/coding-rules/rule/rule-filter-mixin.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-filter-mixin.js
new file mode 100644
index 00000000000..839e1743af0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-filter-mixin.js
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ '../rule-filter-view'
+], function (RuleFilterView) {
+
+ var $ = jQuery;
+
+ return {
+ onRuleFilterClick: function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ $('body').click();
+ var that = this,
+ popup = new RuleFilterView({
+ triggerEl: $(e.currentTarget),
+ bottomRight: true,
+ model: this.model
+ });
+ popup.on('select', function (property, value) {
+ var obj = {};
+ obj[property] = '' + value;
+ that.options.app.state.updateFilter(obj);
+ popup.close();
+ });
+ popup.render();
+ }
+ };
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js
new file mode 100644
index 00000000000..e25e8be81de
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ '../templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ template: Templates['coding-rules-rule-issues'],
+
+ initialize: function () {
+ var that = this;
+ this.total = null;
+ this.projects = [];
+ this.requestIssues().done(function () {
+ that.render();
+ });
+ },
+
+ requestIssues: function () {
+ var that = this,
+ url = baseUrl + '/api/issues/search',
+ options = {
+ rules: this.model.id,
+ resolved: false,
+ ps: 1,
+ facets: 'projectUuids'
+ };
+ return $.get(url, options).done(function (r) {
+ var projectsFacet = _.findWhere(r.facets, { property: 'projectUuids' }),
+ projects = projectsFacet != null ? projectsFacet.values : [];
+ projects = projects.map(function (project) {
+ var projectBase = _.findWhere(r.projects, { uuid: project.val });
+ return _.extend(project, {
+ name: projectBase != null ? projectBase.longName : ''
+ });
+ });
+ that.projects = projects;
+ that.total = r.total;
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ total: this.total,
+ projects: this.projects,
+ baseSearchUrl: baseUrl + '/issues/search#resolved=false|rules=' + encodeURIComponent(this.model.id)
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js
new file mode 100644
index 00000000000..1a4cb4c3f7e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js
@@ -0,0 +1,115 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './rule-filter-mixin',
+ '../templates'
+], function (RuleFilterMixin) {
+
+ return Marionette.ItemView.extend(RuleFilterMixin).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',
+ 'click .js-rule-filter': 'onRuleFilterClick'
+ },
+
+ onRender: function () {
+ this.$('[data-toggle="tooltip"]').tooltip({
+ container: 'body'
+ });
+ },
+
+ onClose: function () {
+ this.$('[data-toggle="tooltip"]').tooltip('destroy');
+ },
+
+ 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');
+ that.ui.tagInput.select2('open');
+ });
+ },
+
+ 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,
+ 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();
+ }).always(function () {
+ that.cancelEdit();
+ });
+ },
+
+ 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')),
+ permalink: baseUrl + '/coding_rules#rule_key=' + encodeURIComponent(this.model.id)
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js
new file mode 100644
index 00000000000..2e5a927621d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ '../templates'
+], function () {
+
+ 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/apps/coding-rules/rule/rule-profile-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js
new file mode 100644
index 00000000000..42902e38c3c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js
@@ -0,0 +1,157 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './profile-activation-view',
+ '../templates'
+], function (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'
+ },
+
+ onRender: function () {
+ this.$('[data-toggle="tooltip"]').tooltip({
+ container: 'body'
+ });
+ },
+
+ change: function () {
+ var that = this,
+ activationView = new ProfileActivationView({
+ model: this.model,
+ collection: this.model.collection,
+ rule: this.options.rule,
+ app: this.options.app
+ });
+ activationView.on('profileActivated', function () {
+ that.options.app.controller.showDetails(that.options.rule);
+ });
+ activationView.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 () {
+ 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 () {
+ that.options.app.controller.showDetails(that.options.rule);
+ });
+ }
+ });
+ },
+
+ deactivate: function () {
+ var that = this,
+ ruleKey = this.options.rule.get('key');
+ window.confirmDialog({
+ title: t('coding_rules.deactivate'),
+ html: tp('coding_rules.deactivate.confirm'),
+ yesHandler: function () {
+ return jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/qualityprofiles/deactivate_rule',
+ data: {
+ profile_key: that.model.get('qProfile'),
+ rule_key: ruleKey
+ }
+ }).done(function () {
+ 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/apps/coding-rules/rule/rule-profiles-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js
new file mode 100644
index 00000000000..7c3dc3df275
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './rule-profile-view',
+ './profile-activation-view',
+ '../templates'
+], function (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'
+ },
+
+ onRender: function () {
+ var isManual = this.model.get('isManual'),
+ qualityProfilesVisible = !isManual;
+
+ if (qualityProfilesVisible) {
+ if (this.model.get('isTemplate')) {
+ qualityProfilesVisible = this.collection.length > 0;
+ }
+ else {
+ qualityProfilesVisible = (this.options.app.canWrite || this.collection.length > 0);
+ }
+ }
+
+ this.$el.toggleClass('hidden', !qualityProfilesVisible);
+ },
+
+ activate: function () {
+ var that = this,
+ activationView = new ProfileActivationView({
+ rule: this.model,
+ collection: this.collection,
+ app: this.options.app
+ });
+ activationView.on('profileActivated', function () {
+ that.options.app.controller.showDetails(that.model);
+ });
+ activationView.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/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs
new file mode 100644
index 00000000000..84f89974491
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs
@@ -0,0 +1,29 @@
+{{#each activeProfiles}}
+ {{severityIcon severity}}
+
+ {{#eq inherit 'OVERRIDES'}}
+ <i class="icon-inheritance" title="{{tp 'coding_rules.overrides' name parent.name}}"></i>
+ {{/eq}}
+ {{#eq inherit 'INHERITED'}}
+ <i class="icon-inheritance" title="{{tp 'coding_rules.inherits' name parent.name}}"></i>
+ {{/eq}}
+
+ {{#if ../canWrite}}
+ <div class="button-group">
+ {{#unless isTemplate}}
+ <button class="coding-rules-detail-quality-profile-change">{{t 'change_verb'}}</button>
+ {{/unless}}
+ {{#if parent}}
+ {{#eq inherit 'OVERRIDES'}}
+ <button class="coding-rules-detail-quality-profile-revert button-red">
+ {{t 'coding_rules.revert_to_parent_definition'}}
+ </button>
+ {{/eq}}
+ {{else}}
+ <button class="coding-rules-detail-quality-profile-deactivate button-red">
+ {{t 'coding_rules.deactivate'}}
+ </button>
+ {{/if}}
+ </div>
+ {{/if}}
+{{/each}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-modal.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-modal.hbs
new file mode 100644
index 00000000000..8816b6cb5af
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-modal.hbs
@@ -0,0 +1,41 @@
+<form>
+ <div class="modal-head">
+ {{#eq action 'activate'}}
+ <h2>{{t 'coding_rules.activate_in_quality_profile'}} ({{state.total}} {{t 'coding_rules._rules'}})</h2>
+ {{/eq}}
+ {{#eq action 'deactivate'}}
+ <h2>{{t 'coding_rules.deactivate_in_quality_profile'}} ({{state.total}} {{t 'coding_rules._rules'}})</h2>
+ {{/eq}}
+ </div>
+
+ <div class="modal-body modal-body-select2">
+ <div class="js-modal-messages"></div>
+
+ <div class="modal-field">
+ <h3>
+ <label for="coding-rules-bulk-change-profile">
+ {{#eq action 'activate'}}{{t 'coding_rules.activate_in'}}{{/eq}}
+ {{#eq action 'deactivate'}}{{t 'coding_rules.deactivate_in'}}{{/eq}}
+ </label>
+ </h3>
+ {{#if qualityProfile}}
+ <h3 class="readonly-field">
+ {{qualityProfileName}}{{#notEq action 'change-severity'}} — {{t 'are_you_sure'}}{{/notEq}}
+ </h3>
+ {{else}}
+ <select id="coding-rules-bulk-change-profile" multiple>
+ {{#each availableQualityProfiles}}
+ <option value="{{key}}" {{#ifLength ../availableQualityProfiles 1}}selected{{/ifLength}}>
+ {{name}} - {{language}}
+ </option>
+ {{/each}}
+ </select>
+ {{/if}}
+ </div>
+ </div>
+
+ <div class="modal-foot">
+ <button id="coding-rules-submit-bulk-change">{{t 'apply'}}</button>
+ <a class="js-modal-close" href="#">{{t 'close'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-popup.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-popup.hbs
new file mode 100644
index 00000000000..c5a4622e9f6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-bulk-change-popup.hbs
@@ -0,0 +1,41 @@
+<div class="bubble-popup-title">{{t 'bulk_change'}}</div>
+
+<ul class="bubble-popup-list">
+
+ {{! activation }}
+
+ <li>
+ <a class="js-bulk-change" data-action="activate">
+ {{t 'coding_rules.activate_in'}}&#8230;
+ </a>
+ </li>
+
+ {{#if allowActivateOnProfile}}
+ <li>
+ <a class="js-bulk-change" data-action="activate" data-param="{{qualityProfile}}">
+ {{t 'coding_rules.activate_in'}} <strong>{{qualityProfileName}}</strong>
+ </a>
+ </li>
+ {{/if}}
+
+
+
+ {{! deactivation }}
+
+ <li>
+ <a class="js-bulk-change" data-action="deactivate">
+ {{t 'coding_rules.deactivate_in'}}&#8230;
+ </a>
+ </li>
+
+ {{#if allowDeactivateOnProfile}}
+ <li>
+ <a class="js-bulk-change" data-action="deactivate" data-param="{{qualityProfile}}">
+ {{tp 'coding_rules.deactivate_in'}} <strong>{{qualityProfileName}}</strong>
+ </a>
+ </li>
+ {{/if}}
+</ul>
+
+
+<div class="bubble-popup-arrow"></div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-filters.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-filters.hbs
new file mode 100644
index 00000000000..e307b61b100
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-filters.hbs
@@ -0,0 +1,3 @@
+<h1 class="page-title">
+ {{t 'coding_rules.page'}}
+</h1>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-layout.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-layout.hbs
new file mode 100644
index 00000000000..8f46e4f8fb1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-layout.hbs
@@ -0,0 +1,10 @@
+<div class="search-navigator-side">
+ <div class="search-navigator-filters"></div>
+ <div class="search-navigator-facets"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header"></div>
+ <div class="search-navigator-workspace-list"></div>
+ <div class="search-navigator-workspace-details"></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-details.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-details.hbs
new file mode 100644
index 00000000000..f889780e6c6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-details.hbs
@@ -0,0 +1,20 @@
+<div class="js-rule-meta"></div>
+<div class="js-rule-description"></div>
+<div class="js-rule-parameters"></div>
+
+{{#if isEditable}}
+ <div class="coding-rules-detail-description">
+ <div class="button-group">
+ {{#if isManual}}
+ <button class="js-edit-manual" id="coding-rules-detail-manual-rule-change">{{t 'edit'}}</button>
+ {{else}}
+ <button class="js-edit-custom" id="coding-rules-detail-custom-rule-change">{{t 'edit'}}</button>
+ {{/if}}
+ <button class="button-red js-delete" id="coding-rules-detail-rule-delete" class="button-red">{{t 'delete'}}</button>
+ </div>
+ </div>
+{{/if}}
+
+<div class="js-rule-custom-rules coding-rule-section"></div>
+<div class="js-rule-profiles coding-rule-section"></div>
+<div class="js-rule-issues coding-rule-section"></div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-filter-form.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-filter-form.hbs
new file mode 100644
index 00000000000..35f896c0264
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-rule-filter-form.hbs
@@ -0,0 +1,38 @@
+<h6>{{t 'coding_rules.filter_similar_rules'}}</h6>
+
+<div class="issue-action-options">
+ <a href="#" class="issue-action-option" data-property="languages" data-value="{{lang}}">
+ {{langName}}
+ </a>
+
+ {{#if severity}}
+ <a href="#" class="issue-action-option" data-property="severities" data-value="{{severity}}">
+ {{severityHelper severity}}
+ </a>
+ {{/if}}
+
+ {{#if debtChar}}
+ <hr>
+
+ <a href="#" class="issue-action-option" data-property="debt_characteristics" data-value="{{debtChar}}">
+ {{debtCharName}}
+ </a>
+
+ {{#if debtSubChar}}
+ <a href="#" class="issue-action-option" data-property="debt_characteristics" data-value="{{debtSubChar}}">
+ {{debtSubCharName}}
+ </a>
+ {{/if}}
+ {{/if}}
+
+ {{#notEmpty tags}}
+ <hr>
+ {{#each tags}}
+ <a href="#" class="issue-action-option" data-property="tags" data-value="{{this}}">
+ <i class="icon-tags icon-half-transparent"></i>&nbsp;{{this}}
+ </a>
+ {{/each}}
+ {{/notEmpty}}
+</div>
+
+<div class="bubble-popup-arrow"></div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs
new file mode 100644
index 00000000000..7e39482dbbc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs
@@ -0,0 +1,38 @@
+<div class="search-navigator-header-component">
+ {{#if state.rule}}
+ <a class="js-back">{{t 'coding_rules.return_to_list'}}</a>
+ {{else}}
+ &nbsp;
+ {{/if}}
+</div>
+
+
+<div class="search-navigator-header-actions">
+ {{#notNull state.total}}
+ <div class="search-navigator-header-pagination">
+ {{#gt state.total 0}}
+ <a class="js-prev icon-prev" title="{{t 'paging_previous'}}"></a>
+ <span class="current">{{sum state.selectedIndex 1}} / <span
+ id="coding-rules-total">{{state.total}}</span></span>
+ <a class="js-next icon-next" title="{{t 'paging_next'}}"></a>
+ {{else}}
+ <span class="current">0 / <span id="coding-rules-total">0</span></span>
+ {{/gt}}
+ </div>
+ {{/notNull}}
+
+
+ <div class="search-navigator-header-buttons button-group">
+ <button class="js-reload">{{t 'reload'}}</button>
+ <button class="js-new-search" id="coding-rules-new-search">{{t 'coding_rules.new_search'}}</button>
+ {{#if canWrite}}
+ <button class="js-bulk-change">{{t 'bulk_change'}}</button>
+ {{/if}}
+ </div>
+
+ {{#if canWrite}}
+ <div class="search-navigator-header-buttons button-group">
+ <button class="js-create-manual-rule">{{t 'coding_rules.create_manual_rule'}}</button>
+ </div>
+ {{/if}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs
new file mode 100644
index 00000000000..92aef7fdc1c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs
@@ -0,0 +1,61 @@
+<table class="coding-rule-table">
+ <tr>
+ {{#if activation}}
+ <td class="coding-rule-table-meta-cell coding-rule-activation">
+ {{severityIcon activation.severity}}
+ {{#eq activation.inherit 'OVERRIDES'}}
+ <i class="icon-inheritance"
+ title="{{tp 'coding_rules.overrides' activation.profile.name activation.parentProfile.name}}"></i>
+ {{/eq}}
+ {{#eq activation.inherit 'INHERITED'}}
+ <i class="icon-inheritance"
+ title="{{tp 'coding_rules.inherits' activation.profile.name activation.parentProfile.name}}"></i>
+ {{/eq}}
+ </td>
+ {{/if}}
+
+ <td>
+ <div class="coding-rule-title">
+ <a class="js-rule link-no-underline">{{name}}</a>
+ </div>
+ </td>
+
+ <td class="coding-rule-table-meta-cell">
+ <div class="coding-rule-meta">
+ {{#notEq status 'READY'}}
+ <span class="text-danger">{{status}}</span>
+ &nbsp;&nbsp;&nbsp;
+ {{/notEq}}
+ <span class="note">{{langName}}</span>
+ {{#notEmpty tags}}
+ &nbsp;&nbsp;&nbsp;
+ <i class="icon-tags"></i>
+ <span class="note">{{join tags ', '}}</span>
+ {{/notEmpty}}
+ <a class="js-rule-filter link-no-underline spacer-left" href="#">
+ <i class="icon-filter icon-half-transparent"></i>&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ </div>
+ </td>
+
+ {{#any activation selectedProfile}}
+ {{#if canWrite}}
+ <td class="coding-rule-table-meta-cell coding-rule-activation-actions">
+ <div class="button-group">
+ {{#if activation}}
+ {{#eq activation.inherit 'NONE'}}
+ <button class="coding-rules-detail-quality-profile-deactivate button-red">
+ {{t 'coding_rules.deactivate'}}
+ </button>
+ {{/eq}}
+ {{else}}
+ {{#unless isTemplate}}
+ <button class="coding-rules-detail-quality-profile-activate">{{t 'coding_rules.activate'}}</button>
+ {{/unless}}
+ {{/if}}
+ </div>
+ </td>
+ {{/if}}
+ {{/any}}
+ </tr>
+</table>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list.hbs
new file mode 100644
index 00000000000..37421cb75c2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list.hbs
@@ -0,0 +1,5 @@
+<div class="js-list"></div>
+
+<div class="search-navigator-workspace-list-more js-more">
+ <i class="spinner"></i>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/_coding-rules-facet-header.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/_coding-rules-facet-header.hbs
new file mode 100644
index 00000000000..2251f570ee9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/_coding-rules-facet-header.hbs
@@ -0,0 +1,4 @@
+<a class="search-navigator-facet-header js-facet-toggle">
+ <i class="icon-checkbox {{#if enabled}}icon-checkbox-checked{{/if}}"></i>
+ {{t 'coding_rules.facet' property}}
+</a>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-available-since-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-available-since-facet.hbs
new file mode 100644
index 00000000000..db6138c3663
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-available-since-facet.hbs
@@ -0,0 +1,5 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-container">
+ <input type="text" class="search-navigator-facet-input" name="availableSince" placeholder="{{t 'date'}}">
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs
new file mode 100644
index 00000000000..60db5dc6d7a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-base-facet.hbs
@@ -0,0 +1,10 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-characteristic-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-characteristic-facet.hbs
new file mode 100644
index 00000000000..645205b0c5c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-characteristic-facet.hbs
@@ -0,0 +1,19 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ {{#eq val 'NONE'}}
+ <a class="facet search-navigator-facet js-facet" data-empty-characteristic
+ title="{{t 'coding_rules.noncharacterized'}}">
+ <span class="facet-name">{{t 'coding_rules.noncharacterized'}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{else}}
+ <a class="facet search-navigator-facet {{#if parent}}search-navigator-facet-indent{{/if}} js-facet"
+ data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/eq}}
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs
new file mode 100644
index 00000000000..9f24a8d450c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-custom-values-facet.hbs
@@ -0,0 +1,14 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+
+ <div class="search-navigator-facet-custom-value">
+ <input type="hidden" class="js-custom-value">
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-key-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-key-facet.hbs
new file mode 100644
index 00000000000..1cc7b5bceed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-key-facet.hbs
@@ -0,0 +1,5 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-container">
+ {{key}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-quality-profile-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-quality-profile-facet.hbs
new file mode 100644
index 00000000000..b83bf1cf51b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-quality-profile-facet.hbs
@@ -0,0 +1,13 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}{{#if extra}} <span class="note">{{extra}}</span>{{/if}}</span>
+ <span class="facet-stat">
+ <span class="js-active facet-toggle facet-toggle-green {{#if ../toggled}}facet-toggle-active{{/if}}">active</span>
+ <span class="js-inactive facet-toggle facet-toggle-red {{#unless ../toggled}}facet-toggle-active{{/unless}}">inactive</span>
+ </span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-query-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-query-facet.hbs
new file mode 100644
index 00000000000..d26b4f3c12c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-query-facet.hbs
@@ -0,0 +1,5 @@
+<div class="search-navigator-facet-query">
+ <form>
+ <input type="text" class="search-navigator-facet-input" name="q" placeholder="{{t 'search_verb'}}">
+ </form>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs
new file mode 100644
index 00000000000..8550a95df84
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-severity-facet.hbs
@@ -0,0 +1,10 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-value="{{val}}" title="{{t 'severity' val}}">
+ <span class="facet-name">{{severityIcon val}} {{t 'severity' val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-template-facet.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-template-facet.hbs
new file mode 100644
index 00000000000..ef7dd5b98f2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/facets/coding-rules-template-facet.hbs
@@ -0,0 +1,12 @@
+{{> '_coding-rules-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ <a class="facet search-navigator-facet js-facet" data-value="true">
+ <span class="facet-name">{{t 'coding_rules.filters.template.is_template'}}</span>
+ <span class="facet-stat"></span>
+ </a>
+ <a class="facet search-navigator-facet js-facet" data-value="false">
+ <span class="facet-name">{{t 'coding_rules.filters.template.is_not_template'}}</span>
+ <span class="facet-stat"></span>
+ </a>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule-creation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule-creation.hbs
new file mode 100644
index 00000000000..660c4a57d51
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule-creation.hbs
@@ -0,0 +1,86 @@
+<form>
+ <div class="modal-head">
+ {{#if change}}
+ <h2>{{t 'coding_rules.update_custom_rule'}}</h2>
+ {{else}}
+ <h2>{{t 'coding_rules.create_custom_rule'}}</h2>
+ {{/if}}
+ </div>
+
+ <div class="modal-body modal-container">
+ <div class="js-modal-messages"></div>
+
+ <table>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'name'}} <em class="mandatory">*</em></h3></th>
+ <td>
+ <input type="text" name="name" id="coding-rules-custom-rule-creation-name"
+ class="coding-rules-name-key" value="{{name}}"/>
+ </td>
+ </tr>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'key'}}{{#unless change}} <em class="mandatory">*</em>{{/unless}}</h3></th>
+ <td>
+ {{#if change}}
+ <span class="coding-rules-detail-custom-rule-key" title="{{key}}">{{key}}</span>
+ {{else}}
+ <input type="text" name="key" id="coding-rules-custom-rule-creation-key"
+ class="coding-rules-name-key" value="{{internalKey}}"/>
+ {{/if}}
+ </td>
+ </tr>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'description'}} <em class="mandatory">*</em></h3></th>
+ <td>
+ <textarea name="markdown_description" id="coding-rules-custom-rule-creation-html-description"
+ class="coding-rules-markdown-description" rows="15">{{{mdDesc}}}</textarea>
+ <span class="text-right">{{> '_markdown-tips' }}</span>
+ </td>
+ </tr>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'severity'}}</h3></th>
+ <td>
+ <select id="coding-rules-custom-rule-creation-severity">
+ {{#each severities}}
+ <option value="{{this}}">{{t 'severity' this}}</option>
+ {{/each}}
+ </select>
+ </td>
+ </tr>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'coding_rules.filters.status'}}</h3></th>
+ <td>
+ <select id="coding-rules-custom-rule-creation-status">
+ {{#each statuses}}
+ <option value="{{id}}">{{text}}</option>
+ {{/each}}
+ </select>
+ </td>
+ </tr>
+ {{#each params}}
+ <tr class="property">
+ <th class="nowrap"><h3>{{key}}</h3></th>
+ <td>
+ {{#eq type 'TEXT'}}
+ <textarea class="width100" rows="3" name="{{key}}" placeholder="{{defaultValue}}">{{value}}</textarea>
+ {{else}}
+ <input type="text" name="{{key}}" value="{{value}}" placeholder="{{defaultValue}}"/>
+ {{/eq}}
+ <div class="note">{{htmlDesc}}</div>
+ {{#if extra}}
+ <div class="note">{{extra}}</div>
+ {{/if}}
+ </td>
+ </tr>
+ {{/each}}
+ </table>
+ </div>
+
+ <div class="modal-foot">
+ <button id="coding-rules-custom-rule-creation-create">
+ {{#if change}}{{t 'save'}}{{else}}{{t 'create'}}{{/if}}
+ </button>
+ <button id="coding-rules-custom-rule-creation-reactivate" class="hidden">{{t 'coding_rules.reactivate'}}</button>
+ <a id="coding-rules-custom-rule-creation-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs
new file mode 100644
index 00000000000..b5371ee37f3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs
@@ -0,0 +1,28 @@
+<td class="coding-rules-detail-list-name">
+ <a href="{{permalink}}">{{name}}</a>
+</td>
+
+<td class="coding-rules-detail-list-severity">
+ {{severityIcon severity}}&nbsp;{{t "severity" severity}}
+</td>
+
+<td class="coding-rules-detail-list-parameters">
+ {{#each params}}
+ {{#if defaultValue}}
+ <div class="coding-rules-detail-list-parameter">
+ <span class="key">{{key}}</span><span class="sep">:&nbsp;</span><span class="value" title="{{value}}">{{defaultValue}}</span>
+ </div>
+ {{/if}}
+ {{/each}}
+ &nbsp;
+</td>
+
+{{#if canWrite}}
+<td class="coding-rules-detail-list-actions">
+ <div class="button-group">
+ <button class="js-delete-custom-rule button-red">
+ {{t 'delete'}}
+ </button>
+ </div>
+</td>
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs
new file mode 100644
index 00000000000..36a79d1951a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs
@@ -0,0 +1,12 @@
+<div class="coding-rules-detail-custom-rules-section">
+ <div class="coding-rule-section-separator"></div>
+
+ <h3 class="coding-rules-detail-title">{{t 'coding_rules.custom_rules'}}</h3>
+
+ {{#if canWrite}}
+ <div class="button-group coding-rules-detail-quality-profiles-activation">
+ <button class="js-create-custom-rule">{{t 'coding_rules.create'}}</button>
+ </div>
+ {{/if}}
+ <table id="coding-rules-detail-custom-rules" class="coding-rules-detail-list"></table>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-manual-rule-creation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-manual-rule-creation.hbs
new file mode 100644
index 00000000000..49856ca84f5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-manual-rule-creation.hbs
@@ -0,0 +1,51 @@
+<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="alert alert-warning hidden">{{t 'coding_rules.reactivate.help'}}</div>
+ <div class="js-modal-messages"></div>
+
+ <table>
+ <tr class="property">
+ <th class="nowrap"><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 class="nowrap"><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 class="nowrap"><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="text-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>
+ <button id="coding-rules-manual-rule-creation-reactivate">{{t 'coding_rules.reactivate'}}</button>
+ <a id="coding-rules-manual-rule-creation-cancel" class="js-modal-close">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs
new file mode 100644
index 00000000000..2b47036fd89
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-profile-activation.hbs
@@ -0,0 +1,82 @@
+<form>
+ <div class="modal-head">
+ {{#if change}}
+ <h2>{{t 'coding_rules.change_details'}}</h2>
+ {{else}}
+ <h2>{{t 'coding_rules.activate_in_quality_profile'}}</h2>
+ {{/if}}
+ </div>
+
+ <div class="modal-body modal-body-select2">
+ <div class="alert alert-danger"></div>
+
+ {{#empty qualityProfiles}}
+ {{#unless change}}
+ <div class="alert alert-info">{{t 'coding_rules.active_in_all_profiles'}}</div>
+ {{/unless}}
+ {{/empty}}
+
+ <table>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'coding_rules.quality_profile'}}</h3></th>
+ <td>
+ {{#any key qProfile}}
+ {{name}}
+ {{else}}
+ <select id="coding-rules-quality-profile-activation-select">
+ {{#each qualityProfiles}}
+ <option value="{{key}}">{{name}}</option>
+ {{/each}}
+ </select>
+ {{/any}}
+ </td>
+ </tr>
+ <tr class="property">
+ <th class="nowrap"><h3>{{t 'severity'}}</h3></th>
+ <td>
+ <select id="coding-rules-quality-profile-activation-severity">
+ {{#each severities}}
+ <option value="{{this}}">{{t 'severity' this}}</option>
+ {{/each}}
+ </select>
+ </td>
+ </tr>
+ {{#if isCustomRule}}
+ <tr class="property">
+ <td colspan="2" class="note">{{t 'coding_rules.custom_rule.activation_notice'}}</td>
+ {{else}}
+ {{#each params}}
+ <tr class="property">
+ <th class="nowrap"><h3>{{key}}</h3></th>
+ <td>
+ {{#eq type 'TEXT'}}
+ <textarea class="width100" rows="3" name="{{key}}" placeholder="{{defaultValue}}">{{value}}</textarea>
+ {{else}}
+ {{#eq type 'BOOLEAN'}}
+ <select name="{{key}}" value="{{value}}">
+ <option value="{{defaultValue}}">{{t 'default'}} ({{t defaultValue}})</option>
+ <option value="true"{{#eq value 'true'}} selected="selected"{{/eq}}>{{t 'true'}}</option>
+ <option value="false"{{#eq value 'false'}} selected="selected"{{/eq}}>{{t 'false'}}</option>
+ </select>
+ {{else}}
+ <input type="text" name="{{key}}" value="{{value}}" placeholder="{{defaultValue}}">
+ {{/eq}}
+ {{/eq}}
+ <div class="note">{{{htmlDesc}}}</div>
+ {{#if extra}}
+ <div class="note">{{extra}}</div>
+ {{/if}}
+ </td>
+ </tr>
+ {{/each}}
+ {{/if}}
+ </table>
+ </div>
+
+ <div class="modal-foot">
+ <button id="coding-rules-quality-profile-activation-activate" {{#unless saveEnabled}}disabled="disabled"{{/unless}}>
+ {{#if change}}{{t 'save'}}{{else}}{{t 'coding_rules.activate'}}{{/if}}
+ </button>
+ <a id="coding-rules-quality-profile-activation-cancel" class="js-modal-close">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs
new file mode 100644
index 00000000000..d24143dc42c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs
@@ -0,0 +1,48 @@
+<div class="coding-rules-detail-description rule-desc markdown">{{{htmlDesc}}}</div>
+
+{{#unless isEditable}}
+ {{#unless isManual}}
+ <div class="coding-rules-detail-description coding-rules-detail-description-extra">
+ <div id="coding-rules-detail-description-extra">
+ {{#if htmlNote}}
+ <div class="rule-desc marginbottom10 markdown">{{{htmlNote}}}</div>
+ {{/if}}
+ {{#if canWrite}}
+ <div class="button-group">
+ <button id="coding-rules-detail-extend-description">{{t 'coding_rules.extend_description'}}</button>
+ </div>
+ {{/if}}
+ </div>
+
+ {{#if canWrite}}
+ <div class="coding-rules-detail-extend-description-form hidden">
+ <table class="width100">
+ <tbody>
+ <tr>
+ <td class="width100" colspan="2">
+ <textarea id="coding-rules-detail-extend-description-text" rows="4"
+ style="width: 100%; margin-bottom: 4px;">{{mdNote}}</textarea>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <div class="button-group">
+ <button id="coding-rules-detail-extend-description-submit">{{t 'save'}}</button>
+ {{#if mdNote}}
+ <button id="coding-rules-detail-extend-description-remove"
+ class="button-red">{{t 'remove'}}</button>
+ {{/if}}
+ </div>
+ <a id="coding-rules-detail-extend-description-cancel" class="action">{{t 'cancel'}}</a>
+ </td>
+ <td class="text-right">
+ {{> '_markdown-tips' }}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ {{/if}}
+ </div>
+ {{/unless}}
+{{/unless}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs
new file mode 100644
index 00000000000..da9a5336d36
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs
@@ -0,0 +1,21 @@
+<div class="coding-rule-section-separator"></div>
+
+<h3 class="coding-rules-detail-title">
+ {{t 'coding_rules.issues'}} (<a class="js-rule-issues" href="{{baseSearchUrl}}">{{total}}</a>)
+</h3>
+
+{{#notEmpty projects}}
+ <table class="coding-rules-detail-list coding-rules-most-violated-projects">
+ <tr>
+ <td class="coding-rules-detail-list-name" colspan="2">{{t 'coding_rules.most_violated_projects'}}</td>
+ </tr>
+ {{#each projects}}
+ <tr>
+ <td class="coding-rules-detail-list-name">{{name}}</td>
+ <td class="coding-rules-detail-list-parameters">
+ <a href="{{../baseSearchUrl}}|projectUuids={{val}}" target="_blank">{{count}}</a>
+ </td>
+ </tr>
+ {{/each}}
+ </table>
+{{/notEmpty}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs
new file mode 100644
index 00000000000..010f827776d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs
@@ -0,0 +1,87 @@
+<header class="page-header">
+ <h3 class="page-title coding-rules-detail-header">
+ <big>{{name}}</big>
+ </h3>
+ <div class="page-actions">
+ <span class="note">{{key}}</span>
+
+ <a class="coding-rules-detail-permalink icon-link spacer-left" target="_blank" href="{{permalink}}"
+ data-toggle="tooltip" data-placement="left" title="Rule permalink"></a>
+
+ <a class="js-rule-filter link-no-underline spacer-left" href="#">
+ <i class="icon-filter icon-half-transparent"></i>&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ </div>
+</header>
+
+<ul class="coding-rules-detail-properties">
+ {{#unless isManual}}
+ <li class="coding-rules-detail-property"
+ data-toggle="tooltip" data-placement="bottom" title="Default rule severity">
+ {{severityIcon severity}}&nbsp;{{t "severity" severity}}
+ </li>
+ {{/unless}}
+
+ {{#notEq status 'READY'}}
+ <li class="coding-rules-detail-property"
+ data-toggle="tooltip" data-placement="bottom" title="Rule status">{{status}}</li>
+ {{/notEq}}
+
+ <li class="coding-rules-detail-property coding-rules-detail-tag-list {{#if canWrite}}coding-rules-detail-tags-change{{/if}}"
+ data-toggle="tooltip" data-placement="bottom" title="Rule tags">
+ <i class="icon-tags"></i>
+ <span>{{#if allTags}}{{join allTags ', '}}{{else}}{{t 'coding_rules.no_tags'}}{{/if}}</span>
+ </li>
+
+ {{#if canWrite}}
+ <li class="coding-rules-detail-property coding-rules-detail-tag-edit hidden">
+ {{#if sysTags}}<i class="icon-tags"></i>
+ <span>{{join sysTags ', '}}</span>{{/if}}
+ <input class="coding-rules-detail-tag-input" type="text" value="{{#if tags}}{{join tags ','}}{{/if}}">
+
+ <div class="button-group">
+ <button class="coding-rules-detail-tag-edit-done">{{t 'Done'}}</button>
+ </div>
+ <a class="coding-rules-details-tag-edit-cancel">{{t 'cancel'}}</a>
+ </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{{#unless isManual}} (language){{/unless}}">
+ {{repoName}}{{#unless isManual}} ({{langName}}){{/unless}}
+ </li>
+
+ {{#if isTemplate}}
+ <li class="coding-rules-detail-property"
+ title="{{t 'coding_rules.rule_template.title'}}">{{t 'coding_rules.rule_template'}}</li>
+ {{/if}}
+
+ {{#if templateKey}}
+ <li class="coding-rules-detail-property"
+ title="{{t 'coding_rules.custom_rule.title'}}">{{t 'coding_rules.custom_rule'}}
+ (<a href="#rule_key={{templateKey}}">{{t 'coding_rules.show_template'}}</a>)
+ </li>
+ {{/if}}
+</ul>
+
+{{#if subCharacteristic}}
+ <ul class="coding-rules-detail-properties">
+ <li class="coding-rules-detail-property"
+ data-toggle="tooltip" data-placement="bottom" title="Rule characteristic">
+ {{subCharacteristic}}
+ </li>
+
+ {{#if debtRemFnType}}
+ <li class="coding-rules-detail-property"
+ data-toggle="tooltip" data-placement="bottom" title="{{t 'coding_rules.remediation_function'}}">
+ {{t 'coding_rules.remediation_function' debtRemFnType}}:
+
+ {{#if debtRemFnOffset}}{{debtRemFnOffset}}{{/if}}
+ {{#if debtRemFnCoeff}}{{#if debtRemFnOffset}}+{{/if}}{{debtRemFnCoeff}}{{/if}}
+ {{#if effortToFixDescription}}{{effortToFixDescription}}{{/if}}
+ </li>
+ {{/if}}
+ </ul>
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs
new file mode 100644
index 00000000000..0298c2aa33e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-parameters.hbs
@@ -0,0 +1,27 @@
+<h3 class="coding-rules-detail-title">{{t 'coding_rules.parameters'}}</h3>
+<table class="coding-rules-detail-parameters">
+ {{#each params}}
+ <tr class="coding-rules-detail-parameter">
+ <td class="coding-rules-detail-parameter-name">{{key}}</td>
+ <td class="coding-rules-detail-parameter-description" data-key="{{key}}">
+ <p>{{{htmlDesc}}}</p>
+ {{#if ../../templateKey}}
+ <div class="note spacer-top">
+ {{#if defaultValue }}
+ <span class="coding-rules-detail-parameter-value">{{defaultValue}}</span>
+ {{else}}
+ {{t 'coding_rules.parameter.empty'}}
+ {{/if}}
+ </div>
+ {{else}}
+ {{#if defaultValue}}
+ <div class="note spacer-top">
+ {{t 'coding_rules.parameters.default_value'}}<br>
+ <span class="coding-rules-detail-parameter-value">{{defaultValue}}</span>
+ </div>
+ {{/if}}
+ {{/if}}
+ </td>
+ </tr>
+ {{/each}}
+</table>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs
new file mode 100644
index 00000000000..441eb2ed22f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs
@@ -0,0 +1,74 @@
+<td class="coding-rules-detail-quality-profile-name">
+ {{name}}
+ {{#if parent}}
+ <div class="coding-rules-detail-quality-profile-inheritance">
+ {{#eq inherit 'OVERRIDES'}}
+ <i class="icon-inheritance" title="{{tp 'coding_rules.overrides' name parent.name}}"></i>
+ {{/eq}}
+ {{#eq inherit 'INHERITED'}}
+ <i class="icon-inheritance" title="{{tp 'coding_rules.inherits' name parent.name}}"></i>
+ {{/eq}}
+ {{parent.name}}
+ </div>
+ {{/if}}
+</td>
+
+{{#if severity}}
+ <td class="coding-rules-detail-quality-profile-severity">
+ <span data-toggle="tooltip" data-placement="bottom" title="Activation severity">
+ {{severityIcon severity}} {{t "severity" severity}}
+ </span>
+ {{#if parent}}{{#notEq severity parent.severity}}
+ <div class="coding-rules-detail-quality-profile-inheritance">
+ {{t 'coding_rules.original'}}&nbsp;{{t 'severity' parent.severity}}
+ </div>
+ {{/notEq}}{{/if}}
+ </td>
+
+ {{#unless templateKey}}
+ <td class="coding-rules-detail-quality-profile-parameters">
+ {{#each parameters}}
+ <div class="coding-rules-detail-quality-profile-parameter">
+ <span class="key">{{key}}</span><span class="sep">:&nbsp;</span><span class="value"
+ title="{{value}}">{{value}}</span>
+ {{#if ../parent}}{{#notEq value original}}
+ <div class="coding-rules-detail-quality-profile-inheritance">
+ {{t 'coding_rules.original'}}&nbsp;<span class="value">{{original}}</span>
+ </div>
+ {{/notEq}}{{/if}}
+ </div>
+ {{/each}}
+ &nbsp;
+ </td>
+ {{/unless}}
+
+ {{#if canWrite}}
+ <td class="coding-rules-detail-quality-profile-actions">
+ <div class="button-group">
+ {{#unless isTemplate}}
+ <button class="coding-rules-detail-quality-profile-change">{{t 'change_verb'}}</button>
+ {{/unless}}
+ {{#if parent}}
+ {{#eq inherit 'OVERRIDES'}}
+ <button class="coding-rules-detail-quality-profile-revert button-red">
+ {{t 'coding_rules.revert_to_parent_definition'}}
+ </button>
+ {{/eq}}
+ {{else}}
+ <button class="coding-rules-detail-quality-profile-deactivate button-red">
+ {{t 'coding_rules.deactivate'}}
+ </button>
+ {{/if}}
+ </div>
+ </td>
+ {{/if}}
+
+{{else}}
+ {{#if canWrite}}{{#unless isTemplate}}
+ <td class="coding-rules-detail-quality-profile-actions">
+ <div class="button-group">
+ <button class="coding-rules-detail-quality-profile-activate">{{t 'coding_rules.activate'}}</button>
+ </div>
+ </td>
+ {{/unless}}{{/if}}
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs
new file mode 100644
index 00000000000..2a28c69d62e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs
@@ -0,0 +1,21 @@
+<div class="coding-rules-detail-quality-profiles-section">
+ <div class="coding-rule-section-separator"></div>
+
+ <h3 class="coding-rules-detail-title">{{t 'coding_rules.quality_profiles'}}</h3>
+
+ {{#if canWrite}}
+ {{#unless isTemplate}}
+ <div class="button-group coding-rules-detail-quality-profiles-activation">
+ <button id="coding-rules-quality-profile-activate">{{t 'coding_rules.activate'}}</button>
+ </div>
+ {{/unless}}
+ {{/if}}
+
+ {{#if isTemplate}}
+ <div class="alert alert-warning">
+ {{t 'coding_rules.quality_profiles.template_caption'}}
+ </div>
+ {{/if}}
+
+ <table id="coding-rules-detail-quality-profiles" class="coding-rules-detail-quality-profiles width100"></table>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js
new file mode 100644
index 00000000000..b194b54fc90
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js
@@ -0,0 +1,77 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/workspace-header-view',
+ './bulk-change-popup-view',
+ './rule/manual-rule-creation-view',
+ './templates'
+], function (WorkspaceHeaderView, BulkChangePopup, ManualRuleCreationView) {
+
+ var $ = jQuery;
+
+ return WorkspaceHeaderView.extend({
+ template: Templates['coding-rules-workspace-header'],
+
+ events: function () {
+ return _.extend(WorkspaceHeaderView.prototype.events.apply(this, arguments), {
+ 'click .js-back': 'onBackClick',
+ 'click .js-bulk-change': 'onBulkChangeClick',
+ 'click .js-create-manual-rule': 'createManualRule',
+ 'click .js-reload': 'reload',
+ 'click .js-new-search': 'newSearch'
+ });
+ },
+
+ onBackClick: function () {
+ this.options.app.controller.hideDetails();
+ },
+
+ onBulkChangeClick: function (e) {
+ e.stopPropagation();
+ $('body').click();
+ new BulkChangePopup({
+ app: this.options.app,
+ triggerEl: $(e.currentTarget),
+ bottomRight: true
+ }).render();
+ },
+
+ createManualRule: function() {
+ new ManualRuleCreationView({
+ app: this.options.app
+ }).render();
+ },
+
+ reload: function () {
+ this.options.app.controller.fetchList(true);
+ },
+
+ newSearch: function () {
+ this.options.app.controller.newSearch();
+ },
+
+ serializeData: function () {
+ return _.extend(WorkspaceHeaderView.prototype.serializeData.apply(this, arguments), {
+ canWrite: this.options.app.canWrite
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js
new file mode 100644
index 00000000000..2aefada2578
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-empty-view.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ return Marionette.ItemView.extend({
+ className: 'search-navigator-no-results',
+
+ template: function () {
+ return t('coding_rules.no_results');
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js
new file mode 100644
index 00000000000..b18ba7b52e1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js
@@ -0,0 +1,106 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/workspace-list-item-view',
+ './rule/profile-activation-view',
+ './rule/rule-filter-mixin',
+ './templates'
+], function (WorkspaceListItemView, ProfileActivationView, RuleFilterMixin) {
+
+ return WorkspaceListItemView.extend(RuleFilterMixin).extend({
+ className: 'coding-rule',
+ template: Templates['coding-rules-workspace-list-item'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click': 'selectCurrent',
+ 'dblclick': 'openRule',
+ 'click .js-rule': 'openRule',
+ 'click .js-rule-filter': 'onRuleFilterClick',
+ 'click .coding-rules-detail-quality-profile-activate': 'activate',
+ 'click .coding-rules-detail-quality-profile-change': 'change',
+ 'click .coding-rules-detail-quality-profile-revert': 'revert',
+ 'click .coding-rules-detail-quality-profile-deactivate': 'deactivate'
+ },
+
+ selectCurrent: function () {
+ this.options.app.state.set({ selectedIndex: this.model.get('index') });
+ },
+
+ openRule: function () {
+ this.options.app.controller.showDetails(this.model);
+ },
+
+ activate: function () {
+ var that = this,
+ selectedProfile = this.options.app.state.get('query').qprofile,
+ othersQualityProfiles = _.reject(this.options.app.qualityProfiles, function (profile) {
+ return profile.key === selectedProfile;
+ }),
+ activationView = new ProfileActivationView({
+ rule: this.model,
+ collection: new Backbone.Collection(othersQualityProfiles),
+ app: this.options.app
+ });
+ activationView.on('profileActivated', function (severity) {
+ var activation = {
+ severity: severity,
+ inherit: 'NONE'
+ };
+ that.model.set({ activation: activation });
+ });
+ activationView.render();
+ },
+
+ deactivate: function () {
+ var that = this,
+ ruleKey = this.model.get('key'),
+ activation = this.model.get('activation');
+ window.confirmDialog({
+ title: t('coding_rules.deactivate'),
+ html: tp('coding_rules.deactivate.confirm'),
+ yesHandler: function () {
+ return jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/qualityprofiles/deactivate_rule',
+ data: {
+ profile_key: activation.qProfile,
+ rule_key: ruleKey
+ }
+ }).done(function () {
+ that.model.unset('activation');
+ });
+ }
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(WorkspaceListItemView.prototype.serializeData.apply(this, arguments), {
+ tags: _.union(this.model.get('sysTags'), this.model.get('tags')),
+ canWrite: this.options.app.canWrite,
+ selectedProfile: this.options.app.state.get('query').qprofile
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js
new file mode 100644
index 00000000000..e5f1ac02eb2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-view.js
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/navigator/workspace-list-view',
+ './workspace-list-item-view',
+ './workspace-list-empty-view',
+ './templates'
+], function (WorkspaceListView, WorkspaceListItemView, WorkspaceListEmptyView) {
+
+ return WorkspaceListView.extend({
+ template: Templates['coding-rules-workspace-list'],
+ itemView: WorkspaceListItemView,
+ itemViewContainer: '.js-list',
+ emptyView: WorkspaceListEmptyView,
+
+ bindShortcuts: function () {
+ WorkspaceListView.prototype.bindShortcuts.apply(this, arguments);
+ var that = this;
+ key('right', 'list', function () {
+ that.options.app.controller.showDetailsForSelected();
+ return false;
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/drilldown/app.js b/server/sonar-web/src/main/js/apps/drilldown/app.js
new file mode 100644
index 00000000000..a6d9b5546b3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/drilldown/app.js
@@ -0,0 +1,52 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/source-viewer/main'
+], function (SourceViewer) {
+
+ var $ = jQuery,
+ App = new Marionette.Application();
+
+ App.addRegions({
+ viewerRegion: '#source-viewer'
+ });
+
+ App.addInitializer(function () {
+ $('.js-drilldown-link').on('click', function (e) {
+ e.preventDefault();
+ $(e.currentTarget).closest('table').find('.selected').removeClass('selected');
+ $(e.currentTarget).closest('tr').addClass('selected');
+ var uuid = $(e.currentTarget).data('uuid'),
+ viewer = new SourceViewer();
+ App.viewerRegion.show(viewer);
+ viewer.open(uuid);
+ if (window.drilldown.period != null) {
+ viewer.on('loaded', function () {
+ viewer.filterLinesByDate(window.drilldown.period, window.drilldown.periodName);
+ });
+ }
+ });
+ });
+
+ var l10nXHR = window.requestMessages();
+ l10nXHR.done(function () {
+ App.start();
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/markdown/app.js b/server/sonar-web/src/main/js/apps/markdown/app.js
new file mode 100644
index 00000000000..80215acba17
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/markdown/app.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(['./markdown-help-view'], function (MarkdownView) {
+
+ var App = new Marionette.Application();
+
+ App.on('start', function (options) {
+ new MarkdownView({ el: options.el }).render();
+ });
+
+ return App;
+
+});
diff --git a/server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js b/server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js
new file mode 100644
index 00000000000..050128861bd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js
@@ -0,0 +1,26 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(['./templates'], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['markdown-help']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs b/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs
new file mode 100644
index 00000000000..49b968a94dd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs
@@ -0,0 +1,100 @@
+<h2 class="spacer-bottom">Markdown Syntax</h2>
+<table class="width-100 data zebra table-bordered">
+ <thead>
+ <tr>
+ <th>Write:</th>
+ <th>To display:</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>*this text is bold*</td>
+ <td class="markdown"><strong>this text is bold</strong></td>
+ </tr>
+ <tr>
+ <td>http://sonarqube.org</td>
+ <td class="markdown"><a href="http://sonarqube.org">http://sonarqube.org</a></td>
+ </tr>
+ <tr>
+ <td class="text-top">
+ [SonarQubeâ„¢ Home Page](http://www.sonarqube.org)
+ </td>
+ <td class="markdown text-top">
+ <a href="http://www.sonarqube.org" target="_blank">SonarQubeâ„¢ Home Page</a>
+ </td>
+ </tr>
+ <tr>
+ <td class="text-top">* first item<br>
+ * second item
+ </td>
+ <td class="markdown">
+ <ul>
+ <li>first item</li>
+ <li>second item</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td class="text-top">1. first item<br>
+ 1. second item
+ </td>
+ <td class="markdown text-top">
+ <ol>
+ <li>first item</li>
+ <li>second item</li>
+ </ol>
+ </td>
+ </tr>
+ <tr>
+ <td class="text-top">
+ = Heading Level 1<br>
+ == Heading Level 2<br>
+ === Heading Level 3<br>
+ ==== Heading Level 4<br>
+ ===== Heading Level 5<br>
+ ====== Heading Level 6<br>
+ <td class="markdown text-top">
+ <h1>Heading Level 1</h1>
+ <h2>Heading Level 2</h2>
+ <h3>Heading Level 3</h3>
+ <h4>Heading Level 4</h4>
+ <h5>Heading Level 5</h5>
+ <h6>Heading Level 6</h6>
+ </td>
+ </tr>
+ <tr>
+ <td class="text-top">``Lists#newArrayList()``</td>
+ <td class="markdown text-top"><code>Lists#newArrayList()</code></td>
+ </tr>
+ <tr>
+ <td class="text-top">
+ ``<br>
+ // code on multiple lines<br>
+ public void foo() {<br>
+ &nbsp;&nbsp;// do some logic here<br>
+ }<br>
+ ``
+ </td>
+ <td class="markdown text-top">
+<pre>
+ // code on multiple lines
+ public void foo() {
+ // do some logic here
+ }
+</pre>
+ </td>
+ </tr>
+ <tr>
+ <td class="text-top">
+ Standard text<br>
+ > Blockquoted text<br>
+ > that spans multiple lines<br>
+ </td>
+ <td class="markdown text-top">
+ <p>Standard text</p>
+ <blockquote>Blockquoted text<br>
+ that spans multiple lines<br></blockquote>
+ </td>
+ </tr>
+ </tbody>
+</table>
diff --git a/server/sonar-web/src/main/js/apps/measures/app.js b/server/sonar-web/src/main/js/apps/measures/app.js
new file mode 100644
index 00000000000..5885cbe87ba
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/measures/app.js
@@ -0,0 +1,192 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(
+ [
+ './measures-filter-bar',
+ 'components/navigator/filters/base-filters',
+ 'components/navigator/filters/checkbox-filters',
+ 'components/navigator/filters/choice-filters',
+ 'components/navigator/filters/ajax-select-filters',
+ 'components/navigator/filters/favorite-filters',
+ 'components/navigator/filters/range-filters',
+ 'components/navigator/filters/string-filters',
+ 'components/navigator/filters/metric-filters'
+ ],
+ function (FilterBar, BaseFilters, CheckboxFilterView, ChoiceFilters, AjaxSelectFilters,
+ FavoriteFilters, RangeFilters, StringFilterView, MetricFilterView) {
+
+ var NavigatorApp = new Marionette.Application();
+
+
+ NavigatorApp.addRegions({
+ filtersRegion: '.navigator-filters'
+ });
+
+
+ NavigatorApp.addInitializer(function () {
+ this.filters = new BaseFilters.Filters();
+
+ if (_.isObject(window.SS.favorites)) {
+ this.filters.add([
+ new BaseFilters.Filter({
+ type: FavoriteFilters.FavoriteFilterView,
+ enabled: true,
+ optional: false,
+ choices: window.SS.favorites,
+ favoriteUrl: '/measures/filter',
+ manageUrl: '/measures/manage'
+ })]);
+ }
+
+ this.filters.add([
+ new BaseFilters.Filter({
+ name: window.SS.phrases.components,
+ property: 'qualifiers[]',
+ type: ChoiceFilters.ChoiceFilterView,
+ enabled: true,
+ optional: false,
+ choices: window.SS.qualifiers,
+ defaultValue: window.SS.phrases.any
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.componentsOf,
+ property: 'base',
+ type: AjaxSelectFilters.ComponentFilterView,
+ multiple: false,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.favoritesOnly,
+ property: 'onFavourites',
+ type: CheckboxFilterView,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.date,
+ propertyFrom: 'fromDate',
+ propertyTo: 'toDate',
+ type: RangeFilters.DateRangeFilterView,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.keyContains,
+ property: 'keySearch',
+ type: StringFilterView,
+ enabled: false,
+ optional: true
+ })
+ ]);
+ });
+
+ NavigatorApp.addInitializer(function () {
+ this.filters.add([
+ new BaseFilters.Filter({
+ name: window.SS.phrases.lastAnalysis,
+ propertyFrom: 'ageMinDays',
+ propertyTo: 'ageMaxDays',
+ type: RangeFilters.RangeFilterView,
+ placeholder: window.SS.phrases.days,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.metric,
+ property: 'c3',
+ type: MetricFilterView,
+ metrics: window.SS.metrics,
+ periods: window.SS.metricPeriods,
+ operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.metric,
+ property: 'c2',
+ type: MetricFilterView,
+ metrics: window.SS.metrics,
+ periods: window.SS.metricPeriods,
+ operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.metric,
+ property: 'c1',
+ type: MetricFilterView,
+ metrics: window.SS.metrics,
+ periods: window.SS.metricPeriods,
+ operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.nameContains,
+ property: 'nameSearch',
+ type: StringFilterView,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.alert,
+ property: 'alertLevels[]',
+ type: ChoiceFilters.ChoiceFilterView,
+ enabled: false,
+ optional: true,
+ choices: {
+ 'error': window.SS.phrases.error,
+ 'warn': window.SS.phrases.warning,
+ 'ok': window.SS.phrases.ok
+ }
+ })
+ ]);
+
+
+ this.filterBarView = new FilterBar({
+ collection: this.filters,
+ extra: {
+ sort: '',
+ asc: false
+ }
+ });
+
+
+ this.filtersRegion.show(this.filterBarView);
+ });
+
+
+ NavigatorApp.start();
+ if (window.queryParams) {
+ NavigatorApp.filterBarView.restoreFromQuery(window.queryParams);
+ }
+ key.setScope('list');
+
+ });
diff --git a/server/sonar-web/src/main/js/apps/measures/measures-filter-bar.js b/server/sonar-web/src/main/js/apps/measures/measures-filter-bar.js
new file mode 100644
index 00000000000..a2ca390e068
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/measures/measures-filter-bar.js
@@ -0,0 +1,28 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(['components/navigator/filters/filter-bar'], function (FilterBarView) {
+
+ return FilterBarView.extend({
+ template: function () {
+ return jQuery('#filter-bar-template').html();
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/app.js b/server/sonar-web/src/main/js/apps/nav/app.js
new file mode 100644
index 00000000000..5d45dd82f97
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/app.js
@@ -0,0 +1,79 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './state',
+ './global-navbar-view',
+ './context-navbar-view',
+ './settings-navbar-view',
+ 'components/workspace/main'
+], function (State, GlobalNavbarView, ContextNavbarView, SettingsNavbarView) {
+
+ var $ = jQuery,
+ App = new Marionette.Application(),
+ state = new State();
+
+ state.set(window.navbarOptions.toJSON());
+
+ App.on('start', function () {
+ state.fetchGlobal();
+
+ this.navbarView = new GlobalNavbarView({
+ app: App,
+ el: $('.navbar-global'),
+ model: state
+ });
+ this.navbarView.render();
+
+ if (state.get('space') === 'component') {
+ state.fetchComponent();
+ this.contextNavbarView = new ContextNavbarView({
+ app: App,
+ el: $('.navbar-context'),
+ model: state
+ });
+ this.contextNavbarView.render();
+ }
+
+ if (state.get('space') === 'settings') {
+ state.fetchSettings();
+ this.settingsNavbarView = new SettingsNavbarView({
+ app: App,
+ el: $('.navbar-context'),
+ model: state
+ });
+ this.settingsNavbarView.render();
+ }
+
+ $(window).on('keypress', function (e) {
+ var tagName = e.target.tagName;
+ if (tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') {
+ var code = e.keyCode || e.which;
+ if (code === 63) {
+ App.navbarView.showShortcutsHelp();
+ }
+ }
+ });
+ });
+
+ window.requestMessages().done(function () {
+ App.start();
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js
new file mode 100644
index 00000000000..07eb0cd159a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery,
+ MORE_URLS = [
+ '/dashboards', '/plugins/resource'
+ ],
+ SETTINGS_URLS = [
+ '/project/settings', '/project/profile', '/project/qualitygate', '/manual_measures/index',
+ '/action_plans/index', '/project/links', '/project_roles/index', '/project/history', '/project/key',
+ '/project/deletion'
+ ];
+
+ return Marionette.ItemView.extend({
+ template: Templates['nav-context-navbar'],
+
+ modelEvents: {
+ 'change:component': 'render'
+ },
+
+ events: {
+ 'click .js-favorite': 'onFavoriteClick'
+ },
+
+ onRender: function () {
+ this.$('[data-toggle="tooltip"]').tooltip({
+ container: 'body'
+ });
+ },
+
+ onFavoriteClick: function () {
+ var that = this,
+ url = baseUrl + '/favourites/toggle/' + this.model.get('contextId'),
+ isContextFavorite = this.model.get('isContextFavorite');
+ this.model.set({ isContextFavorite: !isContextFavorite });
+ return $.post(url).fail(function () {
+ that.model.set({ isContextFavorite: isContextFavorite });
+ });
+ },
+
+ serializeData: function () {
+ var href = window.location.href,
+ search = window.location.search,
+ isMoreActive = _.some(MORE_URLS, function (url) {
+ return href.indexOf(url) !== -1;
+ }) || (href.indexOf('/dashboard') !== -1 && search.indexOf('did=') !== -1),
+ isSettingsActive = _.some(SETTINGS_URLS, function (url) {
+ return href.indexOf(url) !== -1;
+ }),
+ isOverviewActive = !isMoreActive && href.indexOf('/dashboard') !== -1 && search.indexOf('did=') === -1;
+ return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
+ canManageContextDashboards: !!window.SS.user,
+ contextKeyEncoded: encodeURIComponent(this.model.get('componentKey')),
+
+ isOverviewActive: isOverviewActive,
+ isSettingsActive: isSettingsActive,
+ isMoreActive: isMoreActive
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js
new file mode 100644
index 00000000000..9ee43d9fdfe
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js
@@ -0,0 +1,97 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './search-view',
+ './shortcuts-help-view',
+ './templates'
+], function (SearchView, ShortcutsHelpView) {
+
+ return Marionette.Layout.extend({
+ template: Templates['nav-global-navbar'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ regions: {
+ searchRegion: '.js-search-region'
+ },
+
+ events: {
+ 'click .js-login': 'onLoginClick',
+ 'click .js-favorite': 'onFavoriteClick',
+ 'show.bs.dropdown .js-search-dropdown': 'onSearchDropdownShow',
+ 'hidden.bs.dropdown .js-search-dropdown': 'onSearchDropdownHidden',
+ 'click .js-shortcuts': 'onShortcutsClick'
+ },
+
+ onRender: function () {
+ var that = this;
+ if (this.model.has('space')) {
+ this.$el.addClass('navbar-' + this.model.get('space'));
+ }
+ this.$el.addClass('navbar-fade');
+ setTimeout(function () {
+ that.$el.addClass('in');
+ }, 0);
+ },
+
+ onLoginClick: function () {
+ var returnTo = window.location.pathname + window.location.search;
+ window.location = baseUrl + '/sessions/new?return_to=' + encodeURIComponent(returnTo) + window.location.hash;
+ return false;
+ },
+
+ onSearchDropdownShow: function () {
+ var that = this;
+ this.searchRegion.show(new SearchView({
+ model: this.model,
+ hide: function () {
+ that.$('.js-search-dropdown-toggle').dropdown('toggle');
+ }
+ }));
+ },
+
+ onSearchDropdownHidden: function () {
+ this.searchRegion.reset();
+ },
+
+ onShortcutsClick: function () {
+ this.showShortcutsHelp();
+ },
+
+ showShortcutsHelp: function () {
+ new ShortcutsHelpView({ shortcuts: this.model.get('shortcuts') }).render();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
+ user: window.SS.user,
+ userName: window.SS.userName,
+ isUserAdmin: window.SS.isUserAdmin,
+
+ canManageGlobalDashboards: !!window.SS.user,
+ canManageIssueFilters: !!window.SS.user,
+ canManageMeasureFilters: !!window.SS.user
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/search-view.js b/server/sonar-web/src/main/js/apps/nav/search-view.js
new file mode 100644
index 00000000000..57daa695031
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/search-view.js
@@ -0,0 +1,250 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/selectable-collection-view',
+ './templates'
+], function (SelectableCollectionView) {
+
+ var $ = jQuery,
+
+ SearchItemView = Marionette.ItemView.extend({
+ tagName: 'li',
+ template: Templates['nav-search-item'],
+
+ select: function () {
+ this.$el.addClass('active');
+ },
+
+ deselect: function () {
+ this.$el.removeClass('active');
+ },
+
+ submit: function () {
+ this.$('a')[0].click();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ index: this.options.index
+ });
+ }
+ }),
+
+ SearchEmptyView = Marionette.ItemView.extend({
+ tagName: 'li',
+ template: Templates['nav-search-empty']
+ }),
+
+ SearchResultsView = SelectableCollectionView.extend({
+ className: 'menu',
+ tagName: 'ul',
+ itemView: SearchItemView,
+ emptyView: SearchEmptyView
+ });
+
+ return Marionette.Layout.extend({
+ className: 'navbar-search',
+ tagName: 'form',
+ template: Templates['nav-search'],
+
+ regions: {
+ resultsRegion: '.js-search-results'
+ },
+
+ events: {
+ 'submit': 'onSubmit',
+ 'keydown .js-search-input': 'onKeyDown',
+ 'keyup .js-search-input': 'debouncedOnKeyUp'
+ },
+
+ initialize: function () {
+ var that = this;
+ this.results = new Backbone.Collection();
+ this.favorite = [];
+ if (window.SS.user) {
+ this.fetchFavorite().always(function () {
+ that.resetResultsToDefault();
+ });
+ } else {
+ this.resetResultsToDefault();
+ }
+ this.resultsView = new SearchResultsView({ collection: this.results });
+ this.debouncedOnKeyUp = _.debounce(this.onKeyUp, 400);
+ this._bufferedValue = '';
+ },
+
+ onRender: function () {
+ var that = this;
+ this.resultsRegion.show(this.resultsView);
+ setTimeout(function () {
+ that.$('.js-search-input').focus();
+ }, 0);
+ },
+
+ onKeyDown: function (e) {
+ if (e.keyCode === 38) {
+ this.resultsView.selectPrev();
+ return false;
+ }
+ if (e.keyCode === 40) {
+ this.resultsView.selectNext();
+ return false;
+ }
+ if (e.keyCode === 13) {
+ this.resultsView.submitCurrent();
+ return false;
+ }
+ if (e.keyCode === 27) {
+ this.options.hide();
+ return false;
+ }
+ },
+
+ onKeyUp: function () {
+ var value = this.$('.js-search-input').val();
+ if (value === this._bufferedValue) {
+ return;
+ }
+ this._bufferedValue = this.$('.js-search-input').val();
+ if (this.searchRequest != null) {
+ this.searchRequest.abort();
+ }
+ this.searchRequest = this.search(value);
+ },
+
+ onSubmit: function () {
+ return false;
+ },
+
+ fetchFavorite: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/favourites').done(function (r) {
+ that.favorite = r.map(function (f) {
+ var isFile = ['FIL', 'UTS'].indexOf(f.qualifier) !== -1;
+ return {
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(f.key) + dashboardParameters(true),
+ name: isFile ? window.collapsedDirFromPath(f.lname) + window.fileFromPath(f.lname) : f.name,
+ icon: 'favorite'
+ };
+ });
+ that.favorite = _.sortBy(that.favorite, 'name');
+ });
+ },
+
+ resetResultsToDefault: function () {
+ var recentHistory = JSON.parse(localStorage.getItem('sonar_recent_history')),
+ history = (recentHistory || []).map(function (historyItem, index) {
+ return {
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(historyItem.key) + dashboardParameters(true),
+ name: historyItem.name,
+ q: historyItem.icon,
+ extra: index === 0 ? t('browsed_recently') : null
+ };
+ }),
+ favorite = _.first(this.favorite, 6).map(function (f, index) {
+ return _.extend(f, { extra: index === 0 ? t('favorite') : null });
+ }),
+ qualifiers = this.model.get('qualifiers').map(function (q, index) {
+ return {
+ url: baseUrl + '/all_projects?qualifier=' + encodeURIComponent(q),
+ name: t('qualifiers.all', q),
+ extra: index === 0 ? '' : null
+ };
+ });
+ this.results.reset([].concat(history, favorite, qualifiers));
+ },
+
+ search: function (q) {
+ if (q.length < 2) {
+ this.resetResultsToDefault();
+ return;
+ }
+ var that = this,
+ url = baseUrl + '/api/components/suggestions',
+ options = { s: q };
+ return $.get(url, options).done(function (r) {
+ var collection = [];
+ r.results.forEach(function (domain) {
+ domain.items.forEach(function (item, index) {
+ collection.push(_.extend(item, {
+ q: domain.q,
+ extra: index === 0 ? domain.name : null,
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(item.key) + dashboardParameters(true)
+ }));
+ });
+ });
+ that.results.reset([].concat(
+ that.getNavigationFindings(q),
+ that.getGlobalDashboardFindings(q),
+ that.getFavoriteFindings(q),
+ collection
+ ));
+ });
+ },
+
+ getNavigationFindings: function (q) {
+ var DEFAULT_ITEMS = [
+ { name: t('issues.page'), url: baseUrl + '/issues/search' },
+ { name: t('layout.measures'), url: baseUrl + '/measures/search?qualifiers[]=TRK' },
+ { name: t('coding_rules.page'), url: baseUrl + '/coding_rules' },
+ { name: t('quality_profiles.page'), url: baseUrl + '/profiles' },
+ { name: t('quality_gates.page'), url: baseUrl + '/quality_gates' },
+ { name: t('comparison_global.page'), url: baseUrl + '/comparison' },
+ { name: t('dependencies.page'), url: baseUrl + '/dependencies' }
+ ],
+ customItems = [];
+ if (window.SS.isUserAdmin) {
+ customItems.push({ name: t('layout.settings'), url: baseUrl + '/settings' });
+ }
+ var findings = [].concat(DEFAULT_ITEMS, customItems).filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('navigation');
+ }
+ return _.first(findings, 6);
+ },
+
+ getGlobalDashboardFindings: function (q) {
+ var dashboards = this.model.get('globalDashboards') || [],
+ items = dashboards.map(function (d) {
+ return { name: d.name, url: baseUrl + '/dashboard/index?did=' + encodeURIComponent(d.key) };
+ });
+ var findings = items.filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('dashboard.global_dashboards');
+ }
+ return _.first(findings, 6);
+ },
+
+ getFavoriteFindings: function (q) {
+ var findings = this.favorite.filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('favorite');
+ }
+ return _.first(findings, 6);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js
new file mode 100644
index 00000000000..6134e229fd1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js
@@ -0,0 +1,32 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['nav-settings-navbar'],
+
+ modelEvents: {
+ 'change:settings': 'render'
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js b/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js
new file mode 100644
index 00000000000..b054b7c6150
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modals',
+ './templates'
+], function (ModalView) {
+
+ return ModalView.extend({
+ className: 'modal modal-large',
+ template: Templates['nav-shortcuts-help']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/state.js b/server/sonar-web/src/main/js/apps/nav/state.js
new file mode 100644
index 00000000000..f41557663ee
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/state.js
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ var $ = jQuery;
+
+ return Backbone.Model.extend({
+
+ fetchGlobal: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/navigation/global').done(function (r) {
+ that.set(r);
+ });
+ },
+
+ fetchComponent: function () {
+ var that = this,
+ url = baseUrl + '/api/navigation/component',
+ data = { componentKey: this.get('componentKey') };
+ return $.get(url, data).done(function (r) {
+ that.set({ component: r });
+ });
+ },
+
+ fetchSettings: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/navigation/settings').done(function (r) {
+ that.set({ settings: r });
+ });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs b/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs
new file mode 100644
index 00000000000..fcc91b8f24b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs
@@ -0,0 +1 @@
+<img src="{{link '/images/logo.svg'}}" alt="{{t 'layout.sonar.slogan'}}" title="{{t 'layout.sonar.slogan'}}">
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs b/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs
new file mode 100644
index 00000000000..df44ac06af2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs
@@ -0,0 +1 @@
+{{#if labelLocalized}}{{labelLocalized}}{{else}}{{t label}}{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs
new file mode 100644
index 00000000000..11ef4f53ad7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs
@@ -0,0 +1,124 @@
+<div class="container">
+ {{#if component.canBeFavorite}}
+ <div class="navbar-context-favorite">
+ <a class="js-favorite {{#if component.isFavorite}}icon-favorite{{else}}icon-not-favorite{{/if}}"></a>
+ </div>
+ {{/if}}
+ <ul class="nav navbar-nav nav-crumbs">
+ {{#each component.breadcrumbs}}
+ <li>
+ <a href="{{componentPermalink key}}">
+ {{qualifierIcon qualifier}}&nbsp;{{name}}
+ </a>
+ </li>
+ {{/each}}
+ </ul>
+
+ <div class="navbar-right navbar-context-meta">
+ {{#if component.version}}Version {{component.version}}{{/if}}
+ {{#all component.version component.snapshotDate}}/{{/all}}
+ {{#if component.snapshotDate}}{{dt component.snapshotDate}}{{/if}}
+ </div>
+
+ <ul class="nav navbar-nav nav-tabs">
+ <li {{#if isOverviewActive}}class="active"{{/if}}>
+ <a href="{{componentPermalink component.key}}">{{t 'overview.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/components'}}class="active"{{/isActiveLink}}>
+ <a href="{{componentBrowsePermalink component.key}}">{{t 'components.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/component_issues'}}class="active"{{/isActiveLink}}>
+ <a href="{{componentIssuesPermalink component.key}}">{{t 'issues.page'}}</a>
+ </li>
+ {{#if component.configuration.showSettings}}
+ <li class="dropdown {{#if isSettingsActive}}active{{/if}}">
+ <a class="dropdown-toggle navbar-admin-link" data-toggle="dropdown" href="#">{{t 'layout.settings'}}&nbsp;<i
+ class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/project/settings?id=' contextKeyEncoded}}">{{t 'project_settings.page'}}</a>
+ </li>
+ {{#if component.configuration.showQualityProfiles}}
+ <li>
+ <a href="{{link '/project/profile?id=' contextKeyEncoded}}">{{t 'project_quality_profiles.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showQualityGates}}
+ <li>
+ <a href="{{link '/project/qualitygate?id=' contextKeyEncoded}}">{{t 'project_quality_gate.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showManualMeasures}}
+ <li>
+ <a href="{{link '/manual_measures/index?id=' contextKeyEncoded}}">{{t 'manual_measures.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showActionPlans}}
+ <li>
+ <a href="{{link '/action_plans/index?id=' contextKeyEncoded}}">{{t 'action_plans.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showLinks}}
+ <li>
+ <a href="{{link '/project/links?id=' contextKeyEncoded}}">{{t 'project_links.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showPermissions}}
+ <li>
+ <a href="{{link '/project_roles/index?id=' contextKeyEncoded}}">{{t 'permissions.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showHistory}}
+ <li>
+ <a href="{{link '/project/history?id=' contextKeyEncoded}}">{{t 'project_history.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showUpdateKey}}
+ <li>
+ <a href="{{link '/project/key?id=' contextKeyEncoded}}">{{t 'update_key.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showDeletion}}
+ <li>
+ <a href="{{link '/project/deletion?id=' contextKeyEncoded}}">{{t 'deletion.page'}}</a>
+ </li>
+ {{/if}}
+ {{#each component.configuration.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ {{/if}}
+ <li class="dropdown {{#if isMoreActive}}active{{/if}}">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li class="dropdown-header">{{t 'layout.dashboards'}}</li>
+ {{#withoutFirst component.dashboards}}
+ <li>
+ <a href="{{componentDashboardPermalink ../component.key key}}">{{dashboardL10n name}}</a>
+ </li>
+ {{/withoutFirst}}
+ {{#if canManageContextDashboards}}
+ <li class="small-divider"></li>
+ <li>
+ <a href="{{link '/dashboards?resource=' contextKeyEncoded}}">{{t 'dashboard.manage_dashboards'}}</a>
+ </li>
+ {{/if}}
+ <li class="divider"></li>
+ <li class="dropdown-header">Tools</li>
+ {{#if component.isComparable}}
+ <li>
+ <a href="{{link '/comparison/index?resource=' contextKeyEncoded}}">{{t 'comparison.page'}}</a>
+ </li>
+ {{/if}}
+ {{#each component.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs
new file mode 100644
index 00000000000..fcaf9cc2672
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs
@@ -0,0 +1,105 @@
+<div class="container">
+ <div class="navbar-header">
+ <a class="navbar-brand {{#if logoUrl}}navbar-brand-custom{{/if}}" href="{{link '/'}}">
+ {{#if logoUrl}}
+ <img src="{{logoUrl}}" {{#if logoWidth}}width="{{logoWidth}}"{{/if}} height="30"
+ alt="{{t 'layout.sonar.slogan'}}" title="{{t 'layout.sonar.slogan'}}">
+ {{else}}
+ {{> '_nav-logo'}}
+ {{/if}}
+ </a>
+ </div>
+
+ <ul class="nav navbar-nav">
+ <li class="dropdown t-dashboards">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'layout.dashboards'}}&nbsp;<span class="icon-dropdown"></span>
+ </a>
+ <ul class="dropdown-menu">
+ {{#each globalDashboards}}
+ <li>
+ <a href="{{link '/dashboard/index?did=' key}}">{{dashboardL10n name}}</a>
+ </li>
+ {{/each}}
+ {{#if canManageGlobalDashboards}}
+ <li class="divider"></li>
+ <li>
+ <a href="{{link '/dashboards'}}">{{t 'dashboard.manage_dashboards'}}</a>
+ </li>
+ {{/if}}
+ </ul>
+ </li>
+ <li {{#isActiveLink '/issues'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/issues/search'}}">{{t 'issues.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/measures'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/measures/search?qualifiers[]=TRK'}}">{{t 'layout.measures'}}</a>
+ </li>
+ <li {{#isActiveLink '/coding_rules'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/coding_rules'}}">{{t 'coding_rules.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/profiles'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/profiles'}}">{{t 'quality_profiles.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/quality_gates'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/quality_gates'}}">{{t 'quality_gates.page'}}</a>
+ </li>
+ {{#if isUserAdmin}}
+ <li {{#isActiveLink '/settings'}}class="active"{{/isActiveLink}}>
+ <a class="navbar-admin-link" href="{{link '/settings'}}">{{t 'layout.settings'}}</a>
+ </li>
+ {{/if}}
+ <li class="dropdown t-more">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/comparison'}}">{{t 'comparison_global.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/dependencies'}}">{{t 'dependencies.page'}}</a>
+ </li>
+ {{#each globalPages}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ </ul>
+
+ <ul class="nav navbar-nav navbar-right">
+ {{#if user}}
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{userName}}&nbsp;<span class="icon-dropdown"></span>
+ </a>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a href="{{link '/account/index'}}">{{t 'layout.user_panel.my_profile'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/sessions/logout'}}"
+ onclick="if (sonarRecentHistory) { sonarRecentHistory.clear(); }">{{t 'layout.logout'}}</a>
+ </li>
+ </ul>
+ </li>
+ {{else}}
+ <li>
+ <a class="js-login" href="{{link '/sessions/new'}}">{{t 'layout.login'}}</a>
+ </li>
+ {{/if}}
+ <li class="dropdown js-search-dropdown">
+ <a class="dropdown-toggle navbar-search-dropdown js-search-dropdown-toggle" data-toggle="dropdown" href="#">
+ <i class="icon-search navbar-icon"></i>&nbsp;<span class="icon-dropdown"></span>
+ </a>
+
+ <div class="js-search-region dropdown-menu dropdown-menu-right"></div>
+ </li>
+ <li>
+ <a class="js-shortcuts" href="#">
+ <i class="icon-help navbar-icon"></i>
+ </a>
+ </li>
+ </ul>
+
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs
new file mode 100644
index 00000000000..fb76e686612
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs
@@ -0,0 +1 @@
+<span class="note">{{t 'no_results'}}</span>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs
new file mode 100644
index 00000000000..855b5175187
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs
@@ -0,0 +1,22 @@
+{{#notNull extra}}
+ {{#gt index 0}}
+ <div class="divider"></div>
+ {{/gt}}
+ {{#if extra}}
+ <div class="dropdown-header">{{extra}}</div>
+ {{/if}}
+{{/notNull}}
+
+<a href="{{url}}" title="{{name}}">
+ {{#if icon}}<i class="icon-{{icon}} text-text-bottom"></i>{{/if}}
+ {{#if q}}{{qualifierIcon q}}{{/if}}
+ {{#eq q 'FIL'}}
+ {{collapsedDirFromPath name}}{{fileFromPath name}}
+ {{else}}
+ {{#eq q 'UTS'}}
+ {{collapsedDirFromPath name}}{{fileFromPath name}}
+ {{else}}
+ {{name}}
+ {{/eq}}
+ {{/eq}}
+</a>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs
new file mode 100644
index 00000000000..68e1f3ad168
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs
@@ -0,0 +1,8 @@
+<i class="navbar-search-icon icon-search"></i>
+
+<input class="navbar-search-input js-search-input" type="search" name="q" placeholder="{{t 'search_verb'}}"
+ maxlength="30" autocomplete="off">
+
+<div class="js-search-results"></div>
+
+<div class="note navbar-search-subtitle">{{t 'search.shortcut'}}</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
new file mode 100644
index 00000000000..5cc7dbbc1ed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
@@ -0,0 +1,86 @@
+<div class="container">
+ <ul class="nav navbar-nav nav-crumbs">
+ <li>
+ <a href="{{link '/settings'}}">{{t 'layout.settings'}}</a>
+ </li>
+ </ul>
+
+ <ul class="nav navbar-nav nav-tabs">
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.project_settings'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/settings/index'}}">{{t 'settings.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/metrics/index'}}">{{t 'manual_metrics.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/admin_dashboards/index'}}">{{t 'default_dashboards.page'}}</a>
+ </li>
+ {{#each settings.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.security'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/users'}}">{{t 'users.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/groups/index'}}">{{t 'user_groups.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/roles/global'}}">{{t 'global_permissions.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/roles/projects'}}">{{t 'roles.page'}}</a>
+ </li>
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.projects'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ {{#if settings.showProvisioning}}
+ <li>
+ <a href="{{link '/provisioning'}}">{{t 'provisioning.page'}}</a>
+ </li>
+ {{/if}}
+ <li>
+ <a href="{{link '/bulk_deletion'}}">{{t 'bulk_deletion.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/analysis_reports'}}">{{t 'analysis_reports.page'}}</a>
+ </li>
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.system'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ {{#if settings.showUpdateCenter}}
+ <li>
+ <a href="{{link '/updatecenter'}}">{{t 'update_center.page'}}</a>
+ </li>
+ {{/if}}
+ <li>
+ <a href="{{link '/system'}}">{{t 'system_info.page'}}</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
new file mode 100644
index 00000000000..9457d1b7762
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
@@ -0,0 +1,57 @@
+<div class="modal-head">
+ <h2>{{t 'help'}}</h2>
+</div>
+
+<div class="modal-body modal-container">
+ <div class="spacer-bottom">
+ <a href="http://www.sonarqube.org" target="sonar">Community</a> -
+ <a href="http://www.sonarqube.org/documentation" target="sonar_doc">Documentation</a> -
+ <a href="http://www.sonarqube.org/support" target="support">Get Support</a> -
+ <a href="http://redirect.sonarsource.com/doc/plugin-library.html" target="plugins">Plugins</a> -
+ <a href="{{link '/api_documentation'}}">Web Service API</a>
+ </div>
+
+ <h2 class="spacer-top spacer-bottom">{{t 'shortcuts.modal_title'}}</h2>
+
+ <div class="columns">
+ <div class="column-half">
+ <div class="spacer-bottom">
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.global'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">s</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.search'}}</li>
+ <li><span class="shortcut-button">?</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.shortcuts'}}</li>
+ </ul>
+ </div>
+
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.rules'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">&uparrow;</span> <span
+ class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.navigate_between_rules'}}</li>
+ <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.open_details'}}</li>
+ <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.return_to_list'}}</li>
+ </ul>
+ </div>
+
+ <div class="column-half">
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.issues'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">&uparrow;</span> <span
+ class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.navigate_between_issues'}}
+ </li>
+ <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.open_details'}}</li>
+ <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.return_to_list'}}</li>
+ <li><span class="shortcut-button">f</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.do_transition'}}</li>
+ <li><span class="shortcut-button">a</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign'}}</li>
+ <li><span class="shortcut-button">m</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign_to_me'}}</li>
+ <li><span class="shortcut-button">p</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.plan'}}</li>
+ <li><span class="shortcut-button">i</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_severity'}}</li>
+ <li><span class="shortcut-button">c</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.comment'}}</li>
+ <li><span class="shortcut-button">t</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_tags'}}</li>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<div class="modal-foot">
+ <a class="js-modal-close" href="#">{{t 'close'}}</a>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/actions-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/actions-view.js
new file mode 100644
index 00000000000..fc107d10cef
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/actions-view.js
@@ -0,0 +1,116 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './create-profile-view',
+ './restore-profile-view',
+ './restore-built-in-profiles-view',
+ './templates'
+], function (CreateProfileView, RestoreProfileView, RestoreBuiltInProfilesView) {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-profiles-actions'],
+
+ events: {
+ 'click #quality-profiles-create': 'onCreateClick',
+ 'click #quality-profiles-restore': 'onRestoreClick',
+ 'click #quality-profiles-restore-built-in': 'onRestoreBuiltInClick',
+
+ 'click .js-filter-by-language': 'onLanguageClick'
+ },
+
+ onCreateClick: function (e) {
+ e.preventDefault();
+ this.create();
+ },
+
+ onRestoreClick: function (e) {
+ e.preventDefault();
+ this.restore();
+ },
+
+ onRestoreBuiltInClick: function (e) {
+ e.preventDefault();
+ this.restoreBuiltIn();
+ },
+
+ onLanguageClick: function (e) {
+ e.preventDefault();
+ var language = $(e.currentTarget).data('language');
+ this.filterByLanguage(language);
+ },
+
+ create: function () {
+ var that = this;
+ this.requestImporters().done(function () {
+ new CreateProfileView({
+ collection: that.collection,
+ languages: that.languages,
+ importers: that.importers
+ }).render();
+ });
+ },
+
+ restore: function () {
+ new RestoreProfileView({
+ collection: this.collection
+ }).render();
+ },
+
+ restoreBuiltIn: function () {
+ new RestoreBuiltInProfilesView({
+ collection: this.collection,
+ languages: this.languages
+ }).render();
+ },
+
+ requestLanguages: function () {
+ var that = this,
+ url = baseUrl + '/api/languages/list';
+ return $.get(url).done(function (r) {
+ that.languages = r.languages;
+ });
+ },
+
+ requestImporters: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/importers';
+ return $.get(url).done(function (r) {
+ that.importers = r.importers;
+ });
+ },
+
+ filterByLanguage: function (language) {
+ this.selectedLanguage = _.findWhere(this.languages, { key: language });
+ this.render();
+ this.collection.trigger('filter', language);
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canWrite: this.options.canWrite,
+ languages: this.languages,
+ selectedLanguage: this.selectedLanguage
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/app.js b/server/sonar-web/src/main/js/apps/quality-profiles/app.js
new file mode 100644
index 00000000000..30ed90b173c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/app.js
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './router',
+ './controller',
+ './layout',
+ './profiles',
+ './actions-view',
+ './profiles-view'
+], function (Router, Controller, Layout, Profiles, ActionsView, ProfilesView) {
+
+ var $ = jQuery,
+ App = new Marionette.Application();
+
+ App.on('start', function () {
+ // Layout
+ this.layout = new Layout({ el: '#quality-profiles' });
+ this.layout.render();
+ $('#footer').addClass('search-navigator-footer');
+
+ // Profiles List
+ this.profiles = new Profiles();
+
+ // Controller
+ this.controller = new Controller({ app: this });
+
+ // Actions View
+ this.actionsView = new ActionsView({
+ collection: this.profiles,
+ canWrite: this.canWrite
+ });
+ this.actionsView.requestLanguages().done(function () {
+ App.layout.actionsRegion.show(App.actionsView);
+ });
+
+ // Profiles View
+ this.profilesView = new ProfilesView({
+ collection: this.profiles,
+ canWrite: this.canWrite
+ });
+ this.layout.resultsRegion.show(this.profilesView);
+
+ // Router
+ this.router = new Router({ app: this });
+ Backbone.history.start({
+ pushState: true,
+ root: getRoot()
+ });
+
+ });
+
+
+ var requestUser = $.get(baseUrl + '/api/users/current').done(function (r) {
+ App.canWrite = r.permissions.global.indexOf('profileadmin') !== -1;
+ }),
+ requestExporters = $.get(baseUrl + '/api/qualityprofiles/exporters').done(function (r) {
+ App.exporters = r.exporters;
+ });
+
+ $.when(window.requestMessages(), requestUser, requestExporters).done(function () {
+ App.start();
+ });
+
+ function getRoot () {
+ var QUALITY_PROFILES = '/profiles',
+ path = window.location.pathname,
+ pos = path.indexOf(QUALITY_PROFILES);
+ return path.substr(0, pos + QUALITY_PROFILES.length);
+ }
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/change-profile-parent-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/change-profile-parent-view.js
new file mode 100644
index 00000000000..e3c30699fe7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/change-profile-parent-view.js
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-change-profile-parent'],
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+ this.$('select').select2({
+ width: '250px',
+ minimumResultsForSearch: 50
+ });
+ },
+
+ onFormSubmit: function () {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/change_parent',
+ parent = this.$('#change-profile-parent').val(),
+ options = {
+ profileKey: this.model.get('key'),
+ parentKey: parent
+ };
+ return $.ajax({
+ type: 'POST',
+ url: url,
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ that.model.collection.fetch();
+ that.model.trigger('select', that.model);
+ that.close();
+ }).fail(function (jqXHR) {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ that.enableForm();
+ });
+ },
+
+ serializeData: function () {
+ var that = this,
+ profilesData = this.model.collection.toJSON(),
+ profiles = _.filter(profilesData, function (profile) {
+ return profile.language === that.model.get('language') && profile.key !== that.model.id;
+ });
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ profiles: profiles
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/controller.js b/server/sonar-web/src/main/js/apps/quality-profiles/controller.js
new file mode 100644
index 00000000000..282d398dc3f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/controller.js
@@ -0,0 +1,128 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './profile-header-view',
+ './profile-details-view'
+], function (ProfileHeaderView, ProfileDetailsView) {
+
+ var $ = jQuery;
+
+ return Marionette.Controller.extend({
+
+ initialize: function () {
+ this.listenTo(this.options.app.profiles, 'select', this.onProfileSelect);
+ this.listenTo(this.options.app.profiles, 'setAsDefault', this.onProfileSetAsDefault);
+ this.listenTo(this.options.app.profiles, 'destroy', this.onProfileDestroy);
+ },
+
+ index: function () {
+ this.fetchProfiles();
+ },
+
+ show: function (key) {
+ var that = this;
+ this.fetchProfiles().done(function () {
+ var profile = that.options.app.profiles.findWhere({ key: key });
+ if (profile != null) {
+ profile.trigger('select', profile, { trigger: false });
+ }
+ });
+ },
+
+ changelog: function (key, since, to) {
+ var that = this;
+ this.anchor = 'changelog';
+ this.fetchProfiles().done(function () {
+ var profile = that.options.app.profiles.findWhere({ key: key });
+ if (profile != null) {
+ profile.trigger('select', profile, { trigger: false });
+ profile.fetchChangelog({ since: since, to: to });
+ }
+ });
+ },
+
+ compare: function (key, withKey) {
+ var that = this;
+ this.anchor = 'comparison';
+ this.fetchProfiles().done(function () {
+ var profile = that.options.app.profiles.findWhere({ key: key });
+ if (profile != null) {
+ profile.trigger('select', profile, { trigger: false });
+ profile.compareWith(withKey);
+ }
+ });
+ },
+
+ onProfileSelect: function (profile, options) {
+ var that = this,
+ key = profile.get('key'),
+ route = 'show?key=' + encodeURIComponent(key),
+ opts = _.defaults(options || {}, { trigger: true });
+ if (opts.trigger) {
+ this.options.app.router.navigate(route);
+ }
+ this.options.app.profilesView.highlight(key);
+ this.fetchProfile(profile).done(function () {
+ var profileHeaderView = new ProfileHeaderView({
+ model: profile,
+ canWrite: that.options.app.canWrite
+ });
+ that.options.app.layout.headerRegion.show(profileHeaderView);
+
+ var profileDetailsView = new ProfileDetailsView({
+ model: profile,
+ canWrite: that.options.app.canWrite,
+ exporters: that.options.app.exporters,
+ anchor: that.anchor
+ });
+ that.options.app.layout.detailsRegion.show(profileDetailsView);
+ });
+ },
+
+ onProfileSetAsDefault: function (profile) {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/set_default',
+ key = profile.get('key'),
+ options = { profileKey: key };
+ return $.post(url, options).done(function () {
+ profile.set({ isDefault: true });
+ that.fetchProfiles();
+ });
+ },
+
+ onProfileDestroy: function () {
+ this.options.app.router.navigate('');
+ this.options.app.layout.headerRegion.reset();
+ this.options.app.layout.detailsRegion.reset();
+ this.options.app.layout.renderIntro();
+ this.options.app.profilesView.highlight(null);
+ },
+
+ fetchProfiles: function () {
+ return this.options.app.profiles.fetch({ reset: true });
+ },
+
+ fetchProfile: function (profile) {
+ return profile.fetch();
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/copy-profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/copy-profile-view.js
new file mode 100644
index 00000000000..446254c1c4c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/copy-profile-view.js
@@ -0,0 +1,69 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './profile',
+ './templates'
+], function (ModalFormView, Profile) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-copy-profile'],
+
+ onFormSubmit: function () {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/copy',
+ name = this.$('#copy-profile-name').val(),
+ options = {
+ fromKey: this.model.get('key'),
+ toName: name
+ };
+ return $.ajax({
+ type: 'POST',
+ url: url,
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function (r) {
+ that.addProfile(r);
+ that.close();
+ }).fail(function (jqXHR) {
+ that.enableForm();
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ });
+ },
+
+ addProfile: function (profileData) {
+ var profile = new Profile(profileData);
+ this.model.collection.add([profile]);
+ profile.trigger('select', profile);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/create-profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/create-profile-view.js
new file mode 100644
index 00000000000..ec5a0923d4b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/create-profile-view.js
@@ -0,0 +1,106 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ 'components/common/file-upload',
+ './profile',
+ './templates'
+], function (ModalFormView, uploader, Profile) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-create-profile'],
+
+ events: function () {
+ return _.extend(ModalFormView.prototype.events.apply(this, arguments), {
+ 'change #create-profile-language': 'onLanguageChange'
+ });
+ },
+
+ onFormSubmit: function (e) {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.sendRequest(e);
+ },
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+ this.$('select').select2({
+ width: '250px',
+ minimumResultsForSearch: 50
+ });
+ this.onLanguageChange();
+ },
+
+ onLanguageChange: function () {
+ var that = this;
+ var language = this.$('#create-profile-language').val();
+ var importers = this.getImportersForLanguages(language);
+ this.$('.js-importer').each(function () {
+ that.emptyInput($(this));
+ $(this).addClass('hidden');
+ });
+ importers.forEach(function (importer) {
+ that.$('.js-importer[data-key="' + importer.key + '"]').removeClass('hidden');
+ });
+ },
+
+ emptyInput: function (e) {
+ e.wrap('<form>').closest('form').get(0).reset();
+ e.unwrap();
+ },
+
+ sendRequest: function (e) {
+ var that = this;
+ uploader({ form: $(e.currentTarget) }).done(function (r) {
+ if (_.isArray(r.errors) || _.isArray(r.warnings)) {
+ that.showErrors(r.errors, r.warnings);
+ } else {
+ that.addProfile(r.profile);
+ that.close();
+ }
+ });
+ },
+
+ addProfile: function (profileData) {
+ var profile = new Profile(profileData);
+ this.collection.add([profile]);
+ profile.trigger('select', profile);
+ },
+
+ getImportersForLanguages: function (language) {
+ if (language != null) {
+ return this.options.importers.filter(function (importer) {
+ return importer.languages.indexOf(language) !== -1;
+ });
+ } else {
+ return [];
+ }
+ },
+
+ serializeData: function () {
+ return _.extend(ModalFormView.prototype.serializeData.apply(this, arguments), {
+ languages: this.options.languages,
+ importers: this.options.importers
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/delete-profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/delete-profile-view.js
new file mode 100644
index 00000000000..3b2ccee277a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/delete-profile-view.js
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-delete-profile'],
+
+ modelEvents: {
+ 'destroy': 'close'
+ },
+
+ onFormSubmit: function () {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/delete',
+ options = { profileKey: this.model.get('key') };
+ return $.ajax({
+ type: 'POST',
+ url: url,
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ that.model.collection.fetch();
+ that.model.trigger('destroy', that.model, that.model.collection);
+ }).fail(function (jqXHR) {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ that.enableForm();
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/helpers.js b/server/sonar-web/src/main/js/apps/quality-profiles/helpers.js
new file mode 100644
index 00000000000..d241c123e17
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/helpers.js
@@ -0,0 +1,50 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+(function () {
+
+ Handlebars.registerHelper('profileUrl', function (key) {
+ return baseUrl + '/profiles/show?key=' + encodeURIComponent(key);
+ });
+
+ Handlebars.registerHelper('exporterUrl', function (profile, exporterKey) {
+ var url = baseUrl + '/api/qualityprofiles/export';
+ url += '?language=' + encodeURIComponent(profile.language);
+ url += '&name=' + encodeURIComponent(profile.name);
+ if (exporterKey != null) {
+ url += '&exporterKey=' + encodeURIComponent(exporterKey);
+ }
+ return url;
+ });
+
+ Handlebars.registerHelper('severityChangelog', function (severity) {
+ var label = '<i class="icon-severity-' + severity.toLowerCase() + '"></i>&nbsp;' + t('severity', severity),
+ message = tp('quality_profiles.severity_set_to_x', label);
+ return new Handlebars.SafeString(message);
+ });
+
+ Handlebars.registerHelper('parameterChangelog', function (value, parameter) {
+ if (parameter) {
+ return new Handlebars.SafeString(tp('quality_profiles.parameter_set_to_x', value, parameter));
+ } else {
+ return new Handlebars.SafeString(tp('quality_profiles.changelog.parameter_reset_to_default_value_x', parameter));
+ }
+ });
+
+})();
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/intro-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/intro-view.js
new file mode 100644
index 00000000000..abfb416541b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/intro-view.js
@@ -0,0 +1,28 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-profiles-intro']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/layout.js b/server/sonar-web/src/main/js/apps/quality-profiles/layout.js
new file mode 100644
index 00000000000..9099a11fa78
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/layout.js
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './intro-view',
+ './templates'
+], function (IntroView) {
+
+ var $ = jQuery;
+
+ return Marionette.Layout.extend({
+ template: Templates['quality-profiles-layout'],
+
+ regions: {
+ headerRegion: '.search-navigator-workspace-header',
+ actionsRegion: '.search-navigator-filters',
+ resultsRegion: '.quality-profiles-results',
+ detailsRegion: '.search-navigator-workspace-details'
+ },
+
+ onRender: function () {
+ var navigator = $('.search-navigator');
+ navigator.addClass('sticky search-navigator-extended-view');
+ var top = navigator.offset().top;
+ this.$('.search-navigator-workspace-header').css({ top: top });
+ this.$('.search-navigator-side').css({ top: top }).isolatedScroll();
+ this.renderIntro();
+ },
+
+ renderIntro: function () {
+ this.detailsRegion.show(new IntroView());
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile-changelog-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile-changelog-view.js
new file mode 100644
index 00000000000..28838846bba
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile-changelog-view.js
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-profile-changelog'],
+
+ events: {
+ 'submit #quality-profile-changelog-form': 'onFormSubmit',
+ 'click .js-show-more-changelog': 'onShowMoreChangelogClick',
+ 'click .js-hide-changelog': 'onHideChangelogClick'
+ },
+
+ onFormSubmit: function (e) {
+ e.preventDefault();
+ this.model.fetchChangelog(this.getSearchParameters());
+ },
+
+ onShowMoreChangelogClick: function (e) {
+ e.preventDefault();
+ this.model.fetchMoreChangelog();
+ },
+
+ onHideChangelogClick: function (e) {
+ e.preventDefault();
+ this.model.resetChangelog();
+ },
+
+ getSearchParameters: function () {
+ var form = this.$('#quality-profile-changelog-form');
+ return {
+ since: form.find('[name="since"]').val(),
+ to: form.find('[name="to"]').val()
+ };
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile-comparison-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile-comparison-view.js
new file mode 100644
index 00000000000..d5f5ea37d4b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile-comparison-view.js
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-profile-comparison'],
+
+ events: {
+ 'submit #quality-profile-comparison-form': 'onFormSubmit'
+ },
+
+ onRender: function () {
+ this.$('select').select2({
+ width: '250px',
+ minimumResultsForSearch: 50
+ });
+ },
+
+ onFormSubmit: function (e) {
+ e.preventDefault();
+ var withKey = this.$('#quality-profile-comparison-with-key').val();
+ this.model.compareWith(withKey);
+ },
+
+ getProfilesForComparison: function () {
+ var profiles = this.model.collection.toJSON(),
+ key = this.model.id,
+ language = this.model.get('language');
+ return profiles.filter(function (profile) {
+ return profile.language === language && key !== profile.key;
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ profiles: this.getProfilesForComparison()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile-details-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile-details-view.js
new file mode 100644
index 00000000000..ede00f14d73
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile-details-view.js
@@ -0,0 +1,158 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './change-profile-parent-view',
+ './profile-changelog-view',
+ './profile-comparison-view',
+ 'components/common/select-list',
+ './helpers',
+ './templates'
+], function (ChangeProfileParentView, ProfileChangelogView, ProfileComparisonView) {
+
+ var $ = jQuery;
+
+ return Marionette.Layout.extend({
+ template: Templates['quality-profiles-profile-details'],
+
+ regions: {
+ changelogRegion: '#quality-profile-changelog',
+ comparisonRegion: '#quality-profile-comparison'
+ },
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click .js-profile': 'onProfileClick',
+ 'click #quality-profile-change-parent': 'onChangeParentClick'
+ },
+
+ onRender: function () {
+ if (!this.model.get('isDefault')) {
+ this.initProjectsSelect();
+ }
+ this.changelogRegion.show(new ProfileChangelogView({ model: this.model }));
+ this.comparisonRegion.show(new ProfileComparisonView({ model: this.model }));
+ if (this.options.anchor === 'changelog') {
+ this.scrollToChangelog();
+ }
+ if (this.options.anchor === 'comparison') {
+ this.scrollToComparison();
+ }
+ },
+
+ initProjectsSelect: function () {
+ var key = this.model.get('key');
+ this.projectsSelectList = new window.SelectList({
+ el: this.$('#quality-profile-projects-list'),
+ width: '100%',
+ readOnly: !this.options.canWrite,
+ focusSearch: false,
+ format: function (item) {
+ return item.name;
+ },
+ searchUrl: baseUrl + '/api/qualityprofiles/projects?key=' + encodeURIComponent(key),
+ selectUrl: baseUrl + '/api/qualityprofiles/add_project',
+ deselectUrl: baseUrl + '/api/qualityprofiles/remove_project',
+ extra: {
+ profileKey: key
+ },
+ selectParameter: 'projectUuid',
+ selectParameterValue: 'uuid',
+ labels: {
+ selected: t('quality_gates.projects.with'),
+ deselected: t('quality_gates.projects.without'),
+ all: t('quality_gates.projects.all'),
+ noResults: t('quality_gates.projects.noResults')
+ },
+ tooltips: {
+ select: t('quality_gates.projects.select_hint'),
+ deselect: t('quality_gates.projects.deselect_hint')
+ }
+ });
+ this.listenTo(this.projectsSelectList.collection, 'change:selected', this.onProjectsChange);
+ },
+
+ onProfileClick: function (e) {
+ var key = $(e.currentTarget).data('key'),
+ profile = this.model.collection.get(key);
+ if (profile != null) {
+ e.preventDefault();
+ this.model.collection.trigger('select', profile);
+ }
+ },
+
+ onChangeParentClick: function (e) {
+ e.preventDefault();
+ this.changeParent();
+ },
+
+ onProjectsChange: function () {
+ this.model.collection.updateForLanguage(this.model.get('language'));
+ },
+
+ changeParent: function () {
+ new ChangeProfileParentView({
+ model: this.model
+ }).render();
+ },
+
+ scrollTo: function (selector) {
+ var el = this.$(selector),
+ parent = el.scrollParent();
+ var elOffset = el.offset(),
+ parentOffset = parent.offset();
+ if (parent.is(document)) {
+ parentOffset = { top: 0 };
+ }
+ if (elOffset != null && parentOffset != null) {
+ var scrollTop = elOffset.top - parentOffset.top - 53;
+ parent.scrollTop(scrollTop);
+ }
+ },
+
+ scrollToChangelog: function () {
+ this.scrollTo('#quality-profile-changelog');
+ },
+
+ scrollToComparison: function () {
+ this.scrollTo('#quality-profile-comparison');
+ },
+
+ getExporters: function () {
+ var language = this.model.get('language');
+ return this.options.exporters.filter(function (exporter) {
+ return exporter.languages.indexOf(language) !== -1;
+ });
+ },
+
+ serializeData: function () {
+ var key = this.model.get('key'),
+ rulesSearchUrl = '/coding_rules#qprofile=' + encodeURIComponent(key) + '|activation=true';
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ rulesSearchUrl: rulesSearchUrl,
+ canWrite: this.options.canWrite,
+ exporters: this.getExporters()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile-header-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile-header-view.js
new file mode 100644
index 00000000000..57a5d310736
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile-header-view.js
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './copy-profile-view',
+ './rename-profile-view',
+ './delete-profile-view',
+ './templates'
+], function (ProfileCopyView, ProfileRenameView, ProfileDeleteView) {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-profiles-profile-header'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click #quality-profile-backup': 'onBackupClick',
+ 'click #quality-profile-copy': 'onCopyClick',
+ 'click #quality-profile-rename': 'onRenameClick',
+ 'click #quality-profile-set-as-default': 'onDefaultClick',
+ 'click #quality-profile-delete': 'onDeleteClick'
+ },
+
+ onBackupClick: function (e) {
+ $(e.currentTarget).blur();
+ },
+
+ onCopyClick: function (e) {
+ e.preventDefault();
+ this.copy();
+ },
+
+ onRenameClick: function (e) {
+ e.preventDefault();
+ this.rename();
+ },
+
+ onDefaultClick: function (e) {
+ e.preventDefault();
+ this.setAsDefault();
+ },
+
+ onDeleteClick: function (e) {
+ e.preventDefault();
+ this.delete();
+ },
+
+ copy: function () {
+ new ProfileCopyView({ model: this.model }).render();
+ },
+
+ rename: function () {
+ new ProfileRenameView({ model: this.model }).render();
+ },
+
+ setAsDefault: function () {
+ this.model.trigger('setAsDefault', this.model);
+ },
+
+ delete: function () {
+ new ProfileDeleteView({ model: this.model }).render();
+ },
+
+ serializeData: function () {
+ var key = this.model.get('key');
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ encodedKey: encodeURIComponent(key),
+ canWrite: this.options.canWrite
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile-view.js
new file mode 100644
index 00000000000..03e5352e2d0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile-view.js
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ tagName: 'a',
+ className: 'list-group-item',
+ template: Templates['quality-profiles-profile'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click': 'onClick'
+ },
+
+ onRender: function () {
+ this.$el.toggleClass('active', this.options.highlighted);
+ this.$el.attr('data-key', this.model.id);
+ this.$el.attr('data-language', this.model.get('language'));
+ this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
+ },
+
+ onClose: function () {
+ this.$('[data-toggle="tooltip"]').tooltip('destroy');
+ },
+
+ onClick: function (e) {
+ e.preventDefault();
+ this.model.trigger('select', this.model);
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ projectCountFormatted: window.formatMeasure(this.model.get('projectCount'), 'INT')
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profile.js b/server/sonar-web/src/main/js/apps/quality-profiles/profile.js
new file mode 100644
index 00000000000..7315821cf1c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profile.js
@@ -0,0 +1,133 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ var $ = jQuery;
+
+ return Backbone.Model.extend({
+ idAttribute: 'key',
+
+ defaults: {
+ activeRuleCount: 0,
+ projectCount: 0
+ },
+
+ fetch: function () {
+ var that = this;
+ this.fetchChanged = {};
+ return $.when(
+ this.fetchProfileRules(),
+ this.fetchInheritance()
+ ).done(function () {
+ that.set(that.fetchChanged);
+ });
+ },
+
+ fetchProfileRules: function () {
+ var that = this,
+ url = baseUrl + '/api/rules/search',
+ key = this.id,
+ options = {
+ ps: 1,
+ facets: 'active_severities',
+ qprofile: key,
+ activation: 'true'
+ };
+ return $.get(url, options).done(function (r) {
+ var severityFacet = _.findWhere(r.facets, { property: 'active_severities' });
+ if (severityFacet != null) {
+ var severities = severityFacet.values,
+ severityComparator = function (s) {
+ return window.severityColumnsComparator(s.val);
+ },
+ sortedSeverities = _.sortBy(severities, severityComparator);
+ _.extend(that.fetchChanged, { rulesSeverities: sortedSeverities });
+ }
+ });
+ },
+
+ fetchInheritance: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/inheritance',
+ options = { profileKey: this.id };
+ return $.get(url, options).done(function (r) {
+ _.extend(that.fetchChanged, r.profile, {
+ ancestors: r.ancestors,
+ children: r.children
+ });
+ });
+ },
+
+ fetchChangelog: function (options) {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/changelog',
+ opts = _.extend({}, options, { profileKey: this.id });
+ return $.get(url, opts).done(function (r) {
+ that.set({
+ events: r.events,
+ eventsPage: r.p,
+ totalEvents: r.total,
+ eventsParameters: _.clone(options)
+ });
+ });
+ },
+
+ fetchMoreChangelog: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/changelog',
+ page = this.get('eventsPage') || 0,
+ parameters = this.get('eventsParameters') || {},
+ opts = _.extend({}, parameters, { profileKey: this.id, p: page + 1 });
+ return $.get(url, opts).done(function (r) {
+ var events = that.get('events') || [];
+ that.set({
+ events: [].concat(events, r.events),
+ eventsPage: r.p,
+ totalEvents: r.total
+ });
+ });
+ },
+
+
+ resetChangelog: function () {
+ this.unset('events', { silent: true });
+ this.unset('eventsPage', { silent: true });
+ this.unset('totalEvents');
+ },
+
+ compareWith: function (withKey) {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/compare',
+ options = { leftKey: this.id, rightKey: withKey };
+ return $.get(url, options).done(function (r) {
+ var comparison = _.extend(r, {
+ inLeftSize: _.size(r.inLeft),
+ inRightSize: _.size(r.inRight),
+ modifiedSize: _.size(r.modified)
+ });
+ that.set({
+ comparison: comparison,
+ comparedWith: withKey
+ });
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profiles-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/profiles-view.js
new file mode 100644
index 00000000000..2a433476dcd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profiles-view.js
@@ -0,0 +1,83 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './profile-view',
+ './templates'
+], function (ProfileView) {
+
+ return Marionette.CompositeView.extend({
+ className: 'list-group',
+ template: Templates['quality-profiles-profiles'],
+ languageTemplate: Templates['quality-profiles-profiles-language'],
+ itemView: ProfileView,
+ itemViewContainer: '.js-list',
+
+ collectionEvents: {
+ 'filter': 'filterByLanguage'
+ },
+
+ itemViewOptions: function (model) {
+ return {
+ collectionView: this,
+ highlighted: model.get('key') === this.highlighted
+ };
+ },
+
+ highlight: function (key) {
+ this.highlighted = key;
+ this.render();
+ },
+
+ appendHtml: function (compositeView, itemView, index) {
+ var $container = this.getItemViewContainer(compositeView),
+ model = this.collection.at(index);
+ if (model != null) {
+ var prev = this.collection.at(index - 1),
+ putLanguage = prev == null;
+ if (prev != null) {
+ var lang = model.get('language'),
+ prevLang = prev.get('language');
+ if (lang !== prevLang) {
+ putLanguage = true;
+ }
+ }
+ if (putLanguage) {
+ $container.append(this.languageTemplate(model.toJSON()));
+ }
+ }
+ return $container.append(itemView.el);
+ },
+
+ closeChildren: function () {
+ Marionette.CompositeView.prototype.closeChildren.apply(this, arguments);
+ this.$('.js-list-language').remove();
+ },
+
+ filterByLanguage: function (language) {
+ if (language) {
+ this.$('[data-language]').addClass('hidden');
+ this.$('[data-language="' + language + '"]').removeClass('hidden');
+ } else {
+ this.$('[data-language]').removeClass('hidden');
+ }
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/profiles.js b/server/sonar-web/src/main/js/apps/quality-profiles/profiles.js
new file mode 100644
index 00000000000..8b556ce8aa5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/profiles.js
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './profile'
+], function (Profile) {
+
+ return Backbone.Collection.extend({
+ model: Profile,
+ url: baseUrl + '/api/qualityprofiles/search',
+ comparator: 'key',
+
+ parse: function (r) {
+ return r.profiles;
+ },
+
+ updateForLanguage: function (language) {
+ this.fetch({
+ data: {
+ language: language
+ },
+ merge: true,
+ reset: false,
+ remove: false
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/rename-profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/rename-profile-view.js
new file mode 100644
index 00000000000..b61d9d8a794
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/rename-profile-view.js
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-rename-profile'],
+
+ onFormSubmit: function () {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/rename',
+ name = this.$('#rename-profile-name').val(),
+ options = {
+ key: this.model.get('key'),
+ name: name
+ };
+ return $.ajax({
+ type: 'POST',
+ url: url,
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ that.model.set({ name: name });
+ that.close();
+ }).fail(function (jqXHR) {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/restore-built-in-profiles-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/restore-built-in-profiles-view.js
new file mode 100644
index 00000000000..645197f7b55
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/restore-built-in-profiles-view.js
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalFormView) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-restore-built-in-profiles'],
+
+ onFormSubmit: function (e) {
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ onRender: function () {
+ ModalFormView.prototype.onRender.apply(this, arguments);
+ this.$('select').select2({
+ width: '250px',
+ minimumResultsForSearch: 50
+ });
+ },
+
+ sendRequest: function () {
+ var that = this,
+ url = baseUrl + '/api/qualityprofiles/restore_built_in',
+ options = {
+ language: this.$('#restore-built-in-profiles-language').val()
+ };
+ return $.ajax({
+ type: 'POST',
+ url: url,
+ data: options,
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ }).done(function () {
+ that.collection.fetch({ reset: true });
+ that.collection.trigger('destroy');
+ that.close();
+ }).fail(function (jqXHR) {
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ that.enableForm();
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(ModalFormView.prototype.serializeData.apply(this, arguments), {
+ languages: this.options.languages
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/restore-profile-view.js b/server/sonar-web/src/main/js/apps/quality-profiles/restore-profile-view.js
new file mode 100644
index 00000000000..fca5b66476f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/restore-profile-view.js
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modal-form',
+ 'components/common/file-upload',
+ './profile',
+ './templates'
+], function (ModalFormView, uploader, Profile) {
+
+ var $ = jQuery;
+
+ return ModalFormView.extend({
+ template: Templates['quality-profiles-restore-profile'],
+
+ onFormSubmit: function (e) {
+ var that = this;
+ ModalFormView.prototype.onFormSubmit.apply(this, arguments);
+ uploader({ form: $(e.currentTarget) }).done(function (r) {
+ if (_.isArray(r.errors) || _.isArray(r.warnings)) {
+ that.showErrors(r.errors, r.warnings);
+ } else {
+ that.addProfile(r.profile);
+ that.close();
+ }
+ });
+ },
+
+ addProfile: function (profileData) {
+ var profile = new Profile(profileData);
+ this.collection.add([profile], { merge: true });
+ var addedProfile = this.collection.get(profile.id);
+ if (addedProfile != null) {
+ addedProfile.trigger('select', addedProfile);
+ }
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/router.js b/server/sonar-web/src/main/js/apps/quality-profiles/router.js
new file mode 100644
index 00000000000..7970d6d2ea6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/router.js
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ return Backbone.Router.extend({
+ routes: {
+ '': 'index',
+ 'index': 'index',
+ 'show?key=:key': 'show',
+ 'changelog*': 'changelog',
+ 'compare*': 'compare'
+ },
+
+ initialize: function (options) {
+ this.app = options.app;
+ },
+
+ index: function () {
+ this.app.controller.index();
+ },
+
+ show: function (key) {
+ this.app.controller.show(key);
+ },
+
+ changelog: function () {
+ var params = window.getQueryParams();
+ this.app.controller.changelog(params.key, params.since, params.to);
+ },
+
+ compare: function () {
+ var params = window.getQueryParams();
+ if (params.key && params.withKey) {
+ this.app.controller.compare(params.key, params.withKey);
+ }
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-changelog.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-changelog.hbs
new file mode 100644
index 00000000000..92d87282448
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-changelog.hbs
@@ -0,0 +1,63 @@
+<header class="page-header">
+ <div class="page-title">
+ <span class="h3">{{t 'changelog'}}</span>
+ </div>
+</header>
+
+<form class="spacer-bottom" id="quality-profile-changelog-form">
+ {{t 'quality_profiles.changelog_from'}}
+ <input name="since" type="text" value="{{eventsParameters.since}}" placeholder="1970-01-31">
+ {{t 'to'}}
+ <input name="to" type="text" value="{{eventsParameters.to}}" placeholder="1970-01-31">
+ <button id="quality-profile-changelog-form-submit">{{t 'search_verb'}}</button>
+</form>
+
+{{#notEmpty events}}
+ <table class="width-100 data zebra">
+ <thead>
+ <tr>
+ <th>{{t 'date'}}</th>
+ <th>{{t 'user'}}</th>
+ <th>{{t 'action'}}</th>
+ <th>{{t 'rule'}}</th>
+ <th>{{t 'parameters'}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#each events}}
+ <tr>
+ <td class="text-top nowrap thin">{{dt date}}</td>
+ <td class="text-top nowrap thin">{{default authorName 'System'}}</td>
+ <td class="text-top nowrap">{{t 'quality_profiles.changelog' action}}</td>
+ <td class="text-top"><a href="{{rulePermalink ruleKey}}">{{ruleName}}</a></td>
+ <td class="text-top thin">
+ <ul>
+ {{#each params}}
+ <li>
+ {{#eq @key 'severity'}}
+ <span class="nowrap">{{severityChangelog this}}</span>
+ {{else}}
+ {{parameterChangelog @key this}}
+ {{/eq}}
+ </li>
+ {{/each}}
+ </ul>
+ </td>
+ </tr>
+ {{/each}}
+ </tbody>
+ </table>
+
+
+ <p class="spacer-top text-center">
+ {{#unlessLength events totalEvents}}
+ <a class="js-show-more-changelog spacer-right" href="#">{{t 'show_more'}}</a>
+ {{/unlessLength}}
+ <a class="js-hide-changelog" href="#">{{t 'hide'}}</a>
+ </p>
+
+{{else}}
+ {{#notNull totalEvents}}
+ <div class="alert alert-info">{{t 'quality_profiles.changelog.empty'}}</div>
+ {{/notNull}}
+{{/notEmpty}}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-comparison.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-comparison.hbs
new file mode 100644
index 00000000000..e6eeeb28eb5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profile-comparison.hbs
@@ -0,0 +1,85 @@
+<header class="page-header">
+ <div class="page-title">
+ <span class="h3">{{t 'compare'}}</span>
+ </div>
+</header>
+
+{{#notEmpty profiles}}
+ <form class="spacer-bottom" id="quality-profile-comparison-form">
+ <label class="text-middle" for="quality-profile-comparison-with-key">With</label>
+ <select id="quality-profile-comparison-with-key">
+ {{#each profiles}}
+ <option value="{{key}}" {{#eq key ../comparedWith}}selected{{/eq}}>{{name}}</option>
+ {{/each}}
+ </select>
+ <button class="text-middle" id="quality-profile-comparison-form-submit">{{t 'compare'}}</button>
+ </form>
+{{else}}
+ <div class="alert alert-info">There is no profiles to compare with.</div>
+{{/notEmpty}}
+
+{{#notNull comparison}}
+ <table class="width-100 data zebra">
+ {{#notEmpty comparison.inLeft}}
+ <tr>
+ <td class="width-50"><h6>{{tp 'quality_profiles.x_rules_only_in' comparison.inLeftSize}} {{comparison.left.name}}</h6></td>
+ <td class="width-50"></td>
+ </tr>
+ {{#each comparison.inLeft}}
+ <tr class="js-comparison-in-left">
+ <td class="width-50">{{severityIcon severity}}&nbsp;<a href="{{rulePermalink key}}">{{name}}</a></td>
+ <td class="width-50"></td>
+ </tr>
+ {{/each}}
+ {{/notEmpty}}
+
+ {{#notEmpty comparison.inRight}}
+ <tr>
+ <td class="width-50"></td>
+ <td class="width-50"><h6>{{tp 'quality_profiles.x_rules_only_in' comparison.inRightSize}} {{comparison.right.name}}</h6></td>
+ </tr>
+ {{#each comparison.inRight}}
+ <tr class="js-comparison-in-right">
+ <td class="width-50"></td>
+ <td class="width-50">{{severityIcon severity}}&nbsp;<a href="{{rulePermalink key}}">{{name}}</a></td>
+ </tr>
+ {{/each}}
+ {{/notEmpty}}
+
+ {{#notEmpty comparison.modified}}
+ <tr>
+ <td class="text-center width-50" colspan="2">
+ <h6>{{tp 'quality_profiles.x_rules_have_different_configuration' comparison.modifiedSize}}</h6>
+ </td>
+ </tr>
+ <tr>
+ <td class="width-50"><h6>{{comparison.left.name}}</h6></td>
+ <td class="width-50"><h6>{{comparison.right.name}}</h6></td>
+ </tr>
+ {{#each comparison.modified}}
+ <tr class="js-comparison-modified">
+ <td class="width-50">
+ <p>{{severityIcon left.severity}}&nbsp;<a href="{{rulePermalink key}}">{{name}}</a></p>
+ {{#notNull left.params}}
+ <ul>
+ {{#each left.params}}
+ <li class="spacer-top"><code>{{@key}}: {{this}}</code></li>
+ {{/each}}
+ </ul>
+ {{/notNull}}
+ </td>
+ <td class="width-50">
+ <p>{{severityIcon right.severity}}&nbsp;<a href="{{rulePermalink key}}">{{name}}</a></p>
+ {{#notNull right.params}}
+ <ul>
+ {{#each right.params}}
+ <li class="spacer-top"><code>{{@key}}: {{this}}</code></li>
+ {{/each}}
+ </ul>
+ {{/notNull}}
+ </td>
+ </tr>
+ {{/each}}
+ {{/notEmpty}}
+ </table>
+{{/notNull}}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-actions.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-actions.hbs
new file mode 100644
index 00000000000..2d887af6df3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-actions.hbs
@@ -0,0 +1,39 @@
+<header class="page-header">
+ <h1 class="page-title">{{t 'quality_profiles.page'}}</h1>
+
+ {{#if canWrite}}
+ <div class="page-actions">
+ <div class="button-group dropdown">
+ <button id="quality-profiles-create">{{t 'create'}}</button>
+ <a class="button dropdown-toggle" href="#" data-toggle="dropdown"><i class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a id="quality-profiles-restore" href="#">{{t 'quality_profiles.restore_profile'}}</a>
+ </li>
+ <li>
+ <a id="quality-profiles-restore-built-in" href="#">{{t 'quality_profiles.restore_built_in_profiles'}}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ {{/if}}
+</header>
+
+<div class="dropdown" id="quality-profiles-filter-by-language">
+ <span>Show:</span>
+ {{#if selectedLanguage}}
+ <a class="dropdown-toggle link-no-underline" href="#" data-toggle="dropdown">
+ {{tp 'quality_profiles.x_profiles' selectedLanguage.name}} <i class="icon-dropdown"></i>
+ </a>
+ {{else}}
+ <a class="dropdown-toggle link-no-underline" href="#" data-toggle="dropdown">
+ {{t 'quality_profiles.all_profiles'}} <i class="icon-dropdown"></i>
+ </a>
+ {{/if}}
+ <ul class="dropdown-menu">
+ <li><a class="js-filter-by-language" href="#">{{t 'quality_profiles.all_profiles'}}</a></li>
+ {{#each languages}}
+ <li><a class="js-filter-by-language" href="#" data-language="{{key}}">{{tp 'quality_profiles.x_profiles' name}}</a></li>
+ {{/each}}
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs
new file mode 100644
index 00000000000..9cbb1778a7f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs
@@ -0,0 +1,21 @@
+<form id="change-profile-parent-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_profiles.change_parent'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="change-profile-parent">Parent: <em class="mandatory">*</em></label>
+ <select id="change-profile-parent" name="parentKey">
+ <option value="">{{t 'none'}}</option>
+ {{#each profiles}}
+ <option value="{{key}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="change-profile-parent-submit">{{t 'change_verb'}}</button>
+ <a href="#" class="js-modal-close" id="change-profile-parent-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs
new file mode 100644
index 00000000000..6cc034f1209
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs
@@ -0,0 +1,16 @@
+<form id="copy-profile-form">
+ <div class="modal-head">
+ <h2>{{tp 'quality_profiles.copy_x_title' name languageName}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="copy-profile-name">{{t 'quality_profiles.copy_new_name'}}<em class="mandatory">*</em></label>
+ <input id="copy-profile-name" name="name" type="text" size="50" maxlength="100" required>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="copy-profile-submit">{{t 'copy'}}</button>
+ <a href="#" class="js-modal-close" id="copy-profile-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
new file mode 100644
index 00000000000..50fbc195fff
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
@@ -0,0 +1,32 @@
+<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data"
+ method="POST">
+ <div class="modal-head">
+ <h2>{{t 'quality_profiles.new_profile'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label>
+ <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required>
+ </div>
+ <div class="modal-field spacer-bottom">
+ <label for="create-profile-language">{{t 'language'}}<em class="mandatory">*</em></label>
+ <select id="create-profile-language" name="language" required>
+ {{#each languages}}
+ <option value="{{key}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+ {{#each importers}}
+ <div class="modal-field spacer-bottom js-importer" data-key="{{key}}">
+ <label for="create-profile-form-backup-{{key}}">{{name}}</label>
+ <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file">
+ <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p>
+ </div>
+ {{/each}}
+ </div>
+ <div class="modal-foot">
+ <button id="create-profile-submit">{{t 'create'}}</button>
+ <a href="#" class="js-modal-close" id="create-profile-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs
new file mode 100644
index 00000000000..bd011a01694
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs
@@ -0,0 +1,13 @@
+<form id="delete-profile-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_profiles.delete_confirm_title'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <p>{{tp 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants' name languageName}}</p>
+ </div>
+ <div class="modal-foot">
+ <button id="delete-profile-submit">{{t 'delete'}}</button>
+ <a href="#" class="js-modal-close" id="delete-profile-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs
new file mode 100644
index 00000000000..ae369f7f707
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-intro.hbs
@@ -0,0 +1,3 @@
+<p class="spacer-bottom">Quality Profiles are collections of rules to apply during an analysis.</p>
+<p>For each language there is a default profile. All projects not explicitly assigned to some other profile will be
+ analyzed with the default.</p>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-layout.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-layout.hbs
new file mode 100644
index 00000000000..91e2e23a237
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-layout.hbs
@@ -0,0 +1,9 @@
+<div class="search-navigator-side search-navigator-side-light">
+ <div class="search-navigator-filters"></div>
+ <div class="quality-profiles-results panel"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header"></div>
+ <div class="search-navigator-workspace-details"></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-details.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-details.hbs
new file mode 100644
index 00000000000..7f1d8c3c993
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-details.hbs
@@ -0,0 +1,132 @@
+<div class="panel panel-vertical">
+ <div class="columns">
+ <div class="column-two-thirds" id="quality-profile-rules">
+ <header class="page-header">
+ <h3 class="page-title">{{t 'coding_rules'}}</h3>
+ </header>
+ <p>
+ <a class="big" href="{{link rulesSearchUrl}}">{{formatMeasure activeRuleCount 'INT'}}</a>
+ {{tp 'quality_profile.x_active_rules' ''}}
+ </p>
+ {{#notEmpty rulesSeverities}}
+ <div class="abs-width-400 spacer-top">
+ <div class="columns">
+ <div class="column-half">
+ {{#eachEven rulesSeverities}}
+ <p class="spacer-top">
+ {{severityIcon val}}
+ <a href="{{link ../rulesSearchUrl '|active_severities=' val}}">{{formatMeasure count 'INT'}}</a>
+ <span class="text-lowercase">{{t 'severity' val}}</span>
+ </p>
+ {{/eachEven}}
+ </div>
+ <div class="column-half">
+ {{#eachOdd rulesSeverities}}
+ <p class="spacer-top">
+ {{severityIcon val}}
+ <a href="{{link ../rulesSearchUrl '|active_severities=' val}}">{{formatMeasure count 'INT'}}</a>
+ <span class="text-lowercase">{{t 'severity' val}}</span>
+ </p>
+ {{/eachOdd}}
+ </div>
+ </div>
+ </div>
+ {{/notEmpty}}
+ </div>
+
+ <div class="column-third" id="quality-profile-permalinks">
+ <header class="page-header">
+ <h3 class="page-title">{{t 'permalinks'}}</h3>
+ </header>
+ <ul class="list-inline">
+ <li>
+ <a href="{{exporterUrl this null}}" target="_blank"><i class="icon-detach"></i>&nbsp;{{t 'quality_profiles.export_all_rules'}}</a>
+ </li>
+ {{#each exporters}}
+ <li>
+ <a href="{{exporterUrl ../this key}}" target="_blank"><i class="icon-detach"></i>&nbsp;{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+ </div>
+</div>
+
+<div class="panel panel-vertical" id="quality-profile-projects">
+ <header class="page-header">
+ <h3 class="page-title">{{t 'projects'}}</h3>
+ </header>
+ {{#if isDefault}}
+ {{#if canWrite}}
+ <p class="alert alert-info">{{t 'quality_profiles.projects_for_default.edit'}}</p>
+ {{else}}
+ <p class="alert alert-info">{{t 'quality_profiles.projects_for_default'}}</p>
+ {{/if}}
+ {{else}}
+ <div id="quality-profile-projects-list"></div>
+ {{/if}}
+</div>
+
+<div class="panel panel-vertical" id="quality-profile-inheritance">
+ <header class="page-header">
+ <h3 class="page-title">{{t 'quality_profiles.profile_inheritance'}}</h3>
+ {{#if canWrite}}
+ <div class="page-actions">
+ <div class="button-group">
+ <button id="quality-profile-change-parent">{{t 'quality_profiles.change_parent'}}</button>
+ </div>
+ </div>
+ {{/if}}
+ </header>
+ <div class="text-center">
+ {{#notEmpty ancestors}}
+ <ul id="quality-profile-ancestors">
+ {{#eachReverse ancestors}}
+ <li>
+ <div class="alert alert-inline alert-info">
+ <h6><a class="js-profile" data-key="{{key}}" href="{{profileUrl key}}">{{name}}</a></h6>
+ <p class="note">{{tp 'quality_profile.x_active_rules' activeRuleCount}}</p>
+ {{#if overridingRuleCount}}
+ <p class="note">{{tp 'quality_profiles.x_overridden_rules' overridingRuleCount}}</p>
+ {{/if}}
+ </div>
+ <div class="spacer-top spacer-bottom">
+ <i class="icon-move-down"></i>
+ </div>
+ </li>
+ {{/eachReverse}}
+ </ul>
+ {{/notEmpty}}
+
+ <div id="quality-profile-inheritance-current" class="alert alert-inline alert-success">
+ <h6>{{name}}</h6>
+ <p class="note">{{tp 'quality_profile.x_active_rules' activeRuleCount}}</p>
+ {{#if overridingRuleCount}}
+ <p class="note">{{tp 'quality_profiles.x_overridden_rules' overridingRuleCount}}</p>
+ {{/if}}
+ </div>
+
+ {{#notEmpty children}}
+ <div class="spacer-top spacer-bottom">
+ <i class="icon-move-down"></i>
+ </div>
+ <ul id="quality-profile-children" class="list-inline">
+ {{#eachReverse children}}
+ <li>
+ <div class="alert alert-inline alert-info">
+ <h6><a class="js-profile" data-key="{{key}}" href="{{profileUrl key}}">{{name}}</a></h6>
+ <p class="note">{{tp 'quality_profile.x_active_rules' activeRuleCount}}</p>
+ {{#if overridingRuleCount}}
+ <p class="note">{{tp 'quality_profiles.x_overridden_rules' overridingRuleCount}}</p>
+ {{/if}}
+ </div>
+ </li>
+ {{/eachReverse}}
+ </ul>
+ {{/notEmpty}}
+ </div>
+</div>
+
+<div class="panel panel-vertical" id="quality-profile-changelog"></div>
+
+<div class="panel panel-vertical" id="quality-profile-comparison"></div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-header.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-header.hbs
new file mode 100644
index 00000000000..141004e9fdb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile-header.hbs
@@ -0,0 +1,19 @@
+<h2 class="search-navigator-header-component">
+ {{name}}
+ <span class="note">{{languageName}}</span>
+</h2>
+
+<div class="search-navigator-header-actions">
+ <div class="button-group">
+ <a class="button" href="{{link '/api/qualityprofiles/backup?profileKey=' encodedKey}}" target="_blank"
+ id="quality-profile-backup">{{t 'backup_verb'}}</a>
+ {{#if canWrite}}
+ <button id="quality-profile-rename">{{t 'rename'}}</button>
+ <button id="quality-profile-copy">{{t 'copy'}}</button>
+ {{#unless isDefault}}
+ <button id="quality-profile-set-as-default">{{t 'set_as_default'}}</button>
+ <button id="quality-profile-delete" class="button-red">{{t 'delete'}}</button>
+ {{/unless}}
+ {{/if}}
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile.hbs
new file mode 100644
index 00000000000..f6cc63068b8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profile.hbs
@@ -0,0 +1,21 @@
+<table>
+ <tr>
+ <td class="text-top">{{name}}</td>
+ {{#if isInherited}}
+ <td class="text-top thin spacer-left">
+ <i class="icon-inheritance" title="{{tp 'quality_profiles.inherits' parentName}}"
+ data-toggle="tooltip" data-placement="bottom"></i>
+ </td>
+ {{/if}}
+ <td class="text-top thin nowrap spacer-left">
+ {{#if isDefault}}
+ <span class="badge pull-right">{{t 'default'}}</span>
+ {{else}}
+ <span class="note pull-right">{{tp 'quality_profiles.x_projects' projectCountFormatted}}</span>
+ {{/if}}
+ </td>
+ </tr>
+</table>
+
+
+
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles-language.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles-language.hbs
new file mode 100644
index 00000000000..f3ec16f0a9c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles-language.hbs
@@ -0,0 +1 @@
+<h6 class="spacer-top js-list-language" data-language="{{language}}">{{languageName}}</h6>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles.hbs
new file mode 100644
index 00000000000..8022059ffad
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-profiles.hbs
@@ -0,0 +1 @@
+<div class="js-list"></div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs
new file mode 100644
index 00000000000..3da132f8f4f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs
@@ -0,0 +1,16 @@
+<form id="rename-profile-form">
+ <div class="modal-head">
+ <h2>{{tp 'quality_profiles.rename_x_title' name languageName}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="rename-profile-name">{{t 'quality_profiles.new_name'}} <em class="mandatory">*</em></label>
+ <input id="rename-profile-name" name="name" type="text" size="50" maxlength="100" value="{{name}}" required>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="rename-profile-submit">{{t 'rename'}}</button>
+ <a href="#" class="js-modal-close" id="rename-profile-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-built-in-profiles.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-built-in-profiles.hbs
new file mode 100644
index 00000000000..44c02a80497
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-built-in-profiles.hbs
@@ -0,0 +1,20 @@
+<form id="restore-built-in-profiles-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_profiles.new_profile'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="restore-built-in-profiles-language">{{t 'language'}}<em class="mandatory">*</em></label>
+ <select id="restore-built-in-profiles-language" name="language">
+ {{#each languages}}
+ <option value="{{key}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="restore-built-in-profiles-submit">{{t 'restore'}}</button>
+ <a href="#" class="js-modal-close" id="restore-built-in-profiles-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
new file mode 100644
index 00000000000..faffd5950ae
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
@@ -0,0 +1,17 @@
+<form id="restore-profile-form" action="{{link '/api/qualityprofiles/restore'}}" enctype="multipart/form-data"
+ method="POST">
+ <div class="modal-head">
+ <h2>{{t 'quality_profiles.restore_profile'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="restore-profile-backup">{{t 'backup'}}<em class="mandatory">*</em></label>
+ <input type="file" id="restore-profile-backup" name="backup" required>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="restore-profile-submit">{{t 'restore'}}</button>
+ <a href="#" class="js-modal-close" id="restore-profile-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/source-viewer/app.js b/server/sonar-web/src/main/js/apps/source-viewer/app.js
new file mode 100644
index 00000000000..ded35b07041
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/source-viewer/app.js
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+requirejs.config({
+ baseUrl: baseUrl + '/js'
+});
+
+requirejs([
+ 'components/source-viewer/main'
+
+], function (SourceViewer) {
+
+ var App = new Marionette.Application();
+
+ App.addRegions({
+ viewerRegion: '#source-viewer'
+ });
+
+ App.addInitializer(function () {
+ var viewer = new SourceViewer();
+ App.viewerRegion.show(viewer);
+ viewer.open(window.file.uuid);
+ if (typeof window.file.line === 'number') {
+ viewer.on('loaded', function () {
+ viewer
+ .highlightLine(window.file.line)
+ .scrollToLine(window.file.line);
+ });
+ }
+ });
+
+ var l10nXHR = window.requestMessages();
+
+ l10nXHR.done(function () {
+ App.start();
+ });
+
+});