diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
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'}}… + </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'}}… + </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> {{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}} + + {{/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> + + {{/notEq}} + <span class="note">{{langName}}</span> + {{#notEmpty tags}} + + <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> <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}} {{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">: </span><span class="value" title="{{value}}">{{defaultValue}}</span> + </div> + {{/if}} + {{/each}} + +</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> <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}} {{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'}} {{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">: </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'}} <span class="value">{{original}}</span> + </div> + {{/notEq}}{{/if}} + </div> + {{/each}} + + </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> + // 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}} {{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'}} <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'}} <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'}} <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'}} <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}} <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> <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'}} <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'}} <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'}} <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'}} <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> {{t 'shortcuts.section.global.search'}}</li> + <li><span class="shortcut-button">?</span> {{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">↑</span> <span + class="shortcut-button">↓</span> {{t 'shortcuts.section.rules.navigate_between_rules'}}</li> + <li><span class="shortcut-button">→</span> {{t 'shortcuts.section.rules.open_details'}}</li> + <li><span class="shortcut-button">←</span> {{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">↑</span> <span + class="shortcut-button">↓</span> {{t 'shortcuts.section.issues.navigate_between_issues'}} + </li> + <li><span class="shortcut-button">→</span> {{t 'shortcuts.section.issues.open_details'}}</li> + <li><span class="shortcut-button">←</span> {{t 'shortcuts.section.issues.return_to_list'}}</li> + <li><span class="shortcut-button">f</span> {{t 'shortcuts.section.issue.do_transition'}}</li> + <li><span class="shortcut-button">a</span> {{t 'shortcuts.section.issue.assign'}}</li> + <li><span class="shortcut-button">m</span> {{t 'shortcuts.section.issue.assign_to_me'}}</li> + <li><span class="shortcut-button">p</span> {{t 'shortcuts.section.issue.plan'}}</li> + <li><span class="shortcut-button">i</span> {{t 'shortcuts.section.issue.change_severity'}}</li> + <li><span class="shortcut-button">c</span> {{t 'shortcuts.section.issue.comment'}}</li> + <li><span class="shortcut-button">t</span> {{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> ' + 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}} <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}} <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}} <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}} <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> {{t 'quality_profiles.export_all_rules'}}</a> + </li> + {{#each exporters}} + <li> + <a href="{{exporterUrl ../this key}}" target="_blank"><i class="icon-detach"></i> {{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(); + }); + +}); |