aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/Gruntfile.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/collections/action-plans.coffee30
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/collections/issues.coffee70
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/issue-view.coffee350
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/manual-issue-view.coffee113
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/models/changelog.coffee30
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/models/issue.coffee32
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee120
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee148
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee38
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee84
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee48
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee41
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee78
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee65
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee161
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee53
-rw-r--r--server/sonar-web/src/main/js/components/issue/collections/action-plans.js13
-rw-r--r--server/sonar-web/src/main/js/components/issue/collections/issues.js54
-rw-r--r--server/sonar-web/src/main/js/components/issue/issue-view.js332
-rw-r--r--server/sonar-web/src/main/js/components/issue/manual-issue-view.js114
-rw-r--r--server/sonar-web/src/main/js/components/issue/models/changelog.js13
-rw-r--r--server/sonar-web/src/main/js/components/issue/models/issue.js15
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/comment-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/comment-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-assign-form-option.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form-option.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-assign-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-assign-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-changelog.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-changelog.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-more-actions.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-more-actions.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-plan-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-plan-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-set-severity-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-set-severity-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-tags-form-option.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form-option.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-tags-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-tags-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue-transitions-form.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue-transitions-form.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/issue.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/issue.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs (renamed from server/sonar-web/src/main/coffee/components/issue/templates/manual-issue.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/action-options-view.js116
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/assign-form-view.js158
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/changelog-view.js20
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/comment-form-view.js70
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/issue-popup.js33
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/more-actions-view.js23
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/plan-form-view.js74
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/set-severity-form-view.js51
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/tags-form-view.js177
-rw-r--r--server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js36
45 files changed, 1300 insertions, 1462 deletions
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: -> '<div class="bubble-popup-arrow"></div>'
-
-
- 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
index 9bd51396d0b..9bd51396d0b 100644
--- 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
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
index e35905f8c1d..e35905f8c1d 100644
--- 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
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
index 9d20be8d079..9d20be8d079 100644
--- 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
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
index fd6f33d2d25..fd6f33d2d25 100644
--- 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
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
index 18917b5f9c1..18917b5f9c1 100644
--- 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
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
index d4e693aa9d0..d4e693aa9d0 100644
--- 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
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
index 7007fddf67e..7007fddf67e 100644
--- 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
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
index df054fe2466..df054fe2466 100644
--- 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
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
index 9d20be8d079..9d20be8d079 100644
--- 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
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
index 2fa1f4cb7dd..2fa1f4cb7dd 100644
--- 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
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
index f5a2d5e2fda..f5a2d5e2fda 100644
--- a/server/sonar-web/src/main/coffee/components/issue/templates/issue.hbs
+++ b/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
index bbcf333395b..bbcf333395b 100644
--- 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
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 '<div class="bubble-popup-arrow"></div>';
+ },
+
+ 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({});
+ });
+ }
+ });
+
+});