aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-05-26 14:28:13 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-05-26 14:29:44 +0200
commit563b935a13d6cb6a3a932350ad8ea6cfc5769743 (patch)
treed8e571519530d4b071a62e1b13c0105ced9313f8 /server
parent22a324f1513e5a6c476da42f980c0beef247324e (diff)
downloadsonarqube-563b935a13d6cb6a3a932350ad8ea6cfc5769743.tar.gz
sonarqube-563b935a13d6cb6a3a932350ad8ea6cfc5769743.zip
migrate some code from coffee to js
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/Gruntfile.coffee13
-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/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/components/common/dialogs.coffee58
-rw-r--r--server/sonar-web/src/main/coffee/components/common/inputs.coffee110
-rw-r--r--server/sonar-web/src/main/coffee/components/common/popup.coffee66
-rw-r--r--server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee34
-rw-r--r--server/sonar-web/src/main/js/apps/computation/app.js64
-rw-r--r--server/sonar-web/src/main/js/apps/computation/header-view.js9
-rw-r--r--server/sonar-web/src/main/js/apps/computation/layout.js16
-rw-r--r--server/sonar-web/src/main/js/apps/computation/list-footer-view.js34
-rw-r--r--server/sonar-web/src/main/js/apps/computation/list-item-view.js28
-rw-r--r--server/sonar-web/src/main/js/apps/computation/list-view.js11
-rw-r--r--server/sonar-web/src/main/js/apps/computation/report.js22
-rw-r--r--server/sonar-web/src/main/js/apps/computation/reports.js34
-rw-r--r--server/sonar-web/src/main/js/apps/computation/router.js29
-rw-r--r--server/sonar-web/src/main/js/apps/computation/search-view.js34
-rw-r--r--server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs32
-rw-r--r--server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs2
-rw-r--r--server/sonar-web/src/main/js/components/common/dialogs.js44
-rw-r--r--server/sonar-web/src/main/js/components/common/popup.js53
-rw-r--r--server/sonar-web/src/main/js/libs/inputs.js111
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb (renamed from server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee)19
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb6
-rw-r--r--server/sonar-web/src/test/js/computation-spec.js103
-rw-r--r--server/sonar-web/src/test/json/computation-spec/history-big-1.json27
-rw-r--r--server/sonar-web/src/test/json/computation-spec/history-big-2.json17
-rw-r--r--server/sonar-web/src/test/json/computation-spec/history.json37
-rw-r--r--server/sonar-web/src/test/json/computation-spec/queue.json12
-rw-r--r--server/sonar-web/src/test/views/computation.jade5
-rw-r--r--server/sonar-web/src/test/views/layouts/main.jade2
45 files changed, 778 insertions, 727 deletions
diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee
index 48d06ebbac4..ac2a0b1bbf7 100644
--- a/server/sonar-web/Gruntfile.coffee
+++ b/server/sonar-web/Gruntfile.coffee
@@ -87,7 +87,7 @@ module.exports = (grunt) ->
'<%= BUILD_PATH %>/js/libs/graphics/barchart.js'
'<%= BUILD_PATH %>/js/libs/sortable.js'
- '<%= BUILD_PATH %>/js/components/common/inputs.js'
+ '<%= BUILD_PATH %>/js/libs/inputs.js'
'<%= BUILD_PATH %>/js/components/common/dialogs.js'
'<%= BUILD_PATH %>/js/components/common/processes.js'
'<%= BUILD_PATH %>/js/components/common/jquery-isolated-scroll.js'
@@ -132,9 +132,9 @@ module.exports = (grunt) ->
tasks: [
{ grunt: true, args: ['uglify:build'] }
# apps
- { grunt: true, args: ['requirejs:app', '--app=analysis-reports'] }
{ grunt: true, args: ['requirejs:app', '--app=api-documentation'] }
{ grunt: true, args: ['requirejs:app', '--app=coding-rules'] }
+ { grunt: true, args: ['requirejs:app', '--app=computation'] }
{ grunt: true, args: ['requirejs:app', '--app=drilldown'] }
{ grunt: true, args: ['requirejs:app', '--app=markdown'] }
{ grunt: true, args: ['requirejs:app', '--app=measures'] }
@@ -171,6 +171,7 @@ module.exports = (grunt) ->
'casper:workspace'
'casper:users'
'casper:provisioning'
+ 'casper:computation'
]
@@ -213,9 +214,6 @@ module.exports = (grunt) ->
'<%= BUILD_PATH %>/js/apps/api-documentation/templates.js': [
'<%= SOURCE_PATH %>/js/apps/api-documentation/templates/**/*.hbs'
]
- '<%= BUILD_PATH %>/js/apps/analysis-reports/templates.js': [
- '<%= SOURCE_PATH %>/coffee/apps/analysis-reports/templates/**/*.hbs'
- ]
'<%= BUILD_PATH %>/js/apps/nav/templates.js': [
'<%= SOURCE_PATH %>/js/apps/nav/templates/**/*.hbs'
]
@@ -234,6 +232,9 @@ module.exports = (grunt) ->
'<%= BUILD_PATH %>/js/apps/provisioning/templates.js': [
'<%= SOURCE_PATH %>/js/apps/provisioning/templates/**/*.hbs'
]
+ '<%= BUILD_PATH %>/js/apps/computation/templates.js': [
+ '<%= SOURCE_PATH %>/js/apps/computation/templates/**/*.hbs'
+ ]
clean:
@@ -330,6 +331,8 @@ module.exports = (grunt) ->
src: ['src/test/js/users*.js']
provisioning:
src: ['src/test/js/provisioning*.js']
+ computation:
+ src: ['src/test/js/computation*.js']
uglify:
build:
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
deleted file mode 100644
index 0d7c20d4bd4..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-define [
- './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
deleted file mode 100644
index 5aa028e86b2..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- './templates'
-], ->
-
- 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/reports.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee
deleted file mode 100644
index 1689eed3e65..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- './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
deleted file mode 100644
index 1bebf0ee964..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define ->
-
- class 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
deleted file mode 100644
index 90f24e63851..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs
+++ /dev/null
@@ -1,6 +0,0 @@
-<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
deleted file mode 100644
index b044d70fc04..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644
index e00c677a6d6..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs
+++ /dev/null
@@ -1,10 +0,0 @@
-<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
deleted file mode 100644
index b816c2fbec7..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs
+++ /dev/null
@@ -1,24 +0,0 @@
-<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
deleted file mode 100644
index 89ae9048c52..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- '../templates'
-], ->
-
- 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
deleted file mode 100644
index 4af3d06f602..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- '../templates'
-], ->
-
- 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
deleted file mode 100644
index c5c16ea59c0..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- '../templates'
-], ->
-
- 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
deleted file mode 100644
index 32efc6f0707..00000000000
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- './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/components/common/dialogs.coffee b/server/sonar-web/src/main/coffee/components/common/dialogs.coffee
deleted file mode 100644
index e9dd43822dd..00000000000
--- a/server/sonar-web/src/main/coffee/components/common/dialogs.coffee
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-$ = jQuery
-
-window.confirmDialog = (options) ->
- settings = _.extend window.confirmDialog.defaults, options
- dialog = $ """
- <div>
- <div class='modal-head'><h2>#{settings.title}</h2></div>
- <div class='modal-body'>#{settings.html}</div>
- <div class='modal-foot'>
- <button data-confirm='yes'>#{settings.yesLabel}</button>
- <a data-confirm='no' class='action'>#{settings.noLabel}</a>
- </div>
- </div>
- """
-
- $('[data-confirm=yes]', dialog).on 'click', ->
- dialog.dialog 'close'
- settings.yesHandler()
- settings.always()
- $('[data-confirm=no]', dialog).on 'click', ->
- dialog.dialog 'close'
- settings.noHandler()
- settings.always()
-
- dialog.dialog
- modal: true
- minHeight: null
- width: 540
-
-
-window.confirmDialog.defaults =
- title: 'Confirmation'
- html: ''
- yesLabel: 'Yes'
- noLabel: 'Cancel'
- yesHandler: ->
- noHandler: ->
- always: -> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/components/common/inputs.coffee b/server/sonar-web/src/main/coffee/components/common/inputs.coffee
deleted file mode 100644
index 70e7ac99173..00000000000
--- a/server/sonar-web/src/main/coffee/components/common/inputs.coffee
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-$ = jQuery
-
-window.SS = if typeof window.SS == 'object' then window.SS else {}
-window.SS.hoursInDay = window.SS.hoursInDay || 8
-
-transformPattern = (pattern) ->
- return pattern.replace /\{0\}/g, '(\\d+)'
-
-
-convertWorkDuration = (value) ->
- return 0 if value == '0'
-
- daysPattern = transformPattern t('work_duration.x_days')
- hoursPattern = transformPattern t('work_duration.x_hours')
- minutesPattern = transformPattern t('work_duration.x_minutes')
-
- days = value.match daysPattern
- hours = value.match hoursPattern
- minutes = value.match minutesPattern
-
- days = if days then +days[1] else 0
- hours = if hours then +hours[1] else 0
- minutes = if minutes then +minutes[1] else 0
-
- if !value
- value
- else
- (days * window.SS.hoursInDay + hours) * 60 + minutes
-
-
-restoreWorkDuration = (value) ->
- return '0' if (value == '0' || value == 0)
- return value unless /^\d+$/.test value
-
- days = Math.floor(value / (window.SS.hoursInDay * 60))
- hours = Math.floor((value - days * window.SS.hoursInDay * 60) / 60)
- minutes = value % 60
- result = []
- result.push t('work_duration.x_days').replace('{0}', days) if days > 0
- result.push t('work_duration.x_hours').replace('{0}', hours) if hours > 0
- result.push t('work_duration.x_minutes').replace('{0}', minutes) if minutes > 0
- result.join ' '
-
-
-convertRating = (value) ->
- if /^[ABCDE]$/.test(value)
- value.charCodeAt(0) - 'A'.charCodeAt(0) + 1
- else
- value
-
-
-convertValue = (value, input) ->
- type = input.data 'type'
-
- # No convertation if input doesn't have data-type
- return value unless type?
-
- # Do necessary convertion depeneds on input data-type
- switch type
- when 'WORK_DUR' then convertWorkDuration value
- when 'RATING' then convertRating value
- else value
-
-
-restoreRating = (value) ->
- return value unless /^[12345]+$/.test value
- String.fromCharCode(value - 1 + 'A'.charCodeAt(0))
-
-
-restoreValue = (value, input) ->
- type = input.data 'type'
-
- # No convertation if input doesn't have data-type
- return value unless type?
-
- # Do necessary convertion depeneds on input data-type
- switch type
- when 'WORK_DUR' then restoreWorkDuration value
- when 'RATING' then restoreRating value
- else value
-
-
-originalVal = $.fn.val
-$.fn.val = (value) ->
- if arguments.length
- originalVal.call @, (restoreValue value, @)
- else
- convertValue originalVal.call(@), @
-
-$.fn.originalVal = originalVal
diff --git a/server/sonar-web/src/main/coffee/components/common/popup.coffee b/server/sonar-web/src/main/coffee/components/common/popup.coffee
deleted file mode 100644
index 52f28041d57..00000000000
--- a/server/sonar-web/src/main/coffee/components/common/popup.coffee
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define ->
-
- $ = jQuery
-
-
- class PopupView extends Marionette.ItemView
- className: 'bubble-popup'
-
-
- onRender: ->
- @$el.detach().appendTo $('body')
-
- if @options.bottom
- @$el.addClass 'bubble-popup-bottom'
- @$el.css
- top: @options.triggerEl.offset().top + @options.triggerEl.outerHeight()
- left: @options.triggerEl.offset().left
- else if @options.bottomRight
- @$el.addClass 'bubble-popup-bottom-right'
- @$el.css
- top: @options.triggerEl.offset().top + @options.triggerEl.outerHeight()
- right: $(window).width() - @options.triggerEl.offset().left - @options.triggerEl.outerWidth()
- else
- @$el.css
- top: @options.triggerEl.offset().top
- left: @options.triggerEl.offset().left + @options.triggerEl.outerWidth()
-
- @attachCloseEvents()
-
-
- attachCloseEvents: ->
- key 'escape', => @close()
-
- $('body').on 'click.bubble-popup', =>
- $('body').off 'click.bubble-popup'
- @close()
-
- @options.triggerEl.on 'click.bubble-popup', (e) =>
- @options.triggerEl.off 'click.bubble-popup'
- e.stopPropagation()
- @close()
-
-
- onClose: ->
- $('body').off 'click.bubble-popup'
- @options.triggerEl.off 'click.bubble-popup'
diff --git a/server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee b/server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee
deleted file mode 100644
index afc0b4f8cdb..00000000000
--- a/server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-define [
- './string-filters'
-], (
- StringFilterView
-) ->
-
- class DateFilterView extends StringFilterView
-
- render: ->
- super
- @detailsView.$('input').prop('placeholder', '1970-01-31').datepicker
- dateFormat: 'yy-mm-dd'
- changeMonth: true
- changeYear: true
diff --git a/server/sonar-web/src/main/js/apps/computation/app.js b/server/sonar-web/src/main/js/apps/computation/app.js
new file mode 100644
index 00000000000..47b483f3099
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/app.js
@@ -0,0 +1,64 @@
+define([
+ './router',
+ './layout',
+ './reports',
+ './header-view',
+ './search-view',
+ './list-view',
+ './list-footer-view'
+], function (Router, Layout, Reports, HeaderView, SearchView, ListView, ListFooterView) {
+
+ var App = new Marionette.Application(),
+ init = function (options) {
+ // Collection
+ this.reports = new Reports();
+
+ // Router
+ this.router = new Router({ reports: this.reports });
+
+ // Layout
+ this.layout = new Layout({ el: options.el });
+ this.layout.render();
+
+ // Header View
+ this.headerView = new HeaderView({ collection: this.reports });
+ this.layout.headerRegion.show(this.headerView);
+
+ // Search View
+ this.searchView = new SearchView({
+ collection: this.reports,
+ router: this.router
+ });
+ this.layout.searchRegion.show(this.searchView);
+
+ // List View
+ this.listView = new ListView({ collection: this.reports });
+ this.layout.listRegion.show(this.listView);
+
+ // List Footer View
+ this.listFooterView = new ListFooterView({ collection: this.reports });
+ this.layout.listFooterRegion.show(this.listFooterView);
+
+ // Go!
+ Backbone.history.start({
+ pushState: true,
+ root: getRoot()
+ });
+ };
+
+ App.on('start', function (options) {
+ window.requestMessages().done(function () {
+ init.call(App, options);
+ });
+ });
+
+ function getRoot () {
+ var COMPUTATION = '/computation',
+ path = window.location.pathname,
+ pos = path.indexOf(COMPUTATION);
+ return path.substr(0, pos + COMPUTATION.length);
+ }
+
+ return App;
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/header-view.js b/server/sonar-web/src/main/js/apps/computation/header-view.js
new file mode 100644
index 00000000000..8e010c0eded
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/header-view.js
@@ -0,0 +1,9 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['computation-header']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/layout.js b/server/sonar-web/src/main/js/apps/computation/layout.js
new file mode 100644
index 00000000000..f747efea06c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/layout.js
@@ -0,0 +1,16 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.Layout.extend({
+ template: Templates['computation-layout'],
+
+ regions: {
+ headerRegion: '#computation-header',
+ searchRegion: '#computation-search',
+ listRegion: '#computation-list',
+ listFooterRegion: '#computation-list-footer'
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-footer-view.js b/server/sonar-web/src/main/js/apps/computation/list-footer-view.js
new file mode 100644
index 00000000000..5034f25a3e8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/list-footer-view.js
@@ -0,0 +1,34 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['computation-list-footer'],
+
+ collectionEvents: {
+ 'all': 'render'
+ },
+
+ events: {
+ 'click #computation-fetch-more': 'onMoreClick'
+ },
+
+ onMoreClick: function (e) {
+ e.preventDefault();
+ this.fetchMore();
+ },
+
+ fetchMore: function () {
+ this.collection.fetchMore();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ total: this.collection.total,
+ count: this.collection.length,
+ more: this.collection.hasMore()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-item-view.js b/server/sonar-web/src/main/js/apps/computation/list-item-view.js
new file mode 100644
index 00000000000..5545f2ba0ac
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/list-item-view.js
@@ -0,0 +1,28 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ tagName: 'li',
+ className: 'panel panel-vertical',
+ template: Templates['computation-list-item'],
+
+ onRender: function () {
+ this.$el.attr('data-id', this.model.id);
+ this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
+ },
+
+ onClose: function () {
+ this.$('[data-toggle="tooltip"]').tooltip('destroy');
+ },
+
+ serializeData: function () {
+ var dangerStatuses = ['CANCELLED', 'FAIL'];
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ duration: this.model.getDuration(),
+ danger: dangerStatuses.indexOf(this.model.get('status')) !== -1
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-view.js b/server/sonar-web/src/main/js/apps/computation/list-view.js
new file mode 100644
index 00000000000..138c36b7619
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/list-view.js
@@ -0,0 +1,11 @@
+define([
+ './list-item-view',
+ './templates'
+], function (ListItemView) {
+
+ return Marionette.CollectionView.extend({
+ tagName: 'ul',
+ itemView: ListItemView
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/report.js b/server/sonar-web/src/main/js/apps/computation/report.js
new file mode 100644
index 00000000000..1131c24c1e9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/report.js
@@ -0,0 +1,22 @@
+define(function () {
+
+ return Backbone.Model.extend({
+ idAttribute: 'key',
+
+ getDuration: function () {
+ var duration = null;
+ if (this.has('startedAt')) {
+ var startedAtMoment = moment(this.get('startedAt')),
+ finishedAtMoment = moment(this.get('finishedAt') || new Date()),
+ diff = finishedAtMoment.diff(startedAtMoment);
+ duration = {
+ seconds: Math.floor(diff / 1000) % 60,
+ minutes: Math.floor(diff / (1000 * 60)) % 60,
+ hours: Math.floor(diff / (1000 * 60 * 60)) % 24
+ };
+ }
+ return duration;
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/reports.js b/server/sonar-web/src/main/js/apps/computation/reports.js
new file mode 100644
index 00000000000..9c8144333bb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/reports.js
@@ -0,0 +1,34 @@
+define([
+ './report'
+], function (Report) {
+
+ return Backbone.Collection.extend({
+ model: Report,
+ url: '',
+
+ parse: function (r) {
+ this.total = r.total || r.reports.length;
+ this.p = r.p || 1;
+ this.ps = r.ps;
+ return r.reports;
+ },
+
+ fetch: function (options) {
+ var opts = _.defaults(options || {}, { q: this.q }, { q: 'history' });
+ opts.url = baseUrl + '/api/computation/' + opts.q;
+ this.q = opts.q;
+ return Backbone.Collection.prototype.fetch.call(this, opts);
+ },
+
+ fetchMore: function () {
+ var p = this.p + 1;
+ return this.fetch({ add: true, remove: false, data: { p: p, ps: this.ps } });
+ },
+
+ hasMore: function () {
+ return this.total > this.p * this.ps;
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/router.js b/server/sonar-web/src/main/js/apps/computation/router.js
new file mode 100644
index 00000000000..6d874801cd3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/router.js
@@ -0,0 +1,29 @@
+define(function () {
+
+ return Backbone.Router.extend({
+ routes: {
+ '': 'index',
+ 'index': 'index',
+ 'current': 'current',
+ 'past': 'past'
+ },
+
+ initialize: function (options) {
+ this.options = options;
+ },
+
+ index: function () {
+ this.navigate('current');
+ this.current();
+ },
+
+ current: function () {
+ this.options.reports.fetch({ q: 'queue' });
+ },
+
+ past: function () {
+ this.options.reports.fetch({ q: 'history' });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/search-view.js b/server/sonar-web/src/main/js/apps/computation/search-view.js
new file mode 100644
index 00000000000..75ab65c4dad
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/search-view.js
@@ -0,0 +1,34 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['computation-search'],
+
+ collectionEvents: {
+ 'all': 'render'
+ },
+
+ events: {
+ 'click .js-queue': 'queue',
+ 'click .js-history': 'history'
+ },
+
+ queue: function (e) {
+ e.preventDefault();
+ this.options.router.navigate('current', { trigger: true });
+ },
+
+ history: function (e) {
+ e.preventDefault();
+ this.options.router.navigate('past', { trigger: true });
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ tab: this.collection.q
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs
new file mode 100644
index 00000000000..1995afdb34c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs
@@ -0,0 +1,5 @@
+<header class="page-header">
+ <h1 class="page-title">Project Computation</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>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs
new file mode 100644
index 00000000000..0d6d7f04ac5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs
@@ -0,0 +1,6 @@
+<div class="page">
+ <div id="computation-header"></div>
+ <div id="computation-search"></div>
+ <div id="computation-list"></div>
+ <div id="computation-list-footer"></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs
new file mode 100644
index 00000000000..5a612644148
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs
@@ -0,0 +1,6 @@
+<footer class="spacer-top note text-center">
+ {{count}}/{{total}} shown
+ {{#if more}}
+ <a id="computation-fetch-more" class="spacer-left" href="#">show more</a>
+ {{/if}}
+</footer>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs
new file mode 100644
index 00000000000..ab30221734c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs
@@ -0,0 +1,32 @@
+<div class="display-inline-block text-top width-30">
+ <i class="icon-qualifier-trk"></i>
+ <a href="{{dashboardUrl projectKey}}">{{projectName}}</a>
+</div>
+
+<div class="display-inline-block text-top width-30">
+ <ul>
+ {{#if submittedAt}}
+ <li>Submitted: {{dt submittedAt}}</li>
+ {{/if}}
+ {{#if startedAt}}
+ <li>Started: {{dt startedAt}}</li>
+ {{/if}}
+ {{#if finishedAt}}
+ <li>Finished: {{dt finishedAt}}</li>
+ {{/if}}
+ </ul>
+</div>
+
+
+{{#if duration}}
+ <div class="display-inline-block text-top width-30">
+ Duration:
+ {{#gt duration.hours 0}}{{duration.hours}}h{{/gt}}
+ {{#gt duration.minutes 0}}{{duration.minutes}}m{{/gt}}
+ {{duration.seconds}}s
+ </div>
+{{/if}}
+
+<div class="pull-right {{#if danger}}text-danger{{/if}}">
+ #{{key}} {{status}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs
new file mode 100644
index 00000000000..63bd417f154
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs
@@ -0,0 +1,6 @@
+<div class="spacer-top">
+ <ul class="tabs">
+ <li><a class="js-queue {{#eq tab 'queue'}}selected{{/eq}}">{{t 'analysis_reports.current_activity'}}</a></li>
+ <li><a class="js-history {{#eq tab 'history'}}selected{{/eq}}">{{t 'analysis_reports.past_reports'}}</a></li>
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
index 5cc7dbbc1ed..7ecdc88a5f4 100644
--- a/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
@@ -62,7 +62,7 @@
<a href="{{link '/bulk_deletion'}}">{{t 'bulk_deletion.page'}}</a>
</li>
<li>
- <a href="{{link '/analysis_reports'}}">{{t 'analysis_reports.page'}}</a>
+ <a href="{{link '/computation'}}">{{t 'analysis_reports.page'}}</a>
</li>
</ul>
</li>
diff --git a/server/sonar-web/src/main/js/components/common/dialogs.js b/server/sonar-web/src/main/js/components/common/dialogs.js
new file mode 100644
index 00000000000..46f8b68354c
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/dialogs.js
@@ -0,0 +1,44 @@
+(function ($) {
+
+ window.confirmDialog = function (options) {
+ var settings = _.extend(window.confirmDialog.defaults, options),
+ dialog = $('<div><div class="modal-head"><h2>' + settings.title + '</h2></div><div class="modal-body">' +
+ settings.html + '</div><div class="modal-foot"><button data-confirm="yes">' + settings.yesLabel +
+ '</button> <a data-confirm="no" class="action">' + settings.noLabel + '</a></div></div>');
+
+ $('[data-confirm=yes]', dialog).on('click', function () {
+ dialog.dialog('close');
+ settings.yesHandler();
+ return settings.always();
+ });
+
+ $('[data-confirm=no]', dialog).on('click', function () {
+ dialog.dialog('close');
+ settings.noHandler();
+ return settings.always();
+ });
+
+ return dialog.dialog({
+ modal: true,
+ minHeight: null,
+ width: 540
+ });
+ };
+
+ window.confirmDialog.defaults = {
+ title: 'Confirmation',
+ html: '',
+ yesLabel: 'Yes',
+ noLabel: 'Cancel',
+ yesHandler: function () {
+ // no op
+ },
+ noHandler: function () {
+ // no op
+ },
+ always: function () {
+ // no op
+ }
+ };
+
+})(window.jQuery);
diff --git a/server/sonar-web/src/main/js/components/common/popup.js b/server/sonar-web/src/main/js/components/common/popup.js
new file mode 100644
index 00000000000..f978b7ab380
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/popup.js
@@ -0,0 +1,53 @@
+define(function () {
+
+ var $ = jQuery;
+
+ return Marionette.ItemView.extend({
+ className: 'bubble-popup',
+
+ onRender: function () {
+ this.$el.detach().appendTo($('body'));
+ if (this.options.bottom) {
+ this.$el.addClass('bubble-popup-bottom');
+ this.$el.css({
+ top: this.options.triggerEl.offset().top + this.options.triggerEl.outerHeight(),
+ left: this.options.triggerEl.offset().left
+ });
+ } else if (this.options.bottomRight) {
+ this.$el.addClass('bubble-popup-bottom-right');
+ this.$el.css({
+ top: this.options.triggerEl.offset().top + this.options.triggerEl.outerHeight(),
+ right: $(window).width() - this.options.triggerEl.offset().left - this.options.triggerEl.outerWidth()
+ });
+ } else {
+ this.$el.css({
+ top: this.options.triggerEl.offset().top,
+ left: this.options.triggerEl.offset().left + this.options.triggerEl.outerWidth()
+ });
+ }
+ this.attachCloseEvents();
+ },
+
+ attachCloseEvents: function () {
+ var that = this;
+ key('escape', function () {
+ that.close();
+ });
+ $('body').on('click.bubble-popup', function () {
+ $('body').off('click.bubble-popup');
+ that.close();
+ });
+ this.options.triggerEl.on('click.bubble-popup', function (e) {
+ that.options.triggerEl.off('click.bubble-popup');
+ e.stopPropagation();
+ that.close();
+ });
+ },
+
+ onClose: function () {
+ $('body').off('click.bubble-popup');
+ this.options.triggerEl.off('click.bubble-popup');
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/libs/inputs.js b/server/sonar-web/src/main/js/libs/inputs.js
new file mode 100644
index 00000000000..78673a5e70e
--- /dev/null
+++ b/server/sonar-web/src/main/js/libs/inputs.js
@@ -0,0 +1,111 @@
+(function ($) {
+
+ function transformPattern (pattern) {
+ return pattern.replace(/\{0\}/g, '(\\d+)');
+ }
+
+ function convertWorkDuration (value) {
+ var days, daysPattern, hours, hoursPattern, minutes, minutesPattern;
+ if (value === '0') {
+ return 0;
+ }
+ daysPattern = transformPattern(t('work_duration.x_days'));
+ hoursPattern = transformPattern(t('work_duration.x_hours'));
+ minutesPattern = transformPattern(t('work_duration.x_minutes'));
+ days = value.match(daysPattern);
+ hours = value.match(hoursPattern);
+ minutes = value.match(minutesPattern);
+ days = days ? +days[1] : 0;
+ hours = hours ? +hours[1] : 0;
+ minutes = minutes ? +minutes[1] : 0;
+ if (!value) {
+ return value;
+ } else {
+ return (days * window.SS.hoursInDay + hours) * 60 + minutes;
+ }
+ }
+
+ function restoreWorkDuration (value) {
+ var days, hours, minutes, result;
+ if (value === '0' || value === 0) {
+ return '0';
+ }
+ if (!/^\d+$/.test(value)) {
+ return value;
+ }
+ days = Math.floor(value / (window.SS.hoursInDay * 60));
+ hours = Math.floor((value - days * window.SS.hoursInDay * 60) / 60);
+ minutes = value % 60;
+ result = [];
+ if (days > 0) {
+ result.push(t('work_duration.x_days').replace('{0}', days));
+ }
+ if (hours > 0) {
+ result.push(t('work_duration.x_hours').replace('{0}', hours));
+ }
+ if (minutes > 0) {
+ result.push(t('work_duration.x_minutes').replace('{0}', minutes));
+ }
+ return result.join(' ');
+ }
+
+ function convertRating (value) {
+ if (/^[ABCDE]$/.test(value)) {
+ return value.charCodeAt(0) - 'A'.charCodeAt(0) + 1;
+ } else {
+ return value;
+ }
+ }
+
+ function convertValue (value, input) {
+ var type;
+ type = input.data('type');
+ if (type == null) {
+ return value;
+ }
+ switch (type) {
+ case 'WORK_DUR':
+ return convertWorkDuration(value);
+ case 'RATING':
+ return convertRating(value);
+ default:
+ return value;
+ }
+ }
+
+ function restoreRating (value) {
+ if (!/^[12345]+$/.test(value)) {
+ return value;
+ }
+ return String.fromCharCode(value - 1 + 'A'.charCodeAt(0));
+ }
+
+ function restoreValue (value, input) {
+ var type;
+ type = input.data('type');
+ if (type == null) {
+ return value;
+ }
+ switch (type) {
+ case 'WORK_DUR':
+ return restoreWorkDuration(value);
+ case 'RATING':
+ return restoreRating(value);
+ default:
+ return value;
+ }
+ }
+
+ var originalVal = $.fn.val;
+
+ $.fn.val = function (value) {
+ if (arguments.length) {
+ return originalVal.call(this, restoreValue(value, this));
+ } else {
+ return convertValue(originalVal.call(this), this);
+ }
+ };
+
+ $.fn.originalVal = originalVal;
+
+})(window.jQuery);
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb
index 28700ed4a62..5d825bd26a6 100644
--- a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb
@@ -18,7 +18,22 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
-define ->
+class ComputationController < ApplicationController
+ before_filter :admin_required
- class extends Backbone.Model
+ SECTION=Navigation::SECTION_CONFIGURATION
+
+ def index
+ render :action => 'index'
+ end
+
+ def current
+ render :action => 'index'
+ end
+
+ def past
+ render :action => 'index'
+ end
+
+end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb
new file mode 100644
index 00000000000..f77eb797c80
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb
@@ -0,0 +1,6 @@
+<div id="computation"></div>
+<script>
+ require(['apps/computation/app'], function (App) {
+ App.start({ el: '#computation' });
+ });
+</script>
diff --git a/server/sonar-web/src/test/js/computation-spec.js b/server/sonar-web/src/test/js/computation-spec.js
new file mode 100644
index 00000000000..0922feb52b9
--- /dev/null
+++ b/server/sonar-web/src/test/js/computation-spec.js
@@ -0,0 +1,103 @@
+/* globals casper: false */
+var lib = require('../lib'),
+ testName = lib.testName('Computation');
+
+lib.initMessages();
+lib.changeWorkingDirectory('computation-spec');
+lib.configureCasper();
+
+casper.test.begin(testName('List'), 8, function (test) {
+ casper
+ .start(lib.buildUrl('computation'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequestFromFile('/api/computation/queue', 'queue.json');
+ lib.mockRequestFromFile('/api/computation/history', 'history.json');
+ })
+
+ .then(function () {
+ casper.evaluate(function () {
+ require(['apps/computation/app'], function (App) {
+ App.start({ el: '#computation' });
+ });
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('#computation-list li');
+ })
+
+ .then(function () {
+ test.assertElementCount('#computation-list li[data-id]', 1);
+ test.assertSelectorContains('#computation-list', 'SonarQube');
+ test.assertSelectorContains('#computation-list-footer', '1');
+ test.assertExists('.js-queue.selected');
+ })
+
+ .then(function () {
+ casper.click('.js-history');
+ casper.waitForSelectorTextChange('#computation-list-footer');
+ })
+
+ .then(function () {
+ test.assertElementCount('#computation-list li[data-id]', 3);
+ test.assertSelectorContains('#computation-list', 'Duration');
+ test.assertExists('.js-history.selected');
+ })
+
+ .then(function () {
+ casper.click('.js-queue');
+ casper.waitForSelectorTextChange('#computation-list-footer');
+ })
+
+ .then(function () {
+ test.assertElementCount('#computation-list li[data-id]', 1);
+ })
+
+ .then(function () {
+ lib.sendCoverage();
+ })
+ .run(function () {
+ test.done();
+ });
+});
+
+
+casper.test.begin(testName('Show More'), 2, function (test) {
+ casper
+ .start(lib.buildUrl('computation#past'), function () {
+ lib.setDefaultViewport();
+ this.searchMock = lib.mockRequestFromFile('/api/computation/history', 'history-big-1.json');
+ })
+
+ .then(function () {
+ casper.evaluate(function () {
+ require(['apps/computation/app'], function (App) {
+ App.start({ el: '#computation' });
+ });
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('#computation-list li');
+ })
+
+ .then(function () {
+ test.assertElementCount('#computation-list li[data-id]', 2);
+ lib.clearRequestMock(this.searchMock);
+ this.searchMock = lib.mockRequestFromFile('/api/computation/history', 'history-big-2.json',
+ { data: { p: '2' } });
+ casper.click('#computation-fetch-more');
+ casper.waitForSelectorTextChange('#computation-list-footer');
+ })
+
+ .then(function () {
+ test.assertElementCount('#computation-list li[data-id]', 3);
+ })
+
+ .then(function () {
+ lib.sendCoverage();
+ })
+ .run(function () {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/test/json/computation-spec/history-big-1.json b/server/sonar-web/src/test/json/computation-spec/history-big-1.json
new file mode 100644
index 00000000000..c7f53f33ea0
--- /dev/null
+++ b/server/sonar-web/src/test/json/computation-spec/history-big-1.json
@@ -0,0 +1,27 @@
+{
+ "total": 3,
+ "p": 1,
+ "ps": 2,
+ "reports": [
+ {
+ "submittedAt": "2015-05-07T14:25:20+0200",
+ "status": "SUCCESS",
+ "finishedAt": "2015-05-07T14:25:26+0200",
+ "startedAt": "2015-05-07T14:25:20+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "2"
+ },
+ {
+ "submittedAt": "2015-05-06T09:22:14+0200",
+ "status": "SUCCESS",
+ "finishedAt": "2015-05-06T09:22:18+0200",
+ "startedAt": "2015-05-06T09:22:14+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "1"
+ }
+ ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/history-big-2.json b/server/sonar-web/src/test/json/computation-spec/history-big-2.json
new file mode 100644
index 00000000000..f6fea2634a8
--- /dev/null
+++ b/server/sonar-web/src/test/json/computation-spec/history-big-2.json
@@ -0,0 +1,17 @@
+{
+ "total": 3,
+ "p": 2,
+ "ps": 2,
+ "reports": [
+ {
+ "submittedAt": "2015-05-13T15:44:15+0200",
+ "status": "FAIL",
+ "finishedAt": "2015-05-13T15:44:18+0200",
+ "startedAt": "2015-05-13T15:44:15+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "3"
+ }
+ ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/history.json b/server/sonar-web/src/test/json/computation-spec/history.json
new file mode 100644
index 00000000000..fbc51a09bd3
--- /dev/null
+++ b/server/sonar-web/src/test/json/computation-spec/history.json
@@ -0,0 +1,37 @@
+{
+ "total": 3,
+ "p": 1,
+ "ps": 10,
+ "reports": [
+ {
+ "submittedAt": "2015-05-13T15:44:15+0200",
+ "status": "FAIL",
+ "finishedAt": "2015-05-13T15:44:18+0200",
+ "startedAt": "2015-05-13T15:44:15+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "3"
+ },
+ {
+ "submittedAt": "2015-05-07T14:25:20+0200",
+ "status": "SUCCESS",
+ "finishedAt": "2015-05-07T14:25:26+0200",
+ "startedAt": "2015-05-07T14:25:20+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "2"
+ },
+ {
+ "submittedAt": "2015-05-06T09:22:14+0200",
+ "status": "SUCCESS",
+ "finishedAt": "2015-05-06T09:22:18+0200",
+ "startedAt": "2015-05-06T09:22:14+0200",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "1"
+ }
+ ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/queue.json b/server/sonar-web/src/test/json/computation-spec/queue.json
new file mode 100644
index 00000000000..18dcfbcfbba
--- /dev/null
+++ b/server/sonar-web/src/test/json/computation-spec/queue.json
@@ -0,0 +1,12 @@
+{
+ "reports": [
+ {
+ "submittedAt": "2015-05-13T15:44:15+0200",
+ "status": "WORKING",
+ "projectUuid": "id-sonarqube",
+ "projectName": "SonarQube",
+ "projectKey": "key-sonarqube",
+ "key": "4"
+ }
+ ]
+}
diff --git a/server/sonar-web/src/test/views/computation.jade b/server/sonar-web/src/test/views/computation.jade
new file mode 100644
index 00000000000..8145275ac35
--- /dev/null
+++ b/server/sonar-web/src/test/views/computation.jade
@@ -0,0 +1,5 @@
+extends layouts/main
+
+block body
+ #content
+ #computation
diff --git a/server/sonar-web/src/test/views/layouts/main.jade b/server/sonar-web/src/test/views/layouts/main.jade
index e1891e03b55..901a1042055 100644
--- a/server/sonar-web/src/test/views/layouts/main.jade
+++ b/server/sonar-web/src/test/views/layouts/main.jade
@@ -35,7 +35,7 @@ html
script(src='/js/libs/graphics/pie-chart.js')
script(src='/js/libs/graphics/barchart.js')
script(src='/js/libs/sortable.js')
- script(src='/js/components/common/inputs.js')
+ script(src='/js/libs/inputs.js')
script(src='/js/components/common/dialogs.js')
script(src='/js/components/common/processes.js')
script(src='/js/components/common/jquery-isolated-scroll.js')