From 9f163b2b3a99db950c8a32b71e9f1a08d6d9cb13 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Thu, 28 May 2015 17:03:02 +0200 Subject: [PATCH] move issue component from coffee to js --- server/sonar-web/Gruntfile.coffee | 2 +- .../issue/collections/action-plans.coffee | 30 -- .../issue/collections/issues.coffee | 70 ---- .../coffee/components/issue/issue-view.coffee | 350 ------------------ .../components/issue/manual-issue-view.coffee | 113 ------ .../components/issue/models/changelog.coffee | 30 -- .../components/issue/models/issue.coffee | 32 -- .../issue/views/action-options-view.coffee | 120 ------ .../issue/views/assign-form-view.coffee | 148 -------- .../issue/views/changelog-view.coffee | 38 -- .../issue/views/comment-form-view.coffee | 84 ----- .../components/issue/views/issue-popup.coffee | 48 --- .../issue/views/more-actions-view.coffee | 41 -- .../issue/views/plan-form-view.coffee | 78 ---- .../issue/views/set-severity-form-view.coffee | 65 ---- .../issue/views/tags-form-view.coffee | 161 -------- .../issue/views/transitions-form-view.coffee | 53 --- .../issue/collections/action-plans.js | 13 + .../js/components/issue/collections/issues.js | 54 +++ .../main/js/components/issue/issue-view.js | 332 +++++++++++++++++ .../js/components/issue/manual-issue-view.js | 114 ++++++ .../js/components/issue/models/changelog.js | 13 + .../main/js/components/issue/models/issue.js | 15 + .../issue/templates/comment-form.hbs | 0 .../templates/issue-assign-form-option.hbs | 0 .../issue/templates/issue-assign-form.hbs | 0 .../issue/templates/issue-changelog.hbs | 0 .../issue/templates/issue-more-actions.hbs | 0 .../issue/templates/issue-plan-form.hbs | 0 .../templates/issue-set-severity-form.hbs | 0 .../templates/issue-tags-form-option.hbs | 0 .../issue/templates/issue-tags-form.hbs | 0 .../templates/issue-transitions-form.hbs | 0 .../components/issue/templates/issue.hbs | 0 .../issue/templates/manual-issue.hbs | 0 .../issue/views/action-options-view.js | 116 ++++++ .../issue/views/assign-form-view.js | 158 ++++++++ .../components/issue/views/changelog-view.js | 20 + .../issue/views/comment-form-view.js | 70 ++++ .../js/components/issue/views/issue-popup.js | 33 ++ .../issue/views/more-actions-view.js | 23 ++ .../components/issue/views/plan-form-view.js | 74 ++++ .../issue/views/set-severity-form-view.js | 51 +++ .../components/issue/views/tags-form-view.js | 177 +++++++++ .../issue/views/transitions-form-view.js | 36 ++ 45 files changed, 1300 insertions(+), 1462 deletions(-) delete mode 100644 server/sonar-web/src/main/coffee/components/issue/collections/action-plans.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/collections/issues.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/issue-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/manual-issue-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/models/changelog.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/models/issue.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee create mode 100644 server/sonar-web/src/main/js/components/issue/collections/action-plans.js create mode 100644 server/sonar-web/src/main/js/components/issue/collections/issues.js create mode 100644 server/sonar-web/src/main/js/components/issue/issue-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/manual-issue-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/models/changelog.js create mode 100644 server/sonar-web/src/main/js/components/issue/models/issue.js rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/comment-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-assign-form-option.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-assign-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-changelog.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-more-actions.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-plan-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-set-severity-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-tags-form-option.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-tags-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue-transitions-form.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/issue.hbs (100%) rename server/sonar-web/src/main/{coffee => js}/components/issue/templates/manual-issue.hbs (100%) create mode 100644 server/sonar-web/src/main/js/components/issue/views/action-options-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/assign-form-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/changelog-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/comment-form-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/issue-popup.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/more-actions-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/plan-form-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/set-severity-form-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/tags-form-view.js create mode 100644 server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee index 9b8eb566cc9..2339f6fe636 100644 --- a/server/sonar-web/Gruntfile.coffee +++ b/server/sonar-web/Gruntfile.coffee @@ -203,7 +203,7 @@ module.exports = (grunt) -> ] '<%= BUILD_PATH %>/js/components/issue/templates.js': [ '<%= SOURCE_PATH %>/js/components/common/templates/**/*.hbs' - '<%= SOURCE_PATH %>/coffee/components/issue/templates/**/*.hbs' + '<%= SOURCE_PATH %>/js/components/issue/templates/**/*.hbs' ] '<%= BUILD_PATH %>/js/apps/issues/templates.js': [ '<%= SOURCE_PATH %>/coffee/apps/issues/templates/**/*.hbs' diff --git a/server/sonar-web/src/main/coffee/components/issue/collections/action-plans.coffee b/server/sonar-web/src/main/coffee/components/issue/collections/action-plans.coffee deleted file mode 100644 index 91b5a357b42..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/collections/action-plans.coffee +++ /dev/null @@ -1,30 +0,0 @@ -# -# 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 -> - - class ActionPlans extends Backbone.Collection - - url: -> - "#{baseUrl}/api/action_plans/search" - - - parse: (r) -> - r.actionPlans diff --git a/server/sonar-web/src/main/coffee/components/issue/collections/issues.coffee b/server/sonar-web/src/main/coffee/components/issue/collections/issues.coffee deleted file mode 100644 index 208a06ffb3b..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/collections/issues.coffee +++ /dev/null @@ -1,70 +0,0 @@ -# -# 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/issue' -], ( - Issue -) -> - - class extends Backbone.Collection - model: Issue - - url: -> - "#{baseUrl}/api/issues/search" - - - parse: (r) -> - find = (source, key, keyField) -> - searchDict = {} - searchDict[keyField || 'key'] = key - _.findWhere(source, searchDict) || key - - @paging = - p: r.p - ps: r.ps - total: r.total - maxResultsReached: r.p * r.ps >= r.total - - r.issues.map (issue) -> - component = find r.components, issue.component - project = find r.projects, issue.project - rule = find r.rules, issue.rule - assignee = find r.users, issue.assignee, 'login' - - if component - _.extend issue, - componentLongName: component.longName - componentQualifier: component.qualifier - - if project - _.extend issue, - projectLongName: project.longName - projectUuid: project.uuid - - if rule - _.extend issue, - ruleName: rule.name - - if assignee - _.extend issue, - assigneeEmail: assignee.email - - issue diff --git a/server/sonar-web/src/main/coffee/components/issue/issue-view.coffee b/server/sonar-web/src/main/coffee/components/issue/issue-view.coffee deleted file mode 100644 index dd8189f632d..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/issue-view.coffee +++ /dev/null @@ -1,350 +0,0 @@ -# -# 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/changelog' - './views/changelog-view' - - './collections/action-plans' - - './views/issue-popup' - - './views/transitions-form-view' - './views/assign-form-view' - './views/comment-form-view' - './views/plan-form-view' - './views/set-severity-form-view' - './views/more-actions-view' - './views/tags-form-view' - - 'components/workspace/main' - - './templates' - -], ( - ChangeLog - ChangeLogView - - ActionPlans - - IssuePopup - - TransitionsFormView - AssignFormView - CommentFormView - PlanFormView - SetSeverityFormView - MoreActionsView - TagsFormView - - Workspace - -) -> - - $ = jQuery - - - class extends Marionette.ItemView - className: 'issue' - template: Templates['issue'] - - - modelEvents: - 'change': 'render' - - - ui: - tagsChange: '.js-issue-edit-tags' - tagInput: '.issue-tag-input' - tagsEdit: '.issue-tag-edit' - tagsEditDone: '.issue-tag-edit-done' - tagsEditCancel: '.issue-tag-edit-cancel' - tagsList: '.issue-tag-list' - - - events: -> - 'click .js-issue-comment': 'comment' - 'click .js-issue-comment-edit': 'editComment' - 'click .js-issue-comment-delete': 'deleteComment' - - 'click .js-issue-transition': 'transition' - 'click .js-issue-set-severity': 'setSeverity' - 'click .js-issue-assign': 'assign' - 'click .js-issue-assign-to-me': 'assignToMe' - 'click .js-issue-plan': 'plan' - 'click .js-issue-show-changelog': 'showChangeLog' - 'click .js-issue-more': 'showMoreActions' - 'click .js-issue-rule': 'showRule' - 'click .js-issue-edit-tags': 'editTags' - - - onRender: -> - @ui.tagsEdit.hide() - @$el.attr 'data-key', @model.get('key') - - - resetIssue: (options) -> - key = @model.get 'key' - componentUuid = @model.get 'componentUuid' - @model.clear silent: true - @model.set { key: key, componentUuid: componentUuid }, { silent: true } - @model.fetch(options) - .done => - @trigger 'reset' - - - showChangeLog: (e) -> - t = $(e.currentTarget) - changeLog = new ChangeLog() - changeLog.fetch - data: issue: @model.get 'key' - .done => - @popup.close() if @popup - @popup = new ChangeLogView - triggerEl: t - bottomRight: true - collection: changeLog - issue: @model - @popup.render() - - - showActionView: (view, el, position) -> - if el - @popup.close() if @popup - options = - view: view - triggerEl: el - if position? - options[position] = true - else - options['bottom'] = true - @popup = new IssuePopup options - @popup.render() - - - showActionSpinner: -> - @$('.code-issue-actions').addClass 'navigator-fetching' - - - hideActionSpinner: -> - @$('.code-issue-actions').removeClass 'navigator-fetching' - - - updateAfterAction: (fetch) -> - @popup.close() if @popup - if fetch - @resetIssue() - - - comment: (e) -> - e.stopPropagation() - $('body').click() - @popup = new CommentFormView - triggerEl: $(e.currentTarget) - bottom: true - issue: @model - detailView: @ - @popup.render() - - - editComment: (e) -> - e.stopPropagation() - $('body').click() - commentEl = $(e.currentTarget).closest '.issue-comment' - commentKey = commentEl.data 'comment-key' - comment = _.findWhere @model.get('comments'), { key: commentKey } - @popup = new CommentFormView - triggerEl: $(e.currentTarget) - bottomRight: true - model: new Backbone.Model comment - issue: @model - detailView: @ - @popup.render() - - - deleteComment: (e) -> - commentKey = $(e.target).closest('[data-comment-key]').data 'comment-key' - confirmMsg = $(e.target).data 'confirm-msg' - - if confirm(confirmMsg) - $.ajax - type: "POST" - url: baseUrl + "/api/issues/delete_comment?key=" + commentKey - .done => - @updateAfterAction true - - - transition: (e) -> - e.stopPropagation() - $('body').click() - @popup = new TransitionsFormView - triggerEl: $(e.currentTarget) - bottom: true - model: @model - view: @ - @popup.render() - - - setSeverity: (e) -> - e.stopPropagation() - $('body').click() - @popup = new SetSeverityFormView - triggerEl: $(e.currentTarget) - bottom: true - model: @model - @popup.render() - - - assign: (e) -> - e.stopPropagation() - $('body').click() - @popup = new AssignFormView - triggerEl: $(e.currentTarget) - bottom: true - model: @model - @popup.render() - - - assignToMe: -> - view = new AssignFormView model: @model, triggerEl: $('body') - view.submit window.SS.user, window.SS.userName - view.close() - - - plan: (e) -> - t = $(e.currentTarget) - actionPlans = new ActionPlans() - actionPlans.fetch - reset: true - data: project: @model.get('project') - .done => - e.stopPropagation() - $('body').click() - @popup = new PlanFormView - triggerEl: t - bottom: true - model: @model - collection: actionPlans - @popup.render() - - - showMoreActions: (e) -> - e.stopPropagation() - $('body').click() - @popup = new MoreActionsView - triggerEl: $(e.currentTarget) - bottomRight: true - model: @model - detailView: @ - @popup.render() - - - action: (action) -> - $.post "#{baseUrl}/api/issues/do_action", issue: @model.id, actionKey: action - .done => - @resetIssue() - - - showRule: -> - unless Workspace? - Workspace = require 'components/workspace/main' - ruleKey = @model.get 'rule' - Workspace.openRule key: ruleKey - - - editTags: (e)-> - e.stopPropagation() - $('body').click() - @popup = new TagsFormView - triggerEl: $(e.currentTarget) - bottomRight: true - model: @model - @popup.render() - - - changeTags: -> - jQuery.ajax - url: "#{baseUrl}/api/issues/tags?ps=0" - .done (r) => - if @ui.tagInput.select2 - # Prevent synchronization issue with navigation - @ui.tagInput.select2 - tags: (_.difference r.tags, @model.get 'tags') - width: '300px' - if @ui.tagsEdit.show - @ui.tagsEdit.show() - if @ui.tagsList.hide - @ui.tagsList.hide() - @tagsBuffer = @ui.tagInput.select2 'val' - - keyScope = key.getScope() - if keyScope != 'tags' - @previousKeyScope = keyScope - key.setScope 'tags' - key 'escape', 'tags', => @cancelEdit() - - @$('.select2-input').keyup((event) => - if (event.which == 27) - @cancelEdit() - ) - - @ui.tagInput.select2 'focus' - - - cancelEdit: -> - @resetKeyScope() - - if @ui.tagsList.show - @ui.tagsList.show() - if @ui.tagInput.select2 - @ui.tagInput.select2 'val', @tagsBuffer - @ui.tagInput.select2 'close' - if @ui.tagsEdit.hide - @ui.tagsEdit.hide() - - - editDone: -> - @resetKeyScope() - - _tags = @model.get 'tags' - tags = @ui.tagInput.val() - splitTags = if tags then tags.split(',') else null - - @model.set 'tags', splitTags - $.post "#{baseUrl}/api/issues/set_tags", key: @model.get('key'), tags: tags - .done => - @cancelEdit() - .fail => - @model.set 'tags', _tags - .always => - @render() - - - resetKeyScope: -> - key.unbind 'escape', 'tags' - if @previousKeyScope - key.setScope @previousKeyScope - @previousKeyScope = null - - - serializeData: -> - issueKey = encodeURIComponent @model.get 'key' - _.extend super, - permalink: "#{baseUrl}/issues/search#issues=#{issueKey}" diff --git a/server/sonar-web/src/main/coffee/components/issue/manual-issue-view.coffee b/server/sonar-web/src/main/coffee/components/issue/manual-issue-view.coffee deleted file mode 100644 index c0030e14c2d..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/manual-issue-view.coffee +++ /dev/null @@ -1,113 +0,0 @@ -# -# 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' -], -> - - $ = jQuery - API_ISSUE = "#{baseUrl}/api/issues/show" - API_ADD_MANUAL_ISSUE = "#{baseUrl}/api/issues/create" - - - class extends Marionette.ItemView - template: Templates['manual-issue'] - - - events: - 'submit .js-manual-issue-form': 'formSubmit' - 'click .js-cancel': 'cancel' - - - initialize: -> - @rules = [] - $.get("#{baseUrl}/api/rules/search?repositories=manual&f=name&ps=999999").done (data) => - @rules = data.rules - @render() - - - onRender: -> - @delegateEvents() - @$('[name=rule]').select2 - width: '250px' - minimumResultsForSearch: 10 - @$('[name=rule]').select2 'open' if @rules.length > 0 - if key? - @key = key.getScope() - key.setScope '' - - - onClose: -> - key.setScope @key if key? && @key? - - - showSpinner: -> - @$('.js-submit').hide() - @$('.js-spinner').show() - - - hideSpinner: -> - @$('.js-submit').show() - @$('.js-spinner').hide() - - - validateFields: -> - message = @$('[name=message]') - unless message.val() - message.addClass('invalid').focus() - return false - return true - - - formSubmit: (e) -> - e.preventDefault() - return unless @validateFields() - @showSpinner() - data = $(e.currentTarget).serialize() - $.post API_ADD_MANUAL_ISSUE, data - .done (r) => - r = JSON.parse(r) if typeof r == 'string' - @addIssue r.issue.key - .fail (r) => - @hideSpinner() - if r.responseJSON?.errors? - @showError _.pluck(r.responseJSON.errors, 'msg').join '. ' - - - addIssue: (key) -> - $.get API_ISSUE, key: key, (r) => - @trigger 'add', r.issue - @close() - - - showError: (msg) -> - @$('.code-issue-errors').removeClass('hidden').text msg - - - cancel: (e) -> - e.preventDefault() - @close() - - - serializeData: -> - _.extend super, - line: @options.line - component: @options.component - rules: _.sortBy @rules, 'name' diff --git a/server/sonar-web/src/main/coffee/components/issue/models/changelog.coffee b/server/sonar-web/src/main/coffee/components/issue/models/changelog.coffee deleted file mode 100644 index 3aadd10825f..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/models/changelog.coffee +++ /dev/null @@ -1,30 +0,0 @@ -# -# 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 -> - - class ChangeLog extends Backbone.Collection - - url: -> - "#{baseUrl}/api/issues/changelog" - - - parse: (r) -> - return r.changelog diff --git a/server/sonar-web/src/main/coffee/components/issue/models/issue.coffee b/server/sonar-web/src/main/coffee/components/issue/models/issue.coffee deleted file mode 100644 index e5f99dc2bed..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/models/issue.coffee +++ /dev/null @@ -1,32 +0,0 @@ -# -# 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 -> - - class Issue extends Backbone.Model - idAttribute: 'key' - - - url: -> - "#{baseUrl}/api/issues/show?key=#{@get('key')}" - - - parse: (r) -> - if r.issue then r.issue else r diff --git a/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee deleted file mode 100644 index 878d172f09f..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee +++ /dev/null @@ -1,120 +0,0 @@ -# -# 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' -], ( - PopupView -) -> - - $ = jQuery - - - class extends PopupView - keyScope: 'issue-action-options' - - - ui: - options: '.issue-action-option' - - - events: -> - 'click .issue-action-option': 'selectOption' - 'mouseenter .issue-action-option': 'activateOptionByPointer' - - - initialize: -> - @bindShortcuts() - - - onRender: -> - super - @selectInitialOption() - @$('[data-toggle="tooltip"]').tooltip container: 'body' - - - getOptions: -> - @$('.issue-action-option') - - - getActiveOption: -> - @getOptions().filter('.active') - - - makeActive: (option) -> - if option.length > 0 - @getOptions().removeClass 'active' - option.addClass 'active' - - - selectInitialOption: -> - @makeActive @getOptions().first() - - - selectNextOption: -> - @makeActive @getActiveOption().nextAll('.issue-action-option').first() - false # return `false` to use with keymaster - - - selectPreviousOption: -> - @makeActive @getActiveOption().prevAll('.issue-action-option').first() - false # return `false` to use with keymaster - - - activateOptionByPointer: (e) -> - @makeActive $(e.currentTarget) - - - bindShortcuts: -> - @currentKeyScope = key.getScope() - key.setScope @keyScope - key 'down', @keyScope, => @selectNextOption() - key 'up', @keyScope, => @selectPreviousOption() - key 'return', @keyScope, => @selectActiveOption() - key 'escape', @keyScope, => @close() - key 'backspace', @keyScope, => false # disable go back through the history - key 'shift+tab', @keyScope, => false - - - unbindShortcuts: -> - key.unbind 'down', @keyScope - key.unbind 'up', @keyScope - key.unbind 'return', @keyScope - key.unbind 'escape', @keyScope - key.unbind 'backspace', @keyScope - key.unbind 'tab', @keyScope - key.unbind 'shift+tab', @keyScope - key.setScope @currentKeyScope - - - onClose: -> - super - @unbindShortcuts() - @$('[data-toggle="tooltip"]').tooltip 'destroy' - $('.tooltip').remove() - - - selectOption: (e) -> - e.preventDefault() - @close() - - - selectActiveOption: -> - @getActiveOption().click() diff --git a/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee deleted file mode 100644 index ae00ec881a3..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee +++ /dev/null @@ -1,148 +0,0 @@ -# -# 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-options-view' - '../templates' -], ( - ActionOptionsView -) -> - - $ = jQuery - - - class extends ActionOptionsView - template: Templates['issue-assign-form'] - optionTemplate: Templates['issue-assign-form-option'] - - - events: -> - _.extend super, - 'click input': 'onInputClick' - 'keydown input': 'onInputKeydown' - 'keyup input': 'onInputKeyup' - - - initialize: -> - super - @assignees = [] - @debouncedSearch = _.debounce @search, 250 - - - getAssignee: -> - @model.get 'assignee' - - - getAssigneeName: -> - @model.get 'assigneeName' - - - onRender: -> - super - @renderTags() - setTimeout (=> @$('input').focus()), 100 - - - renderTags: -> - @$('.issue-action-option').remove() - @getAssignees().forEach @renderAssignee, @ - @selectInitialOption() - - - renderAssignee: (assignee) -> - html = @optionTemplate assignee - @$('.issue-action-options').append html - - - selectOption: (e) -> - assignee = $(e.currentTarget).data 'value' - assigneeName = $(e.currentTarget).data 'text' - @submit assignee, assigneeName - super - - - submit: (assignee, assigneeName) -> - _assignee = @getAssignee() - _assigneeName = @getAssigneeName() - return if assignee == _assignee - if assignee == '' - @model.set assignee: null, assigneeName: null - else - @model.set assignee: assignee, assigneeName: assigneeName - $.ajax - type: 'POST' - url: "#{baseUrl}/api/issues/assign" - data: - issue: @model.id - assignee: assignee - .fail => - @model.set assignee: _assignee, assigneeName: _assigneeName - - - onInputClick: (e) -> - e.stopPropagation() - - - onInputKeydown: (e) -> - @query = @$('input').val() - return @selectPreviousOption() if e.keyCode == 38 # up - return @selectNextOption() if e.keyCode == 40 # down - return @selectActiveOption() if e.keyCode == 13 # return - return false if e.keyCode == 9 # tab - @close() if e.keyCode == 27 # escape - - - onInputKeyup: -> - query = @$('input').val() - if query != @query - query = '' if query.length < 2 - @query = query - @debouncedSearch query - - - search: (query) -> - if query.length > 1 - $.get "#{baseUrl}/api/users/search", q: query - .done (data) => - @resetAssignees data.users - else - @resetAssignees [] - - - resetAssignees: (users) -> - @assignees = users.map (user) -> - id: user.login - text: user.name - @renderTags() - - - getAssignees: -> - return @assignees if @assignees.length > 0 - assignees = [{ id: '', text: t('unassigned') }] - currentUser = window.SS.user - currentUserName = window.SS.userName - assignees.push id: currentUser, text: currentUserName - if @getAssignee() - assignees.push id: @getAssignee(), text: @getAssigneeName() - @makeUnique assignees - - - makeUnique: (assignees) -> - _.uniq assignees, false, (assignee) -> assignee.id diff --git a/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee deleted file mode 100644 index 34e77c9191b..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee +++ /dev/null @@ -1,38 +0,0 @@ -# -# 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' - '../templates' -], ( - PopupView -) -> - - class extends PopupView - template: Templates['issue-changelog'] - - - collectionEvents: - 'sync': 'render' - - - serializeData: -> - _.extend super, - issue: @options.issue.toJSON() diff --git a/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee deleted file mode 100644 index 95fa6739d4b..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee +++ /dev/null @@ -1,84 +0,0 @@ -# -# 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' - '../templates' -], ( - PopupView -) -> - - $ = jQuery - - - class extends PopupView - className: 'bubble-popup issue-comment-bubble-popup' - template: Templates['comment-form'] - - - ui: - textarea: '.issue-comment-form-text textarea' - cancelButton: '.js-issue-comment-cancel' - submitButton: '.js-issue-comment-submit' - - - events: - 'click': 'onClick' - 'keydown @ui.textarea': 'onKeydown' - 'keyup @ui.textarea': 'toggleSubmit' - 'click @ui.cancelButton': 'cancel' - 'click @ui.submitButton': 'submit' - - - onRender: -> - super - setTimeout (=> @ui.textarea.focus()), 100 - - - toggleSubmit: -> - @ui.submitButton.prop 'disabled', @ui.textarea.val().length == 0 - - - onClick: (e) -> - # disable close by clicking inside - e.stopPropagation() - - - onKeydown: (e) -> - @close() if e.keyCode == 27 # escape - - - cancel: -> - @options.detailView.updateAfterAction false - - - submit: -> - text = @ui.textarea.val() - update = @model && @model.has('key') - method = if update then 'edit_comment' else 'add_comment' - url = "#{baseUrl}/api/issues/#{method}" - data = text: text - if update - data.key = @model.get('key') - else - data.issue = @options.issue.id - $.post url, data - .done => - @options.detailView.updateAfterAction true diff --git a/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee b/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee deleted file mode 100644 index fa54c06c298..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee +++ /dev/null @@ -1,48 +0,0 @@ -# -# 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' -], ( - Popup -) -> - - class extends Popup - className: 'bubble-popup issue-bubble-popup' - - - template: -> '
' - - - events: -> - 'click .js-issue-form-cancel': 'close' - - - onRender: -> - super - @options.view.$el.appendTo @$el - @options.view.render() - - - onClose: -> - @options.view.close() - - - attachCloseEvents: -> diff --git a/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee deleted file mode 100644 index 868dbb2b9fb..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee +++ /dev/null @@ -1,41 +0,0 @@ -# -# 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' - '../templates' -], ( - PopupView -) -> - - $ = jQuery - - - class extends PopupView - template: Templates['issue-more-actions'] - - - events: -> - 'click .js-issue-action': 'action' - - - action: (e) -> - actionKey = $(e.currentTarget).data 'action' - @options.detailView.action actionKey diff --git a/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee deleted file mode 100644 index bface05c07e..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee +++ /dev/null @@ -1,78 +0,0 @@ -# -# 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-options-view' - '../templates' -], ( - ActionOptionsView -) -> - - $ = jQuery - - - class extends ActionOptionsView - template: Templates['issue-plan-form'] - - - getActionPlan: -> - @model.get('actionPlan') || '' - - - getActionPlanName: -> - @model.get 'actionPlanName' - - - selectInitialOption: -> - @makeActive @getOptions().filter("[data-value=#{@getActionPlan()}]") - - - selectOption: (e) -> - actionPlan = $(e.currentTarget).data 'value' - actionPlanName = $(e.currentTarget).data 'text' - @submit actionPlan, actionPlanName - super - - - submit: (actionPlan, actionPlanName) -> - _actionPlan = @getActionPlan() - _actionPlanName = @getActionPlanName() - return if actionPlan == _actionPlan - if actionPlan == '' - @model.set actionPlan: null, actionPlanName: null - else - @model.set actionPlan: actionPlan, actionPlanName: actionPlanName - $.ajax - type: 'POST' - url: "#{baseUrl}/api/issues/plan" - data: - issue: @model.id - plan: actionPlan - .fail => - @model.set assignee: _actionPlan, assigneeName: _actionPlanName - - - getActionPlans: -> - [{ key: '', name: t 'issue.unplanned' }].concat @collection.toJSON() - - - serializeData: -> - _.extend super, - items: @getActionPlans() diff --git a/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee deleted file mode 100644 index bdd813ff449..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee +++ /dev/null @@ -1,65 +0,0 @@ -# -# 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-options-view' - '../templates' -], ( - ActionOptionsView -) -> - - $ = jQuery - - - class extends ActionOptionsView - template: Templates['issue-set-severity-form'] - - - getTransition: -> - @model.get 'severity' - - - selectInitialOption: -> - @makeActive @getOptions().filter("[data-value=#{@getTransition()}]") - - - selectOption: (e) -> - severity = $(e.currentTarget).data 'value' - @submit severity - super - - - submit: (severity) -> - _severity = @getTransition() - return if severity == _severity - @model.set severity: severity - $.ajax - type: 'POST' - url: "#{baseUrl}/api/issues/set_severity" - data: - issue: @model.id - severity: severity - .fail => - @model.set severity: _severity - - - serializeData: -> - _.extend super, - items: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'] diff --git a/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee deleted file mode 100644 index ea08c2c54fd..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee +++ /dev/null @@ -1,161 +0,0 @@ -# -# 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-options-view' - '../templates' -], ( - ActionOptionsView -) -> - - $ = jQuery - - - class extends ActionOptionsView - template: Templates['issue-tags-form'] - optionTemplate: Templates['issue-tags-form-option'] - - - modelEvents: - 'change:tags': 'renderTags' - - - events: -> - _.extend super, - 'click input': 'onInputClick' - 'keydown input': 'onInputKeydown' - 'keyup input': 'onInputKeyup' - - - initialize: -> - super - @query = '' - @tags = [] - @selected = 0 - @debouncedSearch = _.debounce @search, 250 - @requestTags() - - - requestTags: -> - $.get "#{baseUrl}/api/issues/tags", ps: 25 - .done (data) => - @tags = data.tags - @renderTags() - - - onRender: -> - super - @renderTags() - setTimeout (=> @$('input').focus()), 100 - - - selectInitialOption: -> - @selected = Math.max Math.min(@selected, @getOptions().length - 1), 0 - @makeActive @getOptions().eq @selected - - - filterTags: (tags) -> - _.filter tags, (tag) => tag.indexOf(@query) != -1 - - - renderTags: -> - @$('.issue-action-option').remove() - @filterTags(@getTags()).forEach @renderSelectedTag, @ - @filterTags(_.difference(@tags, @getTags())).forEach @renderTag, @ - if @query.length > 0 && @tags.indexOf(@query) == -1 && @getTags().indexOf(@query) == -1 - @renderCustomTag @query - @selectInitialOption() - - - renderSelectedTag: (tag) -> - html = @optionTemplate { tag: tag, selected: true, custom: false } - @$('.issue-action-options').append html - - - renderTag: (tag) -> - html = @optionTemplate { tag: tag, selected: false, custom: false } - @$('.issue-action-options').append html - - - renderCustomTag: (tag) -> - html = @optionTemplate { tag: tag, selected: false, custom: true } - @$('.issue-action-options').append html - - - selectOption: (e) -> - e.preventDefault() - e.stopPropagation() - tags = @getTags().slice() - tag = $(e.currentTarget).data 'value' - if $(e.currentTarget).data('selected')? - tags = _.without tags, tag - else - tags.push tag - @selected = @getOptions().index $(e.currentTarget) - @submit tags - - - submit: (tags) -> - _tags = @getTags() - @model.set tags: tags - $.ajax - type: 'POST' - url: "#{baseUrl}/api/issues/set_tags" - data: - key: @model.id - tags: tags.join() - .fail => - @model.set tags: _tags - - - onInputClick: (e) -> - e.stopPropagation() - - - onInputKeydown: (e) -> - @query = @$('input').val() - return @selectPreviousOption() if e.keyCode == 38 # up - return @selectNextOption() if e.keyCode == 40 # down - return @selectActiveOption() if e.keyCode == 13 # return - return false if e.keyCode == 9 # tab - @close() if e.keyCode == 27 # escape - - - onInputKeyup: -> - query = @$('input').val() - if query != @query - @query = query - @debouncedSearch query - - - search: (query) -> - @query = query - @renderTags() - - - resetAssignees: (users) -> - @assignees = users.map (user) -> - id: user.login - text: user.name - @renderTags() - - - getTags: -> - @model.get('tags') || [] diff --git a/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee deleted file mode 100644 index cf4d47d8592..00000000000 --- a/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee +++ /dev/null @@ -1,53 +0,0 @@ -# -# 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-options-view' - '../templates' -], ( - ActionOptionsView -) -> - - $ = jQuery - - - class extends ActionOptionsView - template: Templates['issue-transitions-form'] - - - selectInitialOption: -> - @makeActive @getOptions().first() - - - selectOption: (e) -> - transition = $(e.currentTarget).data 'value' - @submit transition - super - - - submit: (transition) -> - $.ajax - type: 'POST', - url: baseUrl + '/api/issues/do_transition', - data: - issue: @model.get('key') - transition: transition - .done => - @options.view.resetIssue {} diff --git a/server/sonar-web/src/main/js/components/issue/collections/action-plans.js b/server/sonar-web/src/main/js/components/issue/collections/action-plans.js new file mode 100644 index 00000000000..8cbef0a1a32 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/collections/action-plans.js @@ -0,0 +1,13 @@ +define([], function () { + + return Backbone.Collection.extend({ + url: function () { + return baseUrl + '/api/action_plans/search'; + }, + + parse: function (r) { + return r.actionPlans; + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/collections/issues.js b/server/sonar-web/src/main/js/components/issue/collections/issues.js new file mode 100644 index 00000000000..5925fc0a166 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/collections/issues.js @@ -0,0 +1,54 @@ +define([ + '../models/issue' +], function (Issue) { + + return Backbone.Collection.extend({ + model: Issue, + + url: function () { + return baseUrl + '/api/issues/search'; + }, + + parse: function (r) { + function find (source, key, keyField) { + var searchDict = {}; + searchDict[keyField || 'key'] = key; + return _.findWhere(source, searchDict) || key; + } + + this.paging = { + p: r.p, + ps: r.ps, + total: r.total, + maxResultsReached: r.p * r.ps >= r.total + }; + + return r.issues.map(function (issue) { + var component = find(r.components, issue.component), + project = find(r.projects, issue.project), + rule = find(r.rules, issue.rule), + assignee = find(r.users, issue.assignee, 'login'); + if (component) { + _.extend(issue, { + componentLongName: component.longName, + componentQualifier: component.qualifier + }); + } + if (project) { + _.extend(issue, { + projectLongName: project.longName, + projectUuid: project.uuid + }); + } + if (rule) { + _.extend(issue, { ruleName: rule.name }); + } + if (assignee) { + _.extend(issue, { assigneeEmail: assignee.email }); + } + return issue; + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/issue-view.js b/server/sonar-web/src/main/js/components/issue/issue-view.js new file mode 100644 index 00000000000..98d095d221d --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/issue-view.js @@ -0,0 +1,332 @@ +define([ + './models/changelog', + './views/changelog-view', + './collections/action-plans', + './views/issue-popup', + './views/transitions-form-view', + './views/assign-form-view', + './views/comment-form-view', + './views/plan-form-view', + './views/set-severity-form-view', + './views/more-actions-view', + './views/tags-form-view', + 'components/workspace/main', + './templates' +], function (ChangeLog, ChangeLogView, ActionPlans, IssuePopup, TransitionsFormView, AssignFormView, CommentFormView, + PlanFormView, SetSeverityFormView, MoreActionsView, TagsFormView, Workspace) { + + var $ = jQuery; + + return Marionette.ItemView.extend({ + className: 'issue', + template: Templates.issue, + + modelEvents: { + 'change': 'render' + }, + + ui: { + tagsChange: '.js-issue-edit-tags', + tagInput: '.issue-tag-input', + tagsEdit: '.issue-tag-edit', + tagsEditDone: '.issue-tag-edit-done', + tagsEditCancel: '.issue-tag-edit-cancel', + tagsList: '.issue-tag-list' + }, + + events: function () { + return { + 'click .js-issue-comment': 'comment', + 'click .js-issue-comment-edit': 'editComment', + 'click .js-issue-comment-delete': 'deleteComment', + 'click .js-issue-transition': 'transition', + 'click .js-issue-set-severity': 'setSeverity', + 'click .js-issue-assign': 'assign', + 'click .js-issue-assign-to-me': 'assignToMe', + 'click .js-issue-plan': 'plan', + 'click .js-issue-show-changelog': 'showChangeLog', + 'click .js-issue-more': 'showMoreActions', + 'click .js-issue-rule': 'showRule', + 'click .js-issue-edit-tags': 'editTags' + }; + }, + + onRender: function () { + this.ui.tagsEdit.hide(); + this.$el.attr('data-key', this.model.get('key')); + }, + + resetIssue: function (options) { + var that = this; + var key = this.model.get('key'), + componentUuid = this.model.get('componentUuid'); + this.model.clear({ silent: true }); + this.model.set({ + key: key, + componentUuid: componentUuid + }, { silent: true }); + return this.model.fetch(options).done(function () { + return that.trigger('reset'); + }); + }, + + showChangeLog: function (e) { + var that = this; + var t = $(e.currentTarget), + changeLog = new ChangeLog(); + return changeLog.fetch({ + data: { issue: this.model.get('key') } + }).done(function () { + if (that.popup) { + that.popup.close(); + } + that.popup = new ChangeLogView({ + triggerEl: t, + bottomRight: true, + collection: changeLog, + issue: that.model + }); + that.popup.render(); + }); + }, + + updateAfterAction: function (fetch) { + if (this.popup) { + this.popup.close(); + } + if (fetch) { + this.resetIssue(); + } + }, + + comment: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new CommentFormView({ + triggerEl: $(e.currentTarget), + bottom: true, + issue: this.model, + detailView: this + }); + this.popup.render(); + }, + + editComment: function (e) { + e.stopPropagation(); + $('body').click(); + var commentEl = $(e.currentTarget).closest('.issue-comment'), + commentKey = commentEl.data('comment-key'), + comment = _.findWhere(this.model.get('comments'), { key: commentKey }); + this.popup = new CommentFormView({ + triggerEl: $(e.currentTarget), + bottomRight: true, + model: new Backbone.Model(comment), + issue: this.model, + detailView: this + }); + this.popup.render(); + }, + + deleteComment: function (e) { + var that = this; + var commentKey = $(e.target).closest('[data-comment-key]').data('comment-key'), + confirmMsg = $(e.target).data('confirm-msg'); + if (confirm(confirmMsg)) { + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/delete_comment?key=' + commentKey + }).done(function () { + that.updateAfterAction(true); + }); + } + }, + + transition: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new TransitionsFormView({ + triggerEl: $(e.currentTarget), + bottom: true, + model: this.model, + view: this + }); + this.popup.render(); + }, + + setSeverity: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new SetSeverityFormView({ + triggerEl: $(e.currentTarget), + bottom: true, + model: this.model + }); + this.popup.render(); + }, + + assign: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new AssignFormView({ + triggerEl: $(e.currentTarget), + bottom: true, + model: this.model + }); + this.popup.render(); + }, + + assignToMe: function () { + var view = new AssignFormView({ + model: this.model, + triggerEl: $('body') + }); + view.submit(window.SS.user, window.SS.userName); + view.close(); + }, + + plan: function (e) { + var that = this; + var t = $(e.currentTarget), + actionPlans = new ActionPlans(); + return actionPlans.fetch({ + reset: true, + data: { project: this.model.get('project') } + }).done(function () { + e.stopPropagation(); + $('body').click(); + that.popup = new PlanFormView({ + triggerEl: t, + bottom: true, + model: that.model, + collection: actionPlans + }); + that.popup.render(); + }); + }, + + showMoreActions: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new MoreActionsView({ + triggerEl: $(e.currentTarget), + bottomRight: true, + model: this.model, + detailView: this + }); + this.popup.render(); + }, + + action: function (action) { + var that = this; + return $.post(baseUrl + '/api/issues/do_action', { + issue: this.model.id, + actionKey: action + }).done(function () { + that.resetIssue(); + }); + }, + + showRule: function () { + if (Workspace == null) { + Workspace = require('components/workspace/main'); + } + var ruleKey = this.model.get('rule'); + Workspace.openRule({ key: ruleKey }); + }, + + editTags: function (e) { + e.stopPropagation(); + $('body').click(); + this.popup = new TagsFormView({ + triggerEl: $(e.currentTarget), + bottomRight: true, + model: this.model + }); + this.popup.render(); + }, + + changeTags: function () { + var that = this; + return jQuery.ajax({ + url: baseUrl + '/api/issues/tags?ps=0' + }).done(function (r) { + if (that.ui.tagInput.select2) { + that.ui.tagInput.select2({ + tags: _.difference(r.tags, that.model.get('tags')), + width: '300px' + }); + } + if (that.ui.tagsEdit.show) { + that.ui.tagsEdit.show(); + } + if (that.ui.tagsList.hide) { + that.ui.tagsList.hide(); + } + that.tagsBuffer = that.ui.tagInput.select2('val'); + var keyScope = key.getScope(); + if (keyScope !== 'tags') { + that.previousKeyScope = keyScope; + } + key.setScope('tags'); + key('escape', 'tags', function () { + return that.cancelEdit(); + }); + that.$('.select2-input').keyup(function (event) { + if (event.which === 27) { + return that.cancelEdit(); + } + }); + that.ui.tagInput.select2('focus'); + }); + }, + + cancelEdit: function () { + this.resetKeyScope(); + if (this.ui.tagsList.show) { + this.ui.tagsList.show(); + } + if (this.ui.tagInput.select2) { + this.ui.tagInput.select2('val', this.tagsBuffer); + this.ui.tagInput.select2('close'); + } + if (this.ui.tagsEdit.hide) { + return this.ui.tagsEdit.hide(); + } + }, + + editDone: function () { + var that = this; + this.resetKeyScope(); + var _tags = this.model.get('tags'), + tags = this.ui.tagInput.val(), + splitTags = tags ? tags.split(',') : null; + this.model.set('tags', splitTags); + return $.post(baseUrl + '/api/issues/set_tags', { + key: this.model.get('key'), + tags: tags + }).done(function () { + that.cancelEdit(); + }).fail(function () { + that.model.set('tags', _tags); + }).always(function () { + that.render(); + }); + }, + + resetKeyScope: function () { + key.unbind('escape', 'tags'); + if (this.previousKeyScope) { + key.setScope(this.previousKeyScope); + this.previousKeyScope = null; + } + }, + + serializeData: function () { + var issueKey = encodeURIComponent(this.model.get('key')); + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + permalink: baseUrl + '/issues/search#issues=' + issueKey + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/manual-issue-view.js b/server/sonar-web/src/main/js/components/issue/manual-issue-view.js new file mode 100644 index 00000000000..c1f60b486e6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/manual-issue-view.js @@ -0,0 +1,114 @@ +define([ + './templates' +], function () { + + var $ = jQuery, + API_ISSUE = baseUrl + '/api/issues/show', + API_ADD_MANUAL_ISSUE = baseUrl + '/api/issues/create'; + + return Marionette.ItemView.extend({ + template: Templates['manual-issue'], + + events: { + 'submit .js-manual-issue-form': 'formSubmit', + 'click .js-cancel': 'cancel' + }, + + initialize: function () { + var that = this; + this.rules = []; + $.get(baseUrl + '/api/rules/search?repositories=manual&f=name&ps=9999999').done(function (r) { + that.rules = r.rules; + that.render(); + }); + }, + + onRender: function () { + this.delegateEvents(); + this.$('[name=rule]').select2({ + width: '250px', + minimumResultsForSearch: 10 + }); + if (this.rules.length > 0) { + this.$('[name=rule]').select2('open'); + } + if (key != null) { + this.key = key.getScope(); + key.setScope(''); + } + }, + + onClose: function () { + if (key != null && this.key != null) { + key.setScope(this.key); + } + }, + + showSpinner: function () { + this.$('.js-submit').hide(); + this.$('.js-spinner').show(); + }, + + hideSpinner: function () { + this.$('.js-submit').show(); + this.$('.js-spinner').hide(); + }, + + validateFields: function () { + var message = this.$('[name=message]'); + if (!message.val()) { + message.addClass('invalid').focus(); + return false; + } + return true; + }, + + formSubmit: function (e) { + var that = this; + e.preventDefault(); + if (!this.validateFields()) { + return; + } + this.showSpinner(); + var data = $(e.currentTarget).serialize(); + $.post(API_ADD_MANUAL_ISSUE, data) + .done(function (r) { + if (typeof r === 'string') { + r = JSON.parse(r); + } + that.addIssue(r.issue.key); + }).fail(function (r) { + that.hideSpinner(); + if (r.responseJSON && r.responseJSON.errors) { + that.showError(_.pluck(r.responseJSON.errors, 'msg').join('. ')); + } + }); + }, + + addIssue: function (key) { + var that = this; + return $.get(API_ISSUE, { key: key }).done(function (r) { + that.trigger('add', r.issue); + that.close(); + }); + }, + + showError: function (msg) { + this.$('.code-issue-errors').removeClass('hidden').text(msg); + }, + + cancel: function (e) { + e.preventDefault(); + this.close(); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + line: this.options.line, + component: this.options.component, + rules: _.sortBy(this.rules, 'name') + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/models/changelog.js b/server/sonar-web/src/main/js/components/issue/models/changelog.js new file mode 100644 index 00000000000..a7efe2b886a --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/models/changelog.js @@ -0,0 +1,13 @@ +define([], function () { + + return Backbone.Collection.extend({ + url: function () { + return baseUrl + '/api/issues/changelog'; + }, + + parse: function (r) { + return r.changelog; + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/models/issue.js b/server/sonar-web/src/main/js/components/issue/models/issue.js new file mode 100644 index 00000000000..1c5e928818e --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/models/issue.js @@ -0,0 +1,15 @@ +define([], function () { + + return Backbone.Model.extend({ + idAttribute: 'key', + + url: function () { + return baseUrl + '/api/issues/show?key=' + this.get('key'); + }, + + parse: function (r) { + return r.issue ? r.issue : r; + } + }); + +}); diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/comment-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/comment-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/comment-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/comment-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form-option.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-assign-form-option.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form-option.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-assign-form-option.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-assign-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-assign-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-changelog.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-changelog.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-changelog.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-changelog.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-more-actions.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-more-actions.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-more-actions.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-more-actions.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-plan-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-plan-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-plan-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-plan-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-set-severity-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-set-severity-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-set-severity-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-set-severity-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form-option.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-tags-form-option.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form-option.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-tags-form-option.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-tags-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-tags-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue-transitions-form.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue-transitions-form.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue-transitions-form.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue-transitions-form.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/issue.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/issue.hbs rename to server/sonar-web/src/main/js/components/issue/templates/issue.hbs diff --git a/server/sonar-web/src/main/coffee/components/issue/templates/manual-issue.hbs b/server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs similarity index 100% rename from server/sonar-web/src/main/coffee/components/issue/templates/manual-issue.hbs rename to server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs diff --git a/server/sonar-web/src/main/js/components/issue/views/action-options-view.js b/server/sonar-web/src/main/js/components/issue/views/action-options-view.js new file mode 100644 index 00000000000..402a0cc6ec6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/action-options-view.js @@ -0,0 +1,116 @@ +define([ + 'components/common/popup' +], function (PopupView) { + + var $ = jQuery; + + return PopupView.extend({ + keyScope: 'issue-action-options', + + ui: { + options: '.issue-action-option' + }, + + events: function () { + return { + 'click .issue-action-option': 'selectOption', + 'mouseenter .issue-action-option': 'activateOptionByPointer' + }; + }, + + initialize: function () { + this.bindShortcuts(); + }, + + onRender: function () { + PopupView.prototype.onRender.apply(this, arguments); + this.selectInitialOption(); + this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' }); + }, + + getOptions: function () { + return this.$('.issue-action-option'); + }, + + getActiveOption: function () { + return this.getOptions().filter('.active'); + }, + + makeActive: function (option) { + if (option.length > 0) { + this.getOptions().removeClass('active'); + option.addClass('active'); + } + }, + + selectInitialOption: function () { + this.makeActive(this.getOptions().first()); + }, + + selectNextOption: function () { + this.makeActive(this.getActiveOption().nextAll('.issue-action-option').first()); + return false; + }, + + selectPreviousOption: function () { + this.makeActive(this.getActiveOption().prevAll('.issue-action-option').first()); + return false; + }, + + activateOptionByPointer: function (e) { + this.makeActive($(e.currentTarget)); + }, + + bindShortcuts: function () { + var that = this; + this.currentKeyScope = key.getScope(); + key.setScope(this.keyScope); + key('down', this.keyScope, function () { + return that.selectNextOption(); + }); + key('up', this.keyScope, function () { + return that.selectPreviousOption(); + }); + key('return', this.keyScope, function () { + return that.selectActiveOption(); + }); + key('escape', this.keyScope, function () { + return that.close(); + }); + key('backspace', this.keyScope, function () { + return false; + }); + key('shift+tab', this.keyScope, function () { + return false; + }); + }, + + unbindShortcuts: function () { + key.unbind('down', this.keyScope); + key.unbind('up', this.keyScope); + key.unbind('return', this.keyScope); + key.unbind('escape', this.keyScope); + key.unbind('backspace', this.keyScope); + key.unbind('tab', this.keyScope); + key.unbind('shift+tab', this.keyScope); + key.setScope(this.currentKeyScope); + }, + + onClose: function () { + PopupView.prototype.onClose.apply(this, arguments); + this.unbindShortcuts(); + this.$('[data-toggle="tooltip"]').tooltip('destroy'); + $('.tooltip').remove(); + }, + + selectOption: function (e) { + e.preventDefault(); + this.close(); + }, + + selectActiveOption: function () { + this.getActiveOption().click(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/assign-form-view.js b/server/sonar-web/src/main/js/components/issue/views/assign-form-view.js new file mode 100644 index 00000000000..9bda9b4ea5c --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/assign-form-view.js @@ -0,0 +1,158 @@ +define([ + './action-options-view', + '../templates' +], function (ActionOptionsView) { + + var $ = jQuery; + + return ActionOptionsView.extend({ + template: Templates['issue-assign-form'], + optionTemplate: Templates['issue-assign-form-option'], + + events: function () { + return _.extend(ActionOptionsView.prototype.events.apply(this, arguments), { + 'click input': 'onInputClick', + 'keydown input': 'onInputKeydown', + 'keyup input': 'onInputKeyup' + }); + }, + + initialize: function () { + ActionOptionsView.prototype.initialize.apply(this, arguments); + this.assignees = []; + this.debouncedSearch = _.debounce(this.search, 250); + }, + + getAssignee: function () { + return this.model.get('assignee'); + }, + + getAssigneeName: function () { + return this.model.get('assigneeName'); + }, + + onRender: function () { + var that = this; + ActionOptionsView.prototype.onRender.apply(this, arguments); + this.renderTags(); + setTimeout(function () { + that.$('input').focus(); + }, 100); + }, + + renderTags: function () { + this.$('.issue-action-option').remove(); + this.getAssignees().forEach(this.renderAssignee, this); + this.selectInitialOption(); + }, + + renderAssignee: function (assignee) { + var html = this.optionTemplate(assignee); + this.$('.issue-action-options').append(html); + }, + + selectOption: function (e) { + var assignee = $(e.currentTarget).data('value'), + assigneeName = $(e.currentTarget).data('text'); + this.submit(assignee, assigneeName); + return ActionOptionsView.prototype.selectOption.apply(this, arguments); + }, + + submit: function (assignee, assigneeName) { + var that = this; + var _assignee = this.getAssignee(), + _assigneeName = this.getAssigneeName(); + if (assignee === _assignee) { + return; + } + if (assignee === '') { + this.model.set({ assignee: null, assigneeName: null }); + } else { + this.model.set({ assignee: assignee, assigneeName: assigneeName }); + } + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/assign', + data: { + issue: this.model.id, + assignee: assignee + } + }).fail(function () { + that.model.set({ assignee: _assignee, assigneeName: _assigneeName }); + }); + }, + + onInputClick: function (e) { + e.stopPropagation(); + }, + + onInputKeydown: function (e) { + this.query = this.$('input').val(); + if (e.keyCode === 38) { + return this.selectPreviousOption(); + } + if (e.keyCode === 40) { + return this.selectNextOption(); + } + if (e.keyCode === 13) { + return this.selectActiveOption(); + } + if (e.keyCode === 9) { + return false; + } + if (e.keyCode === 27) { + return this.close(); + } + }, + + onInputKeyup: function () { + var query = this.$('input').val(); + if (query !== this.query) { + if (query.length < 2) { + query = ''; + } + this.query = query; + this.debouncedSearch(query); + } + }, + + search: function (query) { + var that = this; + if (query.length > 1) { + $.get(baseUrl + '/api/users/search', { q: query }).done(function (data) { + that.resetAssignees(data.users); + }); + } else { + this.resetAssignees([]); + } + }, + + resetAssignees: function (users) { + this.assignees = users.map(function (user) { + return { id: user.login, text: user.name }; + }); + this.renderTags(); + }, + + getAssignees: function () { + if (this.assignees.length > 0) { + return this.assignees; + } + var assignees = [{ id: '', text: t('unassigned') }], + currentUser = window.SS.user, + currentUserName = window.SS.userName; + assignees.push({ id: currentUser, text: currentUserName }); + if (this.getAssignee()) { + assignees.push({ id: this.getAssignee(), text: this.getAssigneeName() }); + } + return this.makeUnique(assignees); + }, + + makeUnique: function (assignees) { + return _.uniq(assignees, false, function (assignee) { + return assignee.id; + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/changelog-view.js b/server/sonar-web/src/main/js/components/issue/views/changelog-view.js new file mode 100644 index 00000000000..b834d90054c --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/changelog-view.js @@ -0,0 +1,20 @@ +define([ + 'components/common/popup', + '../templates' +], function (PopupView) { + + return PopupView.extend({ + template: Templates['issue-changelog'], + + collectionEvents: { + 'sync': 'render' + }, + + serializeData: function () { + return _.extend(PopupView.prototype.serializeData.apply(this, arguments), { + issue: this.options.issue.toJSON() + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js b/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js new file mode 100644 index 00000000000..4816ff2eada --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js @@ -0,0 +1,70 @@ +define([ + 'components/common/popup', + '../templates' +], function (PopupView) { + + var $ = jQuery; + + return PopupView.extend({ + className: 'bubble-popup issue-comment-bubble-popup', + template: Templates['comment-form'], + + ui: { + textarea: '.issue-comment-form-text textarea', + cancelButton: '.js-issue-comment-cancel', + submitButton: '.js-issue-comment-submit' + }, + + events: { + 'click': 'onClick', + 'keydown @ui.textarea': 'onKeydown', + 'keyup @ui.textarea': 'toggleSubmit', + 'click @ui.cancelButton': 'cancel', + 'click @ui.submitButton': 'submit' + }, + + onRender: function () { + var that = this; + PopupView.prototype.onRender.apply(this, arguments); + setTimeout(function () { + that.ui.textarea.focus(); + }, 100); + }, + + toggleSubmit: function () { + this.ui.submitButton.prop('disabled', this.ui.textarea.val().length === 0); + }, + + onClick: function (e) { + e.stopPropagation(); + }, + + onKeydown: function (e) { + if (e.keyCode === 27) { + this.close(); + } + }, + + cancel: function () { + this.options.detailView.updateAfterAction(false); + }, + + submit: function () { + var that = this; + var text = this.ui.textarea.val(), + update = this.model && this.model.has('key'), + method = update ? 'edit_comment' : 'add_comment', + url = baseUrl + '/api/issues/' + method, + data = { text: text }; + if (update) { + data.key = this.model.get('key'); + } else { + data.issue = this.options.issue.id; + } + return $.post(url, data).done(function () { + that.options.detailView.updateAfterAction(true); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/issue-popup.js b/server/sonar-web/src/main/js/components/issue/views/issue-popup.js new file mode 100644 index 00000000000..575fb50a52a --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/issue-popup.js @@ -0,0 +1,33 @@ +define([ + 'components/common/popup' +], function (PopupView) { + + return PopupView.extend({ + className: 'bubble-popup issue-bubble-popup', + + template: function () { + return '
'; + }, + + events: function () { + return { + 'click .js-issue-form-cancel': 'close' + }; + }, + + onRender: function () { + PopupView.prototype.onRender.apply(this, arguments); + this.options.view.$el.appendTo(this.$el); + this.options.view.render(); + }, + + onClose: function () { + this.options.view.close(); + }, + + attachCloseEvents: function () { + + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/more-actions-view.js b/server/sonar-web/src/main/js/components/issue/views/more-actions-view.js new file mode 100644 index 00000000000..5b1c85e53b8 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/more-actions-view.js @@ -0,0 +1,23 @@ +define([ + 'components/common/popup', + '../templates' +], function (PopupView) { + + var $ = jQuery; + + return PopupView.extend({ + template: Templates['issue-more-actions'], + + events: function () { + return { + 'click .js-issue-action': 'action' + }; + }, + + action: function (e) { + var actionKey = $(e.currentTarget).data('action'); + return this.options.detailView.action(actionKey); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/plan-form-view.js b/server/sonar-web/src/main/js/components/issue/views/plan-form-view.js new file mode 100644 index 00000000000..5bc566cf67d --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/plan-form-view.js @@ -0,0 +1,74 @@ +define([ + './action-options-view', + '../templates' +], function (ActionOptionsView) { + + var $ = jQuery; + + return ActionOptionsView.extend({ + template: Templates['issue-plan-form'], + + getActionPlan: function () { + return this.model.get('actionPlan') || ''; + }, + + getActionPlanName: function () { + return this.model.get('actionPlanName'); + }, + + selectInitialOption: function () { + this.makeActive(this.getOptions().filter('[data-value="' + this.getActionPlan() + '"]')); + }, + + selectOption: function (e) { + var actionPlan = $(e.currentTarget).data('value'), + actionPlanName = $(e.currentTarget).data('text'); + this.submit(actionPlan, actionPlanName); + return ActionOptionsView.prototype.selectOption.apply(this, arguments); + }, + + submit: function (actionPlan, actionPlanName) { + var that = this; + var _actionPlan = this.getActionPlan(), + _actionPlanName = this.getActionPlanName(); + if (actionPlan === _actionPlan) { + return; + } + if (actionPlan === '') { + this.model.set({ + actionPlan: null, + actionPlanName: null + }); + } else { + this.model.set({ + actionPlan: actionPlan, + actionPlanName: actionPlanName + }); + } + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/plan', + data: { + issue: this.model.id, + plan: actionPlan + } + }).fail(function () { + return that.model.set({ + assignee: _actionPlan, + assigneeName: _actionPlanName + }); + }); + }, + + getActionPlans: function () { + return [{ key: '', name: t('issue.unplanned') }].concat(this.collection.toJSON()); + }, + + serializeData: function () { + return _.extend(ActionOptionsView.prototype.serializeData.apply(this, arguments), { + items: this.getActionPlans() + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/set-severity-form-view.js b/server/sonar-web/src/main/js/components/issue/views/set-severity-form-view.js new file mode 100644 index 00000000000..87e0cd51996 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/set-severity-form-view.js @@ -0,0 +1,51 @@ +define([ + './action-options-view', + '../templates' +], function (ActionOptionsView) { + + var $ = jQuery; + + return ActionOptionsView.extend({ + template: Templates['issue-set-severity-form'], + + getTransition: function () { + return this.model.get('severity'); + }, + + selectInitialOption: function () { + return this.makeActive(this.getOptions().filter('[data-value="' + this.getTransition() + '"]')); + }, + + selectOption: function (e) { + var severity = $(e.currentTarget).data('value'); + this.submit(severity); + return ActionOptionsView.prototype.selectOption.apply(this, arguments); + }, + + submit: function (severity) { + var that = this; + var _severity = this.getTransition(); + if (severity === _severity) { + return; + } + this.model.set({ severity: severity }); + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/set_severity', + data: { + issue: this.model.id, + severity: severity + } + }).fail(function () { + that.model.set({ severity: _severity }); + }); + }, + + serializeData: function () { + return _.extend(ActionOptionsView.prototype.serializeData.apply(this, arguments), { + items: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'] + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/tags-form-view.js b/server/sonar-web/src/main/js/components/issue/views/tags-form-view.js new file mode 100644 index 00000000000..a750a245b63 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/tags-form-view.js @@ -0,0 +1,177 @@ +define([ + './action-options-view', + '../templates' +], function (ActionOptionsView) { + + var $ = jQuery; + + return ActionOptionsView.extend({ + template: Templates['issue-tags-form'], + optionTemplate: Templates['issue-tags-form-option'], + + modelEvents: { + 'change:tags': 'renderTags' + }, + + events: function () { + return _.extend(ActionOptionsView.prototype.events.apply(this, arguments), { + 'click input': 'onInputClick', + 'keydown input': 'onInputKeydown', + 'keyup input': 'onInputKeyup' + }); + }, + + initialize: function () { + ActionOptionsView.prototype.initialize.apply(this, arguments); + this.query = ''; + this.tags = []; + this.selected = 0; + this.debouncedSearch = _.debounce(this.search, 250); + this.requestTags(); + }, + + requestTags: function () { + var that = this; + return $.get(baseUrl + '/api/issues/tags', { ps: 25 }).done(function (data) { + that.tags = data.tags; + that.renderTags(); + }); + }, + + onRender: function () { + var that = this; + ActionOptionsView.prototype.onRender.apply(this, arguments); + this.renderTags(); + setTimeout(function () { + that.$('input').focus(); + }, 100); + }, + + selectInitialOption: function () { + this.selected = Math.max(Math.min(this.selected, this.getOptions().length - 1), 0); + this.makeActive(this.getOptions().eq(this.selected)); + }, + + filterTags: function (tags) { + var that = this; + return _.filter(tags, function (tag) { + return tag.indexOf(that.query) !== -1; + }); + }, + + renderTags: function () { + this.$('.issue-action-option').remove(); + this.filterTags(this.getTags()).forEach(this.renderSelectedTag, this); + this.filterTags(_.difference(this.tags, this.getTags())).forEach(this.renderTag, this); + if (this.query.length > 0 && this.tags.indexOf(this.query) === -1 && this.getTags().indexOf(this.query) === -1) { + this.renderCustomTag(this.query); + } + this.selectInitialOption(); + }, + + renderSelectedTag: function (tag) { + var html = this.optionTemplate({ + tag: tag, + selected: true, + custom: false + }); + return this.$('.issue-action-options').append(html); + }, + + renderTag: function (tag) { + var html = this.optionTemplate({ + tag: tag, + selected: false, + custom: false + }); + return this.$('.issue-action-options').append(html); + }, + + renderCustomTag: function (tag) { + var html = this.optionTemplate({ + tag: tag, + selected: false, + custom: true + }); + return this.$('.issue-action-options').append(html); + }, + + selectOption: function (e) { + e.preventDefault(); + e.stopPropagation(); + var tags = this.getTags().slice(), + tag = $(e.currentTarget).data('value'); + if ($(e.currentTarget).data('selected') != null) { + tags = _.without(tags, tag); + } else { + tags.push(tag); + } + this.selected = this.getOptions().index($(e.currentTarget)); + return this.submit(tags); + }, + + submit: function (tags) { + var that = this; + var _tags = this.getTags(); + this.model.set({ tags: tags }); + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/set_tags', + data: { + key: this.model.id, + tags: tags.join() + } + }).fail(function () { + return that.model.set({ tags: _tags }); + }); + }, + + onInputClick: function (e) { + e.stopPropagation(); + }, + + onInputKeydown: function (e) { + this.query = this.$('input').val(); + if (e.keyCode === 38) { + return this.selectPreviousOption(); + } + if (e.keyCode === 40) { + return this.selectNextOption(); + } + if (e.keyCode === 13) { + return this.selectActiveOption(); + } + if (e.keyCode === 9) { + return false; + } + if (e.keyCode === 27) { + return this.close(); + } + }, + + onInputKeyup: function () { + var query = this.$('input').val(); + if (query !== this.query) { + this.query = query; + this.debouncedSearch(query); + } + }, + + search: function (query) { + this.query = query; + return this.renderTags(); + }, + + resetAssignees: function (users) { + this.assignees = users.map(function (user) { + return { id: user.login, text: user.name }; + }); + this.renderTags(); + }, + + getTags: function () { + return this.model.get('tags') || []; + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js b/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js new file mode 100644 index 00000000000..6558537e225 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js @@ -0,0 +1,36 @@ +define([ + './action-options-view', + '../templates' +], function (ActionOptionsView) { + + var $ = jQuery; + + return ActionOptionsView.extend({ + template: Templates['issue-transitions-form'], + + selectInitialOption: function () { + this.makeActive(this.getOptions().first()); + }, + + selectOption: function (e) { + var transition = $(e.currentTarget).data('value'); + this.submit(transition); + return ActionOptionsView.prototype.selectOption.apply(this, arguments); + }, + + submit: function (transition) { + var that = this; + return $.ajax({ + type: 'POST', + url: baseUrl + '/api/issues/do_transition', + data: { + issue: this.model.get('key'), + transition: transition + } + }).done(function () { + return that.options.view.resetIssue({}); + }); + } + }); + +}); -- 2.39.5