Pārlūkot izejas kodu

SONAR-6331 add a project overview page

tags/5.2-RC1
Stas Vilchik pirms 9 gadiem
vecāks
revīzija
fcc90027f5
36 mainītis faili ar 1632 papildinājumiem un 184 dzēšanām
  1. 9
    0
      server/sonar-web/Gruntfile.coffee
  2. 4
    4
      server/sonar-web/src/main/hbs/nav/nav-context-navbar.hbs
  3. 65
    0
      server/sonar-web/src/main/hbs/overview/overview-coverage.hbs
  4. 35
    0
      server/sonar-web/src/main/hbs/overview/overview-debt.hbs
  5. 34
    0
      server/sonar-web/src/main/hbs/overview/overview-duplications.hbs
  6. 22
    0
      server/sonar-web/src/main/hbs/overview/overview-gate.hbs
  7. 40
    0
      server/sonar-web/src/main/hbs/overview/overview-issues.hbs
  8. 6
    0
      server/sonar-web/src/main/hbs/overview/overview-layout.hbs
  9. 28
    0
      server/sonar-web/src/main/hbs/overview/overview-size.hbs
  10. 125
    16
      server/sonar-web/src/main/js/application.js
  11. 170
    69
      server/sonar-web/src/main/js/common/handlebars-extensions.js
  12. 101
    0
      server/sonar-web/src/main/js/graphics/sparkline.js
  13. 96
    77
      server/sonar-web/src/main/js/graphics/timeline.js
  14. 0
    0
      server/sonar-web/src/main/js/measures.js
  15. 3
    5
      server/sonar-web/src/main/js/nav/context-navbar-view.js
  16. 42
    0
      server/sonar-web/src/main/js/overview/app.js
  17. 47
    0
      server/sonar-web/src/main/js/overview/layout.js
  18. 357
    0
      server/sonar-web/src/main/js/overview/models/state.js
  19. 39
    0
      server/sonar-web/src/main/js/overview/views/coverage-view.js
  20. 43
    0
      server/sonar-web/src/main/js/overview/views/debt-view.js
  21. 39
    0
      server/sonar-web/src/main/js/overview/views/duplications-view.js
  22. 43
    0
      server/sonar-web/src/main/js/overview/views/gate-view.js
  23. 43
    0
      server/sonar-web/src/main/js/overview/views/issues-view.js
  24. 38
    0
      server/sonar-web/src/main/js/overview/views/size-view.js
  25. 13
    1
      server/sonar-web/src/main/less/components/badges.less
  26. 4
    0
      server/sonar-web/src/main/less/components/page.less
  27. 5
    0
      server/sonar-web/src/main/less/components/panels.less
  28. 1
    0
      server/sonar-web/src/main/less/init/lists.less
  29. 4
    0
      server/sonar-web/src/main/less/init/misc.less
  30. 1
    0
      server/sonar-web/src/main/less/pages.less
  31. 90
    0
      server/sonar-web/src/main/less/pages/overview.less
  32. 18
    12
      server/sonar-web/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
  33. 31
    0
      server/sonar-web/src/main/webapp/WEB-INF/app/controllers/overview_controller.rb
  34. 6
    0
      server/sonar-web/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb
  35. 16
    0
      server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb
  36. 14
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 9
- 0
server/sonar-web/Gruntfile.coffee Parādīt failu

@@ -108,6 +108,7 @@ module.exports = (grunt) ->
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/treemap.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/graphics/pie-chart.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/graphics/timeline.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/graphics/sparkline.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/graphics/barchart.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/sortable.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/common/inputs.js'
@@ -145,6 +146,7 @@ module.exports = (grunt) ->
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/widget.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/bubble-chart.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/timeline.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/graphics/sparkline.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/stack-area.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/pie-chart.js'
'<%= grunt.option("assetsDir") || pkg.assets %>js/widgets/histogram.js'
@@ -250,6 +252,10 @@ module.exports = (grunt) ->
name: 'widgets/issue-filter'
out: '<%= grunt.option("assetsDir") || pkg.assets %>build/js/widgets/issue-filter.js'

overview: options:
name: 'overview/app'
out: '<%= grunt.option("assetsDir") || pkg.assets %>build/js/overview/app.js'


handlebars:
options:
@@ -309,6 +315,9 @@ module.exports = (grunt) ->
'<%= grunt.option("assetsDir") || pkg.assets %>js/templates/workspace.js': [
'<%= pkg.sources %>hbs/workspace/**/*.hbs'
]
'<%= grunt.option("assetsDir") || pkg.assets %>js/templates/overview.js': [
'<%= pkg.sources %>hbs/overview/**/*.hbs'
]


clean:

+ 4
- 4
server/sonar-web/src/main/hbs/nav/nav-context-navbar.hbs Parādīt failu

@@ -21,8 +21,8 @@
</div>

