aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/coffee/apps
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-05-18 16:29:19 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-05-19 18:20:09 +0200
commite59e4aa2c06a57566f148f4217d286461231ae58 (patch)
treead0918eb06b2474a6c71997b7ff573bbb93e0170 /server/sonar-web/src/main/coffee/apps
parent8d82c9cf9ba88c1517604b4063ad0fc2a9a1bb9a (diff)
downloadsonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.tar.gz
sonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.zip
change web structure: separate components
Diffstat (limited to 'server/sonar-web/src/main/coffee/apps')
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee88
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee37
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee24
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee50
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee42
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs6
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs1
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs10
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs24
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee50
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee50
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee64
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/app-context.coffee131
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/app-new.coffee109
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee36
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee192
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/controller.coffee225
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee83
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee72
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee105
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee47
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee37
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee35
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee141
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee71
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee56
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee43
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee67
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee83
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee54
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee61
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee71
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee36
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee35
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee63
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee97
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee47
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/layout.coffee69
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee37
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee30
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee95
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/models/state.coffee47
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/router.coffee58
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs18
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs4
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs18
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs30
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs10
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs3
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs32
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs14
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs10
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs7
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs20
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs11
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs11
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs55
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs71
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs5
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs11
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs37
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs77
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs13
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs5
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee64
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee149
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee118
-rw-r--r--server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee103
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee117
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee29
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee48
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee42
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee58
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee26
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee62
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs7
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs8
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs62
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs3
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs45
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs16
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs13
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs2
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs22
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs1
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs13
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs9
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee146
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee27
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee105
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee101
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee55
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee67
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee101
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee42
103 files changed, 5174 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee
new file mode 100644
index 00000000000..0d7c20d4bd4
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee
@@ -0,0 +1,88 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+define [
+ './router'
+ './layout'
+ './models/reports'
+ './views/reports-view'
+ './views/actions-view'
+], (
+ Router
+ Layout
+ Reports
+ ReportsView
+ ActionsView
+) ->
+
+ # Add html class to mark the page as navigator page
+ jQuery('html').addClass 'navigator-page'
+
+
+ # Create an Application
+ App = new Marionette.Application
+
+
+ App.fetchReports = ->
+ fetch = if @state.get 'active' then @reports.fetchActive() else @reports.fetchHistory()
+ @layout.showSpinner 'actionsRegion'
+ @layout.resultsRegion.reset()
+ fetch.done =>
+ @state.set page: @reports.paging.page
+ @reportsView = new ReportsView
+ app: @
+ collection: @reports
+ @layout.resultsRegion.show @reportsView
+
+ unless @state.get('active') || @reports.paging.maxResultsReached
+ @reportsView.bindScrollEvents() unless @state.get 'active'
+
+ @actionsView = new ActionsView
+ app: @
+ collection: @reports
+ @layout.actionsRegion.show @actionsView
+
+
+ App.fetchNextPage = ->
+ @reports.fetchHistory
+ data:
+ p: @state.get('page') + 1
+ remove: false
+ .done =>
+ @state.set page: @reports.paging.page
+
+
+ App.addInitializer ->
+ @state = new Backbone.Model()
+ @state.on 'change:active', => @fetchReports()
+
+
+ App.addInitializer ->
+ @layout = new Layout app: @
+ jQuery('#analysis-reports').empty().append @layout.render().el
+
+
+ App.addInitializer ->
+ @reports = new Reports()
+ @router = new Router app: @
+ Backbone.history.start()
+
+
+ l10nXHR = window.requestMessages()
+ jQuery.when(l10nXHR).done -> App.start()
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee
new file mode 100644
index 00000000000..5aa028e86b2
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee
@@ -0,0 +1,37 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './templates'
+], ->
+
+ class extends Marionette.Layout
+ template: Templates['analysis-reports-layout']
+
+
+
+ regions:
+ actionsRegion: '.analysis-reports-actions'
+ resultsRegion: '.analysis-reports-results'
+
+
+ showSpinner: (region) ->
+ @[region].show new Marionette.ItemView
+ template: _.template('<i class="spinner"></i>')
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee
new file mode 100644
index 00000000000..28700ed4a62
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee
@@ -0,0 +1,24 @@
+#
+# 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 extends Backbone.Model
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee
new file mode 100644
index 00000000000..1689eed3e65
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee
@@ -0,0 +1,50 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './report'
+], (
+ Report
+) ->
+
+
+ class extends Backbone.Collection
+ model: Report
+
+
+ parse: (r) ->
+ @paging =
+ page: r.p
+ pageSize: r.ps
+ total: r.total
+ maxResultsReached: r.p * r.ps >= r.total
+ r.reports
+
+
+ fetchActive: ->
+ @fetch { url: "#{baseUrl}/api/computation/queue" }, { reset: true }
+
+
+ fetchHistory: (options = { }) ->
+ _.extend options,
+ url: "#{baseUrl}/api/computation/history"
+ options.data = options.data || {}
+ options.data.ps = 50
+ @fetch options, { reset: true }
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee
new file mode 100644
index 00000000000..1bebf0ee964
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee
@@ -0,0 +1,42 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class AppRouter extends Backbone.Router
+
+ routes:
+ '': 'showCurrent'
+ 'current': 'showCurrent'
+ 'past': 'showPast'
+
+
+ initialize: (options) ->
+ @options = options
+
+
+ showCurrent: ->
+ @navigate 'current'
+ @options.app.state.set active: true
+
+
+ showPast: ->
+ @navigate 'past'
+ @options.app.state.set active: false
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs
new file mode 100644
index 00000000000..90f24e63851
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs
@@ -0,0 +1,6 @@
+<ul class="tabs">
+ <li><a class="js-show-current-activity {{#if state.active}}selected{{/if}}">{{t 'analysis_reports.current_activity'}}</a></li>
+ <li><a class="js-show-past-reports {{#unless state.active}}selected{{/unless}}">{{t 'analysis_reports.past_reports'}}</a></li>
+</ul>
+
+<div class="analysis-reports-total">{{tp 'analysis_reports.x_reports' total}}</div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs
new file mode 100644
index 00000000000..b044d70fc04
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs
@@ -0,0 +1 @@
+No reports to show
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs
new file mode 100644
index 00000000000..e00c677a6d6
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs
@@ -0,0 +1,10 @@
+<header class="page-header">
+ <h1 class="page-title">{{t 'analysis_reports.page'}}</h1>
+ <p class="page-description">
+ The server is in charge to process reports submitted by batch analyses. This page allows to monitor the queue of
+ pending reports to process, and gives access to the history of past analyses.
+ </p>
+</header>
+
+<div class="analysis-reports-actions"></div>
+<div class="analysis-reports-results"></div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs
new file mode 100644
index 00000000000..b816c2fbec7
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs
@@ -0,0 +1,24 @@
+<div class="line">
+ <span class="analysis-reports-project">
+ <i class="icon-qualifier-trk"></i>
+ <a href="{{dashboardUrl projectKey}}">{{projectName}}</a>
+ </span>
+
+ <span class="analysis-reports-timestamp line-small">
+ {{#if submittedAt}}Submitted: {{dt submittedAt}}<br>{{/if}}
+ {{#if startedAt}}Started: {{dt startedAt}}<br>{{/if}}
+ {{#if finishedAt}}Finished: {{dt finishedAt}}<br>{{/if}}
+ </span>
+
+ {{#if duration}}
+ <span class="analysis-reports-timestamp line-small">
+ Duration:
+ {{#gt duration.hours 0}}{{duration.hours}}h{{/gt}}
+ {{#gt duration.minutes 0}}{{duration.minutes}}m{{/gt}}
+ {{duration.seconds}}s
+ </span>
+ {{/if}}
+
+</div>
+
+<div class="analysis-reports-report-id">{{key}} {{status}}</div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee
new file mode 100644
index 00000000000..89ae9048c52
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee
@@ -0,0 +1,50 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ '../templates'
+], ->
+
+ class extends Marionette.ItemView
+ template: Templates['analysis-reports-actions']
+
+
+ events:
+ 'click .js-show-past-reports': 'showPastReports'
+ 'click .js-show-current-activity': 'showCurrentActivity'
+
+
+ initialize: (options) ->
+ @listenTo options.collection, 'all', @render
+ @listenTo options.app.state, 'change', @render
+
+
+ showPastReports: ->
+ @options.app.router.navigate 'past', trigger: true
+
+
+ showCurrentActivity: ->
+ @options.app.router.navigate 'current', trigger: true
+
+
+ serializeData: ->
+ _.extend super,
+ state: @options.app.state.toJSON()
+ total: @collection.paging.total || @collection.length
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee
new file mode 100644
index 00000000000..4af3d06f602
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee
@@ -0,0 +1,50 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ '../templates'
+], ->
+
+ class extends Marionette.ItemView
+ tagName: 'li'
+ template: Templates['analysis-reports-report']
+
+
+ onRender: ->
+ status = @model.get 'status'
+ @$el.addClass 'analysis-reports-report-pending' if status is 'PENDING'
+ @$el.addClass 'analysis-reports-report-working' if status is 'WORKING'
+ @$el.addClass 'analysis-reports-report-done' if status is 'SUCCESS'
+ @$el.addClass 'analysis-reports-report-cancelled' if status is 'CANCELLED'
+ @$el.addClass 'analysis-reports-report-failed' if status is 'FAIL'
+
+
+ serializeData: ->
+ duration = null
+ if @model.has 'startedAt'
+ startedAtMoment = moment @model.get 'startedAt'
+ finishedAtMoment = moment(@model.get('finishedAt') || new Date())
+ duration = finishedAtMoment.diff startedAtMoment
+ duration =
+ seconds: Math.floor (duration / 1000) % 60
+ minutes: Math.floor (duration / (1000 * 60)) % 60
+ hours: Math.floor (duration / (1000 * 60 * 60)) % 24
+ _.extend super,
+ duration: duration
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee
new file mode 100644
index 00000000000..c5c16ea59c0
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee
@@ -0,0 +1,28 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ '../templates'
+], ->
+
+ class extends Marionette.ItemView
+ className: 'analysis-reports-no-results'
+ tagName: 'li'
+ template: Templates['analysis-reports-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee
new file mode 100644
index 00000000000..32efc6f0707
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee
@@ -0,0 +1,64 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './report-view'
+ './reports-empty-view'
+], (
+ ReportView
+ EmptyView
+) ->
+
+ $ = jQuery
+
+
+ class extends Marionette.CollectionView
+ tagName: 'ol'
+ className: 'navigator-results-list'
+ itemView: ReportView
+ emptyView: EmptyView
+
+
+ itemViewOptions: ->
+ listView: @, app: @options.app
+
+
+ initialize: ->
+ @loadMoreThrottled = _.throttle @loadMore, 200
+
+
+ onClose: ->
+ @unbindScrollEvents()
+
+
+ bindScrollEvents: ->
+ $(window).on 'scroll', (=> @loadMoreThrottled())
+
+
+ unbindScrollEvents: ->
+ $(window).off 'scroll'
+
+
+ loadMore: ->
+ lastItem = this.children.findByIndex(@collection.length - 1)
+ if $(window).scrollTop() + $(window).outerHeight() >= lastItem.$el.offset().top - 40
+ @unbindScrollEvents()
+ @options.app.fetchNextPage().done =>
+ @bindScrollEvents() unless @collection.paging.maxResultsReached
diff --git a/server/sonar-web/src/main/coffee/apps/issues/app-context.coffee b/server/sonar-web/src/main/coffee/apps/issues/app-context.coffee
new file mode 100644
index 00000000000..556b93e1e00
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/app-context.coffee
@@ -0,0 +1,131 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+define [
+ './models/state'
+ './layout'
+ './models/issues'
+ 'components/navigator/models/facets'
+ './models/filters'
+
+ './controller'
+ './router'
+
+ './workspace-list-view'
+ './workspace-header-view'
+
+ './facets-view'
+ './filters-view'
+], (
+ State
+ Layout
+ Issues
+ Facets
+ Filters
+
+ Controller
+ Router
+
+ WorkspaceListView
+ WorkspaceHeaderView
+
+ FacetsView
+ FiltersView
+) ->
+
+ $ = jQuery
+ App = new Marionette.Application
+
+
+ App.getContextQuery = ->
+ componentUuids: window.config.resource
+
+
+ App.getRestrictedFacets = ->
+ 'TRK': ['projectUuids']
+ 'BRC': ['projectUuids']
+ 'DIR': ['projectUuids', 'moduleUuids', 'directories']
+ 'DEV': ['authors']
+ 'DEV_PRJ': ['projectUuids', 'authors']
+
+
+ App.updateContextFacets = ->
+ facets = @state.get 'facets'
+ allFacets = @state.get 'allFacets'
+ facetsFromServer = @state.get 'facetsFromServer'
+ @state.set
+ facets: facets
+ allFacets: _.difference allFacets, @getRestrictedFacets()[window.config.resourceQualifier]
+ facetsFromServer: _.difference facetsFromServer, @getRestrictedFacets()[window.config.resourceQualifier]
+
+
+ App.addInitializer ->
+ @state = new State
+ isContext: true,
+ contextQuery: @getContextQuery()
+ contextComponentUuid: window.config.resource
+ contextComponentName: window.config.resourceName
+ contextComponentQualifier: window.config.resourceQualifier
+ @updateContextFacets()
+ @list = new Issues()
+ @facets = new Facets()
+ @filters = new Filters()
+
+
+ App.addInitializer ->
+ @layout = new Layout app: @
+ $('.issues').empty().append @layout.render().el
+ $('#footer').addClass('search-navigator-footer');
+
+
+ App.addInitializer ->
+ @controller = new Controller app: @
+
+
+ App.addInitializer ->
+ @issuesView = new WorkspaceListView
+ app: @
+ collection: @list
+ @layout.workspaceListRegion.show @issuesView
+ @issuesView.bindScrollEvents()
+
+
+ App.addInitializer ->
+ @workspaceHeaderView = new WorkspaceHeaderView
+ app: @
+ collection: @list
+ @layout.workspaceHeaderRegion.show @workspaceHeaderView
+
+
+ App.addInitializer ->
+ @facetsView = new FacetsView
+ app: @
+ collection: @facets
+ @layout.facetsRegion.show @facetsView
+
+
+ App.addInitializer ->
+ @controller.fetchFilters().done =>
+ key.setScope 'list'
+ @router = new Router app: @
+ Backbone.history.start()
+
+
+ l10nXHR = window.requestMessages()
+ jQuery.when(l10nXHR).done -> App.start()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/app-new.coffee b/server/sonar-web/src/main/coffee/apps/issues/app-new.coffee
new file mode 100644
index 00000000000..c69a48e6207
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/app-new.coffee
@@ -0,0 +1,109 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+define [
+ './models/state'
+ './layout'
+ './models/issues'
+ 'components/navigator/models/facets'
+ './models/filters'
+
+ './controller'
+ './router'
+
+ './workspace-list-view'
+ './workspace-header-view'
+
+ './facets-view'
+ './filters-view'
+], (
+ State
+ Layout
+ Issues
+ Facets
+ Filters
+
+ Controller
+ Router
+
+ WorkspaceListView
+ WorkspaceHeaderView
+
+ FacetsView
+ FiltersView
+) ->
+
+ $ = jQuery
+ App = new Marionette.Application
+
+ App.addInitializer ->
+ @state = new State()
+ @list = new Issues()
+ @facets = new Facets()
+ @filters = new Filters()
+
+
+ App.addInitializer ->
+ @layout = new Layout app: @
+ $('.issues').empty().append @layout.render().el
+ $('#footer').addClass('search-navigator-footer');
+
+
+ App.addInitializer ->
+ @controller = new Controller app: @
+
+
+ App.addInitializer ->
+ @issuesView = new WorkspaceListView
+ app: @
+ collection: @list
+ @layout.workspaceListRegion.show @issuesView
+ @issuesView.bindScrollEvents()
+
+
+ App.addInitializer ->
+ @workspaceHeaderView = new WorkspaceHeaderView
+ app: @
+ collection: @list
+ @layout.workspaceHeaderRegion.show @workspaceHeaderView
+
+
+ App.addInitializer ->
+ @facetsView = new FacetsView
+ app: @
+ collection: @facets
+ @layout.facetsRegion.show @facetsView
+
+
+ App.addInitializer ->
+ @filtersView = new FiltersView
+ app: @
+ collection: @filters
+ @layout.filtersRegion.show @filtersView
+
+
+ App.addInitializer ->
+ @controller.fetchFilters().done =>
+ key.setScope 'list'
+ @router = new Router app: @
+ Backbone.history.start()
+
+
+ l10nXHR = window.requestMessages()
+ jQuery.when(l10nXHR).done -> App.start()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee
new file mode 100644
index 00000000000..b626cff1070
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee
@@ -0,0 +1,36 @@
+#
+# 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 [
+ '../workspace-list-item-view'
+], (
+ IssueView
+) ->
+
+ class extends IssueView
+
+ onRender: ->
+ super
+ @$el.removeClass 'issue-navigate-right'
+
+
+ serializeData: ->
+ _.extend super,
+ showComponent: false
diff --git a/server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee
new file mode 100644
index 00000000000..1a23d070331
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee
@@ -0,0 +1,192 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/source-viewer/main'
+ '../models/issues'
+ './issue-view'
+ '../templates'
+], (
+ SourceViewer
+ Issues
+ IssueView
+) ->
+
+ $ = jQuery
+
+ TOP_OFFSET = 38
+ BOTTOM_OFFSET = 10
+
+
+ class extends SourceViewer
+
+ events: ->
+ _.extend super,
+ 'click .js-close-component-viewer': 'closeComponentViewer'
+ 'click .code-issue': 'selectIssue'
+
+
+ initialize: (options) ->
+ super
+ @listenTo options.app.state, 'change:selectedIndex', @select
+
+
+ onLoaded: ->
+ super
+ @bindShortcuts()
+ if @baseIssue?
+ @scrollToLine @baseIssue.get 'line'
+
+
+ bindShortcuts: ->
+ doAction = (action) =>
+ selectedIssueView = @getSelectedIssueEl()
+ return unless selectedIssueView
+ selectedIssueView.find(".js-issue-#{action}").click()
+
+ key 'up', 'componentViewer', =>
+ @options.app.controller.selectPrev()
+ false
+
+ key 'down', 'componentViewer', =>
+ @options.app.controller.selectNext()
+ false
+
+ key 'left,backspace', 'componentViewer', =>
+ @options.app.controller.closeComponentViewer()
+ false
+
+ key 'f', 'componentViewer', -> doAction 'transition'
+ key 'a', 'componentViewer', -> doAction 'assign'
+ key 'm', 'componentViewer', -> doAction 'assign-to-me'
+ key 'p', 'componentViewer', -> doAction 'plan'
+ key 'i', 'componentViewer', -> doAction 'set-severity'
+ key 'c', 'componentViewer', -> doAction 'comment'
+
+
+ unbindShortcuts: ->
+ key.deleteScope 'componentViewer'
+
+
+ onClose: ->
+ super
+ @unbindScrollEvents()
+ @unbindShortcuts()
+
+
+ select: ->
+ selected = @options.app.state.get 'selectedIndex'
+ selectedIssue = @options.app.list.at selected
+ if selectedIssue.get('component') == @model.get('key')
+ @scrollToIssue selectedIssue.get('key')
+ else
+ @unbindShortcuts()
+ @options.app.controller.showComponentViewer selectedIssue
+
+
+ getSelectedIssueEl: ->
+ selected = @options.app.state.get 'selectedIndex'
+ return null unless selected?
+ selectedIssue = @options.app.list.at selected
+ return null unless selectedIssue?
+ selectedIssueView = @$("#issue-#{selectedIssue.get('key')}")
+ if selectedIssueView.length > 0 then selectedIssueView else null
+
+
+ selectIssue: (e) ->
+ key = $(e.currentTarget).data 'issue-key'
+ issue = @issues.find (issue) -> issue.get('key') == key
+ index = @options.app.list.indexOf issue
+ @options.app.state.set selectedIndex: index
+
+
+ scrollToIssue: (key) ->
+ el = @$("#issue-#{key}")
+ if el.length > 0
+ line = el.closest('[data-line-number]').data 'line-number'
+ this.scrollToLine line
+ else
+ @unbindShortcuts()
+ selected = @options.app.state.get 'selectedIndex'
+ selectedIssue = @options.app.list.at selected
+ @options.app.controller.showComponentViewer selectedIssue
+
+
+ openFileByIssue: (issue) ->
+ @baseIssue = issue
+ componentKey = issue.get 'component'
+ componentUuid = issue.get 'componentUuid'
+ @open componentUuid, componentKey
+
+
+ linesLimit: ->
+ line = @LINES_LIMIT / 2
+ if @baseIssue? && @baseIssue.has('line')
+ line = Math.max line, @baseIssue.get('line')
+ from: line - @LINES_LIMIT / 2 + 1
+ to: line + @LINES_LIMIT / 2
+
+
+ limitIssues: (issues) ->
+ index = @ISSUES_LIMIT / 2
+ if @baseIssue? && @baseIssue.has('index')
+ index = Math.max index, @baseIssue.get('index')
+ x = issues.filter (issue) =>
+ Math.abs(issue.get('index') - index) <= @ISSUES_LIMIT / 2
+ x
+
+
+ requestIssues: ->
+ if @options.app.list.last().get('component') == @model.get('key')
+ r = @options.app.controller.fetchNextPage()
+ else r = $.Deferred().resolve().promise()
+ r.done =>
+ @issues.reset @options.app.list.filter (issue) => issue.get('component') == @model.key()
+ @issues.reset @limitIssues @issues
+ @addIssuesPerLineMeta @issues
+
+
+ renderIssues: ->
+ @issues.forEach @renderIssue, @
+ @$('.source-line-issues').addClass('hidden');
+
+
+ renderIssue: (issue) ->
+ issueView = new IssueView
+ el: '#issue-' + issue.get('key')
+ model: issue
+ app: @options.app
+ @issueViews.push issueView
+ issueView.render()
+
+
+ addNextIssuesPage: ->
+ componentKey = @model.get 'key'
+ @issues.add @options.app.list.filter (issue) => issue.get('component') == componentKey
+
+
+ scrollToLine: (line) ->
+ row = @$("[data-line-number=#{line}]")
+ goal = if row.length > 0 then row.offset().top - 200 else 0
+ $(window).scrollTop goal
+
+
+ closeComponentViewer: ->
+ @options.app.controller.closeComponentViewer()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/controller.coffee b/server/sonar-web/src/main/coffee/apps/issues/controller.coffee
new file mode 100644
index 00000000000..18749e884a4
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/controller.coffee
@@ -0,0 +1,225 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/controller'
+
+ './component-viewer/main'
+ './workspace-home-view'
+], (
+ Controller
+
+ ComponentViewer
+ HomeView
+) ->
+
+ $ = jQuery
+ EXTRA_FIELDS = 'actions,transitions,assigneeName,reporterName,actionPlanName'
+ FACET_DATA_FIELDS = ['components', 'projects', 'users', 'rules', 'actionPlans', 'languages']
+
+
+ class extends Controller
+
+ _facetsFromServer: ->
+ facets = super || []
+ facets.push 'assigned_to_me'
+ facets
+
+
+ _issuesParameters: ->
+ p: @options.app.state.get 'page'
+ ps: @pageSize
+ s: 'FILE_LINE'
+ asc: true
+ extra_fields: EXTRA_FIELDS
+ facets: @_facetsFromServer().join()
+
+
+ _myIssuesFromResponse: (r) ->
+ myIssuesData = _.findWhere r.facets, property: 'assigned_to_me'
+ if myIssuesData? && _.isArray(myIssuesData.values) && myIssuesData.values.length > 0
+ @options.app.state.set { myIssues: myIssuesData.values[0].count }, { silent: true }
+ else
+ @options.app.state.unset 'myIssues', { silent: true }
+
+
+ fetchList: (firstPage = true) ->
+ if firstPage
+ @options.app.state.set { selectedIndex: 0, page: 1 }, { silent: true }
+ @hideHomePage()
+ @closeComponentViewer()
+
+ data = @_issuesParameters()
+ _.extend data, @options.app.state.get 'query'
+ _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
+
+ $.get "#{baseUrl}/api/issues/search", data
+ .done (r) =>
+ issues = @options.app.list.parseIssues r
+ if firstPage
+ @options.app.list.reset issues
+ else
+ @options.app.list.add issues
+ @options.app.list.setIndex()
+ FACET_DATA_FIELDS.forEach (field) => @options.app.facets[field] = r[field]
+ @options.app.facets.reset @_allFacets()
+ @options.app.facets.add _.reject(r.facets, (f) -> f.property == 'assigned_to_me'), merge: true
+ @_myIssuesFromResponse r
+ @enableFacets @_enabledFacets()
+ @options.app.state.set
+ page: r.p
+ pageSize: r.ps
+ total: r.total
+ maxResultsReached: r.p * r.ps >= r.total
+ if firstPage && @isIssuePermalink()
+ @showComponentViewer @options.app.list.first()
+
+
+ isIssuePermalink: ->
+ query = @options.app.state.get('query')
+ query.issues? && @options.app.list.length == 1
+
+
+ fetchFilters: ->
+ $.get "#{baseUrl}/api/issue_filters/app", (r) =>
+ @options.app.state.set
+ canBulkChange: r.canBulkChange
+ canManageFilters: r.canManageFilters
+ @options.app.filters.reset r.favorites
+
+
+ _mergeCollections: (a, b) ->
+ collection = new Backbone.Collection a
+ collection.add b, merge: true
+ collection.toJSON()
+
+
+ requestFacet: (id) ->
+ return @requestAssigneeFacet() if id == 'assignees'
+ facet = @options.app.facets.get id
+ data = _.extend { facets: id, ps: 1 }, @options.app.state.get('query')
+ _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
+ $.get "#{baseUrl}/api/issues/search", data, (r) =>
+ FACET_DATA_FIELDS.forEach (field) =>
+ @options.app.facets[field] = @_mergeCollections @options.app.facets[field], r[field]
+ facetData = _.findWhere r.facets, property: id
+ facet.set facetData if facetData?
+
+
+ requestAssigneeFacet: ->
+ facet = @options.app.facets.get 'assignees'
+ data = _.extend { facets: 'assignees,assigned_to_me', ps: 1 }, @options.app.state.get('query')
+ _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
+ $.get "#{baseUrl}/api/issues/search", data, (r) =>
+ FACET_DATA_FIELDS.forEach (field) =>
+ @options.app.facets[field] = @_mergeCollections @options.app.facets[field], r[field]
+ facetData = _.findWhere r.facets, property: 'assignees'
+ @_myIssuesFromResponse r
+ facet.set facetData if facetData?
+
+
+ newSearch: ->
+ @options.app.state.unset 'filter'
+ @options.app.state.setQuery resolved: 'false'
+
+
+ applyFilter: (filter, ignoreQuery = false) ->
+ unless ignoreQuery
+ filterQuery = @parseQuery filter.get 'query'
+ @options.app.state.setQuery filterQuery
+ @options.app.state.set filter: filter, changed: false
+
+
+ parseQuery: ->
+ q = super
+ # Do not allow to modify the sorting
+ delete q.asc
+ delete q.s
+ q
+
+
+ getQuery: (separator = '|', addContext = false) ->
+ filter = @options.app.state.get 'query'
+ _.extend filter, @options.app.state.get 'contextQuery' if addContext && @options.app.state.get 'isContext'
+ route = []
+ _.map filter, (value, property) ->
+ route.push '' + property + '=' + encodeURIComponent(value)
+ route.join separator
+
+
+ getRoute: ->
+ filter = @options.app.state.get 'filter'
+ query = super
+ if filter?
+ if @options.app.state.get('changed') && query.length > 0
+ query = "id=#{filter.id}|#{query}"
+ else
+ query = "id=#{filter.id}"
+ query
+
+
+ _prepareComponent: (issue) ->
+ key: issue.get 'component'
+ name: issue.get 'componentLongName'
+ qualifier: issue.get 'componentQualifier'
+ project: issue.get 'project'
+ projectName: issue.get 'projectLongName'
+
+
+ showComponentViewer: (issue) ->
+ @options.app.layout.workspaceComponentViewerRegion.reset()
+ key.setScope 'componentViewer'
+ @options.app.issuesView.unbindScrollEvents()
+ @options.app.state.set 'component', @_prepareComponent(issue)
+ @options.app.componentViewer = new ComponentViewer app: @options.app
+ @options.app.layout.workspaceComponentViewerRegion.show @options.app.componentViewer
+ @options.app.layout.showComponentViewer()
+ @options.app.componentViewer.openFileByIssue issue
+
+
+ closeComponentViewer: ->
+ key.setScope 'list'
+ # close all popups
+ $('body').click()
+ @options.app.state.unset 'component'
+ @options.app.layout.workspaceComponentViewerRegion.reset()
+ @options.app.layout.hideComponentViewer()
+ @options.app.issuesView.bindScrollEvents()
+ @options.app.issuesView.scrollTo()
+
+
+ showHomePage: ->
+ @fetchList()
+ @options.app.layout.workspaceComponentViewerRegion.reset()
+ key.setScope 'home'
+ @options.app.issuesView.unbindScrollEvents()
+ @options.app.homeView = new HomeView app: @options.app
+ @options.app.layout.workspaceHomeRegion.show @options.app.homeView
+ @options.app.layout.showHomePage()
+
+
+ hideHomePage: ->
+ @options.app.layout.workspaceComponentViewerRegion.reset()
+ @options.app.layout.workspaceHomeRegion.reset()
+ key.setScope 'list'
+ @options.app.layout.hideHomePage()
+ @options.app.issuesView.bindScrollEvents()
+ @options.app.issuesView.scrollTo()
+
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee
new file mode 100644
index 00000000000..a8a17947ffd
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee
@@ -0,0 +1,83 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/facets-view'
+
+ './facets/base-facet'
+ './facets/severity-facet'
+ './facets/status-facet'
+ './facets/project-facet'
+ './facets/module-facet'
+ './facets/assignee-facet'
+ './facets/rule-facet'
+ './facets/tag-facet'
+ './facets/resolution-facet'
+ './facets/creation-date-facet'
+ './facets/action-plan-facet'
+ './facets/file-facet'
+ './facets/reporter-facet'
+ './facets/language-facet'
+ './facets/author-facet'
+ './facets/issue-key-facet'
+ './facets/context-facet'
+], (
+ FacetsView
+
+ BaseFacet
+ SeverityFacet
+ StatusFacet
+ ProjectFacet
+ ModuleFacet
+ AssigneeFacet
+ RuleFacet
+ TagFacet
+ ResolutionFacet
+ CreationDateFacet
+ ActionPlanFacet
+ FileFacet
+ ReporterFacet
+ LanguageFacet
+ AuthorFacet
+ IssueKeyFacet
+ ContextFacet
+) ->
+
+ class extends FacetsView
+
+ getItemView: (model) ->
+ switch model.get 'property'
+ when 'severities' then SeverityFacet
+ when 'statuses' then StatusFacet
+ when 'assignees' then AssigneeFacet
+ when 'resolutions' then ResolutionFacet
+ when 'createdAt' then CreationDateFacet
+ when 'projectUuids' then ProjectFacet
+ when 'moduleUuids' then ModuleFacet
+ when 'rules' then RuleFacet
+ when 'tags' then TagFacet
+ when 'actionPlans' then ActionPlanFacet
+ when 'fileUuids' then FileFacet
+ when 'reporters' then ReporterFacet
+ when 'languages' then LanguageFacet
+ when 'authors' then AuthorFacet
+ when 'issues' then IssueKeyFacet
+ when 'context' then ContextFacet
+ else BaseFacet
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee
new file mode 100644
index 00000000000..a2a746e1007
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee
@@ -0,0 +1,72 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+ $ = jQuery
+
+
+ class extends BaseFacet
+ template: Templates['issues-action-plan-facet']
+
+
+ onRender: ->
+ super
+ value = @options.app.state.get('query')['planned']
+ if value? && (!value || value == 'false')
+ @$('.js-facet').filter("[data-unplanned]").addClass 'active'
+
+
+ toggleFacet: (e) ->
+ unplanned = $(e.currentTarget).is "[data-unplanned]"
+ $(e.currentTarget).toggleClass 'active'
+ if unplanned
+ checked = $(e.currentTarget).is '.active'
+ value = if checked then 'false' else null
+ @options.app.state.updateFilter planned: value, actionPlans: null
+ else
+ @options.app.state.updateFilter planned: null, actionPlans: @getValue()
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ actionPlans = @options.app.facets.actionPlans
+ values.forEach (v) =>
+ key = v.val
+ label = null
+ if key
+ actionPlan = _.findWhere actionPlans, key: key
+ label = actionPlan.name if actionPlan?
+ v.label = label
+ values
+
+
+ disable: ->
+ @options.app.state.updateFilter planned: null, actionPlans: null
+
+
+ serializeData: ->
+ _.extend super,
+ values: @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee
new file mode 100644
index 00000000000..83d0a78da80
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee
@@ -0,0 +1,105 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+ '../templates'
+], (
+ CustomValuesFacet
+) ->
+
+ $ = jQuery
+
+
+ class extends CustomValuesFacet
+ template: Templates['issues-assignee-facet']
+
+
+ getUrl: ->
+ "#{baseUrl}/api/users/search"
+
+ prepareAjaxSearch: ->
+ quietMillis: 300
+ url: @getUrl()
+ data: (term, page) -> { q: term, p: page }
+ results: window.usersToSelect2
+
+ onRender: ->
+ super
+ value = @options.app.state.get('query')['assigned']
+ if value? && (!value || value == 'false')
+ @$('.js-facet').filter("[data-unassigned]").addClass 'active'
+
+
+ toggleFacet: (e) ->
+ unassigned = $(e.currentTarget).is "[data-unassigned]"
+ $(e.currentTarget).toggleClass 'active'
+ if unassigned
+ checked = $(e.currentTarget).is '.active'
+ value = if checked then 'false' else null
+ @options.app.state.updateFilter assigned: value, assignees: null
+ else
+ @options.app.state.updateFilter assigned: null, assignees: @getValue()
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ users = @options.app.facets.users
+ values.forEach (v) =>
+ login = v.val
+ name = ''
+ if login
+ user = _.findWhere users, login: login
+ name = user.name if user?
+ v.label = name
+ values
+
+
+ disable: ->
+ @options.app.state.updateFilter assigned: null, assignees: null
+
+
+ addCustomValue: ->
+ property = @model.get 'property'
+ customValue = @$('.js-custom-value').select2 'val'
+ value = @getValue()
+ value += ',' if value.length > 0
+ value += customValue
+ obj = {}
+ obj[property] = value
+ obj.assigned = null
+ @options.app.state.updateFilter obj
+
+
+ sortValues: (values) ->
+ # put "unassigned" first
+ _.sortBy values, (v) ->
+ x = if v.val == '' then -999999 else -v.count
+ x
+
+
+ getNumberOfMyIssues: ->
+ @options.app.state.get 'myIssues'
+
+
+ serializeData: ->
+ _.extend super,
+ myIssues: @getNumberOfMyIssues()
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee
new file mode 100644
index 00000000000..25e8c989749
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee
@@ -0,0 +1,47 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ getUrl: ->
+ "#{baseUrl}/api/issues/authors"
+
+
+ prepareSearch: ->
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 2
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ formatInputTooShort: -> tp 'select2.tooShort', 2
+ width: '100%'
+ ajax:
+ quietMillis: 300
+ url: @getUrl()
+ data: (term) -> { q: term, ps: 25 }
+ results: (data) -> { more: false, results: data.authors.map (author) -> { id: author, text: author } }
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee
new file mode 100644
index 00000000000..4d41996b164
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee
@@ -0,0 +1,37 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/facets/base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+ class extends BaseFacet
+ template: Templates['issues-base-facet']
+
+ onRender: ->
+ super
+ @$('[data-toggle="tooltip"]').tooltip container: 'body'
+
+
+ onClose: ->
+ @$('[data-toggle="tooltip"]').tooltip 'destroy'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee
new file mode 100644
index 00000000000..3809f07b856
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee
@@ -0,0 +1,35 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+
+ class extends BaseFacet
+ template: Templates['issues-context-facet']
+
+
+ serializeData: ->
+ _.extend super,
+ state: @options.app.state.toJSON()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee
new file mode 100644
index 00000000000..c1a29019ba1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee
@@ -0,0 +1,141 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+ $ = jQuery
+
+
+ class extends BaseFacet
+ template: Templates['issues-creation-date-facet']
+
+
+ events: ->
+ _.extend super,
+ 'change input': 'applyFacet'
+ 'click .js-select-period-start': 'selectPeriodStart'
+ 'click .js-select-period-end': 'selectPeriodEnd'
+
+ 'click .sonar-d3 rect': 'selectBar'
+
+ 'click .js-all': 'onAllClick'
+ 'click .js-last-week': 'onLastWeekClick'
+ 'click .js-last-month': 'onLastMonthClick'
+ 'click .js-last-year': 'onLastYearClick'
+
+
+ onRender: ->
+ @$el.toggleClass 'search-navigator-facet-box-collapsed', !@model.get('enabled')
+
+ @$('input').datepicker
+ dateFormat: 'yy-mm-dd'
+ changeMonth: true
+ changeYear: true
+
+ props = ['createdAfter', 'createdBefore', 'createdAt']
+ query = @options.app.state.get 'query'
+ props.forEach (prop) =>
+ value = query[prop]
+ @$("input[name=#{prop}]").val value if value?
+
+ values = @model.getValues()
+ unless _.isArray(values) && values.length > 0
+ date = moment()
+ values = []
+ for i in [0..10]
+ values.push count: 0, val: date.toDate().toString()
+ date = date.subtract 1, 'days'
+ values.reverse()
+ @$('.js-barchart').barchart values
+
+
+ selectPeriodStart: ->
+ @$('.js-period-start').datepicker 'show'
+
+
+ selectPeriodEnd: ->
+ @$('.js-period-end').datepicker 'show'
+
+
+ applyFacet: ->
+ obj = createdAt: null, createdInLast: null
+ @$('input').each ->
+ property = $(@).prop 'name'
+ value = $(@).val()
+ obj[property] = value
+ @options.app.state.updateFilter obj
+
+
+ disable: ->
+ @options.app.state.updateFilter
+ createdAfter: null
+ createdBefore: null
+ createdAt: null
+ createdInLast: null
+
+
+ selectBar: (e) ->
+ periodStart = $(e.currentTarget).data 'period-start'
+ periodEnd = $(e.currentTarget).data 'period-end'
+ @options.app.state.updateFilter
+ createdAfter: periodStart
+ createdBefore: periodEnd
+ createdAt: null
+ createdInLast: null
+
+
+ selectPeriod: (period) ->
+ @options.app.state.updateFilter
+ createdAfter: null
+ createdBefore: null
+ createdAt: null
+ createdInLast: period
+
+
+ onAllClick: ->
+ @disable()
+
+
+ onLastWeekClick: (e) ->
+ e.preventDefault()
+ @selectPeriod '1w'
+
+
+ onLastMonthClick: (e) ->
+ e.preventDefault()
+ @selectPeriod '1m'
+
+
+ onLastYearClick: (e) ->
+ e.preventDefault()
+ @selectPeriod '1y'
+
+
+ serializeData: ->
+ _.extend super,
+ periodStart: @options.app.state.get('query').createdAfter
+ periodEnd: @options.app.state.get('query').createdBefore
+ createdAt: @options.app.state.get('query').createdAt
+ createdInLast: @options.app.state.get('query').createdInLast
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee
new file mode 100644
index 00000000000..9cd45d02579
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee
@@ -0,0 +1,71 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+
+ class extends BaseFacet
+ template: Templates['issues-custom-values-facet']
+
+
+ events: ->
+ _.extend super,
+ 'change .js-custom-value': 'addCustomValue'
+
+
+ getUrl: ->
+
+
+ onRender: ->
+ super
+ @prepareSearch()
+
+
+ prepareSearch: ->
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 2
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ formatInputTooShort: -> tp 'select2.tooShort', 2
+ width: '100%'
+ ajax: @prepareAjaxSearch()
+
+ prepareAjaxSearch: ->
+ quietMillis: 300
+ url: @getUrl()
+ data: (term, page) -> { s: term, p: page }
+ results: (data) -> { more: data.more, results: data.results }
+
+ addCustomValue: ->
+ property = @model.get 'property'
+ customValue = @$('.js-custom-value').select2 'val'
+ value = @getValue()
+ value += ',' if value.length > 0
+ value += customValue
+ obj = {}
+ obj[property] = value
+ @options.app.state.updateFilter obj
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee
new file mode 100644
index 00000000000..62081444f4b
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee
@@ -0,0 +1,56 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+ $ = jQuery
+
+
+ class extends BaseFacet
+ template: Templates['issues-file-facet']
+
+
+ onRender: ->
+ super
+ maxValueWidth = _.max @$('.facet-stat').map(-> $(@).outerWidth()).get()
+ @$('.facet-name').css 'padding-right', maxValueWidth
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ source = @options.app.facets.components
+ values.forEach (v) =>
+ key = v.val
+ label = null
+ if key
+ item = _.findWhere source, uuid: key
+ label = item.longName if item?
+ v.label = label
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee
new file mode 100644
index 00000000000..53afcf4f441
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee
@@ -0,0 +1,43 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+
+ class extends BaseFacet
+ template: Templates['issues-issue-key-facet']
+
+
+ onRender: ->
+ @$el.toggleClass 'hidden', !@options.app.state.get('query').issues
+
+
+ disable: ->
+ @options.app.state.updateFilter issues: null
+
+
+ serializeData: ->
+ _.extend super,
+ issues: @options.app.state.get('query').issues
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee
new file mode 100644
index 00000000000..3d67584dd02
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee
@@ -0,0 +1,67 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ getUrl: ->
+ "#{baseUrl}/api/languages/list"
+
+
+ prepareSearch: ->
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 2
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ formatInputTooShort: -> tp 'select2.tooShort', 2
+ width: '100%'
+ ajax:
+ quietMillis: 300
+ url: @getUrl()
+ data: (term) -> { q: term, ps: 0 }
+ results: (data) ->
+ more: false
+ results: data.languages.map (lang) -> { id: lang.key, text: lang.name }
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ source = @options.app.facets.languages
+ values.forEach (v) =>
+ key = v.val
+ label = null
+ if key
+ item = _.findWhere source, key: key
+ label = item.name if item?
+ v.label = label
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee
new file mode 100644
index 00000000000..425f4b5c2d4
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee
@@ -0,0 +1,44 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+], (
+ BaseFacet
+) ->
+
+ class extends BaseFacet
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ components = @options.app.facets.components
+ values.forEach (v) =>
+ uuid = v.val
+ label = uuid
+ if uuid
+ component = _.findWhere components, uuid: uuid
+ label = component.longName if component?
+ v.label = label
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee
new file mode 100644
index 00000000000..d7ea58a1dca
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee
@@ -0,0 +1,83 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ getUrl: ->
+ q = @options.app.state.get 'contextComponentQualifier'
+ if q == 'VW' || q == 'SVW'
+ "#{baseUrl}/api/components/search"
+ else
+ "#{baseUrl}/api/resources/search?f=s2&q=TRK&display_uuid=true"
+
+
+ prepareSearch: ->
+ q = @options.app.state.get 'contextComponentQualifier'
+ if q == 'VW' || q == 'SVW'
+ @prepareSearchForViews()
+ else super
+
+
+ prepareSearchForViews: ->
+ componentUuid = this.options.app.state.get 'contextComponentUuid'
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 2
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ formatInputTooShort: -> tp 'select2.tooShort', 2
+ width: '100%'
+ ajax:
+ quietMillis: 300
+ url: @getUrl()
+ data: (term, page) ->
+ q: term
+ componentUuid: componentUuid
+ p: page
+ ps: 25
+ results: (data) ->
+ more: data.p * data.ps < data.total,
+ results: data.components.map (c) -> id: c.uuid, text: c.name
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ projects = @options.app.facets.projects
+ values.forEach (v) =>
+ uuid = v.val
+ label = ''
+ if uuid
+ project = _.findWhere projects, uuid: uuid
+ label = project.longName if project?
+ v.label = label
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee
new file mode 100644
index 00000000000..e8e10a4d06f
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee
@@ -0,0 +1,54 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ getUrl: ->
+ "#{baseUrl}/api/users/search"
+
+ prepareAjaxSearch: ->
+ quietMillis: 300
+ url: @getUrl()
+ data: (term, page) -> { q: term, p: page }
+ results: window.usersToSelect2
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ source = @options.app.facets.users
+ values.forEach (v) =>
+ key = v.val
+ label = null
+ if key
+ item = _.findWhere source, login: key
+ label = item.name if item?
+ v.label = label
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee
new file mode 100644
index 00000000000..7c7eef0bd74
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee
@@ -0,0 +1,61 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+ $ = jQuery
+
+
+ class extends BaseFacet
+ template: Templates['issues-resolution-facet']
+
+
+ onRender: ->
+ super
+
+ value = @options.app.state.get('query')['resolved']
+ if value? && (!value || value == 'false')
+ @$('.js-facet').filter("[data-unresolved]").addClass 'active'
+
+
+ toggleFacet: (e) ->
+ unresolved = $(e.currentTarget).is "[data-unresolved]"
+ $(e.currentTarget).toggleClass 'active'
+ if unresolved
+ checked = $(e.currentTarget).is '.active'
+ value = if checked then 'false' else null
+ @options.app.state.updateFilter resolved: value, resolutions: null
+ else
+ @options.app.state.updateFilter resolved: null, resolutions: @getValue()
+
+
+ disable: ->
+ @options.app.state.updateFilter resolved: null, resolutions: null
+
+
+ sortValues: (values) ->
+ order = ['', 'FIXED', 'FALSE-POSITIVE', 'WONTFIX', 'REMOVED']
+ _.sortBy values, (v) -> order.indexOf v.val
+
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee
new file mode 100644
index 00000000000..25ea9f9dd9c
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee
@@ -0,0 +1,71 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ prepareSearch: ->
+ url = "#{baseUrl}/api/rules/search?f=name,langName"
+ languages = @options.app.state.get('query').languages
+ if languages?
+ url += "&languages=#{languages}"
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 2
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ formatInputTooShort: -> tp 'select2.tooShort', 2
+ width: '100%'
+ ajax:
+ quietMillis: 300
+ url: url
+ data: (term, page) -> { q: term, p: page }
+ results: (data) ->
+ results = data.rules.map (rule) ->
+ id: rule.key, text: "(#{rule.langName}) #{rule.name}"
+ { more: (data.p * data.ps < data.total), results: results }
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ rules = @options.app.facets.rules
+ values.forEach (v) =>
+ key = v.val
+ label = ''
+ extra = ''
+ if key
+ rule = _.findWhere rules, key: key
+ label = rule.name if rule?
+ extra = rule.langName if rule?
+ v.label = label
+ v.extra = extra
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee
new file mode 100644
index 00000000000..c0639ef97a6
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee
@@ -0,0 +1,36 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+
+ class extends BaseFacet
+ template: Templates['issues-severity-facet']
+
+
+ sortValues: (values) ->
+ order = ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR']
+ _.sortBy values, (v) -> order.indexOf v.val
+
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee
new file mode 100644
index 00000000000..f3a0eb7a040
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee
@@ -0,0 +1,35 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './base-facet'
+ '../templates'
+], (
+ BaseFacet
+) ->
+
+
+ class extends BaseFacet
+ template: Templates['issues-status-facet']
+
+
+ sortValues: (values) ->
+ order = ['OPEN', 'RESOLVED', 'REOPENED', 'CLOSED', 'CONFIRMED']
+ _.sortBy values, (v) -> order.indexOf v.val
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee
new file mode 100644
index 00000000000..fb061cf96f8
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee
@@ -0,0 +1,63 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './custom-values-facet'
+], (
+ CustomValuesFacet
+) ->
+
+
+ class extends CustomValuesFacet
+
+ prepareSearch: ->
+ url = "#{baseUrl}/api/issues/tags?ps=10"
+ tags = @options.app.state.get('query').tags
+ if tags?
+ url += "&tags=#{tags}"
+ @$('.js-custom-value').select2
+ placeholder: 'Search...'
+ minimumInputLength: 0
+ allowClear: false
+ formatNoMatches: -> t 'select2.noMatches'
+ formatSearching: -> t 'select2.searching'
+ width: '100%'
+ ajax:
+ quietMillis: 300
+ url: url
+ data: (term, page) -> { q: term, ps: 10 }
+ results: (data) ->
+ results = data.tags.map (tag) ->
+ id: tag, text: tag
+ { more: false, results: results }
+
+
+ getValuesWithLabels: ->
+ values = @model.getValues()
+ tags = @options.app.facets.tags
+ values.forEach (v) =>
+ v.label = v.val
+ v.extra = ''
+ values
+
+
+ serializeData: ->
+ _.extend super,
+ values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee
new file mode 100644
index 00000000000..71024317783
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee
@@ -0,0 +1,97 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './templates'
+], ->
+
+ $ = jQuery
+
+
+ class extends Marionette.ItemView
+ template: Templates['issues-filters']
+
+
+ events:
+ 'click .js-toggle-filters': 'toggleFilters'
+ 'click .js-filter': 'applyFilter'
+ 'click .js-filter-save-as': 'saveAs'
+ 'click .js-filter-save': 'save'
+ 'click .js-filter-copy': 'copy'
+ 'click .js-filter-edit': 'edit'
+
+
+ initialize: (options) ->
+ @listenTo options.app.state, 'change:filter', @render
+ @listenTo options.app.state, 'change:changed', @render
+ @listenTo options.app.filters, 'all', @render
+ window.onSaveAs = window.onCopy = window.onEdit = (id) =>
+ $('#modal').dialog 'close'
+ @options.app.controller.fetchFilters().done =>
+ filter = @collection.get id
+ filter.fetch().done => @options.app.controller.applyFilter filter
+
+
+ onRender: ->
+ @$el.toggleClass 'search-navigator-filters-selected', @options.app.state.has('filter')
+
+
+ toggleFilters: (e) ->
+ e.stopPropagation()
+ @$('.search-navigator-filters-list').toggle()
+ $('body').on 'click.issues-filters', =>
+ $('body').off 'click.issues-filters'
+ @$('.search-navigator-filters-list').hide()
+
+
+ applyFilter: (e) ->
+ id = $(e.currentTarget).data 'id'
+ filter = @collection.get id
+ filter.fetch().done => @options.app.controller.applyFilter filter
+
+
+ saveAs: ->
+ query = @options.app.controller.getQuery '&'
+ url = "#{baseUrl}/issues/save_as_form?#{query}"
+ openModalWindow url, {}
+
+
+ save: ->
+ query = @options.app.controller.getQuery '&'
+ url = "#{baseUrl}/issues/save/#{@options.app.state.get('filter').id}?#{query}"
+ $.post(url).done =>
+ @options.app.state.set changed: false
+
+
+ copy: ->
+ url = "#{baseUrl}/issues/copy_form/#{@options.app.state.get('filter').id}"
+ openModalWindow url, {}
+
+
+ edit: ->
+ url = "#{baseUrl}/issues/edit_form/#{@options.app.state.get('filter').id}"
+ openModalWindow url, {}
+
+
+ serializeData: ->
+ _.extend super,
+ state: @options.app.state.toJSON()
+ filter: @options.app.state.get('filter')?.toJSON()
+ currentUser: window.SS.user
diff --git a/server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee
new file mode 100644
index 00000000000..cb3ee4d95f7
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee
@@ -0,0 +1,47 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/issue/views/action-options-view'
+ './templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issues-issue-filter-form']
+
+
+ selectInitialOption: ->
+ @makeActive @getOptions().first()
+
+
+ selectOption: (e) ->
+ property = $(e.currentTarget).data 'property'
+ value = $(e.currentTarget).data 'value'
+ @trigger 'select', property, value
+ super
+
+
+ serializeData: ->
+ _.extend super, s: @model.get 'severity'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/layout.coffee b/server/sonar-web/src/main/coffee/apps/issues/layout.coffee
new file mode 100644
index 00000000000..ac81d542e7c
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/layout.coffee
@@ -0,0 +1,69 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './templates'
+], ->
+
+ $ = jQuery
+
+
+ class extends Marionette.Layout
+ template: Templates['issues-layout']
+
+
+ regions:
+ filtersRegion: '.search-navigator-filters'
+ facetsRegion: '.search-navigator-facets'
+ workspaceHeaderRegion: '.search-navigator-workspace-header'
+ workspaceListRegion: '.search-navigator-workspace-list'
+ workspaceComponentViewerRegion: '.issues-workspace-component-viewer'
+ workspaceHomeRegion: '.issues-workspace-home'
+
+
+ onRender: ->
+ @$(@filtersRegion.el).addClass('hidden') if @options.app.state.get('isContext')
+ $('.search-navigator').addClass 'sticky'
+ top = $('.search-navigator').offset().top
+ @$('.search-navigator-workspace-header').css top: top
+ @$('.search-navigator-side').css({ top: top }).isolatedScroll()
+
+
+ showSpinner: (region) ->
+ @[region].show new Marionette.ItemView
+ template: _.template('<i class="spinner"></i>')
+
+
+ showComponentViewer: ->
+ @scroll = $(window).scrollTop()
+ $('.issues').addClass 'issues-extended-view'
+
+
+ hideComponentViewer: ->
+ $('.issues').removeClass 'issues-extended-view'
+ $(window).scrollTop @scroll if @scroll?
+
+
+ showHomePage: ->
+ $('.issues').addClass 'issues-home-view'
+
+
+ hideHomePage: ->
+ $('.issues').removeClass 'issues-home-view'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee
new file mode 100644
index 00000000000..96bc2d81e00
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee
@@ -0,0 +1,37 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class extends Backbone.Model
+ idAttribute: 'property'
+
+
+ defaults:
+ enabled: false
+
+
+ getValues: ->
+ @get('values') || []
+
+
+ toggle: ->
+ enabled = @get 'enabled'
+ @set enabled: !enabled
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee
new file mode 100644
index 00000000000..3f6a957b3d7
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee
@@ -0,0 +1,28 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './facet'
+], (
+ Facet
+) ->
+
+ class extends Backbone.Collection
+ model: Facet
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee
new file mode 100644
index 00000000000..9759bc31ff1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee
@@ -0,0 +1,30 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class extends Backbone.Model
+
+ url: ->
+ "/api/issue_filters/show/#{@id}"
+
+
+ parse: (r) ->
+ if r.filter? then r.filter else r
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee
new file mode 100644
index 00000000000..b002dba96a6
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee
@@ -0,0 +1,28 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './filter'
+], (
+ Filter
+) ->
+
+ class extends Backbone.Collection
+ model: Filter
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee
new file mode 100644
index 00000000000..08926040335
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee
@@ -0,0 +1,95 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/issue/models/issue'
+], (
+ Issue
+) ->
+
+ class extends Backbone.Collection
+ model: Issue
+
+ url: ->
+ "#{baseUrl}/api/issues/search"
+
+
+ # Used to parse /api/issues/search response
+ parseIssues: (r) ->
+ find = (source, key, keyField) ->
+ searchDict = {}
+ searchDict[keyField || 'key'] = key
+ _.findWhere(source, searchDict) || key
+
+ r.issues.map (issue, index) ->
+ component = find r.components, issue.component
+ project = find r.projects, issue.project
+ subProject = find r.components, issue.subProject
+ rule = find r.rules, issue.rule
+
+ _.extend issue,
+ index: index
+
+ if component
+ _.extend issue,
+ componentUuid: component.uuid
+ componentLongName: component.longName
+ componentQualifier: component.qualifier
+
+ if project
+ _.extend issue,
+ projectLongName: project.longName
+ projectUuid: project.uuid
+
+ if subProject
+ _.extend issue,
+ subProjectLongName: subProject.longName
+ subProjectUuid: subProject.uuid
+
+ if rule
+ _.extend issue,
+ ruleName: rule.name
+
+ if _.isArray(issue.sources) && issue.sources.length > 0
+ source = ''
+ issue.sources.forEach (line) ->
+ source = line[1] if line[0] == issue.line
+ _.extend issue, source: source
+
+
+ if _.isArray(issue.scm) && issue.scm.length > 0
+ scmAuthor = ''
+ scmDate = ''
+
+ issue.scm.forEach (line) ->
+ if line[0] == issue.line
+ scmAuthor = line[1]
+ scmDate = line[2]
+
+ _.extend issue,
+ scmAuthor: scmAuthor
+ scmDate: scmDate
+
+ issue
+
+
+ setIndex: ->
+ @forEach (issue, index) ->
+ issue.set index: index
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/state.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/state.coffee
new file mode 100644
index 00000000000..b0588d25f7d
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/models/state.coffee
@@ -0,0 +1,47 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/models/state'
+], (State) ->
+ class extends State
+
+ defaults:
+ page: 1
+ maxResultsReached: false
+ query: {}
+ facets: ['severities', 'resolutions']
+ isContext: false
+
+ allFacets: ['issues', 'severities', 'resolutions', 'statuses', 'createdAt', 'rules', 'tags', 'projectUuids',
+ 'moduleUuids', 'directories', 'fileUuids', 'assignees', 'reporters', 'authors', 'languages',
+ 'actionPlans'],
+ facetsFromServer: ['severities', 'statuses', 'resolutions', 'actionPlans', 'projectUuids', 'directories', 'rules',
+ 'moduleUuids', 'tags', 'assignees', 'reporters', 'authors', 'fileUuids', 'languages',
+ 'createdAt'],
+ transform: {
+ 'resolved': 'resolutions'
+ 'assigned': 'assignees'
+ 'planned': 'actionPlans'
+ 'createdBefore': 'createdAt'
+ 'createdAfter': 'createdAt'
+ 'createdInLast': 'createdAt'
+ }
+
diff --git a/server/sonar-web/src/main/coffee/apps/issues/router.coffee b/server/sonar-web/src/main/coffee/apps/issues/router.coffee
new file mode 100644
index 00000000000..ca220549f1f
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/router.coffee
@@ -0,0 +1,58 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/router'
+], (
+ Router
+) ->
+
+ class extends Router
+ routes:
+ '': 'home'
+ ':query': 'index'
+
+
+ initialize: (options) ->
+ super
+ @listenTo options.app.state, 'change:filter', @updateRoute
+
+
+ home: ->
+ if @options.app.state.get 'isContext'
+ @navigate 'resolved=false', { trigger: true, replace: true }
+ else
+ @options.app.controller.showHomePage()
+
+
+ index: (query) ->
+ query = @options.app.controller.parseQuery query
+ if query.id?
+ filter = @options.app.filters.get query.id
+ delete query.id
+ filter.fetch().done =>
+ if Object.keys(query).length > 0
+ @options.app.controller.applyFilter filter, true
+ @options.app.state.setQuery query
+ @options.app.state.set changed: true
+ else
+ @options.app.controller.applyFilter filter
+ else
+ @options.app.state.setQuery query
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs
new file mode 100644
index 00000000000..374d15ab48f
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs
@@ -0,0 +1,18 @@
+{{#if filter.name}}
+ {{filter.name}}
+ <span class="note nowrap">
+ {{#unless filter.shared}}
+ [{{t 'issue_filter.private'}}]
+ {{else}}
+ {{#eq filter.user currentUser}}
+ [{{t 'issue_filter.shared_with_all_users'}}]
+ {{else}}
+ {{#if filter.user}}
+ [{{t 'issue_filter.shared'}}]
+ {{/if}}
+ {{/eq}}
+ {{/unless}}
+ </span>
+{{else}}
+ {{t 'issues'}}
+{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs
new file mode 100644
index 00000000000..3b57c6021ea
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs
@@ -0,0 +1,4 @@
+<a class="search-navigator-facet-header js-facet-toggle">
+ <i class="icon-checkbox {{#if enabled}}icon-checkbox-checked{{/if}}"></i>
+ {{t 'issues.facet' property}}
+</a>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs
new file mode 100644
index 00000000000..20958a58b4c
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs
@@ -0,0 +1,18 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ {{#eq val ''}}
+ {{! unplanned }}
+ <a class="facet search-navigator-facet js-facet" data-unplanned title="{{t 'issue.unplanned'}}">
+ <span class="facet-name">{{t 'issue.unplanned'}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{else}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{label}}">
+ <span class="facet-name">{{label}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/eq}}
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs
new file mode 100644
index 00000000000..9654cf06f1e
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs
@@ -0,0 +1,30 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#notNull myIssues}}
+ <a class="facet search-navigator-facet js-facet" data-value="__me__" title="{{t 'me'}}">
+ <span class="facet-name">{{t 'me'}}</span>
+ <span class="facet-stat">{{myIssues}}</span>
+ </a>
+ <hr>
+ {{/notNull}}
+
+ {{#each values}}
+ {{#eq val ''}}
+ {{! unassigned }}
+ <a class="facet search-navigator-facet js-facet" data-unassigned title="{{t 'unassigned'}}">
+ <span class="facet-name">{{t 'unassigned'}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{else}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{label}}">
+ <span class="facet-name">{{label}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/eq}}
+ {{/each}}
+
+ <div class="search-navigator-facet-custom-value">
+ <input type="hidden" class="js-custom-value">
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs
new file mode 100644
index 00000000000..f5b0f44f777
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs
@@ -0,0 +1,10 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs
new file mode 100644
index 00000000000..9f981c07c1a
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs
@@ -0,0 +1,3 @@
+<div class="search-navigator-facet-query">
+ Issues of &nbsp;&nbsp; {{qualifierIcon state.contextComponentQualifier}}&nbsp;{{state.contextComponentName}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs
new file mode 100644
index 00000000000..4cc52cc4ae6
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs
@@ -0,0 +1,32 @@
+{{> '_issues-facet-header'}}
+
+{{#if createdAt}}
+ <input type="hidden" name="createdAt">
+ <div class="search-navigator-facet-container">
+ {{dt createdAt}} ({{fromNow createdAt}})
+ </div>
+{{else}}
+ <div class="search-navigator-facet-container">
+ <div class="js-barchart" data-height="75" {{#if periodEnd}}data-end-date="{{periodEnd}}"{{/if}}></div>
+ <div class="search-navigator-date-facet-selection">
+ <a class="js-select-period-start search-navigator-date-facet-selection-dropdown-left">
+ {{#if periodStart}}{{d periodStart}}{{else}}Past{{/if}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <a class="js-select-period-end search-navigator-date-facet-selection-dropdown-right">
+ {{#if periodEnd}}{{d periodEnd}}{{else}}Now{{/if}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <input class="js-period-start search-navigator-date-facet-selection-input-left"
+ type="text" value="{{#if periodStart}}{{ds periodStart}}{{/if}}" name="createdAfter">
+ <input class="js-period-end search-navigator-date-facet-selection-input-right"
+ type="text" value="{{#if periodEnd}}{{ds periodEnd}}{{/if}}" name="createdBefore">
+ </div>
+
+ <div class="spacer-top">
+ <span class="spacer-right">{{t 'issues.facet.createdAt.or'}}</span>
+ <a class="js-all spacer-right" href="#">{{t 'issues.facet.createdAt.all'}}</a>
+ <a class="js-last-week spacer-right {{#eq createdInLast '1w'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_week'}}</a>
+ <a class="js-last-month spacer-right {{#eq createdInLast '1m'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_month'}}</a>
+ <a class="js-last-year {{#eq createdInLast '1y'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_year'}}</a>
+ </div>
+ </div>
+{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs
new file mode 100644
index 00000000000..c7d8a081ef1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs
@@ -0,0 +1,14 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{#if extra}}({{extra}}) {{/if}}{{default label val}}">
+ <span class="facet-name">{{default label val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+
+ <div class="search-navigator-facet-custom-value">
+ <input type="hidden" class="js-custom-value">
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs
new file mode 100644
index 00000000000..530959b7d72
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs
@@ -0,0 +1,10 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list search-navigator-facet-list-align-right">
+ {{#each values}}
+ <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
+ <span class="facet-name">{{default label val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs
new file mode 100644
index 00000000000..d68931e4487
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs
@@ -0,0 +1,7 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-container">
+ <div class="facet search-navigator-facet active" style="cursor: default;">
+ <span class="facet-name">{{issues}}</span>
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs
new file mode 100644
index 00000000000..08c1b25a15a
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs
@@ -0,0 +1,20 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ {{#eq val ''}}
+ {{! unresolved }}
+ <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-unresolved
+ title="{{t 'issue.unresolved.description'}}" data-toggle="tooltip" data-placement="right">
+ <span class="facet-name">{{t 'unresolved'}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{else}}
+ <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-value="{{val}}"
+ title="{{t 'issue.resolution' val 'description'}}" data-toggle="tooltip" data-placement="right">
+ <span class="facet-name">{{t 'issue.resolution' val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/eq}}
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs
new file mode 100644
index 00000000000..96a6470ef72
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs
@@ -0,0 +1,11 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet search-navigator-facet-half js-facet"
+ data-value="{{val}}" title="{{t 'severity' val 'description'}}" data-toggle="tooltip" data-placement="right">
+ <span class="facet-name">{{severityIcon val}} {{t 'severity' val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs
new file mode 100644
index 00000000000..33e556abf5e
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs
@@ -0,0 +1,11 @@
+{{> '_issues-facet-header'}}
+
+<div class="search-navigator-facet-list">
+ {{#each values}}
+ <a class="facet search-navigator-facet search-navigator-facet-half js-facet"
+ data-value="{{val}}" title="{{t 'issue.status' val 'description'}}" data-toggle="tooltip" data-placement="right">
+ <span class="facet-name">{{statusIcon val}} {{t 'issue.status' val}}</span>
+ <span class="facet-stat">{{numberShort count}}</span>
+ </a>
+ {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs
new file mode 100644
index 00000000000..a197f5335d1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs
@@ -0,0 +1,55 @@
+<h1 class="page-title dropdown">
+ {{#if state.canManageFilters}}
+ <a class="search-navigator-filters-show-list dropdown-toggle" data-toggle="dropdown">
+ <i class="icon-list"></i><span class="issues-filters-name">{{> '_issues-filter-name'}}</span>
+ </a>
+ <ul class="dropdown-menu">
+ {{#each items}}
+ <li>
+ <a class="search-navigator-filters-button search-navigator-filters-filter js-filter" data-id="{{id}}">
+ {{name}}
+ </a>
+ </li>
+ {{/each}}
+ {{#notEmpty items}}
+ <li class="divider"></li>
+ {{/notEmpty}}
+ <li>
+ <a class="search-navigator-filters-manage" href="{{link '/issues/manage'}}">{{t 'manage'}}</a>
+ </li>
+ </ul>
+ {{#if filter.description}}
+ <div class="search-navigator-filters-description">{{filter.description}}</div>
+ {{/if}}
+ {{else}}
+ <span class="search-navigator-filters-name">{{t 'issues'}}</span>
+ {{/if}}
+</h1>
+
+<div class="page-actions">
+ <div class="button-group">
+ {{#if state.canManageFilters}}
+ {{#if filter.canModify}}
+ {{#if state.changed}}
+ <button class="js-filter-save" id="issues-filter-save">{{t 'save'}}</button>
+ {{/if}}
+ {{/if}}
+
+ {{#unless filter.id}}
+ <button class="js-filter-save-as" id="issues-filter-save-as">{{t 'save_as'}}</button>
+ {{/unless}}
+
+ {{#if filter.id}}
+ {{#unless state.changed}}
+ <button class="js-filter-copy" id="issues-filter-copy">{{t 'copy'}}</button>
+ {{/unless}}
+ {{/if}}
+
+ {{#if filter.canModify}}
+ {{#if filter.id}}
+ <button class="js-filter-edit" id="issues-filter-edit">{{t 'edit'}}</button>
+ {{/if}}
+ {{/if}}
+ {{/if}}
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs
new file mode 100644
index 00000000000..01515749698
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs
@@ -0,0 +1,71 @@
+<h6>{{t 'issue.filter_similar_issues'}}</h6>
+
+<div class="issue-action-options">
+ <a href="#" class="issue-action-option" data-property="severities" data-value="{{s}}">
+ {{severityIcon severity}}&nbsp;{{t 'severity' severity}}
+ </a>
+
+ <a href="#" class="issue-action-option" data-property="statuses" data-value="{{status}}">
+ {{statusIcon status}}&nbsp;{{t 'issue.status' status}}
+ </a>
+
+ {{#if resolution}}
+ <a href="#" class="issue-action-option" data-property="resolutions" data-value="{{resolution}}">
+ {{t 'issue.resolution' resolution}}
+ </a>
+ {{else}}
+ <a href="#" class="issue-action-option" data-property="resolved" data-value="false">
+ {{t 'unresolved'}}
+ </a>
+ {{/if}}
+
+ {{#if assignee}}
+ <a href="#" class="issue-action-option" data-property="assignees" data-value="{{assignee}}">
+ {{t 'assigned_to'}} {{assigneeName}}
+ </a>
+ {{else}}
+ <a href="#" class="issue-action-option" data-property="assigned" data-value="false">
+ {{t 'unassigned'}}
+ </a>
+ {{/if}}
+
+ {{#if actionPlan}}
+ <a href="#" class="issue-action-option" data-property="actionPlans" data-value="{{actionPlan}}">
+ {{t 'issue.planned_for'}} {{actionPlanName}}
+ </a>
+ {{else}}
+ <a href="#" class="issue-action-option" data-property="planned" data-value="false">
+ {{t 'issue.unplanned'}}
+ </a>
+ {{/if}}
+
+ <hr>
+
+ <a href="#" class="issue-action-option" data-property="rules" data-value="{{rule}}">
+ {{limitString ruleName}}
+ </a>
+
+ {{#each tags}}
+ <a href="#" class="issue-action-option" data-property="tags" data-value="{{this}}">
+ <i class="icon-tags icon-half-transparent"></i>&nbsp;{{this}}
+ </a>
+ {{/each}}
+
+ <hr>
+
+ <a href="#" class="issue-action-option" data-property="projectUuids" data-value="{{projectUuid}}">
+ {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
+ </a>
+
+ {{#if subProject}}
+ <a href="#" class="issue-action-option" data-property="moduleUuids" data-value="{{subProjectUuid}}">
+ {{qualifierIcon 'BRC'}}&nbsp;{{subProjectLongName}}
+ </a>
+ {{/if}}
+
+ <a href="#" class="issue-action-option" data-property="fileUuids" data-value="{{componentUuid}}">
+ {{qualifierIcon componentQualifier}}&nbsp;{{fileFromPath componentLongName}}
+ </a>
+</div>
+
+<div class="bubble-popup-arrow"></div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs
new file mode 100644
index 00000000000..875bc3bc4a5
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs
@@ -0,0 +1,5 @@
+<div class="issue-meta">
+ <a class="issue-action issue-action-with-options js-issue-filter" href="#">
+ <i class="icon-filter icon-half-transparent"></i>&nbsp;<i class="icon-dropdown"></i>
+ </a>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs
new file mode 100644
index 00000000000..5a61d1f66bf
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs
@@ -0,0 +1,11 @@
+<div class="search-navigator-side">
+ <div class="search-navigator-filters"></div>
+ <div class="search-navigator-facets"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header"></div>
+ <div class="search-navigator-workspace-list"></div>
+ <div class="issues-workspace-component-viewer"></div>
+ <div class="issues-workspace-home"></div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs
new file mode 100644
index 00000000000..4b4958fd1d1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs
@@ -0,0 +1,37 @@
+<div class="issues-header-component nowrap">
+ {{#if state.component}}
+ <a class="js-back">{{t 'issues.return_to_list'}}</a>&nbsp;&nbsp;&nbsp;
+
+ {{#with state.component}}
+ {{qualifierIcon 'TRK'}}&nbsp;<a href="{{dashboardUrl project}}" title="{{projectName}}">{{projectName}}</a>
+ &nbsp;&nbsp;
+ {{qualifierIcon qualifier}}&nbsp;<a href="{{dashboardUrl key}}" title="{{name}}">{{name}}</a>
+ {{/with}}
+ {{else}}
+ &nbsp;
+ {{/if}}
+</div>
+
+
+<div class="search-navigator-header-actions">
+ {{#notNull state.total}}
+ <div class="search-navigator-header-pagination">
+ {{#gt state.total 0}}
+ <a class="js-prev icon-prev" title="{{t 'paging_previous'}}"></a>
+ <span class="current">{{sum state.selectedIndex 1}} / <span id="issues-total">{{state.total}}</span></span>
+ <a class="js-next icon-next" title="{{t 'paging_next'}}"></a>
+ {{else}}
+ <span class="current">0 / <span id="issues-total">0</span></span>
+ {{/gt}}
+ </div>
+ {{/notNull}}
+
+
+ <div class="search-navigator-header-buttons button-group">
+ <button id="issues-reload" class="js-reload">{{t 'reload'}}</button>
+ <button class="js-new-search" id="issues-new-search">{{t 'issue_filter.new_search'}}</button>
+ {{#if state.canBulkChange}}
+ <button id="issues-bulk-change" class="js-bulk-change">{{t 'bulk_change'}}</button>
+ {{/if}}
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs
new file mode 100644
index 00000000000..3889dcdd978
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs
@@ -0,0 +1,77 @@
+<div class="spacer-top spacer-bottom">
+ <div class="columns">
+ <div class="column-half {{#unless user}}column-one{{/unless}}">
+ <h3 class="text-center">{{t 'issues.home.recent_issues'}}</h3>
+ <p class="note text-center">({{t 'issues.home.over_last_week'}})</p>
+
+ <div class="spacer-top text-center js-barchart" data-height="75" data-width="300"></div>
+ <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.projects'}}</h4>
+ <table class="data zebra spacer-top">
+ {{#each projects}}
+ <tr>
+ <td>{{qualifierIcon 'TRK'}}&nbsp;<a href="{{issuesHomeLink 'projectUuids' val}}">{{label}}</a></td>
+ <td class="thin text-right">+{{numberShort count}}</td>
+ </tr>
+ {{/each}}
+ </table>
+ <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.authors'}}</h4>
+ <table class="data zebra spacer-top">
+ {{#each authors}}
+ <tr>
+ <td><a href="{{issuesHomeLink 'authors' val}}">{{val}}</a></td>
+ <td class="thin text-right">+{{numberShort count}}</td>
+ </tr>
+ {{/each}}
+ </table>
+ <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.tags'}}</h4>
+ <ul class="list-inline">
+ {{#each tags}}
+ <li><a class="link-no-underline" style="font-size: {{size}}px;" data-toggle="tooltip" data-placement="bottom"
+ href="{{issuesHomeLink 'tags' val}}"
+ title="+{{numberShort count}}">{{val}}</a></li>
+ {{/each}}
+ </ul>
+ </div>
+
+ {{#if user}}
+ <div class="column-half">
+ <h3 class="text-center">{{t 'issues.home.my_recent_issues'}}</h3>
+ <p class="note text-center">({{t 'issues.home.over_last_week'}})</p>
+
+ <div class="spacer-top text-center js-my-barchart" data-height="75" data-width="300"></div>
+ {{#notEmpty myProjects}}
+ <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.projects'}}</h4>
+ <table class="data zebra spacer-top">
+ {{#each myProjects}}
+ <tr>
+ <td>{{qualifierIcon 'TRK'}}&nbsp;<a href="{{myIssuesHomeLink 'projectUuids' val}}">{{label}}</a></td>
+ <td class="thin text-right">+{{numberShort count}}</td>
+ </tr>
+ {{/each}}
+ </table>
+ {{/notEmpty}}
+ {{#notEmpty myTags}}
+ <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.tags'}}</h4>
+ <ul class="list-inline">
+ {{#each myTags}}
+ <li><a class="link-no-underline" style="font-size: {{size}}px;" data-toggle="tooltip" data-placement="bottom"
+ href="{{myIssuesHomeLink 'tags' val}}"
+ title="+{{numberShort count}}">{{val}}</a></li>
+ {{/each}}
+ </ul>
+ {{/notEmpty}}
+ {{#notEmpty filters}}
+ <h3 class="spacer-bottom text-center" style="padding-top: 12px; margin-top: 20px; border-top: 1px solid #efefef;">
+ {{t 'issues.home.my_filters'}}</h3>
+ <ul class="list-inline">
+ {{#each filters}}
+ <li>
+ <a href="{{issueFilterHomeLink id}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ {{/notEmpty}}
+ </div>
+ {{/if}}
+ </div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs
new file mode 100644
index 00000000000..d5d0b439952
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs
@@ -0,0 +1,13 @@
+<div class="issues-workspace-list-component">
+ <a class="issues-workspace-list-component-part" href="{{dashboardUrl project}}">
+ {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
+ </a>
+ {{#if subProject}}
+ <a class="issues-workspace-list-component-part" href="{{dashboardUrl subProject}}">
+ {{qualifierIcon 'TRK'}}&nbsp;{{subProjectLongName}}
+ </a>
+ {{/if}}
+ <a class="issues-workspace-list-component-part" href="{{dashboardUrl component}}">
+ {{qualifierIcon componentQualifier}}&nbsp;{{componentLongName}}
+ </a>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs
new file mode 100644
index 00000000000..37421cb75c2
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs
@@ -0,0 +1,5 @@
+<div class="js-list"></div>
+
+<div class="search-navigator-workspace-list-more js-more">
+ <i class="spinner"></i>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee
new file mode 100644
index 00000000000..7672d1ac41c
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee
@@ -0,0 +1,64 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/workspace-header-view'
+ './templates'
+], (
+ WorkspaceHeaderView
+) ->
+
+ $ = jQuery
+
+
+ class extends WorkspaceHeaderView
+ template: Templates['issues-workspace-header']
+
+
+ events: ->
+ _.extend super,
+ 'click .js-back': 'returnToList'
+ 'click .js-new-search': 'newSearch'
+
+
+ initialize: ->
+ super
+ @_onBulkIssues = window.onBulkIssues
+ window.onBulkIssues = =>
+ $('#modal').dialog 'close'
+ @options.app.controller.fetchList()
+
+
+ onClose: ->
+ window.onBulkIssues = @_onBulkIssues
+
+
+ returnToList: ->
+ @options.app.controller.closeComponentViewer()
+
+
+ newSearch: ->
+ @options.app.controller.newSearch()
+
+
+ bulkChange: ->
+ query = @options.app.controller.getQuery '&', true
+ url = "#{baseUrl}/issues/bulk_change_form?#{query}"
+ openModalWindow url, {}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee
new file mode 100644
index 00000000000..6bf9c15fcee
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee
@@ -0,0 +1,149 @@
+#
+# 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 [
+ 'widgets/issue-filter/widget'
+ './templates'
+], (IssueFilter) ->
+
+ $ = jQuery
+
+
+ Handlebars.registerHelper 'issuesHomeLink', (property, value) ->
+ "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|#{property}=#{encodeURIComponent value}"
+
+ Handlebars.registerHelper 'myIssuesHomeLink', (property, value) ->
+ "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|assignees=__me__|#{property}=#{encodeURIComponent value}"
+
+ Handlebars.registerHelper 'issueFilterHomeLink', (id) ->
+ "#{baseUrl}/issues/search#id=#{id}"
+
+
+ class extends Marionette.ItemView
+ template: Templates['issues-workspace-home']
+
+
+ modelEvents:
+ 'change': 'render'
+
+
+ events:
+ 'click .js-barchart rect': 'selectBar'
+ 'click .js-my-barchart rect': 'selectMyBar'
+
+
+ initialize: ->
+ @model = new Backbone.Model
+ @requestIssues()
+ @requestMyIssues()
+
+
+ _getProjects: (r) ->
+ projectFacet = _.findWhere r.facets, property: 'projectUuids'
+ if projectFacet?
+ values = _.head projectFacet.values, 3
+ values.forEach (v) =>
+ project = _.findWhere r.projects, uuid: v.val
+ v.label = project.longName
+ values
+
+
+ _getAuthors: (r) ->
+ authorFacet = _.findWhere r.facets, property: 'authors'
+ if authorFacet?
+ values = _.head authorFacet.values, 3
+ values
+
+
+ _getTags: (r) ->
+ MIN_SIZE = 10
+ MAX_SIZE = 24
+ tagFacet = _.findWhere r.facets, property: 'tags'
+ if tagFacet?
+ values = _.head tagFacet.values, 10
+ minCount = _.min(values, (v) -> v.count).count
+ maxCount = _.max(values, (v) -> v.count).count
+ scale = d3.scale.linear().domain([minCount, maxCount]).range([MIN_SIZE, MAX_SIZE])
+ values.forEach (v) =>
+ v.size = scale v.count
+ values
+
+
+ requestIssues: ->
+ url = "#{baseUrl}/api/issues/search"
+ options =
+ resolved: false
+ createdInLast: '1w'
+ ps: 1
+ facets: 'createdAt,projectUuids,authors,tags'
+ $.get(url, options).done (r) =>
+ @model.set
+ createdAt: _.findWhere(r.facets, property: 'createdAt')?.values
+ projects: @_getProjects r
+ authors: @_getAuthors r
+ tags: @_getTags r
+
+
+ requestMyIssues: ->
+ url = "#{baseUrl}/api/issues/search"
+ options =
+ resolved: false
+ createdInLast: '1w'
+ assignees: '__me__'
+ ps: 1
+ facets: 'createdAt,projectUuids,authors,tags'
+ $.get(url, options).done (r) =>
+ @model.set
+ myCreatedAt: _.findWhere(r.facets, property: 'createdAt')?.values
+ myProjects: @_getProjects r
+ myTags: @_getTags r
+
+
+ onRender: ->
+ values = @model.get 'createdAt'
+ myValues = @model.get 'myCreatedAt'
+ @$('.js-barchart').barchart values if values?
+ @$('.js-my-barchart').barchart myValues if myValues?
+ @$('[data-toggle="tooltip"]').tooltip container: 'body'
+
+
+ selectBar: (e) ->
+ periodStart = $(e.currentTarget).data 'period-start'
+ periodEnd = $(e.currentTarget).data 'period-end'
+ @options.app.state.setQuery
+ resolved: false
+ createdAfter: periodStart
+ createdBefore: periodEnd
+
+
+ selectMyBar: (e) ->
+ periodStart = $(e.currentTarget).data 'period-start'
+ periodEnd = $(e.currentTarget).data 'period-end'
+ @options.app.state.setQuery
+ resolved: false
+ assignees: '__me__'
+ createdAfter: periodStart
+ createdBefore: periodEnd
+
+
+ serializeData: ->
+ _.extend super,
+ user: window.SS.user
+ filters: _.sortBy @options.app.filters.toJSON(), 'name'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee
new file mode 100644
index 00000000000..23bba0eef47
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee
@@ -0,0 +1,28 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class extends Marionette.ItemView
+ className: 'search-navigator-no-results'
+
+
+ template: ->
+ t 'issue_filter.no_issues'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee
new file mode 100644
index 00000000000..62b223fb57f
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee
@@ -0,0 +1,118 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/issue/issue-view'
+ './issue-filter-view'
+ './templates'
+], (
+ IssueView
+ IssueFilterView
+) ->
+
+ $ = jQuery
+
+ SHOULD_NULL =
+ any: ['issues']
+ resolutions: ['resolved']
+ resolved: ['resolutions']
+ assignees: ['assigned']
+ assigned: ['assignees']
+ actionPlans: ['planned']
+ planned: ['actionPlans']
+
+ class extends IssueView
+ filterTemplate: Templates['issues-issue-filter']
+
+ events: ->
+ _.extend super,
+ 'click': 'selectCurrent'
+ 'dblclick': 'openComponentViewer'
+ 'click .js-issue-navigate': 'openComponentViewer'
+ 'click .js-issue-filter': 'onIssueFilterClick'
+
+
+ initialize: (options) ->
+ super
+ @listenTo options.app.state, 'change:selectedIndex', @select
+
+
+ onRender: ->
+ super
+ @select()
+ @addFilterSelect()
+ @$el.addClass 'issue-navigate-right'
+
+
+ onIssueFilterClick: (e) ->
+ e.preventDefault()
+ e.stopPropagation()
+ $('body').click()
+ @popup = new IssueFilterView
+ triggerEl: $(e.currentTarget)
+ bottomRight: true
+ model: @model
+ @popup.on 'select', (property, value) =>
+ obj = {}
+ obj[property] = '' + value
+ SHOULD_NULL.any.forEach (p) -> obj[p] = null
+ if SHOULD_NULL[property]?
+ SHOULD_NULL[property].forEach (p) -> obj[p] = null
+ @options.app.state.updateFilter obj
+ @popup.close()
+ @popup.render()
+
+
+
+ addFilterSelect: ->
+ @$('.issue-table-meta-cell-first').find('.issue-meta-list').append @filterTemplate @model.toJSON()
+
+
+ select: ->
+ selected = @model.get('index') == @options.app.state.get 'selectedIndex'
+ @$el.toggleClass 'selected', selected
+
+
+ selectCurrent: ->
+ @options.app.state.set selectedIndex: @model.get('index')
+
+
+ resetIssue: (options) ->
+ key = @model.get 'key'
+ componentUuid = @model.get 'componentUuid'
+ index = @model.get 'index'
+ @model.clear silent: true
+ @model.set { key: key, componentUuid: componentUuid, index: index }, { silent: true }
+ @model.fetch(options)
+ .done =>
+ @trigger 'reset'
+
+
+ openComponentViewer: ->
+ @options.app.state.set selectedIndex: @model.get('index')
+ if @options.app.state.has 'component'
+ @options.app.controller.closeComponentViewer()
+ else
+ @options.app.controller.showComponentViewer @model
+
+
+ serializeData: ->
+ _.extend super,
+ showComponent: true
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee
new file mode 100644
index 00000000000..889b03310a2
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee
@@ -0,0 +1,103 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/navigator/workspace-list-view'
+ './workspace-list-item-view'
+ './workspace-list-empty-view'
+ './templates'
+], (
+ WorkspaceListView
+ IssueView
+ EmptyView
+) ->
+
+ $ = jQuery
+
+ COMPONENT_HEIGHT = 29
+ BOTTOM_OFFSET = 10
+
+
+ class extends WorkspaceListView
+ template: Templates['issues-workspace-list']
+ componentTemplate: Templates['issues-workspace-list-component']
+ itemView: IssueView
+ itemViewContainer: '.js-list'
+ emptyView: EmptyView
+
+
+ bindShortcuts: ->
+ doAction = (action) =>
+ selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
+ return unless selectedIssue?
+ selectedIssueView = @children.findByModel selectedIssue
+ selectedIssueView.$(".js-issue-#{action}").click()
+
+ super
+
+ key 'right', 'list', =>
+ selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
+ @options.app.controller.showComponentViewer selectedIssue
+ return false
+
+ key 'f', 'list', -> doAction 'transition'
+ key 'a', 'list', -> doAction 'assign'
+ key 'm', 'list', -> doAction 'assign-to-me'
+ key 'p', 'list', -> doAction 'plan'
+ key 'i', 'list', -> doAction 'set-severity'
+ key 'c', 'list', -> doAction 'comment'
+ key 't', 'list', -> doAction 'edit-tags'
+
+
+ scrollTo: ->
+ selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
+ return unless selectedIssue?
+ selectedIssueView = @children.findByModel selectedIssue
+ parentTopOffset = @$el.offset().top
+ viewTop = selectedIssueView.$el.offset().top - parentTopOffset
+ if selectedIssueView.$el.prev().is('.issues-workspace-list-component')
+ viewTop -= COMPONENT_HEIGHT
+ viewBottom = selectedIssueView.$el.offset().top + selectedIssueView.$el.outerHeight() + BOTTOM_OFFSET
+ windowTop = $(window).scrollTop()
+ windowBottom = windowTop + $(window).height()
+ if viewTop < windowTop
+ $(window).scrollTop viewTop
+ if viewBottom > windowBottom
+ $(window).scrollTop $(window).scrollTop() - windowBottom + viewBottom
+
+
+ appendHtml: (compositeView, itemView, index) ->
+ $container = this.getItemViewContainer compositeView
+ model = @collection.at(index)
+ if model?
+ prev = @collection.at(index - 1)
+ putComponent = !prev?
+ if prev?
+ fullComponent = [model.get('project'), model.get('component')].join ' '
+ fullPrevComponent = [prev.get('project'), prev.get('component')].join ' '
+ putComponent = true unless fullComponent == fullPrevComponent
+ if putComponent
+ $container.append @componentTemplate model.toJSON()
+ $container.append itemView.el
+
+
+ closeChildren: ->
+ super
+ @$('.issues-workspace-list-component').remove()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee
new file mode 100644
index 00000000000..770d1f1e454
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee
@@ -0,0 +1,117 @@
+#
+# 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 [
+ './collections/quality-gates',
+ './views/quality-gate-sidebar-list-view',
+ './views/quality-gate-actions-view',
+ './views/quality-gate-edit-view',
+ './router',
+ './layout'
+], (QualityGates,
+ QualityGateSidebarListItemView,
+ QualityGateActionsView,
+ QualityGateEditView,
+ QualityGateRouter,
+ QualityGateLayout) ->
+
+ # Create a Quality Gate Application
+ App = new Marionette.Application
+
+
+ App.qualityGates = new QualityGates
+
+
+ App.openFirstQualityGate = ->
+ App.layout.detailsRegion.reset()
+
+
+ App.deleteQualityGate = (id) ->
+ App.qualityGates.remove id
+ App.openFirstQualityGate()
+
+
+ App.unsetDefaults = (id) ->
+ App.qualityGates.each (gate) ->
+ gate.set('default', false) unless gate.id == id
+
+
+ # Construct layout
+ App.addInitializer ->
+ @layout = new QualityGateLayout app: @
+ jQuery('#quality-gates').append @layout.render().el
+ jQuery('#footer').addClass 'search-navigator-footer'
+
+
+ # Construct actions bar
+ App.addInitializer ->
+ @codingRulesHeaderView = new QualityGateActionsView
+ app: @
+ @layout.actionsRegion.show @codingRulesHeaderView
+
+
+ # Construct sidebar
+ App.addInitializer ->
+ @qualityGateSidebarListView = new QualityGateSidebarListItemView
+ collection: @qualityGates
+ app: @
+ @layout.resultsRegion.show @qualityGateSidebarListView
+
+
+ # Construct edit view
+ App.addInitializer ->
+ @qualityGateEditView = new QualityGateEditView app: @
+ @qualityGateEditView.render()
+
+
+ # Start router
+ App.addInitializer ->
+ @router = new QualityGateRouter app: @
+
+ QUALITY_GATES = '/quality_gates'
+ path = window.location.pathname
+ pos = path.indexOf QUALITY_GATES
+ root = path.substr(0, pos + QUALITY_GATES.length)
+ Backbone.history.start pushState: true, root: root
+
+
+ # Open first quality gate when come to the page
+ App.addInitializer ->
+ initial = Backbone.history.fragment == ''
+ App.openFirstQualityGate() if initial
+
+
+ # Call app, Load metrics and the list of quality gates before start the application
+ appXHR = jQuery.ajax
+ url: "#{baseUrl}/api/qualitygates/app"
+ .done (r) =>
+ App.canEdit = r.edit
+ App.periods = r.periods
+ App.metrics = r.metrics
+
+ qualityGatesXHR = App.qualityGates.fetch()
+
+ # Message bundles
+ l10nXHR = window.requestMessages()
+
+ jQuery.when(qualityGatesXHR, appXHR, l10nXHR)
+ .done ->
+ # Start the application
+ App.start()
+
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee
new file mode 100644
index 00000000000..a3ecccc29d1
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee
@@ -0,0 +1,29 @@
+#
+# 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/condition'
+], (
+ Condition
+) ->
+
+ class Conditions extends Backbone.Collection
+ model: Condition
+ comparator: 'metric'
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee
new file mode 100644
index 00000000000..c74a50c4a97
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee
@@ -0,0 +1,48 @@
+#
+# 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/quality-gate'
+], (
+ QualityGate
+) ->
+
+ class QualityGates extends Backbone.Collection
+ model: QualityGate
+
+
+ url: ->
+ "#{baseUrl}/api/qualitygates/list"
+
+
+ # {
+ # "qualitygates": [
+ # { "id": 42, "name": "QG 1" },
+ # { "id": 43, "name": "QG 2" },
+ # { "id": 44, "name": "QG 3" }
+ # ],
+ # "default": 42
+ # }
+ parse: (r) ->
+ r.qualitygates.map (gate) ->
+ _.extend gate, default: gate.id == r.default
+
+
+ comparator: (item) -> item.get('name').toLowerCase()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee
new file mode 100644
index 00000000000..36da66f1941
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee
@@ -0,0 +1,42 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './templates'
+], ->
+
+ $ = jQuery
+
+ class AppLayout extends Marionette.Layout
+ template: Templates['quality-gates-layout']
+
+
+ regions:
+ headerRegion: '.search-navigator-workspace-header'
+ actionsRegion: '.search-navigator-filters'
+ resultsRegion: '.quality-gates-results'
+ detailsRegion: '.search-navigator-workspace-details'
+
+
+ onRender: ->
+ $('.search-navigator').addClass 'sticky search-navigator-extended-view'
+ top = $('.search-navigator').offset().top
+ @$('.search-navigator-workspace-header').css top: top
+ @$('.search-navigator-side').css({ top: top }).isolatedScroll()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee
new file mode 100644
index 00000000000..7cf8ca03b82
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee
@@ -0,0 +1,58 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class Condition extends Backbone.Model
+
+ url: ->
+ "#{baseUrl}/api/qualitygates/create_condition"
+
+
+ save: ->
+ method = unless @isNew() then 'update' else 'create'
+ data =
+ metric: @get('metric').key
+ op: @get('op')
+ warning: @get('warning')
+ error: @get('error')
+
+ unless @get('period') == '0'
+ data.period = @get('period')
+
+ unless @isNew()
+ data.id = @id
+ else
+ data.gateId = @get('gateId')
+
+ jQuery.ajax({
+ url: "#{baseUrl}/api/qualitygates/#{method}_condition"
+ type: 'POST'
+ data: data
+ }).done (r) =>
+ @set 'id', r.id
+
+
+ delete: ->
+ jQuery.ajax
+ url: "#{baseUrl}/api/qualitygates/delete_condition"
+ type: 'POST'
+ data: id: @id
+
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee
new file mode 100644
index 00000000000..3b6dc6d1f41
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee
@@ -0,0 +1,26 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define ->
+
+ class QualityGate extends Backbone.Model
+
+ url: ->
+ "#{baseUrl}/api/qualitygates/show?id=#{@get('id')}"
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee
new file mode 100644
index 00000000000..31fc35abe68
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee
@@ -0,0 +1,62 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './models/quality-gate',
+ './views/quality-gate-detail-view',
+ './views/quality-gate-detail-header-view',
+], (
+ QualityGate,
+ QualityGateDetailView,
+ QualityGateDetailHeaderView
+) ->
+
+ class QualityGateRouter extends Backbone.Router
+
+ routes:
+ '': 'index'
+ 'show/:id': 'show'
+
+
+ initialize: (options) ->
+ @app = options.app
+
+
+ index: ->
+
+
+ show: (id) ->
+ qualityGate = @app.qualityGates.get id
+ if qualityGate
+ @app.qualityGateSidebarListView.highlight id
+
+ qualityGateDetailHeaderView = new QualityGateDetailHeaderView
+ app: @app
+ model: qualityGate
+ @app.layout.headerRegion.show qualityGateDetailHeaderView
+
+ qualityGateDetailView = new QualityGateDetailView
+ app: @app
+ model: qualityGate
+ @app.layout.detailsRegion.show qualityGateDetailView
+ qualityGateDetailView.$el.hide()
+
+ qualityGate.fetch().done ->
+ qualityGateDetailView.$el.show()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs
new file mode 100644
index 00000000000..6f194fb7fbb
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs
@@ -0,0 +1,7 @@
+<div class="">
+ <p class="spacer-bottom">Quality Gates are collections of simple boolean thresholds set on project measures. A project
+ must pass each of the thresholds in order to pass the Quality Gate as a whole.</p>
+
+ <p>It is possible to set a default Quality Gate, which will be applied to all projects not explicitly assigned to some
+ other gate.</p>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs
new file mode 100644
index 00000000000..6826aa94353
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs
@@ -0,0 +1,8 @@
+<h1 class="page-title">{{t 'quality_gates.page'}}</h1>
+{{#if canEdit}}
+ <div class="page-actions">
+ <div class="button-group">
+ <button id="quality-gate-add">{{t 'create'}}</button>
+ </div>
+ </div>
+{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs
new file mode 100644
index 00000000000..9be26807f78
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs
@@ -0,0 +1,62 @@
+<td nowrap>
+ {{metric.name}}
+ {{#if metric.hidden}}
+ <span class="deprecated">{{t 'deprecated'}}</span>
+ {{/if}}
+</td>
+<td width="20%" nowrap>
+ {{#if canEdit}}
+ <select name="period" style="width: 100%">
+ {{#unless isDiffMetric}}<option value="0">{{t 'value'}}</option>{{/unless}}
+ {{#each periods}}<option value="{{key}}">&Delta; {{text}}</option>{{/each}}
+ </select>
+ {{else}}
+ {{#if periodText}}&Delta; {{periodText}}
+ {{else}}{{t 'value'}}
+ {{/if}}
+ {{/if}}
+</td>
+<td width="10%" nowrap>
+ {{#if canEdit}}
+ <select name="operator">
+ {{#operators metric.type}}
+ <option value="{{this}}">{{t 'quality_gates.operator' this}}</option>
+ {{/operators}}
+ </select>
+ {{else}}
+ {{t 'quality_gates.operator' op}}
+ {{/if}}
+</td>
+<td width="1" class="nowrap">
+ <i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
+ {{#if canEdit}}
+ <input name="warning" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+ type="text">
+ {{else}}
+ {{warning}}
+ {{/if}}
+</td>
+<td width="1" class="nowrap">
+ <i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
+ {{#if canEdit}}
+ <input name="error" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+ type="text">
+ {{else}}
+ {{error}}
+ {{/if}}
+</td>
+<td class="quality-gate-condition-actions" width="120px" nowrap>
+ {{#if canEdit}}
+ {{#if id}}
+ <div class="button-group">
+ <button class="update-condition" disabled>{{t 'update_verb'}}</button>
+ <button class="button-red delete-condition">{{t 'delete'}}</button>
+ </div>
+ {{else}}
+ <div class="button-group">
+ <button class="add-condition">{{t 'add_verb'}}</button>
+ <a class="action cancel-add-condition">{{t 'cancel'}}</a>
+ </div>
+ {{/if}}
+ {{/if}}
+</td>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs
new file mode 100644
index 00000000000..40c92668496
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs
@@ -0,0 +1,3 @@
+<td colspan="6">
+ {{t 'quality_gates.no_conditions'}}
+</td> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs
new file mode 100644
index 00000000000..74888876431
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs
@@ -0,0 +1,45 @@
+<div class="quality-gate-section-name">{{t 'quality_gates.conditions'}}</div>
+
+<div class="quality-gate-introduction">
+ <p>{{t 'quality_gates.introduction'}}
+ <a class="link-action quality-gate-introduction-show-more">{{t 'more'}}</a>
+ </p>
+ <div class="quality-gate-introduction-more inline-help">
+ {{t 'quality_gates.health_icons'}}
+ <ul>
+ <li>
+ <i class="icon-alert-ok"></i>
+ {{t 'alerts.notes.ok'}}
+ </li>
+ <li>
+ <i class="icon-alert-warn"></i>
+ {{t 'alerts.notes.warn'}}
+ </li>
+ <li>
+ <i class="icon-alert-error"></i>
+ {{t 'alerts.notes.error'}}
+ </li>
+ </ul>
+ </div>
+</div>
+
+{{#if canEdit}}
+ <div class="quality-gate-new-condition">
+ <label for="quality-gate-new-condition-metric">{{t 'quality_gates.add_condition'}}:</label>
+ <select id="quality-gate-new-condition-metric">
+ <option></option>
+ {{#each metricGroups}}
+ <optgroup label="{{domain}}">
+ {{#each metrics}}<option value="{{key}}">{{name}}</option>{{/each}}
+ </optgroup>
+ {{/each}}
+ </select>
+ </div>
+{{/if}}
+
+<div class="quality-gate-conditions-wrap">
+ <table class="data zebra width100 marginbottom10 spaced quality-gate-conditions">
+ <thead><tr></tr></thead>
+ <tbody></tbody>
+ </table>
+</div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs
new file mode 100644
index 00000000000..71f7e3b06de
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs
@@ -0,0 +1,16 @@
+<h2 class="search-navigator-header-component">{{name}}</h2>
+
+{{#if canEdit}}
+ <div class="search-navigator-header-actions">
+ <div class="button-group">
+ <button id="quality-gate-rename">{{t 'rename'}}</button>
+ <button id="quality-gate-copy">{{t 'copy'}}</button>
+ {{#if default}}
+ <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
+ {{else}}
+ <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
+ {{/if}}
+ <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
+ </div>
+ </div>
+{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs
new file mode 100644
index 00000000000..989cae0f31e
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs
@@ -0,0 +1,13 @@
+<div class="quality-gate-section-name">{{t 'quality_gates.projects'}}</div>
+
+{{#if default}}
+ <p class="quality-gate-default-message">
+ {{#if canEdit}}
+ {{t 'quality_gates.projects_for_default.edit'}}
+ {{else}}
+ {{t 'quality_gates.projects_for_default'}}
+ {{/if}}
+ </p>
+{{else}}
+ <div id="select-list-projects"></div>
+{{/if}} \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs
new file mode 100644
index 00000000000..0c5467b3476
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs
@@ -0,0 +1,2 @@
+<div id="quality-gate-conditions" class="quality-gate-section"></div>
+<div id="quality-gate-projects" class="quality-gate-section"></div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs
new file mode 100644
index 00000000000..5aeb891a816
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs
@@ -0,0 +1,22 @@
+<form>
+ <div class="modal-head">
+ {{#if method}}
+ <h2>{{t 'quality_gates' method }}</h2>
+ {{/if}}
+ </div>
+
+ <div class="modal-body">
+ <div class="alert alert-danger"></div>
+ <div class="modal-field">
+ <label for="quality-gate-edit-name">{{t 'name'}} <em class="mandatory">*</em></label>
+ <input id="quality-gate-edit-name" type="text" size="50" maxlength="100" value="{{name}}">
+ </div>
+ </div>
+
+ <div class="modal-foot">
+ {{#eq method "rename"}}<button>{{t 'save'}}</button>{{/eq}}
+ {{#eq method "copy"}}<button>{{t 'copy'}}</button>{{/eq}}
+ {{#eq method "create"}}<button>{{t 'create'}}</button>{{/eq}}
+ <a id="quality-gate-cancel-create" class="action">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs
new file mode 100644
index 00000000000..8b017f09987
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs
@@ -0,0 +1 @@
+<div class="line line-nowrap">{{t 'quality_gates.noQualityGates'}}</div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs
new file mode 100644
index 00000000000..2e92b2eedde
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs
@@ -0,0 +1,13 @@
+<table>
+ <tr>
+ <td class="text-top">{{name}}</td>
+ <td class="text-top thin nowrap spacer-left">
+ {{#if default}}
+ <span class="badge pull-right">{{t 'default'}}</span>
+ {{/if}}
+ </td>
+ </tr>
+</table>
+
+
+
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs
new file mode 100644
index 00000000000..3428ac4d466
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs
@@ -0,0 +1,9 @@
+<div class="search-navigator-side search-navigator-side-light">
+ <div class="search-navigator-filters"></div>
+ <div class="quality-gates-results panel"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header">&nbsp;</div>
+ <div class="search-navigator-workspace-details">{{> '_quality-gate-intro'}}</div>
+</div>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee
new file mode 100644
index 00000000000..46e5f3e6d99
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee
@@ -0,0 +1,44 @@
+#
+# 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/quality-gate'
+ '../templates'
+], (
+ QualityGate
+) ->
+
+ class QualityGateActionsView extends Marionette.ItemView
+ template: Templates['quality-gate-actions']
+
+
+ events:
+ 'click #quality-gate-add': 'add'
+
+
+ add: ->
+ qualityGate = new QualityGate()
+ @options.app.qualityGateEditView.method = 'create'
+ @options.app.qualityGateEditView.model = qualityGate
+ @options.app.qualityGateEditView.show()
+
+
+ serializeData: ->
+ _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee
new file mode 100644
index 00000000000..54c106126e2
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee
@@ -0,0 +1,146 @@
+#
+# 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'
+], ->
+
+ class QualityGateDetailConditionView extends Marionette.ItemView
+ tagName: 'tr'
+ template: Templates['quality-gate-detail-condition']
+ spinner: '<i class="spinner"></i>'
+
+
+ modelEvents:
+ 'change:id': 'render'
+
+
+ ui:
+ periodSelect: '[name=period]'
+ operatorSelect: '[name=operator]'
+ warningInput: '[name=warning]'
+ errorInput: '[name=error]'
+ actionsBox: '.quality-gate-condition-actions'
+ updateButton: '.update-condition'
+ deleteButton: '.delete-condition'
+
+
+ events:
+ 'click @ui.updateButton': 'saveCondition'
+ 'click @ui.deleteButton': 'deleteCondition'
+ 'click .add-condition': 'saveCondition'
+ 'click .cancel-add-condition': 'cancelAddCondition'
+ 'keyup :input': 'enableUpdate'
+ 'change :input': 'enableUpdate'
+
+
+ initialize: ->
+ @populateMetric()
+
+
+ populateMetric: ->
+ metricKey = @model.get('metric')
+ metric = _.findWhere @options.app.metrics, key: metricKey
+ if metric?
+ switch metric.type
+ when 'WORK_DUR' then metric.placeholder = '1d 7h 59min'
+ when 'RATING' then metric.placeholder = 'A'
+ @model.set { metric: metric }, { silent: true }
+ @model.set { isDiffMetric: metric.key.indexOf('new_') == 0 }, { silent: true }
+
+
+ onRender: ->
+ @ui.periodSelect.val @model.get('period') || '0'
+ @ui.operatorSelect.val @model.get('op')
+ @ui.warningInput.val @model.get('warning')
+ @ui.errorInput.val @model.get('error')
+
+ @ui.periodSelect.select2
+ allowClear: false
+ minimumResultsForSearch: 999
+ width: '100%'
+
+ @ui.operatorSelect.select2
+ allowClear: false
+ minimumResultsForSearch: 999
+ width: '150px'
+
+ @ui.periodSelect.select2('open') if @model.isNew()
+
+
+ showSpinner: ->
+ jQuery(@spinner).prependTo @ui.actionsBox
+ @ui.actionsBox.find(':not(.spinner)').hide()
+
+
+ hideSpinner: ->
+ @ui.actionsBox.find('.spinner').remove()
+ @ui.actionsBox.find(':not(.spinner)').show()
+
+
+ saveCondition: ->
+ @showSpinner()
+ @model.set
+ period: @ui.periodSelect.val()
+ op: @ui.operatorSelect.val()
+ warning: @ui.warningInput.val()
+ error: @ui.errorInput.val()
+ @model.save()
+ .always =>
+ @ui.updateButton.prop 'disabled', true
+ @hideSpinner()
+ .done =>
+ @options.collectionView.updateConditions()
+
+
+ deleteCondition: ->
+ confirmDialog
+ title: t 'quality_gates.delete_condition'
+ html: tp 'quality_gates.delete_condition.confirm.message', @model.get('metric').name
+ yesLabel: t 'delete'
+ noLabel: t 'cancel'
+ yesHandler: =>
+ @showSpinner()
+ @model.delete().done =>
+ @options.collectionView.collection.remove @model
+ @options.collectionView.updateConditions()
+ @close()
+ always: => @ui.deleteButton.blur()
+
+
+ cancelAddCondition: ->
+ @close()
+
+
+ enableUpdate: ->
+ @ui.updateButton.prop 'disabled', false
+
+
+ serializeData: ->
+ period = _.findWhere(@options.app.periods, key: this.model.get('period'))
+ data = _.extend super,
+ canEdit: @options.app.canEdit
+ periods: @options.app.periods
+ periodText: period?.text
+ unless @options.app.canEdit
+ _.extend data,
+ warning: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('warning')).originalVal()
+ error: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('error')).originalVal()
+ data
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee
new file mode 100644
index 00000000000..b285ca05617
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee
@@ -0,0 +1,27 @@
+#
+# 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'
+], ->
+
+ class QualityGateDetailConditionsView extends Marionette.ItemView
+ tagName: 'tr'
+ template: Templates['quality-gate-detail-conditions-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee
new file mode 100644
index 00000000000..29843cbf32c
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee
@@ -0,0 +1,105 @@
+#
+# 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/condition',
+ './quality-gate-detail-condition-view',
+ './quality-gate-detail-conditions-empty-view'
+ '../templates'
+], (
+ Condition,
+ QualityGateDetailConditionView,
+ QualityGateDetailConditionsEmptyView,
+) ->
+
+ class QualityGateDetailConditionsView extends Marionette.CompositeView
+ template: Templates['quality-gate-detail-conditions']
+ itemView: QualityGateDetailConditionView
+ emptyView: QualityGateDetailConditionsEmptyView
+ itemViewContainer: '.quality-gate-conditions tbody'
+
+
+ ui:
+ metricSelect: '#quality-gate-new-condition-metric'
+ introductionShowMore: '.quality-gate-introduction-show-more'
+ introductionMore: '.quality-gate-introduction-more'
+
+
+ events:
+ 'click @ui.introductionShowMore': 'showMoreIntroduction'
+ 'change @ui.metricSelect': 'addCondition'
+
+
+ itemViewOptions: ->
+ app: @options.app
+ collectionView: @
+
+
+ appendHtml: (compositeView, itemView) ->
+ if (compositeView.isBuffering)
+ compositeView.elBuffer.appendChild itemView.el
+ compositeView._bufferedChildren.push itemView
+ else
+ container = @getItemViewContainer compositeView
+ container.prepend itemView.el
+
+
+ onRender: ->
+ @ui.introductionMore.hide()
+ @ui.metricSelect.select2
+ allowClear: false,
+ width: '250px',
+ placeholder: t('alerts.select_metric')
+
+
+ groupedMetrics: ->
+ metrics = @options.app.metrics
+ metrics = _.filter metrics, (metric) ->
+ !metric.hidden
+ metrics = _.groupBy metrics, 'domain'
+ metrics = _.map metrics, (metrics, domain) ->
+ domain: domain, metrics: _.sortBy metrics, 'short_name'
+ _.sortBy metrics, 'domain'
+
+
+ serializeData: ->
+ _.extend super,
+ canEdit: @options.app.canEdit
+ metricGroups: @groupedMetrics()
+
+
+ showMoreIntroduction: ->
+ @ui.introductionShowMore.hide()
+ @ui.introductionMore.show()
+
+
+ addCondition: ->
+ metric = @ui.metricSelect.val()
+ @ui.metricSelect.select2('val', '')
+ condition = new Condition
+ metric: metric
+ gateId: @options.gateId
+ @collection.unshift condition
+
+
+ updateConditions: ->
+ conditions = @collection.map (item) -> _.extend item.toJSON(),
+ metric: item.get('metric').key
+ @options.qualityGate.set { conditions: conditions }, { silent: true }
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee
new file mode 100644
index 00000000000..1980b9ed7c2
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee
@@ -0,0 +1,101 @@
+#
+# 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/quality-gate'
+ '../templates',
+], (
+ QualityGate
+) ->
+
+ class QualityGateDetailHeaderView extends Marionette.ItemView
+ template: Templates['quality-gate-detail-header']
+ spinner: '<i class="spinner"></i>'
+
+
+ modelEvents:
+ 'change': 'render'
+
+
+ ui:
+ deleteButton: '#quality-gate-delete'
+
+
+ events:
+ 'click #quality-gate-rename': 'renameQualityGate'
+ 'click #quality-gate-copy': 'copyQualityGate'
+ 'click @ui.deleteButton': 'deleteQualityGate'
+ 'click #quality-gate-set-as-default': 'setAsDefault'
+ 'click #quality-gate-unset-as-default': 'unsetAsDefault'
+
+
+ renameQualityGate: ->
+ @options.app.qualityGateEditView.method = 'rename'
+ @options.app.qualityGateEditView.model = @model
+ @options.app.qualityGateEditView.show()
+
+
+ copyQualityGate: ->
+ copiedModel = new QualityGate @model.toJSON()
+ copiedModel.set 'default', false
+ @options.app.qualityGateEditView.method = 'copy'
+ @options.app.qualityGateEditView.model = copiedModel
+ @options.app.qualityGateEditView.show()
+
+
+ deleteQualityGate: ->
+ message = if @model.get 'default' then 'quality_gates.delete.confirm.default' else 'quality_gates.delete.confirm.message'
+ message = tp message, @model.get('name')
+ confirmDialog
+ title: t 'quality_gates.delete'
+ html: message
+ yesLabel: t 'delete'
+ noLabel: t 'cancel'
+ yesHandler: =>
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/qualitygates/destroy"
+ data: id: @model.id
+ .done => @options.app.deleteQualityGate @model.id
+ always: => @ui.deleteButton.blur()
+
+
+ changeDefault: (set) ->
+ data = if set then { id: @model.id } else {}
+ method = if set then 'set_as_default' else 'unset_default'
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/qualitygates/#{method}"
+ data: data
+ .done =>
+ @options.app.unsetDefaults @model.id
+ @model.set 'default', !@model.get('default')
+
+
+ setAsDefault: ->
+ @changeDefault true
+
+
+ unsetAsDefault: ->
+ @changeDefault false
+
+
+ serializeData: ->
+ _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee
new file mode 100644
index 00000000000..726d95ac5c9
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee
@@ -0,0 +1,55 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ 'components/common/select-list'
+ '../templates'
+], ->
+
+ class QualityGateDetailProjectsView extends Marionette.ItemView
+ template: Templates['quality-gate-detail-projects']
+
+
+ onRender: ->
+ unless @model.get('default')
+ new SelectList
+ el: @$('#select-list-projects')
+ width: '100%'
+ readOnly: !@options.app.canEdit
+ focusSearch: false
+ format: (item) -> item.name
+ searchUrl: "#{baseUrl}/api/qualitygates/search?gateId=#{@options.gateId}"
+ selectUrl: "#{baseUrl}/api/qualitygates/select"
+ deselectUrl: "#{baseUrl}/api/qualitygates/deselect"
+ extra:
+ gateId: @options.gateId
+ selectParameter: 'projectId'
+ selectParameterValue: 'id'
+ labels:
+ selected: t('quality_gates.projects.with')
+ deselected: t('quality_gates.projects.without')
+ all: t('quality_gates.projects.all')
+ noResults: t('quality_gates.projects.noResults')
+ tooltips:
+ select: t('quality_gates.projects.select_hint')
+ deselect: t('quality_gates.projects.deselect_hint')
+
+ serializeData: ->
+ _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee
new file mode 100644
index 00000000000..10b4b6e0ceb
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee
@@ -0,0 +1,67 @@
+#
+# 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 [
+ '../collections/conditions',
+ './quality-gate-detail-header-view',
+ './quality-gate-detail-conditions-view',
+ './quality-gate-detail-projects-view'
+ '../templates',
+], (
+ Conditions,
+ QualityGateDetailHeaderView,
+ QualityGateDetailConditionsView,
+ QualityGateDetailProjectsView
+) ->
+
+ class QualityGateDetailView extends Marionette.Layout
+ template: Templates['quality-gate-detail']
+
+
+ regions:
+ conditionsRegion: '#quality-gate-conditions'
+ projectsRegion: '#quality-gate-projects'
+
+
+ modelEvents:
+ 'change': 'render'
+
+
+ onRender: ->
+ @showConditions()
+ @showProjects()
+
+
+ showConditions: ->
+ conditions = new Conditions @model.get('conditions')
+ view = new QualityGateDetailConditionsView
+ app: @options.app
+ collection: conditions
+ gateId: @model.id
+ qualityGate: @model
+ @conditionsRegion.show view
+
+
+ showProjects: ->
+ view = new QualityGateDetailProjectsView
+ app: @options.app
+ model: @model
+ gateId: @model.id
+ @projectsRegion.show view
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee
new file mode 100644
index 00000000000..67adfeca477
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee
@@ -0,0 +1,101 @@
+#
+# 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'
+], ->
+
+ class QualityGateEditView extends Marionette.ItemView
+ template: Templates['quality-gate-edit']
+
+
+ ui:
+ nameInput: '#quality-gate-edit-name'
+
+
+ events:
+ 'submit form': 'onSubmit'
+ 'click #quality-gate-cancel-create': 'hide'
+
+
+ onRender: ->
+ @$el.dialog
+ dialogClass: 'no-close',
+ width: '600px',
+ draggable: false,
+ autoOpen: false,
+ modal: true,
+ minHeight: 50,
+ resizable: false,
+ title: null
+
+
+ show: ->
+ @render()
+ @$el.dialog 'open'
+ @ui.nameInput.focus()
+
+
+ hide: ->
+ @$el.dialog 'close'
+
+
+ saveRequest: (data) ->
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/qualitygates/#{@method}"
+ data: data
+ .done => @hide()
+
+
+ onSubmit: (e) ->
+ e.preventDefault()
+ switch @method
+ when 'create' then @createQualityGate()
+ when 'copy' then @copyQualityGate()
+ when 'rename' then @saveQualityGate()
+ else
+
+
+ createQualityGate: ->
+ data = name: @ui.nameInput.val()
+ @saveRequest(data).done (r) =>
+ @model.set id: r.id, name: r.name
+ @options.app.qualityGates.add @model
+ @options.app.router.navigate "show/#{r.id}", trigger: true
+
+
+ saveQualityGate: ->
+ data = id: @model.id, name: @ui.nameInput.val()
+ @saveRequest(data).done (r) =>
+ @model.set name: r.name
+
+
+ copyQualityGate: ->
+ data = id: @model.id, name: @ui.nameInput.val()
+ @saveRequest(data).done (r) =>
+ @model.set id: r.id, name: r.name
+ @options.app.qualityGates.add @model
+ @options.app.router.navigate "show/#{r.id}", trigger: true
+
+
+ serializeData: ->
+ if @model
+ _.extend @model.toJSON(), method: @method
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee
new file mode 100644
index 00000000000..9ec9393dd9e
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee
@@ -0,0 +1,28 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ '../templates'
+], ->
+
+ class QualityGateSidebarListEmptyView extends Marionette.ItemView
+ tagName: 'li'
+ className: 'empty'
+ template: Templates['quality-gate-sidebar-list-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
new file mode 100644
index 00000000000..3527a6c6e1a
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
@@ -0,0 +1,44 @@
+#
+# 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'
+], ->
+
+ class QualityGateSidebarListItemView extends Marionette.ItemView
+ tagName: 'a'
+ className: 'list-group-item'
+ template: Templates['quality-gate-sidebar-list-item']
+
+
+ modelEvents:
+ 'change': 'render'
+
+
+ events:
+ 'click': 'showQualityGate'
+
+
+ onRender: ->
+ @$el.toggleClass 'active', @options.highlighted
+
+
+ showQualityGate: ->
+ @options.app.router.navigate "show/#{@model.id}", trigger: true
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee
new file mode 100644
index 00000000000..7fd40da3121
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee
@@ -0,0 +1,42 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+define [
+ './quality-gate-sidebar-list-item-view',
+ './quality-gate-sidebar-list-empty-view'
+], (
+ QualityGateSidebarListItemView,
+ QualityGateSidebarListEmptyView,
+) ->
+
+ class QualityGateSidebarListView extends Marionette.CollectionView
+ className: 'list-group'
+ itemView: QualityGateSidebarListItemView
+ emptyView: QualityGateSidebarListEmptyView
+
+
+ itemViewOptions: (model) ->
+ app: @options.app
+ highlighted: model.get('id') == +@highlighted
+
+
+ highlight: (id) ->
+ @highlighted = id
+ @render()