aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server/src/main/coffee
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-server/src/main/coffee')
-rw-r--r--sonar-server/src/main/coffee/component-viewer/app.coffee32
-rw-r--r--sonar-server/src/main/coffee/component-viewer/main.coffee35
-rw-r--r--sonar-server/src/main/coffee/component-viewer/source.coffee27
-rw-r--r--sonar-server/src/main/coffee/issues/collections/action-plans.coffee14
-rw-r--r--sonar-server/src/main/coffee/issues/issue-view.coffee229
-rw-r--r--sonar-server/src/main/coffee/issues/models/issue.coffee14
-rw-r--r--sonar-server/src/main/coffee/issues/models/rule.coffee14
-rw-r--r--sonar-server/src/main/coffee/issues/views/assign-form-view.coffee81
-rw-r--r--sonar-server/src/main/coffee/issues/views/comment-form-view.coffee60
-rw-r--r--sonar-server/src/main/coffee/issues/views/plan-form-view.coffee59
-rw-r--r--sonar-server/src/main/coffee/issues/views/rule-view.coffee20
-rw-r--r--sonar-server/src/main/coffee/issues/views/set-severity-form-view.coffee52
12 files changed, 601 insertions, 36 deletions
diff --git a/sonar-server/src/main/coffee/component-viewer/app.coffee b/sonar-server/src/main/coffee/component-viewer/app.coffee
deleted file mode 100644
index 85ec0f9026d..00000000000
--- a/sonar-server/src/main/coffee/component-viewer/app.coffee
+++ /dev/null
@@ -1,32 +0,0 @@
-requirejs.config
- baseUrl: "#{baseUrl}/js"
-
- paths:
- 'backbone': 'third-party/backbone'
- 'backbone.marionette': 'third-party/backbone.marionette'
- 'handlebars': 'third-party/handlebars'
- 'jquery.mockjax': 'third-party/jquery.mockjax'
-
- shim:
- 'backbone.marionette':
- deps: ['backbone']
- exports: 'Marionette'
- 'backbone':
- exports: 'Backbone'
- 'handlebars':
- exports: 'Handlebars'
-
-
-requirejs [
- 'component-viewer/main',
-], (
- ComponentViewer
-) ->
-
- TEST_RESOURCE_KEY = 'org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/resources/ResourceTypeTree.java'
-
-
- @componentViewer = new ComponentViewer()
- @componentViewer.render().$el.appendTo '#body'
-
- @componentViewer.open TEST_RESOURCE_KEY \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/main.coffee b/sonar-server/src/main/coffee/component-viewer/main.coffee
index c92b1c74c36..c231570efd1 100644
--- a/sonar-server/src/main/coffee/component-viewer/main.coffee
+++ b/sonar-server/src/main/coffee/component-viewer/main.coffee
@@ -40,11 +40,20 @@ define [
model: @source
main: @
- @settings = new Backbone.Model issues: false, coverage: true, duplications: false, scm: false
+ @settings = new Backbone.Model
+ issues: false
+ coverage: false
+ duplications: false
+ scm: false
+ workspace: false
onRender: ->
- @workspaceRegion.show @workspaceView
+ if @settings.get 'workspace'
+ @workspaceRegion.show @workspaceView
+ @$el.addClass 'component-viewer-workspace-enabled'
+ else
+ @$el.removeClass 'component-viewer-workspace-enabled'
@sourceRegion.show @sourceView
@@ -109,6 +118,28 @@ define [
@sourceView.render()
+ showWorkspace: ->
+ @settings.set 'workspace', true
+ @render()
+
+
+ hideWorkspace: ->
+ @settings.set 'workspace', false
+ @render()
+
+
+ showIssues: (issues, scrollToFirst) ->
+ @settings.set 'issues', true
+ if _.isArray(issues) && issues.length > 0
+ @source.set 'issues', issues
+ @sourceView.render()
+
+
+ hideIssues: ->
+ @settings.set 'issues', false
+ @sourceView.render()
+
+
addTransition: (key, transition, optionsForCurrent, options) ->
if optionsForCurrent?
last = @workspace.at(@workspace.length - 1)
diff --git a/sonar-server/src/main/coffee/component-viewer/source.coffee b/sonar-server/src/main/coffee/component-viewer/source.coffee
index 27f72f9da40..72f7c7e7b14 100644
--- a/sonar-server/src/main/coffee/component-viewer/source.coffee
+++ b/sonar-server/src/main/coffee/component-viewer/source.coffee
@@ -2,11 +2,15 @@ define [
'backbone.marionette'
'templates/component-viewer'
'component-viewer/coverage-popup'
+ 'issues/issue-view'
+ 'issues/models/issue'
'common/handlebars-extensions'
], (
Marionette
Templates
CoveragePopupView
+ IssueView
+ Issue
) ->
$ = jQuery
@@ -19,6 +23,7 @@ define [
events:
'click .settings-toggle button': 'toggleSettings'
'change #source-coverage': 'toggleCoverage'
+ 'change #source-workspace': 'toggleWorkspace'
'click .coverage-tests': 'showCoveragePopup'
@@ -26,12 +31,23 @@ define [
@delegateEvents()
@showSettings = false
+ @renderIssues() if @options.main.settings.get('issues') && @model.has('issues')
+
+
+ renderIssues: ->
+ issues = @model.get 'issues'
+ issues.forEach (issue) =>
+ line = issue.line || 0
+ container = @$("[data-line-number=#{line}]").children('.line')
+ container.addClass 'issue' if line > 0
+ issueView = new IssueView model: new Issue issue
+ issueView.render().$el.appendTo container
+
showSpinner: ->
@$el.html '<div style="padding: 10px;"><i class="spinner"></i></div>'
-
toggleSettings: ->
@$('.settings-toggle button').toggleClass 'open'
@$('.component-viewer-source-settings').toggleClass 'open'
@@ -43,6 +59,12 @@ define [
if active then @options.main.showCoverage() else @options.main.hideCoverage()
+ toggleWorkspace: (e) ->
+ active = $(e.currentTarget).is ':checked'
+ @showSettings = true
+ if active then @options.main.showWorkspace() else @options.main.hideWorkspace()
+
+
showCoveragePopup: (e) ->
e.stopPropagation()
$('body').click()
@@ -88,4 +110,5 @@ define [
serializeData: ->
source: @prepareSource()
settings: @options.main.settings.toJSON()
- showSettings: @showSettings \ No newline at end of file
+ showSettings: @showSettings
+ component: @options.main.component.toJSON() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/collections/action-plans.coffee b/sonar-server/src/main/coffee/issues/collections/action-plans.coffee
new file mode 100644
index 00000000000..69e0d459de1
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/collections/action-plans.coffee
@@ -0,0 +1,14 @@
+define [
+ 'backbone'
+], (
+ Backbone
+) ->
+
+ class ActionPlans extends Backbone.Collection
+
+ url: ->
+ "#{baseUrl}/api/action_plans/search"
+
+
+ parse: (r) ->
+ r.actionPlans \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/issue-view.coffee b/sonar-server/src/main/coffee/issues/issue-view.coffee
new file mode 100644
index 00000000000..0c1dc02ceb8
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/issue-view.coffee
@@ -0,0 +1,229 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+
+ 'issues/models/rule'
+ 'issues/views/rule-view'
+
+ 'issues/collections/action-plans'
+
+ 'issues/views/assign-form-view'
+ 'issues/views/comment-form-view'
+ 'issues/views/plan-form-view'
+ 'issues/views/set-severity-form-view'
+
+], (
+ Marionette
+ Templates
+
+ Rule
+ RuleView
+
+ ActionPlans
+
+ AssignFormView
+ CommentFormView
+ PlanFormView
+ SetSeverityFormView
+
+) ->
+
+ $ = jQuery
+
+
+ class IssueView extends Marionette.Layout
+ className: 'code-issues'
+ template: Templates['issue']
+
+
+ regions:
+ formRegion: '.code-issue-form'
+ ruleRegion: '#tab-issue-rule'
+
+
+ modelEvents:
+ 'change': 'render'
+
+
+ events:
+ 'click': 'setDetailScope',
+
+ 'click .code-issue-toggle': 'toggleCollapsed',
+
+ 'click [href=#tab-issue-rule]': 'fetchRule',
+
+ 'click #issue-comment': 'comment',
+ 'click .issue-comment-edit': 'editComment',
+ 'click .issue-comment-delete': 'deleteComment',
+ 'click .issue-transition': 'transition',
+ 'click #issue-set-severity': 'setSeverity',
+ 'click #issue-assign': 'assign',
+ 'click #issue-assign-to-me': 'assignToMe',
+ 'click #issue-plan': 'plan',
+ 'click .issue-action': 'action'
+
+
+ onRender: ->
+ @$('.code-issue-details').tabs()
+ @$('.code-issue-form').hide()
+ @rule = new Rule key: this.model.get('rule')
+ @ruleRegion.show new RuleView model: @rule, issue: @model
+
+
+ setDetailScope: ->
+ key.setScope 'detail'
+
+
+ onClose: ->
+ @ruleRegion.reset() if @ruleRegion
+
+
+ resetIssue: (options) ->
+ key = @model.get 'key'
+ @model.clear silent: true
+ @model.set { key: key }, { silent: true }
+ @model.fetch options
+
+
+ toggleCollapsed: ->
+ @$('.code-issue').toggleClass 'code-issue-collapsed'
+ @fetchRule()
+
+
+ fetchRule: ->
+ unless @rule.has 'name'
+ @$('#tab-issue-rule').addClass 'navigator-fetching'
+ @rule.fetch
+ success: => @$('#tab-issue-rule').removeClass 'navigator-fetching'
+
+
+ showActionView: (view) ->
+ @$('.code-issue-actions').hide()
+ @$('.code-issue-form').show()
+ @formRegion.show view
+
+
+ showActionSpinner: ->
+ @$('.code-issue-actions').addClass 'navigator-fetching'
+
+
+ hideActionSpinner: ->
+ @$('.code-issue-actions').removeClass 'navigator-fetching'
+
+
+ updateAfterAction: (fetch) ->
+ @formRegion.reset()
+ @$('.code-issue-actions').show()
+ @$('.code-issue-form').hide()
+ @$('[data-comment-key]').show()
+
+ if fetch
+ $.when(@resetIssue()).done => @hideActionSpinner()
+
+
+ comment: ->
+ commentFormView = new CommentFormView
+ issue: @model
+ detailView: @
+ @showActionView commentFormView
+
+
+ editComment: (e) ->
+ commentEl = $(e.target).closest '[data-comment-key]'
+ commentKey = commentEl.data 'comment-key'
+ comment = _.findWhere this.model.get('comments'), { key: commentKey }
+
+ commentEl.hide();
+
+ commentFormView = new CommentFormView
+ model: new Backbone.Model comment
+ issue: @model
+ detailView: @
+ @showActionView commentFormView
+
+
+ deleteComment: (e) ->
+ commentKey = $(e.target).closest('[data-comment-key]').data 'comment-key'
+ confirmMsg = $(e.target).data 'confirm-msg'
+
+ if confirm(confirmMsg)
+ @showActionSpinner()
+ $.ajax
+ type: "POST"
+ url: baseUrl + "/issue/delete_comment?id=" + commentKey
+ .done => @updateAfterAction true
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @hideActionSpinner()
+
+
+ transition: (e) ->
+ @showActionSpinner();
+ $.ajax
+ type: 'POST',
+ url: baseUrl + '/api/issues/do_transition',
+ data:
+ issue: @model.get('key')
+ transition: $(e.target).data('transition')
+ .done => @resetIssue()
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @hideActionSpinner()
+
+
+ setSeverity: ->
+ setSeverityFormView = new SetSeverityFormView
+ issue: @model
+ detailView: @
+ @showActionView setSeverityFormView
+
+
+ assign: ->
+ assignFormView = new AssignFormView
+ issue: @model
+ detailView: this
+ @showActionView assignFormView
+
+
+ assignToMe: ->
+ @showActionSpinner()
+ $.ajax
+ type: 'POST'
+ url: baseUrl + '/api/issues/assign'
+ data:
+ issue: @model.get('key')
+ assignee: window.SS.currentUser
+ .done => @resetIssue()
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @hideActionSpinner()
+
+
+ plan: ->
+ actionPlans = new ActionPlans()
+ planFormView = new PlanFormView
+ collection: actionPlans
+ issue: @model
+ detailView: @
+ @showActionSpinner()
+ actionPlans.fetch
+ reset: true
+ data: project: @model.get('project')
+ success: =>
+ @hideActionSpinner()
+ @showActionView planFormView
+
+
+ action: (e) ->
+ actionKey = $(e.target).data 'action'
+ @showActionSpinner()
+ $.ajax
+ type: 'POST'
+ url: baseUrl + '/api/issues/do_action'
+ data:
+ issue: @model.get('key')
+ actionKey: actionKey
+ .done => @resetIssue()
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @hideActionSpinner() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/models/issue.coffee b/sonar-server/src/main/coffee/issues/models/issue.coffee
new file mode 100644
index 00000000000..9011a28c596
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/models/issue.coffee
@@ -0,0 +1,14 @@
+define [
+ 'backbone'
+], (
+ Backbone
+) ->
+
+ class Issue extends Backbone.Model
+
+ url: ->
+ "#{baseUrl}/api/issues/show?key=#{@get('key')}"
+
+
+ parse: (r) ->
+ if r.issue then r.issue else r \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/models/rule.coffee b/sonar-server/src/main/coffee/issues/models/rule.coffee
new file mode 100644
index 00000000000..570feb88e94
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/models/rule.coffee
@@ -0,0 +1,14 @@
+define [
+ 'backbone'
+], (
+ Backbone
+) ->
+
+ class Rule extends Backbone.Model
+
+ url: ->
+ "#{baseUrl}/api/rules/show/?key=#{@get('key')}"
+
+
+ parse: (r) ->
+ if r.rule then r.rule else r \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/views/assign-form-view.coffee b/sonar-server/src/main/coffee/issues/views/assign-form-view.coffee
new file mode 100644
index 00000000000..433a1b68ed3
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/views/assign-form-view.coffee
@@ -0,0 +1,81 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+], (
+ Marionette
+ Templates
+) ->
+
+ $ = jQuery
+
+
+ class AssignFormView extends Marionette.ItemView
+ template: Templates['assign-form']
+
+
+ ui:
+ select: '#issue-assignee-select'
+
+
+ events:
+ 'click #issue-assign-cancel': 'cancel'
+ 'click #issue-assign-submit': 'submit'
+
+
+ onRender: ->
+ currentUser = window.SS.currentUser
+ assignee = @options.issue.get('assignee')
+ additionalChoices = []
+
+ if !assignee || currentUser != assignee
+ additionalChoices.push id: currentUser, text: translate('assignedToMe')
+
+ if !!assignee
+ additionalChoices.push id: '', text: translate('unassigned')
+
+ select2Options =
+ allowClear: false
+ width: '250px'
+ formatNoMatches: -> translate('select2.noMatches')
+ formatSearching: -> translate('select2.searching')
+ formatInputTooShort: -> translate('select2.tooShort')
+
+ if additionalChoices.length > 0
+ select2Options.minimumInputLength = 0
+ select2Options.query = (query) ->
+ if query.term.length == 0
+ query.callback results: additionalChoices
+ else if query.term.length >= 2
+ $.ajax
+ url: baseUrl + '/api/users/search?f=s2'
+ data: s: query.term
+ dataType: 'jsonp'
+ .done (data) -> query.callback data
+ else
+ select2Options.minimumInputLength = 2
+ select2Options.ajax =
+ quietMillis: 300
+ url: baseUrl + '/api/users/search?f=s2'
+ data: (term, page) -> s: term, p: page
+ results: (data) -> more: data.more, results: data.results
+
+ @ui.select.select2(select2Options).select2 'open'
+
+
+ cancel: ->
+ @options.detailView.updateAfterAction false
+
+
+ submit: ->
+ @options.detailView.showActionSpinner()
+
+ $.ajax
+ type: 'POST'
+ url: baseUrl + '/api/issues/assign'
+ data:
+ issue: @options.issue.get('key')
+ assignee: @ui.select.val()
+ .done => @options.detailView.updateAfterAction true
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @options.detailView.hideActionSpinner() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/views/comment-form-view.coffee b/sonar-server/src/main/coffee/issues/views/comment-form-view.coffee
new file mode 100644
index 00000000000..57c0393ce0a
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/views/comment-form-view.coffee
@@ -0,0 +1,60 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+], (
+ Marionette
+ Templates
+) ->
+
+ $ = jQuery
+
+
+ class IssueDetailCommentFormView extends Marionette.ItemView
+ template: Templates['comment-form']
+
+
+ ui:
+ textarea: '#issue-comment-text'
+ cancelButton: '#issue-comment-cancel'
+ submitButton: '#issue-comment-submit'
+
+
+ events:
+ 'keyup #issue-comment-text': 'toggleSubmit'
+ 'click #issue-comment-cancel': 'cancel'
+ 'click #issue-comment-submit': 'submit'
+
+
+ onDomRefresh: ->
+ @ui.textarea.focus()
+
+
+ toggleSubmit: ->
+ @ui.submitButton.prop 'disabled', @ui.textarea.val().length == 0
+
+
+ cancel: ->
+ @options.detailView.updateAfterAction false
+
+
+ submit: ->
+ text = @ui.textarea.val()
+ update = @model && @model.has('key')
+ url = baseUrl + '/api/issues/' + (if update then 'edit_comment' else 'add_comment')
+ data = text: text
+
+ if update
+ data.key = @model.get('key')
+ else
+ data.issue = @options.issue.get('key')
+
+ @options.detailView.showActionSpinner()
+
+ $.ajax
+ type: 'POST'
+ url: url
+ data: data
+ .done => @options.detailView.updateAfterAction true
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors 'msg').join(' ')
+ @options.detailView.hideActionSpinner() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/views/plan-form-view.coffee b/sonar-server/src/main/coffee/issues/views/plan-form-view.coffee
new file mode 100644
index 00000000000..c7130201516
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/views/plan-form-view.coffee
@@ -0,0 +1,59 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+], (
+ Marionette
+ Templates
+) ->
+
+ $ = jQuery
+
+
+ class PlanFormView extends Marionette.ItemView
+ template: Templates['plan-form']
+
+
+ collectionEvents:
+ 'reset': 'render'
+
+
+ ui:
+ select: '#issue-detail-plan-select'
+
+
+ events:
+ 'click #issue-plan-cancel': 'cancel'
+ 'click #issue-plan-submit': 'submit'
+
+
+ onRender: ->
+ @ui.select.select2
+ width: '250px'
+ minimumResultsForSearch: 100
+
+ @$('.error a').prop('href', baseUrl + '/action_plans/index/' + this.options.issue.get('project'))
+
+
+ cancel: ->
+ @options.detailView.updateAfterAction(false);
+
+
+ submit: ->
+ plan = @ui.select.val()
+ @options.detailView.showActionSpinner()
+
+ $.ajax
+ type: 'POST'
+ url: baseUrl + '/api/issues/plan'
+ data:
+ issue: this.options.issue.get('key'),
+ plan: if plan == '#unplan' then '' else plan
+ .done => @options.detailView.updateAfterAction true
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @options.detailView.hideActionSpinner()
+
+
+ serializeData: ->
+ items: @collection.toJSON()
+ issue: @options.issue.toJSON() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/views/rule-view.coffee b/sonar-server/src/main/coffee/issues/views/rule-view.coffee
new file mode 100644
index 00000000000..caf05d4c72f
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/views/rule-view.coffee
@@ -0,0 +1,20 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+], (
+ Marionette
+ Templates
+) ->
+
+ class IssueDetailRuleView extends Marionette.ItemView
+ className: 'rule-desc'
+ template: Templates['rule']
+
+ modelEvents:
+ 'change': 'render'
+
+
+ serializeData: ->
+ _.extend super,
+ characteristic: this.options.issue.get 'characteristic'
+ subCharacteristic: this.options.issue.get 'subCharacteristic' \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/issues/views/set-severity-form-view.coffee b/sonar-server/src/main/coffee/issues/views/set-severity-form-view.coffee
new file mode 100644
index 00000000000..96d756db7b4
--- /dev/null
+++ b/sonar-server/src/main/coffee/issues/views/set-severity-form-view.coffee
@@ -0,0 +1,52 @@
+define [
+ 'backbone.marionette'
+ 'templates/issues'
+], (
+ Marionette
+ Templates
+) ->
+
+ $ = jQuery
+
+
+ class SetSeverityFormView extends Marionette.ItemView
+ template: Templates['set-severity-form']
+
+
+ ui:
+ select: '#issue-set-severity-select'
+
+
+ events:
+ 'click #issue-set-severity-cancel': 'cancel'
+ 'click #issue-set-severity-submit': 'submit'
+
+
+ onRender: ->
+ format = (state) ->
+ return state.text unless state.id
+ '<i class="icon-severity-' + state.id.toLowerCase() + '"></i> ' + state.text
+
+ @ui.select.select2
+ minimumResultsForSearch: 100
+ formatResult: format
+ formatSelection: format
+ escapeMarkup: (m) -> m
+
+
+ cancel: ->
+ @options.detailView.updateAfterAction false
+
+
+ submit: ->
+ @options.detailView.showActionSpinner()
+ $.ajax
+ type: 'POST'
+ url: baseUrl + '/api/issues/set_severity'
+ data:
+ issue: @options.issue.get('key')
+ severity: @ui.select.val()
+ .done => @options.detailView.updateAfterAction true
+ .fail (r) =>
+ alert _.pluck(r.responseJSON.errors, 'msg').join(' ')
+ @options.detailView.hideActionSpinner() \ No newline at end of file