From f06fbc859117dd39cbc28e5827067dc60858a62a Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 22 Apr 2013 17:41:20 +0200 Subject: [PATCH] SONAR-3755 Add Issues Widget : Rule Compliance, Active Issues, False Positive Issues and My Issues --- .../org/sonar/plugins/core/CorePlugin.java | 7 + .../plugins/core/widgets/CoreWidget.java | 3 +- .../plugins/core/widgets/RulesWidget2.java | 30 ++++ .../widgets/issues/ActiveIssuesWidget.java | 36 ++++ .../issues/FalsePositiveIssuesWidget.java | 36 ++++ .../core/widgets/issues/MyIssuesWidget.java | 36 ++++ .../resources/org/sonar/l10n/core.properties | 51 ++++++ .../widgets/issues/active_issues.html.erb | 15 ++ .../issues/false_positive_issues.html.erb | 16 ++ .../core/widgets/issues/my_issues.html.erb | 16 ++ .../plugins/core/widgets/rules2.html.erb | 169 ++++++++++++++++++ .../app/controllers/issues_controller.rb | 14 ++ .../app/controllers/resource_controller.rb | 1 - .../WEB-INF/app/helpers/application_helper.rb | 4 + .../WEB-INF/app/helpers/resource_helper.rb | 4 - .../main/webapp/WEB-INF/app/models/user.rb | 2 +- .../WEB-INF/app/views/issues/_issue.html.erb | 90 ++++++++++ .../WEB-INF/app/views/issues/_view.html.erb | 7 + .../widgets/issues/_issues_list.html.erb | 107 +++++++++++ .../issues/_issues_list_widget.html.erb | 19 ++ .../app/views/resource/_issue.html.erb | 2 +- 21 files changed, 657 insertions(+), 8 deletions(-) create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/RulesWidget2.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ActiveIssuesWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/FalsePositiveIssuesWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/MyIssuesWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/active_issues.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/false_positive_issues.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/my_issues.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/rules2.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list_widget.html.erb diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index c6cb88dc7fe..a405fed81eb 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -47,6 +47,9 @@ import org.sonar.plugins.core.timemachine.*; import org.sonar.plugins.core.web.Lcom4Viewer; import org.sonar.plugins.core.web.TestsViewer; import org.sonar.plugins.core.widgets.*; +import org.sonar.plugins.core.widgets.issues.ActiveIssuesWidget; +import org.sonar.plugins.core.widgets.issues.FalsePositiveIssuesWidget; +import org.sonar.plugins.core.widgets.issues.MyIssuesWidget; import org.sonar.plugins.core.widgets.reviews.*; import java.util.List; @@ -359,6 +362,7 @@ public final class CorePlugin extends SonarPlugin { DescriptionWidget.class, ComplexityWidget.class, RulesWidget.class, + RulesWidget2.class, SizeWidget.class, EventsWidget.class, CustomMeasuresWidget.class, @@ -368,8 +372,11 @@ public final class CorePlugin extends SonarPlugin { HotspotMostViolatedResourcesWidget.class, HotspotMostViolatedRulesWidget.class, MyReviewsWidget.class, + MyIssuesWidget.class, ProjectReviewsWidget.class, + ActiveIssuesWidget.class, FalsePositiveReviewsWidget.class, + FalsePositiveIssuesWidget.class, ReviewsPerDeveloperWidget.class, PlannedReviewsWidget.class, UnplannedReviewsWidget.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CoreWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CoreWidget.java index a1d60156f80..721917c36ff 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CoreWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CoreWidget.java @@ -41,6 +41,7 @@ public abstract class CoreWidget extends AbstractRubyTemplate implements RubyRai @Override protected String getTemplatePath() { - return templatePath; + return "/Users/julienlancelot/Dev/Sources/sonar/plugins/sonar-core-plugin/src/main/resources" + templatePath; + //return templatePath; } } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/RulesWidget2.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/RulesWidget2.java new file mode 100644 index 00000000000..559204c1efe --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/RulesWidget2.java @@ -0,0 +1,30 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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. + */ +package org.sonar.plugins.core.widgets; + +import org.sonar.api.web.WidgetCategory; + +@WidgetCategory({"Rules"}) +public class RulesWidget2 extends CoreWidget { + + public RulesWidget2() { + super("rules2", "Rules Compliance 2", "/org/sonar/plugins/core/widgets/rules2.html.erb"); + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ActiveIssuesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ActiveIssuesWidget.java new file mode 100644 index 00000000000..aa497b52cf1 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ActiveIssuesWidget.java @@ -0,0 +1,36 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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. + */ +package org.sonar.plugins.core.widgets.issues; + +import org.sonar.api.web.WidgetCategory; +import org.sonar.api.web.WidgetProperties; +import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertyType; +import org.sonar.plugins.core.widgets.CoreWidget; + +@WidgetCategory({"Issues"}) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") +}) +public class ActiveIssuesWidget extends CoreWidget { + public ActiveIssuesWidget() { + super("project_reviews2", "Active issues", "/org/sonar/plugins/core/widgets/issues/active_issues.html.erb"); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/FalsePositiveIssuesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/FalsePositiveIssuesWidget.java new file mode 100644 index 00000000000..400197ef66e --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/FalsePositiveIssuesWidget.java @@ -0,0 +1,36 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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. + */ +package org.sonar.plugins.core.widgets.issues; + +import org.sonar.api.web.WidgetCategory; +import org.sonar.api.web.WidgetProperties; +import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertyType; +import org.sonar.plugins.core.widgets.CoreWidget; + +@WidgetCategory({"Issues"}) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") +}) +public class FalsePositiveIssuesWidget extends CoreWidget { + public FalsePositiveIssuesWidget() { + super("false_positive_reviews2", "False positive open issues", "/org/sonar/plugins/core/widgets/issues/false_positive_issues.html.erb"); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/MyIssuesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/MyIssuesWidget.java new file mode 100644 index 00000000000..c3205aa44d3 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/MyIssuesWidget.java @@ -0,0 +1,36 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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. + */ +package org.sonar.plugins.core.widgets.issues; + +import org.sonar.api.web.WidgetCategory; +import org.sonar.api.web.WidgetProperties; +import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertyType; +import org.sonar.plugins.core.widgets.CoreWidget; + +@WidgetCategory({"Issues"}) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") +}) +public class MyIssuesWidget extends CoreWidget { + public MyIssuesWidget() { + super("my_reviews2", "My active issues", "/org/sonar/plugins/core/widgets/issues/my_issues.html.erb"); + } +} diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties index 4bb470dd910..0186a6732cf 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -517,6 +517,21 @@ reviews.filtered_by.unplanned=Unplanned reviews.filtered_by.from=From date reviews.filtered_by.to=To date +#------------------------------------------------------------------------------ +# +# ISSUES +# +#------------------------------------------------------------------------------ + +issues.issue_number=Issue #{0} +issues.hide_this_message=Hide this message +issues.status.REOPENED=Reopened +issues.status.RESOLVED=Resolved +issues.status.OPEN=Open +issues.status.CLOSED=Closed +issues.resolution.FALSE-POSITIVE=False-positive +issues.resolution.FIXED=Fixed + #------------------------------------------------------------------------------ # @@ -890,6 +905,7 @@ widget.events.show_more=Show more widget.rules.name=Rules Compliance widget.rules.description=Reports violations and compliance index on coding standards. widget.rules.violations=Violations +widget.rules.issues=Issues widget.rules.rules_compliance=Rules compliance widget.rules.added=Added: widget.rules.removed=Removed: @@ -978,16 +994,33 @@ widget.my_reviews.no_review=No review. widget.my_reviews.property.numberOfLines.name=Number of lines widget.my_reviews.property.numberOfLines.desc=Maximum number of reviews displayed at the same time. +widget.my_reviews2.name=My issues +widget.my_reviews2.description=Shows open/reopened issues assigned to the current user. +widget.my_reviews2.no_review=No issue. +widget.my_reviews2.property.numberOfLines.name=Number of lines +widget.my_reviews2.property.numberOfLines.desc=Maximum number of issues displayed at the same time. + widget.project_reviews.name=Active reviews widget.project_reviews.description=Shows all the open/reopened reviews. widget.project_reviews.property.numberOfLines.name=Number of lines widget.project_reviews.property.numberOfLines.desc=Maximum number of reviews displayed at the same time. +widget.project_reviews2.name=Active issues +widget.project_reviews2.description=Shows all the open/reopened issues. +widget.project_reviews2.property.numberOfLines.name=Number of lines +widget.project_reviews2.property.numberOfLines.desc=Maximum number of issues displayed at the same time. + widget.false_positive_reviews.name=False positives widget.false_positive_reviews.description=Shows all the false positives found on the project. widget.false_positive_reviews.property.numberOfLines.name=Number of lines widget.false_positive_reviews.property.numberOfLines.desc=Maximum number of reviews displayed at the same time. +widget.false_positive_reviews2.name=False positives 2 +widget.false_positive_reviews2.description=Shows all the false positives found on the project. +widget.false_positive_reviews2.property.numberOfLines.name=Number of lines +widget.false_positive_reviews2.property.numberOfLines.desc=Maximum number of reviews displayed at the same time. + + widget.reviews_per_developer.name=Active reviews per developer widget.reviews_per_developer.description=Shows the number of open/reopened reviews per developer. widget.reviews_per_developer.not_assigned=Not assigned @@ -2196,3 +2229,21 @@ metric.unassigned_reviews.abbreviation=Unassigned rws metric.unplanned_reviews.name=Unplanned reviews metric.unplanned_reviews.description=Active unplanned reviews metric.unplanned_reviews.abbreviation=Unplanned rws + + +#-------------------------------------------------------------------------------------------------------------------- +# +# ISSUES METRICS +# +#-------------------------------------------------------------------------------------------------------------------- +metric.false_positive_issues.name=False-positive issues +metric.false_positive_issues.description=Active false-positive issues +metric.false_positive_issues.abbreviation=False-positive issues + +metric.issues.name=Active issues +metric.issues.description=Active open and reopened issues +metric.issues.abbreviation=Active issues + +metric.unassigned_issues.name=Unassigned issues +metric.unassigned_issues.description=Active unassigned issues +metric.unassigned_issues.abbreviation=Unassigned issues diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/active_issues.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/active_issues.html.erb new file mode 100644 index 00000000000..66171043988 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/active_issues.html.erb @@ -0,0 +1,15 @@ +<% + if has_role?(:user, @project) + + search_options = {} + search_options['componentRoots'] = @project.key + search_options['statuses'] = "OPEN,REOPENED" + + title = message('widget.project_reviews2.name') +%> + + <%= render :partial => 'project/widgets/issues/issues_list_widget', + :locals => {:search_options => search_options, :title => title, + :widget_id => widget.id.to_s, :widget_properties => widget_properties} %> + +<% end %> \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/false_positive_issues.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/false_positive_issues.html.erb new file mode 100644 index 00000000000..3b5cf9ac666 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/false_positive_issues.html.erb @@ -0,0 +1,16 @@ +<% + if has_role?(:user, @project) + + search_options = {} + search_options['components'] = @project.key + search_options['statuses'] = "RESOLVED" + search_options['resolutions'] = "FALSE_POSITIVE" + + title = message('widget.false_positive_reviews2.name') +%> + + <%= render :partial => 'project/widgets/issues/issues_list_widget', + :locals => {:search_options => search_options, :title => title, + :widget_id => widget.id.to_s, :widget_properties => widget_properties} %> + +<% end %> \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/my_issues.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/my_issues.html.erb new file mode 100644 index 00000000000..e23833e3b36 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/my_issues.html.erb @@ -0,0 +1,16 @@ +<% + if current_user && has_role?(:user, @project) + + search_options = {} + search_options['componentRoots'] = @project.key + search_options['statuses'] = "OPEN,REOPENED" + search_options['assignees'] = current_user.login + + title = message('widget.my_reviews2.name') +%> + + <%= render :partial => 'project/widgets/issues/issues_list_widget', + :locals => {:search_options => search_options, :title => title, + :widget_id => widget.id.to_s, :widget_properties => widget_properties} %> + +<% end %> \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/rules2.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/rules2.html.erb new file mode 100644 index 00000000000..c0e24340584 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/rules2.html.erb @@ -0,0 +1,169 @@ +<% + issues = @snapshot.measure('issues') + density = @snapshot.measure('issues_density') + blocker_issues = @snapshot.measure('blocker_issues') + critical_issues = @snapshot.measure('critical_issues') + major_issues = @snapshot.measure('major_issues') + minor_issues = @snapshot.measure('minor_issues') + info_issues = @snapshot.measure('info_issues') + new_issues = @snapshot.measure('new_issues') +%> + + + + + + +
+
+

<%= message('widget.rules.issues') -%>

+
+ + <%= format_measure(issues, :url => url_for(:controller => 'drilldown', :action => 'issues', :id => @project.key)) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(issues) : trend_icon(issues) -%> + <% + if @dashboard_configuration.selected_period? + issues_variation = variation_value(issues) + new_issues_variation = variation_value(new_issues) + estimated_cleared_issues = (new_issues_variation - issues_variation).to_i if issues_variation && new_issues_variation + %> + <% if new_issues_variation && new_issues_variation > 0 %> +
+ + <%= message('widget.rules.added') -%>  + <%= format_variation(new_issues, :style => 'none', :default => '-') -%> + + <% end %> + <% if estimated_cleared_issues && estimated_cleared_issues > 0 %> +
+ + <%= message('widget.rules.removed') -%>  + <%= estimated_cleared_issues -%> + + <% end %> + <% end %> +
+ <% if density %> +

<%= message('widget.rules.rules_compliance') -%>

+
+ + <%= format_measure(density, :url => url_for_drilldown('weighted_issues', {:highlight => 'weighted_issues'})) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(density) : trend_icon(density) -%> +
+ <% end %> +
+
+ <% + values = [blocker_issues, critical_issues, major_issues, minor_issues, info_issues] + if dashboard_configuration.selected_period? + values = values.map{|m| m ? (m.variation(dashboard_configuration.period_index)||0) : 0} + else + values = values.map{|m| m ? (m.value||0) : 0} + end + max = values.map{|val| val.abs}.max + %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
<%= image_tag 'priority/BLOCKER.png'%>  <%= link_to message('blocker'), {:controller => 'drilldown', :action => 'issues', :id => @project.key, :severity => 'BLOCKER'} %> + <%= format_measure(blocker_issues) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(blocker_issues) : trend_icon(blocker_issues, :empty => true) -%> + + <% if max > 0 %> + <% if dashboard_configuration.selected_period? %> + <%= barchart(:width => 35, :percent => (values[0]<0 ? (100 * values[0] / max).to_i : 0), :color => '#078C00') %> + <%= barchart(:width => 35, :percent => (values[0]>0 ? (100 * values[0] / max).to_i : 0), :color => '#cc0000') %> + <% else %> + <%= barchart(:width => 70, :percent => (100 * values[0] / max).to_i) %> + <% end %> + <% end %> +
<%= image_tag 'priority/CRITICAL.png' %>  <%= link_to message('critical'), {:controller => 'drilldown', :action => 'issues', :id => @project.key, :severity => 'CRITICAL'} %> + <%= format_measure(critical_issues) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(critical_issues) : trend_icon(critical_issues, :empty => true) -%> + + <% if max > 0 %> + <% if dashboard_configuration.selected_period? %> + <%= barchart(:width => 35, :percent => (values[1]<0 ? (100 * values[1] / max).to_i : 0), :color => '#078C00') %> + <%= barchart(:width => 35, :percent => (values[1]>0 ? (100 * values[1] / max).to_i : 0), :color => '#cc0000') %> + <% else %> + <%= barchart(:width => 70, :percent => (100 * values[1] / max).to_i) %> + <% end %> + <% end %> +
<%= image_tag 'priority/MAJOR.png' %>  <%= link_to message('major'), {:controller => 'drilldown', :action => 'issues', :id => @project.key, :severity => 'MAJOR'} %> + <%= format_measure(major_issues) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(major_issues) : trend_icon(major_issues, :empty => true) -%> + + <% if max > 0 %> + <% if dashboard_configuration.selected_period? %> + <%= barchart(:width => 35, :percent => (values[2]<0 ? (100 * values[2] / max).to_i : 0), :color => '#078C00') %> + <%= barchart(:width => 35, :percent => (values[2]>0 ? (100 * values[2] / max).to_i : 0), :color => '#cc0000') %> + <% else %> + <%= barchart(:width => 70, :percent => (100 * values[2] / max).to_i) %> + <% end %> + <% end %> +
<%= image_tag 'priority/MINOR.png' %>  <%= link_to message('minor'), {:controller => 'drilldown', :action => 'issues', :id => @project.key, :severity => 'MINOR'} %> + <%= format_measure(minor_issues) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(minor_issues) : trend_icon(minor_issues, :empty => true) -%> + + <% if max > 0 %> + <% if dashboard_configuration.selected_period? %> + <%= barchart(:width => 35, :percent => (values[3]<0 ? (100 * values[3] / max).to_i : 0), :color => '#078C00') %> + <%= barchart(:width => 35, :percent => (values[3]>0 ? (100 * values[3] / max).to_i : 0), :color => '#cc0000') %> + <% else %> + <%= barchart(:width => 70, :percent => (100 * values[3] / max).to_i) %> + <% end %> + <% end %> +
<%= image_tag 'priority/INFO.png' %>  <%= link_to message('info'), {:controller => 'drilldown', :action => 'issues', :id => @project.key, :severity => 'INFO'} %> + <%= format_measure(info_issues) -%> + + <%= dashboard_configuration.selected_period? ? format_variation(info_issues) : trend_icon(info_issues, :empty => true) -%> + + <% if max > 0 %> + <% if dashboard_configuration.selected_period? %> + <%= barchart(:width => 35, :percent => (values[4]<0 ? (100 * values[4] / max).to_i : 0), :color => '#078C00') %> + <%= barchart(:width => 35, :percent => (values[4]>0 ? (100 * values[4] / max).to_i : 0), :color => '#cc0000') %> + <% else %> + <%= barchart(:width => 70, :percent => (100 * values[4] / max).to_i) %> + <% end %> + <% end %> +
+
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb index 0d037e50813..3f7e4e42db5 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb @@ -20,10 +20,24 @@ class IssuesController < ApplicationController + SECTION=Navigation::SECTION_RESOURCE + def index @issues = find_issues({}) end + # Used for the permalink, e.g. http://localhost:9000/issues/view/1 + def view + issues = find_issues({'keys' => params[:id]}) + if issues.length == 1 + @issue = issues[0] + @resource = Project.by_key(@issue.component_key) + render 'issues/_view', :locals => {:issue => @issue} + else + render :text => "Cannot access this issue : not found." + end + end + protected def find_issues(map) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb index 0492f69db62..e8b6d713333 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb @@ -481,7 +481,6 @@ class ResourceController < ApplicationController end end - if @period date = @snapshot.period_datetime(@period) if date diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb index 8cce4f6448b..8505c4241d3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb @@ -839,4 +839,8 @@ module ApplicationHelper {:base => status, :hits => hits_status, :conditions => conditions_status} end + def to_date(java_date) + java_date ? Time.at(java_date.time/1000) : nil + end + end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/resource_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/resource_helper.rb index 24f11a7c023..54d176e0388 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/resource_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/resource_helper.rb @@ -43,8 +43,4 @@ module ResourceHelper '(' + (value1.to_i - value2.to_i).to_s + '/' + value1 + ')' end - def to_date(java_date) - java_date ? Time.at(java_date.time/1000) : nil - end - end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb index a4ca237391d..5d0735ae719 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb @@ -115,7 +115,7 @@ class User < ActiveRecord::Base end def self.find_active_by_login(login) - User.find(:first, :conditions => ["login=:login AND active=:active", {:login => login, :active => true}]) + User.first(:conditions => ["login=:login AND active=:active", {:login => login, :active => true}]) end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb new file mode 100644 index 00000000000..05d2bd36866 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb @@ -0,0 +1,90 @@ +
+
+

<%= message('issues.issue_number', :params => h(issue.key.to_s)) -%> - <%= h(issue.description) -%>

+
+ + <% if defined?(error_message) && error_message %> + + <% end %> + + + + + + + + + + + + + + + + + + + + + + + <% # TODO action plan %> + <% if issue.rule_key %> + <% rule = Rule.by_key_or_id(issue.rule_key.to_s) %> + + + + + <% end %> + + + + +
+ <%= message('status') -%>: + + <%= image_tag "status/#{issue.status}.png" -%> <%= message("issues.status.#{issue.status}") -%> + <% if issue.resolution.present? %> + (<%= message("issues.resolution.#{issue.resolution}") -%>) + <% end %> + + <%= message('severity') -%>: + + <%= image_tag "priority/#{issue.severity}.png" -%> <%= message("severity.#{issue.severity}") -%> +
+ <%= message('assignee') -%>: + + <% assignee = issue.assignee %> + <%= assignee ? h(assignee) : '-' -%> + + <%= message('author') -%>: + + <% author = issue.user_login %> + <%= author ? h(author) : '-' -%> +
+ <%= message('created') -%>: + + <%= l(to_date(issue.created_at)) -%> + + <%= message('updated') -%>: + + <%= l(to_date(issue.updated_at)) if issue.updated_at -%> +
+ <%= message('rule') -%>: + + <%= h(rule.name) -%> +
+ <%= message('file') -%>: + + <%= qualifier_icon(@resource) -%> + <% if @resource.root_project.id != @resource.id %> + <%= @resource.root_project.long_name -%> <%= image_tag 'sep12.png' -%> + <% end %> + <% if @resource.last_snapshot %> + <%= link_to_resource(@resource, @resource.long_name, {:tab => :violations, :rule => issue.resolution == "FALSE-POSITIVE" ? "false_positive_issues" : ""}) %> + <% else %> + <%= @resource.long_name -%> + <% end %> +
+ +
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb new file mode 100644 index 00000000000..a75722ceb02 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb @@ -0,0 +1,7 @@ +
+ <% + # hack in case 'error_message' is nil (this should disappear when refactoring the '_view' and '_issue' partials) + error_message = error_message + %> + <%= render :partial => 'issues/issue', :locals => {:issue => @issue, :workflow => @issue, :error_message => error_message} -%> +
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb new file mode 100644 index 00000000000..7ce774c2b8e --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb @@ -0,0 +1,107 @@ +<% + table_limit = params[:table_limit] unless table_limit + widget_id = params[:widget_id] unless widget_id + search_options = params unless search_options + + # TODO + search_options['sort'] = 'updated_at' + if @dashboard_configuration.selected_period? + from_date = @dashboard_configuration.from_datetime + # TODO + search_options['from'] = from_date + to_date = DateTime.now + # TODO + search_options['to'] = to_date + end + #puts "### search_options" + #search_options.each {|key, value| puts key.to_s + "->"+ value.to_s} + user = current_user ? current_user.id : nil + issues = Api.issues.find(search_options, user).issues + + # table pagination + page_size = table_limit.to_i + total_number = issues.size + if issues.size > page_size + page_id = (params[:page_id] ? params[:page_id].to_i : 1) + page_count = issues.size / page_size + page_count += 1 if (issues.size % page_size > 0) + from = (page_id-1) * page_size + to = (page_id*page_size)-1 + to = issues.size-1 if to >= issues.size + issues = issues[from..to] + end +%> + +<% if issues.nil? || issues.size ==0 %> + + <%= message('widget.my_reviews2.no_review') -%> + +<% else %> + + + + + + + + + + + + + + <% + issues.each do |issue| + # TODO display comment + #comment = review.comments.last + %> + + + + + + <% + end + %> + +
+
+ <%= total_number -%> <%= message('results').downcase -%> + <% + if page_count + page_count = 20 if page_count>20 + link_params = search_options + # TODO + link_params[:controller] = 'project_reviews' + # TODO + link_params[:action] = 'widget_reviews_list' + link_params[:snapshot_id] = @snapshot.id + link_params[:table_limit] = table_limit + link_params[:widget_id] = widget_id + link_params[:period] = params[:period] + %> + | + <%= link_to_remote(message('paging_previous'), + :update => "issues-widget-#{widget_id}", + :url => {:params => link_params.merge({:page_id => page_id-1})}) if page_id>1 %> + <%= message('paging_previous') unless page_id>1 %> + <% for index in 1..page_count %> + <%= index.to_s if index==page_id %> + <%= link_to_remote(index.to_s, + :update => "issues-widget-#{widget_id}", + :url => {:params => link_params.merge({:page_id => index})}) unless index==page_id %> + <% end %> + <%= link_to_remote(message('paging_next'), + :update => "issues-widget-#{widget_id}", + :url => {:params => link_params.merge({:page_id => page_id+1})}) if page_id + <%= message('paging_next') unless page_id + <% + end + %> +
+ <%= link_to h(issue.description), :controller => "issues", :action => "view", :id => issue.key -%> + + <%= distance_of_time_in_words_to_now(to_date(issue.updated_at || issue.created_at)) -%> +
+ +<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list_widget.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list_widget.html.erb new file mode 100644 index 00000000000..28105a8233c --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list_widget.html.erb @@ -0,0 +1,19 @@ +<% + table_limit = widget_properties["numberOfLines"] + + if @dashboard_configuration.selected_period? + # TODO + search_options['from'] = @dashboard_configuration.from_datetime + # TODO + search_options['to'] = DateTime.now + end +%> + +

<%= title -%>

+ +
+ <%= render :partial => 'project/widgets/issues/issues_list', + :locals => {:search_options => search_options, + :table_limit => table_limit, + :widget_id => widget_id} %> +
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb index 481664b6a15..0aaee8410bd 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb @@ -48,7 +48,7 @@ <% # TODO display assignee name instead of login #if violation.review && violation.review.assignee_id - if false + if issue.assignee %> <%= image_tag 'sep12.png' -%>   -- 2.39.5