<ul class="nav navbar-nav nav-tabs">
<li {{#if isOverviewActive}}class="active"{{/if}}>
<a href="{{dashboardUrl contextKey}}">{{t 'overview.page'}}</a>
<li {{#isActiveLink '/overview'}}class="active"{{/isActiveLink}}>
<a href="{{link '/overview/index?id=' contextKeyEncoded }}">{{t 'overview.page'}}</a>
</li>
<li {{#isActiveLink '/components'}}class="active"{{/isActiveLink}}>
<a href="{{link '/components/index/' contextId }}">{{t 'components.page'}}</a>
@@ -46,11 +46,11 @@
<a class="dropdown-toggle" data-toggle="dropdown" href="#">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
<ul class="dropdown-menu">
<li class="dropdown-header">{{t 'layout.dashboards'}}</li>
{{#withoutFirst contextDashboards}}
{{#each contextDashboards}}
<li>
<a href="{{link url}}">{{name}}</a>
</li>
{{/withoutFirst}}
{{/each}}
{{#if canManageContextDashboards}}
<li class="small-divider"></li>
<li>

+ 65
- 0
server/sonar-web/src/main/hbs/overview/overview-coverage.hbs Parādīt failu

@@ -0,0 +1,65 @@
<h6 class="note">{{t 'overview.coverage'}}</h6>

<table class="width100">
<tr>
<td class="width-55">
<div class="overview-main-measure">
<a href="{{urlForDrilldown componentKey 'overall_coverage'}}">{{formatMeasure coverage 'PERCENT'}}</a>
</div>
<div class="overview-trend">
<div id="overview-coverage-trend" data-width="100" data-height="30" data-color="#4b9fd5"></div>
</div>
</td>
<td class="width-15">
{{#notNull coverage1}}
<a class="overview-measure" href="{{urlForDrilldown componentKey 'overall_coverage' 1}}">
{{formatMeasureVariation coverage1 'PERCENT'}}
</a>
<p class="note">{{period1Name}}</p>
{{/notNull}}
</td>
<td class="width-15">
{{#notNull coverage2}}
<a class="overview-measure" href="{{urlForDrilldown componentKey 'overall_coverage' 2}}">
{{formatMeasureVariation coverage2 'PERCENT'}}
</a>
<p class="note">{{period2Name}}</p>
{{/notNull}}
</td>
<td class="width-15">
{{#notNull coverage3}}
<a class="overview-measure" href="{{urlForDrilldown componentKey 'overall_coverage' 3}}">
{{formatMeasureVariation coverage3 'PERCENT'}}
</a>
<p class="note">{{period3Name}}</p>
{{/notNull}}
</td>
</tr>
<tr>
<td class="width-55"></td>
<td class="width-15">
{{#notNull newCoverage1}}
<a class="overview-measure" href="{{urlForDrilldown componentKey 'new_overall_coverage' 1}}">
{{formatMeasure newCoverage1 'PERCENT'}}
</a>
<p class="note">on new code</p>
{{/notNull}}
</td>
<td class="width-15">
{{#notNull newCoverage2}}
<a class="overview-measure spacer-top" href="{{urlForDrilldown componentKey 'new_overall_coverage' 2}}">
{{formatMeasure newCoverage2 'PERCENT'}}
</a>
<p class="note">on new code</p>
{{/notNull}}
</td>
<td class="width-15">
{{#notNull newCoverage3}}
<a class="overview-measure spacer-top" href="{{urlForDrilldown componentKey 'new_overall_coverage' 3}}">
{{formatMeasure newCoverage3 'PERCENT'}}
</a>
<p class="note">on new code</p>
{{/notNull}}
</td>
</tr>
</table>

+ 35
- 0
server/sonar-web/src/main/hbs/overview/overview-debt.hbs Parādīt failu

@@ -0,0 +1,35 @@
<h6 class="note">{{t 'overview.debt'}}</h6>

<table class="width100">
<tr>
<td class="width-55">
<div class="overview-main-measure">
<a href="{{urlForDrilldown componentKey 'sqale_index'}}">{{formatMeasure debt 'WORK_DUR'}}</a>
</div>
<div class="overview-trend">
<div id="overview-debt-trend" data-width="100" data-height="30" data-color="#4b9fd5"></div>
</div>
</td>
<td class="width-15">
<a href="{{urlForDrilldown componentKey 'sqale_index' 1}}"
class="overview-measure {{#gt deb1 0}}text-danger{{/gt}}{{#lt debt1 0}}text-success{{/lt}}">
{{formatMeasureVariation debt1 'WORK_DUR'}}
</a>
<p class="note">{{period1Name}}</p>
</td>
<td class="width-15">
<a href="{{urlForDrilldown componentKey 'sqale_index' 2}}"
class="overview-measure {{#gt debt2 0}}text-danger{{/gt}}{{#lt debt2 0}}text-success{{/lt}}">
{{formatMeasureVariation debt2 'WORK_DUR'}}
</a>
<p class="note">{{period2Name}}</p>
</td>
<td class="width-15">
<a href="{{urlForDrilldown componentKey 'sqale_index' 3}}"
class="overview-measure {{#gt debt3 0}}text-danger{{/gt}}{{#lt debt3 0}}text-success{{/lt}}">
{{formatMeasureVariation debt3 'WORK_DUR'}}
</a>
<p class="note">{{period3Name}}</p>
</td>
</tr>
</table>

+ 34
- 0
server/sonar-web/src/main/hbs/overview/overview-duplications.hbs Parādīt failu

@@ -0,0 +1,34 @@
<h6 class="note">{{t 'overview.duplications'}}</h6>

<table class="width100">
<tr>
<td class="width-55">
<div class="overview-main-measure">
<a href="{{urlForDrilldown componentKey 'duplicated_lines_density'}}">
{{formatMeasure duplications 'PERCENT'}}
</a>
</div>
<div class="overview-trend">
<div id="overview-duplications-trend" data-width="100" data-height="30" data-color="#4b9fd5"></div>
</div>
</td>
<td class="width-15">
<a class="overview-measure" href="{{urlForDrilldown componentKey 'duplicated_lines_density' 1}}">
{{formatMeasureVariation duplications1 'PERCENT'}}
</a>
<p class="note">{{period1Name}}</p>
</td>
<td class="width-15">
<a class="overview-measure" href="{{urlForDrilldown componentKey 'duplicated_lines_density' 2}}">
{{formatMeasureVariation duplications2 'PERCENT'}}
</a>
<p class="note">{{period2Name}}</p>
</td>
<td class="width-15">
<a class="overview-measure" href="{{urlForDrilldown componentKey 'duplicated_lines_density' 3}}">
{{formatMeasureVariation duplications3 'PERCENT'}}
</a>
<p class="note">{{period3Name}}</p>
</td>
</tr>
</table>

+ 22
- 0
server/sonar-web/src/main/hbs/overview/overview-gate.hbs Parādīt failu

@@ -0,0 +1,22 @@
<div class="text-center">
{{#notEmpty gateConditions}}
<ul class="list-inline spacer-top" style="display: inline-block;">
{{#each gateConditions}}
<li>
{{#canHaveDrilldownUrl metric period}}
<a href="{{urlForDrilldown ../../componentKey metric period periodDate}}"
class="overview-status overview-status-{{level}}"
title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}"
data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</a>
{{else}}
<span class="overview-status overview-status-{{level}}"
title="{{#notEq level 'OK'}}{{t 'quality_gates.operator' op 'short'}} {{/notEq}}{{#eq level 'ERROR'}}{{formatMeasure error type}}{{/eq}}{{#eq level 'WARN'}}{{formatMeasure warning type}}{{/eq}}"
data-toggle="tooltip" data-placement="bottom">{{formatMeasure actual type}}</span>
{{/canHaveDrilldownUrl}}
<p class="note text-lowercase" style="padding-top: 4px;">{{t 'metric' metric 'name'}}</p>
<p class="note">{{default periodName period}}</p>
</li>
{{/each}}
</ul>
{{/notEmpty}}
</div>

+ 40
- 0
server/sonar-web/src/main/hbs/overview/overview-issues.hbs Parādīt failu

@@ -0,0 +1,40 @@
<h6 class="note">{{t 'overview.issues'}}</h6>

<table class="width100">
<tr>
<td class="width-55">
<div class="overview-main-measure">
<a href="{{urlForIssuesOverview componentKey}}">{{formatMeasure issues 'INT'}}</a>
</div>
<div class="overview-trend">
<div id="overview-issues-trend" data-width="100" data-height="30" data-color="#4b9fd5"></div>
</div>
</td>
<td class="width-15">
<a href="{{urlForIssuesOverview componentKey period1Date}}"
class="overview-measure {{#gt issues1 0}}text-danger{{else}}text-success{{/gt}}">
{{formatMeasureVariation issues1 'INT'}}
</a>
<span class="note">new</span>
<p class="note">{{period1Name}}</p>
</td>
<td class="width-15">
<div style="display: inline-block; vertical-align: middle;">
<a href="{{urlForIssuesOverview componentKey period2Date}}"
class="overview-measure {{#gt issues2 0}}text-danger{{else}}text-success{{/gt}}">
{{formatMeasureVariation issues2 'INT'}}
</a>
<span class="note">new</span>
<p class="note">{{period2Name}}</p>
</div>
</td>
<td class="width-15">
<a href="{{urlForIssuesOverview componentKey period3Date}}"
class="overview-measure {{#gt issues3 0}}text-danger{{else}}text-success{{/gt}}">
{{formatMeasureVariation issues3 'INT'}}
</a>
<span class="note">new</span>
<p class="note">{{period3Name}}</p>
</td>
</tr>
</table>

+ 6
- 0
server/sonar-web/src/main/hbs/overview/overview-layout.hbs Parādīt failu

@@ -0,0 +1,6 @@
<div class="overview-card" id="overview-gate"></div>
<div class="overview-card" id="overview-size"></div>
<div class="overview-card" id="overview-issues"></div>
<div class="overview-card" id="overview-debt"></div>
<div class="overview-card" id="overview-coverage"></div>
<div class="overview-card" id="overview-duplications"></div>

+ 28
- 0
server/sonar-web/src/main/hbs/overview/overview-size.hbs Parādīt failu

@@ -0,0 +1,28 @@
<h6 class="note">{{t 'overview.lines_of_code'}}</h6>

<table class="width100">
<tr>
<td class="width-55">
<div>
<div class="overview-main-measure">
<a href="{{urlForDrilldown componentKey 'ncloc'}}">{{formatMeasure ncloc 'INT'}}</a>
</div>
<div class="overview-trend">
<div id="overview-size-trend" data-width="100" data-height="30" data-color="#4b9fd5"></div>
</div>
</div>
</td>
<td class="width-15">
<span class="overview-measure">{{formatMeasureVariation ncloc1 'INT'}}</span>
<p class="note">{{period1Name}}</p>
</td>
<td class="width-15">
<span class="overview-measure">{{formatMeasureVariation ncloc2 'INT'}}</span>
<p class="note">{{period2Name}}</p>
</td>
<td class="width-15">
<span class="overview-measure">{{formatMeasureVariation ncloc3 'INT'}}</span>
<p class="note">{{period3Name}}</p>
</td>
</tr>
</table>

+ 125
- 16
server/sonar-web/src/main/js/application.js Parādīt failu

@@ -17,27 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
function showMessage(div_id, message) {
function showMessage (div_id, message) {
$j('#' + div_id + 'msg').html(message);
$j('#' + div_id).show();
}
function error(message) {
function error (message) {
showMessage('error', message);
}
function warning(message) {
function warning (message) {
showMessage('warning', message);
}
function info(message) {
function info (message) {
showMessage('info', message);
}
function toggleFav(resourceId, elt) {
$j.ajax({type: 'POST', dataType: 'json', url: baseUrl + '/favourites/toggle/' + resourceId,
function toggleFav (resourceId, elt) {
$j.ajax({
type: 'POST', dataType: 'json', url: baseUrl + '/favourites/toggle/' + resourceId,
success: function (data) {
var star = $j(elt);
star.removeClass('icon-favorite icon-not-favorite');
star.addClass(data.css);
star.attr('title', data.title);
}});
}
});
}

function dashboardParameters (urlHasSomething) {
@@ -68,7 +70,7 @@ function dashboardParameters (urlHasSomething) {

var treemaps = {};

function treemapById(id) {
function treemapById (id) {
return treemaps[id];
}
var TreemapContext = function (rid, label) {
@@ -163,7 +165,7 @@ Treemap.prototype.initNodes = function () {
});
};

function openModalWindow(url, options) {
function openModalWindow (url, options) {
var width = (options && options.width) || 540;
var $dialog = $j('#modal');
if (!$dialog.length) {
@@ -199,7 +201,7 @@ function openModalWindow(url, options) {
return this.each(function () {
var obj = $j(this);
var url = obj.attr('modal-url') || obj.attr('href');
return openModalWindow(url, {'width': obj.attr('modal-width')});
return openModalWindow(url, { 'width': obj.attr('modal-width') });
});
},
modal: function () {
@@ -247,12 +249,12 @@ function openModalWindow(url, options) {
});
})(jQuery);

function closeModalWindow() {
function closeModalWindow () {
$j('#modal').dialog('close');
return false;
}

function supportsHTML5Storage() {
function supportsHTML5Storage () {
try {
return 'localStorage' in window && window.localStorage !== null;
} catch (e) {
@@ -262,7 +264,7 @@ function supportsHTML5Storage() {

//******************* HANDLING OF ACCORDION NAVIGATION [BEGIN] ******************* //

function openAccordionItem(url) {
function openAccordionItem (url) {
return $j.ajax({
url: url
}).fail(function (jqXHR, textStatus) {
@@ -305,11 +307,11 @@ var clickOnDropdownMenuLink = function (event) {
}
};

function showDropdownMenu(menuId) {
function showDropdownMenu (menuId) {
showDropdownMenuOnElement($j('#' + menuId));
}

function showDropdownMenuOnElement(elt) {
function showDropdownMenuOnElement (elt) {
var dropdownElt = $j(elt);

if (dropdownElt === currentlyDisplayedDropdownMenu) {
@@ -327,7 +329,7 @@ function showDropdownMenuOnElement(elt) {

//******************* HANDLING OF DROPDOWN MENUS [END] ******************* //

function openPopup(url, popupId) {
function openPopup (url, popupId) {
window.open(url, popupId, 'height=800,width=900,scrollbars=1,resizable=1');
return false;
}
@@ -366,6 +368,113 @@ function fileFromPath (path) {
}


/*
* Measures
*/

(function () {

/**
* Format a work duration measure
* @param {number} value
* @returns {string}
*/
var durationFormatter = function (value) {
if (value === 0) {
return '0';
}
var hoursInDay = window.SS.hoursInDay || 8,
isNegative = value < 0,
absValue = Math.abs(value);
var days = Math.floor(absValue / hoursInDay / 60);
var remainingValue = absValue - days * hoursInDay * 60;
var hours = Math.floor(remainingValue / 60);
remainingValue -= hours * 60;
var minutes = remainingValue;
var formatted = '';
if (days > 0) {
formatted += tp('work_duration.x_days', isNegative ? -1 * days : days);
}
if (hours > 0 && days < 10) {
if (formatted.length > 0) {
formatted += ' ';
}
formatted += tp('work_duration.x_hours', isNegative && formatted.length === 0 ? -1 * hours : hours);
}
if (minutes > 0 && hours < 10 && days === 0) {
if (formatted.length > 0) {
formatted += ' ';
}
formatted += tp('work_duration.x_minutes', isNegative && formatted.length === 0 ? -1 * minutes : minutes);
}
return formatted;
};

/**
* Format a work duration variation
* @param value
*/
var durationVariationFormatter = function (value) {
if (value === 0) {
return '0';
}
var formatted = durationFormatter(value);
return formatted[0] !== '-' ? '+' + formatted : formatted;
};

/**
* Format a measure according to its type
* @param measure
* @param {string} type
* @returns {string|null}
*/
window.formatMeasure = function (measure, type) {
var formatted = null,
formatters = {
'INT': function (value) {
return numeral(value).format('0,0');
},
'FLOAT': function (value) {
return numeral(value).format('0,0.0');
},
'PERCENT': function (value) {
return numeral(+value / 100).format('0,0.0%');
},
'WORK_DUR': durationFormatter
};
if (measure != null && type != null) {
formatted = formatters[type] != null ? formatters[type](measure) : measure;
}
return formatted;
};

/**
* Format a measure variation according to its type
* @param measure
* @param {string} type
* @returns {string|null}
*/
window.formatMeasureVariation = function (measure, type) {
var formatted = null,
formatters = {
'INT': function (value) {
return value === 0 ? '0' : numeral(value).format('+0,0');
},
'FLOAT': function (value) {
return value === 0 ? '0' : numeral(value).format('+0,0.0');
},
'PERCENT': function (value) {
return value === 0 ? '0%' : numeral(+value / 100).format('+0,0.0%');
},
'WORK_DUR': durationVariationFormatter
};
if (measure != null && type != null) {
formatted = formatters[type] != null ? formatters[type](measure) : measure;
}
return formatted;
};
})();

jQuery(function () {

// Process login link in order to add the anchor

+ 170
- 69
server/sonar-web/src/main/js/common/handlebars-extensions.js Parādīt failu

@@ -20,17 +20,25 @@
(function () {
var defaultActions = ['comment', 'assign', 'assign_to_me', 'plan', 'set_severity', 'set_tags'];

Handlebars.registerHelper('log', function() {
function isIssuesMetric (metric) {
var METRICS = ['violations', 'blocker_violations', 'critical_violations', 'major_violations', 'minor_violations',
'info_violations', 'new_blocker_violations', 'new_critical_violations', 'new_major_violations',
'new_minor_violations', 'new_info_violations', 'open_issues', 'reopened_issues', 'confirmed_issues',
'false_positive_issues'];
return METRICS.indexOf(metric) !== -1;
}

Handlebars.registerHelper('log', function () {
var args = Array.prototype.slice.call(arguments, 0, -1);
console.log.apply(console, args);
});

Handlebars.registerHelper('link', function() {
Handlebars.registerHelper('link', function () {
var url = Array.prototype.slice.call(arguments, 0, -1).join('');
return baseUrl + url;
});

Handlebars.registerHelper('isActiveLink', function() {
Handlebars.registerHelper('isActiveLink', function () {
var args = Array.prototype.slice.call(arguments, 0, -1),
options = arguments[arguments.length - 1],
prefix = args.join(''),
@@ -39,29 +47,29 @@
return match ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('capitalize', function(string) {
Handlebars.registerHelper('capitalize', function (string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
});

Handlebars.registerHelper('severityIcon', function(severity) {
Handlebars.registerHelper('severityIcon', function (severity) {
return new Handlebars.SafeString(
'<i class="icon-severity-' + severity.toLowerCase() + '"></i>'
);
});

Handlebars.registerHelper('severity', function(severity) {
Handlebars.registerHelper('severity', function (severity) {
return new Handlebars.SafeString(
'<i class="icon-severity-' + severity.toLowerCase() + '"></i>&nbsp;' + t('severity', severity)
'<i class="icon-severity-' + severity.toLowerCase() + '"></i>&nbsp;' + t('severity', severity)
);
});

Handlebars.registerHelper('statusIcon', function(status) {
Handlebars.registerHelper('statusIcon', function (status) {
return new Handlebars.SafeString(
'<i class="icon-status-' + status.toLowerCase() + '"></i>'
);
});

Handlebars.registerHelper('statusHelper', function(status, resolution) {
Handlebars.registerHelper('statusHelper', function (status, resolution) {
var s = '<i class="icon-status-' + status.toLowerCase() + '"></i>&nbsp;' + t('issue.status', status);
if (resolution != null) {
s = s + '&nbsp;(' + t('issue.resolution', resolution) + ')';
@@ -69,41 +77,41 @@
return new Handlebars.SafeString(s);
});

Handlebars.registerHelper('testStatusIcon', function(status) {
Handlebars.registerHelper('testStatusIcon', function (status) {
return new Handlebars.SafeString(
'<i class="icon-test-status-' + status.toLowerCase() + '"></i>'
'<i class="icon-test-status-' + status.toLowerCase() + '"></i>'
);
});

Handlebars.registerHelper('testStatusIconClass', function(status) {
Handlebars.registerHelper('testStatusIconClass', function (status) {
return new Handlebars.SafeString('' +
'icon-test-status-' + status.toLowerCase()
'icon-test-status-' + status.toLowerCase()
);
});

Handlebars.registerHelper('alertIconClass', function(alert) {
Handlebars.registerHelper('alertIconClass', function (alert) {
return new Handlebars.SafeString(
'icon-alert-' + alert.toLowerCase()
);
});

Handlebars.registerHelper('qualifierIcon', function(qualifier) {
Handlebars.registerHelper('qualifierIcon', function (qualifier) {
return new Handlebars.SafeString(
qualifier ? '<i class="icon-qualifier-' + qualifier.toLowerCase() + '"></i>': ''
qualifier ? '<i class="icon-qualifier-' + qualifier.toLowerCase() + '"></i>' : ''
);
});

Handlebars.registerHelper('default', function() {
Handlebars.registerHelper('default', function () {
var args = Array.prototype.slice.call(arguments, 0, -1);
return args.reduce(function(prev, current) {
return args.reduce(function (prev, current) {
return prev != null ? prev : current;
}, null);
});

Handlebars.registerHelper('show', function() {
Handlebars.registerHelper('show', function () {
var args = Array.prototype.slice.call(arguments),
ret = null;
args.forEach(function(arg) {
args.forEach(function (arg) {
if (typeof arg === 'string' && ret == null) {
ret = arg;
}
@@ -111,7 +119,7 @@
return ret || '';
});

Handlebars.registerHelper('percent', function(value, total) {
Handlebars.registerHelper('percent', function (value, total) {
if (total > 0) {
return '' + ((value || 0) / total * 100) + '%';
} else {
@@ -158,53 +166,57 @@
return ret;
});

Handlebars.registerHelper('eq', function(v1, v2, options) {
Handlebars.registerHelper('eq', function (v1, v2, options) {
// use `==` instead of `===` to ignore types
return v1 == v2 ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('notEq', function(v1, v2, options) {
Handlebars.registerHelper('notEq', function (v1, v2, options) {
// use `==` instead of `===` to ignore types
return v1 != v2 ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('gt', function(v1, v2, options) {
Handlebars.registerHelper('gt', function (v1, v2, options) {
return v1 > v2 ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('notNull', function(value, options) {
Handlebars.registerHelper('lt', function (v1, v2, options) {
return v1 < v2 ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('notNull', function (value, options) {
return value != null ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('notEmpty', function(array, options) {
Handlebars.registerHelper('notEmpty', function (array, options) {
var cond = _.isArray(array) && array.length > 0;
return cond ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('empty', function(array, options) {
Handlebars.registerHelper('empty', function (array, options) {
var cond = _.isArray(array) && array.length > 0;
return cond ? options.inverse(this) : options.fn(this);
});

Handlebars.registerHelper('all', function() {
Handlebars.registerHelper('all', function () {
var args = Array.prototype.slice.call(arguments, 0, -1),
options = arguments[arguments.length - 1],
all = args.reduce(function(prev, current) {
all = args.reduce(function (prev, current) {
return prev && current;
}, true);
return all ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('any', function() {
Handlebars.registerHelper('any', function () {
var args = Array.prototype.slice.call(arguments, 0, -1),
options = arguments[arguments.length - 1],
any = args.reduce(function(prev, current) {
any = args.reduce(function (prev, current) {
return prev || current;
}, false);
return any ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('inArray', function(array, element, options) {
Handlebars.registerHelper('inArray', function (array, element, options) {
if (_.isArray(array)) {
if (array.indexOf(element) !== -1) {
return options.fn(this);
@@ -214,20 +226,20 @@
}
});

Handlebars.registerHelper('ifNotEmpty', function() {
Handlebars.registerHelper('ifNotEmpty', function () {
var args = Array.prototype.slice.call(arguments, 0, -1),
options = arguments[arguments.length - 1],
notEmpty = args.reduce(function(prev, current) {
notEmpty = args.reduce(function (prev, current) {
return prev || (current && current.length > 0);
}, false);
return notEmpty ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('join', function(array, separator) {
Handlebars.registerHelper('join', function (array, separator) {
return array.join(separator);
});

Handlebars.registerHelper('eachReverse', function(array, options) {
Handlebars.registerHelper('eachReverse', function (array, options) {
var ret = '';

if (array && array.length > 0) {
@@ -241,7 +253,7 @@
return ret;
});

Handlebars.registerHelper('joinEach', function(array, separator, options) {
Handlebars.registerHelper('joinEach', function (array, separator, options) {
var ret = '';

if (array && array.length > 0) {
@@ -258,14 +270,14 @@
return ret;
});

Handlebars.registerHelper('sum', function(a, b) {
Handlebars.registerHelper('sum', function (a, b) {
var args = Array.prototype.slice.call(arguments, 0, -1);
return args.reduce(function (p, c) {
return p + +c;
}, 0);
});

Handlebars.registerHelper('dashboardUrl', function(componentKey, componentQualifier) {
Handlebars.registerHelper('dashboardUrl', function (componentKey, componentQualifier) {
var url = baseUrl + '/dashboard/index?id=' + encodeURIComponent(componentKey);
if (componentQualifier === 'FIL' || componentQualifier === 'CLA') {
url += '&metric=sqale_index';
@@ -273,57 +285,57 @@
return url;
});

Handlebars.registerHelper('translate', function() {
Handlebars.registerHelper('translate', function () {
var args = Array.prototype.slice.call(arguments, 0, -1);
return window.translate.apply(this, args);
});

Handlebars.registerHelper('t', function() {
Handlebars.registerHelper('t', function () {
var args = Array.prototype.slice.call(arguments, 0, -1);
return window.t.apply(this, args);
});

Handlebars.registerHelper('tp', function() {
Handlebars.registerHelper('tp', function () {
var args = Array.prototype.slice.call(arguments, 0, -1);
return window.tp.apply(this, args);
});

Handlebars.registerHelper('d', function(date) {
Handlebars.registerHelper('d', function (date) {
return moment(date).format('LL');
});

Handlebars.registerHelper('dt', function(date) {
Handlebars.registerHelper('dt', function (date) {
return moment(date).format('LLL');
});

Handlebars.registerHelper('ds', function(date) {
Handlebars.registerHelper('ds', function (date) {
return moment(date).format('YYYY-MM-DD');
});

Handlebars.registerHelper('fromNow', function(date) {
Handlebars.registerHelper('fromNow', function (date) {
return moment(date).fromNow();
});

Handlebars.registerHelper('durationFromNow', function(date, units) {
Handlebars.registerHelper('durationFromNow', function (date, units) {
return moment(new Date()).diff(date, units);
});

Handlebars.registerHelper('numberShort', function(number) {
Handlebars.registerHelper('numberShort', function (number) {
if (number > 9999) {
return numeral(number).format('0.[0]a');
} else {
return number;
return numeral(number).format('0,0');
}
});

Handlebars.registerHelper('pluginActions', function(actions, options) {
Handlebars.registerHelper('pluginActions', function (actions, options) {
var pluginActions = _.difference(actions, defaultActions);
return pluginActions.reduce(function(prev, current) {
return pluginActions.reduce(function (prev, current) {
return prev + options.fn(current);
}, '');
});

Handlebars.registerHelper('ifHasExtraActions', function(actions, options) {
Handlebars.registerHelper('ifHasExtraActions', function (actions, options) {
var actionsLeft = _.difference(actions, defaultActions);
if (actionsLeft.length > 0) {
return options.fn(this);
@@ -332,7 +344,7 @@
}
});

Handlebars.registerHelper('withFirst', function(list, options) {
Handlebars.registerHelper('withFirst', function (list, options) {
if (list && list.length > 0) {
return options.fn(list[0]);
} else {
@@ -340,7 +352,7 @@
}
});

Handlebars.registerHelper('withLast', function(list, options) {
Handlebars.registerHelper('withLast', function (list, options) {
if (list && list.length > 0) {
return options.fn(list[list.length - 1]);
} else {
@@ -348,9 +360,9 @@
}
});

Handlebars.registerHelper('withoutFirst', function(list, options) {
Handlebars.registerHelper('withoutFirst', function (list, options) {
if (list && list.length > 1) {
return list.slice(1).reduce(function(prev, current) {
return list.slice(1).reduce(function (prev, current) {
return prev + options.fn(current);
}, '');
} else {
@@ -359,27 +371,27 @@
});

var audaciousFn;
Handlebars.registerHelper('recursive', function(children, options) {
Handlebars.registerHelper('recursive', function (children, options) {
var out = '';

if (options.fn !== undefined) {
audaciousFn = options.fn;
}

children.forEach(function(child){
children.forEach(function (child) {
out = out + audaciousFn(child);
});

return out;
});

Handlebars.registerHelper('sources', function(source, scm, options) {
Handlebars.registerHelper('sources', function (source, scm, options) {
if (options == null) {
options = scm;
scm = null;
}

var sources = _.map(source, function(code, line) {
var sources = _.map(source, function (code, line) {
return {
lineNumber: line,
code: code,
@@ -387,20 +399,20 @@
};
});

return sources.reduce(function(prev, current, index) {
return sources.reduce(function (prev, current, index) {
return prev + options.fn(_.extend({ first: index === 0 }, current));
}, '');
});

Handlebars.registerHelper('operators', function(metricType, options) {
Handlebars.registerHelper('operators', function (metricType, options) {
var ops = ['LT', 'GT', 'EQ', 'NE'];

return ops.reduce(function(prev, current) {
return ops.reduce(function (prev, current) {
return prev + options.fn(current);
}, '');
});

Handlebars.registerHelper('changelog', function(diff) {
Handlebars.registerHelper('changelog', function (diff) {
var message = '';
if (diff.newValue != null) {
message = tp('issue.changelog.changed_to', t('issue.changelog.field', diff.key), diff.newValue);
@@ -415,7 +427,7 @@
return message;
});

Handlebars.registerHelper('ifMeasureShouldBeShown', function(measure, period, options) {
Handlebars.registerHelper('ifMeasureShouldBeShown', function (measure, period, options) {
if (measure != null || period != null) {
return options.fn(this);
} else {
@@ -423,18 +435,18 @@
}
});

Handlebars.registerHelper('ifSCMChanged', function(source, line, options) {
Handlebars.registerHelper('ifSCMChanged', function (source, line, options) {
var currentLine = _.findWhere(source, { lineNumber: line }),
prevLine = _.findWhere(source, { lineNumber: line - 1 }),
changed = true;
if (currentLine && prevLine && currentLine.scm && prevLine.scm) {
changed = (currentLine.scm.author !== prevLine.scm.author) ||
(currentLine.scm.date !== prevLine.scm.date) || (!prevLine.show);
(currentLine.scm.date !== prevLine.scm.date) || (!prevLine.show);
}
return changed ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('ifSCMChanged2', function(source, line, options) {
Handlebars.registerHelper('ifSCMChanged2', function (source, line, options) {
var currentLine = _.findWhere(source, { line: line }),
prevLine = _.findWhere(source, { line: line - 1 }),
changed = true;
@@ -444,7 +456,7 @@
return changed ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('ifTestData', function(test, options) {
Handlebars.registerHelper('ifTestData', function (test, options) {
if ((test.status !== 'OK') || ((test.status === 'OK') && test.coveredLines)) {
return options.fn(this);
} else {
@@ -496,4 +508,93 @@
return str.length > LIMIT ? str.substr(0, LIMIT) + '...' : str;
});

Handlebars.registerHelper('withSign', function (number) {
return number >= 0 ? '+' + number : number;
});

Handlebars.registerHelper('formatMeasure', function (measure, type) {
return window.formatMeasure(measure, type);
});

Handlebars.registerHelper('formatMeasureVariation', function (measure, type) {
return window.formatMeasureVariation(measure, type);
});

Handlebars.registerHelper('urlForDrilldown', function (component, metric, period, periodDate) {

function buildIssuesUrl (component, metric, periodDate) {
var url = baseUrl + '/component_issues/index?id=' + encodeURIComponent(component) + '#';
if (periodDate != null) {
url += 'createdAfter=' + encodeURIComponent(periodDate) + '|';
}
switch (metric) {
case 'blocker_violations':
case 'new_blocker_violations':
url += 'resolved=false|severities=BLOCKER';
break;
case 'critical_violations':
case 'new_critical_violations':
url += 'resolved=false|severities=CRITICAL';
break;
case 'major_violations':
case 'new_major_violations':
url += 'resolved=false|severities=MAJOR';
break;
case 'minor_violations':
case 'new_minor_violations':
url += 'resolved=false|severities=MINOR';
break;
case 'info_violations':
case 'new_info_violations':
url += 'resolved=false|severities=INFO';
break;
case 'open_issues':
url += 'resolved=false|statuses=OPEN';
break;
case 'reopened_issues':
url += 'resolved=false|statuses=REOPENED';
break;
case 'confirmed_issues':
url += 'resolved=false|statuses=CONFIRMED';
break;
case 'false_positive_issues':
url += 'resolutions=FALSE-POSITIVE';
break;
default:
url += 'resolved=false';
}
return url;
}

var url;
if (isIssuesMetric(metric)) {
url = buildIssuesUrl(component, metric, periodDate);
} else {
if (metric === 'package_cycles') {
url = baseUrl + '/design/index?id=' + encodeURIComponent(component);
} else {
url = baseUrl + '/drilldown/measures?id=' + encodeURIComponent(component) + '&metric=' + metric;
if (period != null && !_.isObject(period)) {
url += '&period=' + period;
}
}
}
return url;
});

Handlebars.registerHelper('canHaveDrilldownUrl', function (metric, period, options) {
var isDifferentialMetric = metric.indexOf('new_') === 0,
_isIssuesMetric = isIssuesMetric(metric),
r = isDifferentialMetric || period == null || _isIssuesMetric;
return r ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper('urlForIssuesOverview', function (componentKey, periodDate) {
var url = baseUrl + '/component_issues/index?id=' + encodeURIComponent(componentKey) + '#resolved=false';
if (typeof periodDate === 'string') {
url += '|createdAfter=' + encodeURIComponent(periodDate);
}
return url;
});

})();

+ 101
- 0
server/sonar-web/src/main/js/graphics/sparkline.js Parādīt failu

@@ -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.
*/
(function ($) {

function trans (left, top) {
return 'translate(' + left + ', ' + top + ')';
}

var defaults = {
height: 30,
color: '#1f77b4',
interpolate: 'bundle',
tension: 1
};

/*
* data = [
* { val: '2015-01-30', count: 30 },
* ...
* ]
*/

$.fn.sparkline = function (data, opts) {
$(this).each(function () {
var options = _.defaults(opts || {}, $(this).data(), defaults);
if (!options.width) {
_.extend(options, { width: $(this).width() });
}

var container = d3.select(this),
svg = container.append('svg')
.attr('width', options.width + 1)
.attr('height', options.height + 1)
.classed('sonar-d3', true),

plot = svg.append('g')
.classed('plot', true),

xScale = d3.time.scale()
.domain(d3.extent(data, function (d) {
return new Date(d.val);
})),

yScale = d3.scale.linear()
.domain(d3.extent(data, function (d) {
return d.count;
})),

line = d3.svg.line()
.x(function (d) {
return xScale(new Date(d.val));
})
.y(function (d) {
return yScale(d.count);
})
.interpolate(options.interpolate)
.tension(options.tension);

_.extend(options, {
marginLeft: 1,
marginRight: 1,
marginTop: 6,
marginBottom: 6
});

_.extend(options, {
availableWidth: options.width - options.marginLeft - options.marginRight,
availableHeight: options.height - options.marginTop - options.marginBottom
});

plot.attr('transform', trans(options.marginLeft, options.marginTop));
xScale.range([0, options.availableWidth]);
yScale.range([options.availableHeight, 0]);

plot.append('path')
.datum(data)
.attr('d', line)
.classed('line', true)
.style('stroke', options.color);
}
);
};

})(window.jQuery);

+ 96
- 77
server/sonar-web/src/main/js/graphics/timeline.js Parādīt failu

@@ -26,7 +26,8 @@
var defaults = {
height: 140,
color: '#1f77b4',
interpolate: 'basis'
interpolate: 'basis',
type: 'UNKNOWN'
};

/*
@@ -36,82 +37,100 @@
* ]
*/

$.fn.timeline = function (data) {
$.fn.timeline = function (data, opts) {
$(this).each(function () {
var options = _.defaults($(this).data(), defaults);
_.extend(options, { width: $(this).width() });

var container = d3.select(this),
svg = container.append('svg')
.attr('width', options.width + 2)
.attr('height', options.height + 2)
.classed('sonar-d3', true),

plot = svg.append('g')
.classed('plot', true),

xScale = d3.time.scale()
.domain(d3.extent(data, function (d) {
return new Date(d.val);
})),

yScale = d3.scale.linear()
.domain([
0, d3.max(data, function (d) {
return d.count;
})
]),

line = d3.svg.line()
.x(function (d) {
return xScale(new Date(d.val));
})
.y(function (d) {
return yScale(d.count);
})
.interpolate(options.interpolate),

minDate = xScale.domain()[0],
minDateTick = svg.append('text')
.classed('subtitle', true)
.text(moment(minDate).format('LL')),

maxDate = xScale.domain()[1],
maxDateTick = svg.append('text')
.classed('subtitle', true)
.text(moment(maxDate).format('LL'))
.style('text-anchor', 'end');

_.extend(options, {
marginLeft: 1,
marginRight: 1,
marginTop: 1,
marginBottom: 1 + maxDateTick.node().getBBox().height
});

_.extend(options, {
availableWidth: options.width - options.marginLeft - options.marginRight,
availableHeight: options.height - options.marginTop - options.marginBottom
});

plot.attr('transform', trans(options.marginLeft, options.marginTop));
xScale.range([0, options.availableWidth]);
yScale.range([0, options.availableHeight]);

minDateTick
.attr('x', options.marginLeft)
.attr('y', options.height);
maxDateTick
.attr('x', options.width - options.marginRight)
.attr('y', options.height);

plot.append('path')
.datum(data)
.attr('d', line)
.attr('class', 'line')
.style('stroke', options.color);

});
var options = _.defaults(opts || {}, $(this).data(), defaults);
_.extend(options, { width: $(this).width() });

var container = d3.select(this),
svg = container.append('svg')
.attr('width', options.width + 12)
.attr('height', options.height + 12)
.classed('sonar-d3', true),

extra = svg.append('g'),

plot = svg.append('g')
.classed('plot', true),

xScale = d3.time.scale()
.domain(d3.extent(data, function (d) {
return new Date(d.val);
})),

yScale = d3.scale.linear()
.domain(d3.extent(data, function (d) {
return d.count;
})),

line = d3.svg.line()
.x(function (d) {
return xScale(new Date(d.val));
})
.y(function (d) {
return yScale(d.count);
})
.interpolate(options.interpolate);

// Medians
var medianValue = getNiceMedian(0.5, data, function (d) {
return d.count;
}),
medianLabel = extra.append('text')
.text(window.formatMeasure(medianValue, options.type))
.style('text-anchor', 'end')
.style('font-size', '10px')
.style('fill', '#ccc')
.attr('dy', '0.32em'),
medianLabelWidth = medianLabel.node().getBBox().width;

_.extend(options, {
marginLeft: 1,
marginRight: 1 + medianLabelWidth + 4,
marginTop: 6,
marginBottom: 6
});

_.extend(options, {
availableWidth: options.width - options.marginLeft - options.marginRight,
availableHeight: options.height - options.marginTop - options.marginBottom
});

plot.attr('transform', trans(options.marginLeft, options.marginTop));
xScale.range([0, options.availableWidth]);
yScale.range([options.availableHeight, 0]);

plot.append('path')
.datum(data)
.attr('d', line)
.classed('line', true)
.style('stroke', options.color);

medianLabel
.attr('x', options.width - 1)
.attr('y', options.marginTop + yScale(medianValue));
extra.append('line')
.attr('x1', options.marginLeft)
.attr('y1', options.marginTop + yScale(medianValue))
.attr('x2', options.availableWidth + options.marginLeft)
.attr('y2', options.marginTop + yScale(medianValue))
.style('stroke', '#eee')
.style('shape-rendering', 'crispedges');
}
)
;
};

})(window.jQuery);
function getNiceMedian (p, array, accessor) {
var min = d3.min(array, accessor),
max = d3.max(array, accessor),
median = d3.median(array, accessor),
threshold = (max - min) / 2,
threshold10 = Math.pow(10, Math.floor(Math.log(threshold) / Math.LN10) - 1);
return (p - 0.5) > 0.0001 ?
Math.floor(median / threshold10) * threshold10 :
Math.ceil(median / threshold10) * threshold10;
}

})
(window.jQuery);

+ 0
- 0
server/sonar-web/src/main/js/measures.js Parādīt failu


+ 3
- 5
server/sonar-web/src/main/js/nav/context-navbar-view.js Parādīt failu

@@ -23,7 +23,7 @@ define([

var $ = jQuery,
OVERVIEW_URLS = [
'/design', '/libraries', '/dashboards'
'/design', '/libraries', '/dashboards', '/dashboard'
],
SETTINGS_URLS = [
'/project/settings', '/project/profile', '/project/qualitygate', '/manual_measures/index',
@@ -63,17 +63,15 @@ define([
search = window.location.search,
isMoreActive = _.some(OVERVIEW_URLS, function (url) {
return href.indexOf(url) !== -1;
}) || (href.indexOf('/dashboard') !== -1 && search.indexOf('did=') !== -1),
});
isSettingsActive = _.some(SETTINGS_URLS, function (url) {
return href.indexOf(url) !== -1;
}),
isOverviewActive = !isMoreActive && href.indexOf('/dashboard') !== -1 && search.indexOf('did=') === -1;
});

return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
canManageContextDashboards: !!window.SS.user,
contextKeyEncoded: encodeURIComponent(this.model.get('contextKey')),

isOverviewActive: isOverviewActive,
isSettingsActive: isSettingsActive,
isMoreActive: isMoreActive
});

+ 42
- 0
server/sonar-web/src/main/js/overview/app.js Parādīt failu

@@ -0,0 +1,42 @@
requirejs([
'overview/layout',
'overview/models/state',
'overview/views/gate-view',
'overview/views/size-view',
'overview/views/issues-view',
'overview/views/debt-view',
'overview/views/coverage-view',
'overview/views/duplications-view'
], function (Layout,
State,
GateView,
SizeView,
IssuesView,
DebtView,
CoverageView,
DuplicationsView) {

var $ = jQuery,
App = new Marionette.Application();

App.addInitializer(function () {
$('body').addClass('dashboard-page');
this.state = new State(window.overviewConf);
this.layout = new Layout({
el: '.overview',
model: this.state
}).render();
this.layout.gateRegion.show(new GateView({ model: this.state }));
this.layout.sizeRegion.show(new SizeView({ model: this.state }));
this.layout.issuesRegion.show(new IssuesView({ model: this.state }));
this.layout.debtRegion.show(new DebtView({ model: this.state }));
this.layout.coverageRegion.show(new CoverageView({ model: this.state }));
this.layout.duplicationsRegion.show(new DuplicationsView({ model: this.state }));
this.state.fetch();
});

window.requestMessages().done(function () {
App.start();
});

});

+ 47
- 0
server/sonar-web/src/main/js/overview/layout.js Parādīt failu

@@ -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([
'templates/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-layout'],

regions: {
gateRegion: '#overview-gate',
sizeRegion: '#overview-size',
issuesRegion: '#overview-issues',
debtRegion: '#overview-debt',
coverageRegion: '#overview-coverage',
duplicationsRegion: '#overview-duplications'
},

modelEvents: {
'change:gateConditions': 'toggleGate'
},

toggleGate: function () {
var conditions = this.model.get('gateConditions'),
hasGate = _.isArray(conditions) && conditions.length > 0;
this.$(this.gateRegion.el).toggle(hasGate);
}
});

});

+ 357
- 0
server/sonar-web/src/main/js/overview/models/state.js Parādīt failu

@@ -0,0 +1,357 @@
define(function () {

var $ = jQuery;

return Backbone.Model.extend({
defaults: function () {
return {
qualityGateStatus: 'ERROR'
};
},

fetch: function () {
return $.when(
this.fetchGate(),

this.fetchSize(),
this.fetchSizeTrend(),

this.fetchIssues(),
this.fetchIssues1(),
this.fetchIssues2(),
this.fetchIssues3(),
this.fetchIssuesTrend(),

this.fetchDebt(),
this.fetchDebtTrend(),

this.fetchCoverage(),
this.fetchCoverageTrend(),

this.fetchDuplications(),
this.fetchDuplicationsTrend()
);
},

fetchGate: function () {
var that = this,
url = baseUrl + '/api/resources/index',
options = {
resource: this.get('componentKey'),
metrics: 'quality_gate_details'
};
return $.get(url, options).done(function (r) {
var gateData = JSON.parse(r[0].msr[0].data),
gateConditions = gateData.conditions,
urlMetrics = baseUrl + '/api/metrics';
$.get(urlMetrics).done(function (r) {
var gateConditionsWithMetric = gateConditions.map(function (c) {
var metric = _.findWhere(r, { key: c.metric }),
type = metric != null ? metric.val_type : null,
periodDate = that.get('period' + c.period + 'Date'),
periodName = that.get('period' + c.period + 'Name');
return _.extend(c, {
type: type,
periodName: periodName,
periodDate: periodDate
});
});
that.set({
gateStatus: gateData.level,
gateConditions: gateConditionsWithMetric
});
});
});
},

fetchSize: function () {
var that = this,
url = baseUrl + '/api/resources/index',
options = {
resource: this.get('componentKey'),
metrics: 'ncloc,ncloc_language_distribution,function_complexity,file_complexity',
includetrends: true
};
return $.get(url, options).done(function (r) {
var msr = r[0].msr,
nclocMeasure = _.findWhere(msr, { key: 'ncloc' }),
nclocLangMeasure = _.findWhere(msr, { key: 'ncloc_language_distribution' }),
nclocLangParsed = nclocLangMeasure.data.split(';').map(function (token) {
var tokens = token.split('=');
return { key: tokens[0], value: +tokens[1] };
}),
nclocLangSorted = _.sortBy(nclocLangParsed, function (item) {
return -item.value;
}),
nclocLang = _.first(nclocLangSorted, 2),
functionComplexityMeasure = _.findWhere(msr, { key: 'function_complexity' }),
fileComplexityMeasure = _.findWhere(msr, { key: 'file_complexity' });
that.set({
ncloc: nclocMeasure.val,
ncloc1: nclocMeasure.var1,
ncloc2: nclocMeasure.var2,
ncloc3: nclocMeasure.var3,
nclocLang: nclocLang,

functionComplexity: functionComplexityMeasure.val,
functionComplexity1: functionComplexityMeasure.var1,
functionComplexity2: functionComplexityMeasure.var2,
functionComplexity3: functionComplexityMeasure.var3,

fileComplexity: fileComplexityMeasure.val,
fileComplexity1: fileComplexityMeasure.var1,
fileComplexity2: fileComplexityMeasure.var2,
fileComplexity3: fileComplexityMeasure.var3
});
});
},

fetchSizeTrend: function () {
var that = this,
url = baseUrl + '/api/timemachine/index',
options = {
resource: this.get('componentKey'),
metrics: 'ncloc'
};
return $.get(url, options).done(function (r) {
var trend = r[0].cells.map(function (cell) {
return { val: cell.d, count: cell.v[0] };
});
that.set({ sizeTrend: trend });
});
},

fetchIssues: function () {
var that = this,
url = baseUrl + '/api/issues/search',
options = {
ps: 1,
resolved: 'false',
componentUuids: this.get('componentUuid'),
facets: 'severities,statuses,tags'
};
return $.get(url, options).done(function (r) {
var severityFacet = _.findWhere(r.facets, { property: 'severities' }),
statusFacet = _.findWhere(r.facets, { property: 'statuses' }),
tagFacet = _.findWhere(r.facets, { property: 'tags' }),
tags = _.first(tagFacet.values, 10),
minTagCount = _.min(tags, function (t) {
return t.count;
}).count,
maxTagCount = _.max(tags, function (t) {
return t.count;
}).count,
tagScale = d3.scale.linear().domain([minTagCount, maxTagCount]).range([10, 24]),
sizedTags = tags.map(function (tag) {
return _.extend(tag, { size: tagScale(tag.count) });
});
that.set({
issues: r.total,
blockerIssues: _.findWhere(severityFacet.values, { val: 'BLOCKER' }).count,
criticalIssues: _.findWhere(severityFacet.values, { val: 'CRITICAL' }).count,
majorIssues: _.findWhere(severityFacet.values, { val: 'MAJOR' }).count,
openIssues: _.findWhere(statusFacet.values, { val: 'OPEN' }).count +
_.findWhere(statusFacet.values, { val: 'REOPENED' }).count,
issuesTags: sizedTags
});
});
},

fetchIssues1: function () {
var that = this,
url = baseUrl + '/api/issues/search',
options = {
ps: 1,
resolved: 'false',
createdAfter: this.get('period1Date'),
componentUuids: this.get('componentUuid'),
facets: 'severities,statuses'
};
return $.get(url, options).done(function (r) {
var severityFacet = _.findWhere(r.facets, { property: 'severities' }),
statusFacet = _.findWhere(r.facets, { property: 'statuses' });
that.set({
issues1: r.total,
blockerIssues1: _.findWhere(severityFacet.values, { val: 'BLOCKER' }).count,
criticalIssues1: _.findWhere(severityFacet.values, { val: 'CRITICAL' }).count,
majorIssues1: _.findWhere(severityFacet.values, { val: 'MAJOR' }).count,
openIssues1: _.findWhere(statusFacet.values, { val: 'OPEN' }).count +
_.findWhere(statusFacet.values, { val: 'REOPENED' }).count
});
});
},

fetchIssues2: function () {
var that = this,
url = baseUrl + '/api/issues/search',
options = {
ps: 1,
resolved: 'false',
createdAfter: this.get('period2Date'),
componentUuids: this.get('componentUuid'),
facets: 'severities,statuses'
};
return $.get(url, options).done(function (r) {
var severityFacet = _.findWhere(r.facets, { property: 'severities' }),
statusFacet = _.findWhere(r.facets, { property: 'statuses' });
that.set({
issues2: r.total,
blockerIssues2: _.findWhere(severityFacet.values, { val: 'BLOCKER' }).count,
criticalIssues2: _.findWhere(severityFacet.values, { val: 'CRITICAL' }).count,
majorIssues2: _.findWhere(severityFacet.values, { val: 'MAJOR' }).count,
openIssues2: _.findWhere(statusFacet.values, { val: 'OPEN' }).count +
_.findWhere(statusFacet.values, { val: 'REOPENED' }).count
});
});
},

fetchIssues3: function () {
var that = this,
url = baseUrl + '/api/issues/search',
options = {
ps: 1,
resolved: 'false',
createdAfter: this.get('period3Date'),
componentUuids: this.get('componentUuid'),
facets: 'severities,statuses'
};
return $.get(url, options).done(function (r) {
var severityFacet = _.findWhere(r.facets, { property: 'severities' }),
statusFacet = _.findWhere(r.facets, { property: 'statuses' });
that.set({
issues3: r.total,
blockerIssues3: _.findWhere(severityFacet.values, { val: 'BLOCKER' }).count,
criticalIssues3: _.findWhere(severityFacet.values, { val: 'CRITICAL' }).count,
majorIssues3: _.findWhere(severityFacet.values, { val: 'MAJOR' }).count,
openIssues3: _.findWhere(statusFacet.values, { val: 'OPEN' }).count +
_.findWhere(statusFacet.values, { val: 'REOPENED' }).count
});
});
},

fetchIssuesTrend: function () {
var that = this,
url = baseUrl + '/api/timemachine/index',
options = {
resource: this.get('componentKey'),
metrics: 'violations'
};
return $.get(url, options).done(function (r) {
var trend = r[0].cells.map(function (cell) {
return { val: cell.d, count: cell.v[0] };
});
that.set({ issuesTrend: trend });
});
},

fetchDebt: function () {
var that = this,
url = baseUrl + '/api/resources/index',
options = {
resource: this.get('componentKey'),
metrics: 'sqale_index',
includetrends: true
};
return $.get(url, options).done(function (r) {
var msr = r[0].msr,
debtMeasure = _.findWhere(msr, { key: 'sqale_index' });
that.set({
debt: debtMeasure.val,
debt1: debtMeasure.var1,
debt2: debtMeasure.var2,
debt3: debtMeasure.var3
});
});
},

fetchDebtTrend: function () {
var that = this,
url = baseUrl + '/api/timemachine/index',
options = {
resource: this.get('componentKey'),
metrics: 'sqale_index'
};
return $.get(url, options).done(function (r) {
var trend = r[0].cells.map(function (cell) {
return { val: cell.d, count: cell.v[0] };
});
that.set({ debtTrend: trend });
});
},

fetchCoverage: function () {
var that = this,
url = baseUrl + '/api/resources/index',
options = {
resource: this.get('componentKey'),
metrics: 'overall_coverage,new_overall_coverage',
includetrends: true
};
return $.get(url, options).done(function (r) {
var msr = r[0].msr,
coverageMeasure = _.findWhere(msr, { key: 'overall_coverage' }),
newCoverageMeasure = _.findWhere(msr, { key: 'new_overall_coverage' });
that.set({
coverage: coverageMeasure.val,
coverage1: coverageMeasure.var1,
coverage2: coverageMeasure.var2,
coverage3: coverageMeasure.var3,
newCoverage1: newCoverageMeasure.var1,
newCoverage2: newCoverageMeasure.var2,
newCoverage3: newCoverageMeasure.var3
});
});
},

fetchCoverageTrend: function () {
var that = this,
url = baseUrl + '/api/timemachine/index',
options = {
resource: this.get('componentKey'),
metrics: 'coverage'
};
return $.get(url, options).done(function (r) {
var trend = r[0].cells.map(function (cell) {
return { val: cell.d, count: cell.v[0] };
});
that.set({ coverageTrend: trend });
});
},

fetchDuplications: function () {
var that = this,
url = baseUrl + '/api/resources/index',
options = {
resource: this.get('componentKey'),
metrics: 'duplicated_lines_density',
includetrends: true
};
return $.get(url, options).done(function (r) {
var msr = r[0].msr,
duplicationsMeasure = _.findWhere(msr, { key: 'duplicated_lines_density' });
that.set({
duplications: duplicationsMeasure.val,
duplications1: duplicationsMeasure.var1,
duplications2: duplicationsMeasure.var2,
duplications3: duplicationsMeasure.var3
});
});
},

fetchDuplicationsTrend: function () {
var that = this,
url = baseUrl + '/api/timemachine/index',
options = {
resource: this.get('componentKey'),
metrics: 'duplicated_lines_density'
};
return $.get(url, options).done(function (r) {
var trend = r[0].cells.map(function (cell) {
return { val: cell.d, count: cell.v[0] };
});
that.set({ duplicationsTrend: trend });
});
}
});

});

+ 39
- 0
server/sonar-web/src/main/js/overview/views/coverage-view.js Parādīt failu

@@ -0,0 +1,39 @@
/*
* 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/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-coverage'],

modelEvents: {
'change': 'render'
},

onRender: function () {
this.$('.js-pie-chart').pieChart();
if (this.model.has('coverageTrend')) {
this.$('#overview-coverage-trend').sparkline(this.model.get('coverageTrend'));
}
}
});

});

+ 43
- 0
server/sonar-web/src/main/js/overview/views/debt-view.js Parādīt failu

@@ -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([
'templates/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-debt'],

modelEvents: {
'change': 'render'
},

onRender: function () {
if (this.model.has('debtTrend')) {
this.$('#overview-debt-trend').sparkline(this.model.get('debtTrend'));
}
this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
},

onClose: function () {
this.$('[data-toggle="tooltip"]').tooltip('destroy');
}
});

});

+ 39
- 0
server/sonar-web/src/main/js/overview/views/duplications-view.js Parādīt failu

@@ -0,0 +1,39 @@
/*
* 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/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-duplications'],

modelEvents: {
'change': 'render'
},

onRender: function () {
this.$('.js-pie-chart').pieChart();
if (this.model.has('duplicationsTrend')) {
this.$('#overview-duplications-trend').sparkline(this.model.get('duplicationsTrend'));
}
}
});

});

+ 43
- 0
server/sonar-web/src/main/js/overview/views/gate-view.js Parādīt failu

@@ -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([
'templates/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-gate'],

modelEvents: {
'change': 'render'
},

onRender: function () {
if (this.model.has('gateStatus')) {
this.$el.closest('.overview-card').addClass('overview-gate-' + this.model.get('gateStatus').toLowerCase());
}
this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
},

onClose: function () {
this.$('[data-toggle="tooltip"]').tooltip('destroy');
}
});

});

+ 43
- 0
server/sonar-web/src/main/js/overview/views/issues-view.js Parādīt failu

@@ -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([
'templates/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-issues'],

modelEvents: {
'change': 'render'
},

onRender: function () {
if (this.model.has('issuesTrend')) {
this.$('#overview-issues-trend').sparkline(this.model.get('issuesTrend'));
}
this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
},

onClose: function () {
this.$('[data-toggle="tooltip"]').tooltip('destroy');
}
});

});

+ 38
- 0
server/sonar-web/src/main/js/overview/views/size-view.js Parādīt failu

@@ -0,0 +1,38 @@
/*
* 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/overview'
], function () {

return Marionette.Layout.extend({
template: Templates['overview-size'],

modelEvents: {
'change': 'render'
},

onRender: function () {
if (this.model.has('sizeTrend')) {
this.$('#overview-size-trend').sparkline(this.model.get('sizeTrend'));
}
}
});

});

+ 13
- 1
server/sonar-web/src/main/less/components/badges.less Parādīt failu

@@ -19,6 +19,7 @@
*/
@import (reference) "../variables";
@import (reference) "../mixins";
@import (reference) "../init/links";

.badge {
display: inline-block;
@@ -26,7 +27,7 @@
padding: 2px 7px;
font-size: 11px;
font-weight: 300;
color: #fff;
color: @white;
line-height: 12px;
vertical-align: baseline;
white-space: nowrap;
@@ -35,6 +36,10 @@

&:empty { display: none; }

&:hover, &:focus, &:active { color: @white; }

a& { .link-no-underline; }

.list-group-item > &,
.list-group-item-heading > & {
float: right;
@@ -45,3 +50,10 @@
margin-right: 5px;
}
}

.badge-muted {
background-color: transparent;
color: @secondFontColor;

&:hover, &:focus, &:active { color: @blue; }
}

+ 4
- 0
server/sonar-web/src/main/less/components/page.less Parādīt failu

@@ -63,6 +63,10 @@

.page-actions {
float: right;

.badge {
margin: 3px 0;
}
}

.page-description {

+ 5
- 0
server/sonar-web/src/main/less/components/panels.less Parādīt failu

@@ -23,3 +23,8 @@
.panel {
padding: 10px;
}

.panel-info {
border: 1px solid @blue;
background-color: @lightBlue;
}

+ 1
- 0
server/sonar-web/src/main/less/init/lists.less Parādīt failu

@@ -48,6 +48,7 @@ ol, ul {

.list-inline > li {
display: inline-block;
vertical-align: top;
padding-right: 5px;
padding-left: 5px;
}

+ 4
- 0
server/sonar-web/src/main/less/init/misc.less Parādīt failu

@@ -35,6 +35,7 @@
.note {
color: @secondFontColor;
font-size: @smallFontSize;
font-weight: 300;
}

.spacer-left { margin-left: 8px; }
@@ -55,8 +56,11 @@ td.spacer-top { padding-top: 8px; }

.width-100 { width: 100%; }
.width-80 { width: 80%; }
.width-60 { width: 60%; }
.width-55 { width: 55%; }
.width-40 { width: 40%; }
.width-20 { width: 20%; }
.width-15 { width: 15%; }

.justify {
margin-bottom: -1em;

+ 1
- 0
server/sonar-web/src/main/less/pages.less Parādīt failu

@@ -25,3 +25,4 @@
@import "pages/issues";
@import "pages/libraries";
@import "pages/quality-gates";
@import "pages/overview";

+ 90
- 0
server/sonar-web/src/main/less/pages/overview.less Parādīt failu

@@ -0,0 +1,90 @@
/*
* 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.
*/
@import (reference) "../variables";
@import (reference) "../mixins";
@import (reference) "../init/links";

.overview {
padding: 10px;
.box-sizing(border-box);
overflow: hidden;
}

.overview-card {
padding: 10px;
background: @white;
border: 1px solid @barBorderColor;
}

.overview-card + .overview-card {
margin-top: 10px;
}

.overview-gate-ok { border: 2px solid @green; }

.overview-gate-warn { border: 2px solid @orange; }

.overview-gate-error { border: 2px solid @red; }

.overview-card-header {
padding-bottom: 5px;
border-bottom: 1px solid @barBorderColor;
}

.overview-status {
margin: 0;
padding: 0 6px;
color: #fff !important;
font-size: 24px;
font-weight: 300;

a& {
.link-no-underline;

&:hover, &:focus, &:active {
opacity: 0.8;
}
}
}

.overview-status-OK { background-color: @green; }
.overview-status-WARN { background-color: @orange; }
.overview-status-ERROR { background-color: @red; }

.overview-main-measure {
display: inline-block;
vertical-align: middle;
font-size: 36px;
font-weight: 300;
}

.overview-trend {
display: inline-block;
vertical-align: middle;
margin-left: 15px;
}

.overview-measure {
font-size: 16px;
}

.overview-measure ~ .note:last-child {
padding-top: 3px;
}

+ 18
- 12
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb Parādīt failu

@@ -17,6 +17,7 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#

class DashboardController < ApplicationController

SECTION=Navigation::SECTION_RESOURCE
@@ -25,20 +26,25 @@ class DashboardController < ApplicationController

def index
load_resource()
if !@resource || @resource.display_dashboard?
if @resource && !params[:did] && !params[:name]
url = url_for :controller => 'overview', :action => 'index'
url = url + '?id=' + @resource.key.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
return redirect_to url
end
if !@resource || @resource.display_dashboard?
redirect_if_bad_component()
load_dashboard()
load_authorized_widget_definitions()
else
if !@resource || !@snapshot
redirect_if_bad_component()
load_dashboard()
load_authorized_widget_definitions()
else
if !@resource || !@snapshot
redirect_if_bad_component()
else
# display the layout of the parent without the sidebar, usually the directory, but display the file viewers
@hide_sidebar = true
@file = @resource
@project = @snapshot.parent.project
@metric=params[:metric]
render :action => 'no_dashboard'
# display the layout of the parent without the sidebar, usually the directory, but display the file viewers
@hide_sidebar = true
@file = @resource
@project = @snapshot.parent.project
@metric=params[:metric]
render :action => 'no_dashboard'
end
end
end

+ 31
- 0
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/overview_controller.rb Parādīt failu

@@ -0,0 +1,31 @@
#
# 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.
#

class OverviewController < ApplicationController
before_filter :init_resource_for_user_role
helper DashboardHelper

SECTION=Navigation::SECTION_RESOURCE

def index

end

end

+ 6
- 0
server/sonar-web/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb Parādīt failu

@@ -78,6 +78,12 @@ module DashboardHelper
end
end

def short_period_label(snapshot, index)
if snapshot.project_snapshot
Api::Utils.java_facade.getPeriodLabel(index)
end
end

def violation_period_select_options(snapshot, index)
return nil if snapshot.nil? || snapshot.project_snapshot.nil?
mode=snapshot.project_snapshot.send "period#{index}_mode"

+ 16
- 0
server/sonar-web/src/main/webapp/WEB-INF/app/views/overview/index.html.erb Parādīt failu

@@ -0,0 +1,16 @@
<div class="overview"></div>

<script>
window.overviewConf = {
componentKey: '<%= @resource.key -%>',
componentUuid: '<%= @resource.uuid -%>',

period1Name: '<%= short_period_label(@snapshot, 1) -%>',
period1Date: '<%= @snapshot.period_datetime(1).strftime('%FT%T%z') -%>',
period2Name: '<%= short_period_label(@snapshot, 2) -%>',
period2Date: '<%= @snapshot.period_datetime(2).strftime('%FT%T%z') -%>',
period3Name: '<%= short_period_label(@snapshot, 3) -%>',
period3Date: '<%= @snapshot.period_datetime(3).strftime('%FT%T%z') -%>'
};
require(['overview/app']);
</script>

+ 14
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Parādīt failu

@@ -2984,3 +2984,17 @@ workspace.minimize=Minimize
workspace.full_window=Expand to full window
workspace.normal_size=Collapse to normal size
workspace.close=Remove from the list of pinned files




#------------------------------------------------------------------------------
#
# OVERVIEW
#
#------------------------------------------------------------------------------
overview.lines_of_code=Lines of Code
overview.issues=Issues
overview.debt=Debt
overview.coverage=Coverage
overview.duplications=Duplications

Notiek ielāde…
Atcelt
Saglabāt