From 3635c6c5f851ebbc6ca3e3a11f4f687570884d6e Mon Sep 17 00:00:00 2001 From: simonbrandhof Date: Wed, 2 Mar 2011 14:49:14 +0100 Subject: [PATCH] SONAR-2218 display headers of source, coverage and violations tabs --- .../org/sonar/server/ui/DefaultPages.java | 94 ++++++++++ .../java/org/sonar/server/ui/JRubyFacade.java | 5 +- .../main/java/org/sonar/server/ui/Views.java | 25 ++- .../app/controllers/browse_controller.rb | 79 ++++++-- .../views/browse/_header_coverage.html.erb | 40 +++++ .../app/views/browse/_header_source.html.erb | 139 +++++++++++++++ .../views/browse/_header_violations.html.erb | 38 ++++ .../app/views/browse/_rules_filter.html.erb | 47 +++++ .../WEB-INF/app/views/browse/_tabs.html.erb | 168 ++++++++++++++++++ .../app/views/browse/extension.html.erb | 16 ++ .../WEB-INF/app/views/browse/index.html.erb | 168 ++---------------- .../WEB-INF/app/views/browse/nothing.html.erb | 1 + .../java/org/sonar/server/ui/ViewsTest.java | 12 +- 13 files changed, 648 insertions(+), 184 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_coverage.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_source.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_violations.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/_rules_filter.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/_tabs.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/extension.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/browse/nothing.html.erb diff --git a/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java b/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java new file mode 100644 index 00000000000..e49af9fe149 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java @@ -0,0 +1,94 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.ui; + +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.resources.Resource; +import org.sonar.api.web.*; + +/** + * @since 2.7 + */ +public final class DefaultPages { + + private static final View[] PAGES = {new SourceTab(), new CoverageTab(), new ViolationsTab()}; + + public static View[] getPages() { + return PAGES; + } + + @ResourceScope(Resource.SCOPE_ENTITY) + @NavigationSection(NavigationSection.RESOURCE_TAB) + @DefaultTab + @UserRole(UserRole.CODEVIEWER) + private static final class SourceTab implements RubyRailsPage { + public String getTemplate() { + //not used, hardcoded in BrowseController + return "browse/index"; + } + + public String getId() { + return "source"; + } + + public String getTitle() { + return "Source"; + } + } + + + @ResourceQualifier(Resource.QUALIFIER_CLASS) + @NavigationSection(NavigationSection.RESOURCE_TAB) + @DefaultTab(metrics = {CoreMetrics.COVERAGE_KEY, CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.LINE_COVERAGE_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY, CoreMetrics.BRANCH_COVERAGE_KEY}) + @UserRole(UserRole.CODEVIEWER) + private static final class CoverageTab implements RubyRailsPage { + public String getTemplate() { + //not used, hardcoded in BrowseController + return "browse/index"; + } + + public String getId() { + return "coverage"; + } + + public String getTitle() { + return "Coverage"; + } + } + + @NavigationSection(NavigationSection.RESOURCE_TAB) + @DefaultTab(metrics = {CoreMetrics.VIOLATIONS_DENSITY_KEY, CoreMetrics.WEIGHTED_VIOLATIONS_KEY, CoreMetrics.VIOLATIONS_KEY, CoreMetrics.BLOCKER_VIOLATIONS_KEY, CoreMetrics.CRITICAL_VIOLATIONS_KEY, CoreMetrics.MAJOR_VIOLATIONS_KEY, CoreMetrics.MINOR_VIOLATIONS_KEY, CoreMetrics.INFO_VIOLATIONS_KEY}) + @ResourceQualifier({Resource.QUALIFIER_CLASS, Resource.QUALIFIER_FILE}) + @UserRole(UserRole.CODEVIEWER) + private static final class ViolationsTab implements RubyRailsPage { + public String getTemplate() { + //not used, hardcoded in BrowseController + return "browse/index"; + } + + public String getId() { + return "violations"; + } + + public String getTitle() { + return "Violations"; + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 06675cd00c8..d0544c03692 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -28,7 +28,6 @@ import org.sonar.api.ServerComponent; import org.sonar.api.profiles.ProfileExporter; import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.resources.Language; -import org.sonar.api.rules.DefaultRulesManager; import org.sonar.api.rules.RuleRepository; import org.sonar.api.utils.ValidationMessages; import org.sonar.api.web.*; @@ -132,6 +131,10 @@ public final class JRubyFacade implements ServerComponent { return getContainer().getComponent(Views.class).getPages(NavigationSection.RESOURCE_TAB, null, null, null); } + public List> getResourceTabs(String scope, String qualifier, String language) { + return getContainer().getComponent(Views.class).getPages(NavigationSection.RESOURCE_TAB, scope, qualifier, language); + } + public ViewProxy getPage(String id) { return getContainer().getComponent(Views.class).getPage(id); } diff --git a/sonar-server/src/main/java/org/sonar/server/ui/Views.java b/sonar-server/src/main/java/org/sonar/server/ui/Views.java index 94b789eda46..d1bf0e9c80d 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/Views.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/Views.java @@ -40,16 +40,23 @@ public class Views implements ServerComponent { } public Views(View[] views) { + for (View view : DefaultPages.getPages()) { + register(view); + } for (View view : views) { - ViewProxy proxy = new ViewProxy(view); - if (view instanceof Widget) { - widgets.add(proxy); - widgetsPerId.put(proxy.getId(), proxy); - - } else if (view instanceof Page) { - pagesPerId.put(proxy.getId(), proxy); - pages.add(proxy); - } + register(view); + } + } + + private void register(View view) { + ViewProxy proxy = new ViewProxy(view); + if (view instanceof Widget) { + widgets.add(proxy); + widgetsPerId.put(proxy.getId(), proxy); + + } else if (view instanceof Page) { + pagesPerId.put(proxy.getId(), proxy); + pages.add(proxy); } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/browse_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/browse_controller.rb index 5021fe3eefc..bacf6c73325 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/browse_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/browse_controller.rb @@ -27,7 +27,23 @@ class BrowseController < ApplicationController if (@resource && has_role?(:user, @resource)) @snapshot=@resource.last_snapshot - render_resource() + + params[:layout]='false' + load_extensions() + + if @extension + if (@extension.getId()=='violations') + render_violations() + elsif (@extension.getId()=='coverage') + render_coverage() + elsif (@extension.getId()=='source') + render_source() + else + render_extension() + end + else + render_nothing() + end else access_denied end @@ -35,8 +51,23 @@ class BrowseController < ApplicationController private + def load_extensions + @extensions=[] + java_facade.getResourceTabs(@resource.scope, @resource.qualifier, @resource.language).each do |tab| + tab.getUserRoles().each do |role| + if has_role?(role, @resource) + @extensions< 'index' end - def load_violations_tab + + + def render_violations + load_sources() @display_violations=true @global_violations=[] @expandable=true @@ -135,7 +161,8 @@ class BrowseController < ApplicationController end end - RuleFailure.find(:all, :include => 'rule', :conditions => [conditions] + values, :order => 'failure_level DESC').each do |violation| + @violations=RuleFailure.find(:all, :include => 'rule', :conditions => [conditions] + values, :order => 'failure_level DESC') + @violations.each do |violation| # sorted by severity => from blocker to info if violation.line && violation.line>0 @lines[violation.line-1].add_violation(violation) @@ -155,12 +182,19 @@ class BrowseController < ApplicationController end end end + render :action => 'index' end + + + - def load_source_tab + def render_source + load_sources() filter_lines_by_date() + render :action => 'index' end + def filter_lines_by_date if @period date=@snapshot.period_datetime(@period) @@ -201,4 +235,15 @@ class BrowseController < ApplicationController @datetime ? @datetime.to_date : nil end end + + + + + def render_extension() + render :action => 'extension' + end + +def render_nothing() + render :action => 'nothing' + end end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_coverage.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_coverage.html.erb new file mode 100644 index 00000000000..696f331c72c --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_coverage.html.erb @@ -0,0 +1,40 @@ +
+ + + + +
<%= format_measure('coverage', :default => '-') -%>
+ + + <% if m=measure('line_coverage') %> + + + + + <% end %> + <% if m=measure('uncovered_lines') %> + + + + + <% end %> +
Line coverage:<%= format_measure(m) -%>
Uncovered lines:<%= format_measure(m) -%> / <%= format_measure('lines_to_cover') -%>
+ + + <% if m=measure('branch_coverage') %> + + + + + <% end %> + <% if m=measure('uncovered_conditions') %> + + + + + <% end %> +
Branch coverage:<%= format_measure(m) -%>
Uncovered conditions:<%= format_measure(m) -%> / <%= format_measure('conditions_to_cover') -%>
+ +
+
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_source.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_source.html.erb new file mode 100644 index 00000000000..dd0e808d83a --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_source.html.erb @@ -0,0 +1,139 @@ +
+ + <% if m=measure('lines') %> + + + + + <% end %> + <% if m=measure('ncloc') %> + + + + + <% end %> + <% if m=measure('functions') %> + + + + + <% end %> + <% if m=measure('accessors') %> + + + + + <% end %> + <% if m=measure('paragraphs') %> + + + + + <% end %> +
Lines: <%= format_measure(m) -%>
Lines of code: <%= format_measure(m) -%>
Methods: <%= format_measure(m) -%>
Accessors: <%= format_measure(m) -%>
Paragraphs: <%= format_measure(m) -%>
+ + + <% if m=measure('statements') %> + + + + + <% end %> + <% if m=measure('complexity') %> + + + + + <% end %> + <% if m=measure('function_complexity') %> + + + + + <% end %> + <% if m=measure('paragraph_complexity') %> + + + + + <% end %> +
Statements: <%= format_measure(m) -%>
Complexity: <%= format_measure(m) -%>
Complexity/method: <%= format_measure(m) -%>
Complexity/paragraph: <%= format_measure(m) -%>
+ + + <% if m=measure('comment_lines_density') %> + + + + + <% end %> + <% if m=measure('comment_lines') %> + + + + + <% end %> + <% if m=measure('commented_out_code_lines') %> + + + + + <% end %> + <% if m=measure('comment_blank_lines') %> + + + + + <% end %> +
Comments: <%= format_measure(m) -%>
Comment lines: <%= format_measure(m) -%>
Commented-out LOC: <%= format_measure(m) -%>
Blank comments: <%= format_measure(m) -%>
+ + + <% if m=measure('public_documented_api_density') %> + + + + + <% end %> + <% if m=measure('public_undocumented_api') %> + + + + + <% end %> + <% if m=measure('public_api') %> + + + + + <% end %> +
Public documented API: <%= format_measure(m) -%>
Public undocumented API: <%= format_measure(m) -%>
Public API: <%= format_measure(m) -%>
+ + + <% if m=measure('classes') %> + + + + + <% end %> + <% if m=measure('noc') %> + + + + + <% end %> + <% if m=measure('dit') %> + + + + + <% end %> + <% if m=measure('rfc') %> + + + + + <% end %> +
Classes: <%= format_measure(m) -%>
Number of Children: <%= format_measure(m) -%>
Depth in Tree: <%= format_measure(m) -%>
Response for Class (RFC): <%= format_measure(m) -%>
+ +
+
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_violations.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_violations.html.erb new file mode 100644 index 00000000000..ad5aadeed0d --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_violations.html.erb @@ -0,0 +1,38 @@ +
+ + + + +
<%= format_measure('violations', :default => '-') -%> violations
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
<%= image_tag 'priority/BLOCKER.png'-%>Blocker:<%= format_measure('blocker_violations', :default => 0) -%>
<%= image_tag 'priority/CRITICAL.png'-%>Critical:<%= format_measure('critical_violations', :default => 0) -%>
<%= image_tag 'priority/MAJOR.png'-%>Major:<%= format_measure('major_violations', :default => 0) -%>
<%= image_tag 'priority/MINOR.png'-%>Minor:<%= format_measure('minor_violations', :default => 0) -%>
<%= image_tag 'priority/INFO.png'-%>Info:<%= format_measure('info_violations', :default => 0) -%>
+ +
+
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_rules_filter.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_rules_filter.html.erb new file mode 100644 index 00000000000..cd37ea4255e --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_rules_filter.html.erb @@ -0,0 +1,47 @@ +<% + blocker_violations = @snapshot.measure('blocker_violations') + critical_violations = @snapshot.measure('critical_violations') + major_violations = @snapshot.measure('major_violations') + minor_violations = @snapshot.measure('minor_violations') + info_violations = @snapshot.measure('info_violations') + + rule_counts={} + rules=[] + + @violations.each do |violation| + count=(rule_counts[violation.rule_id]||0) +1 + rule_counts[violation.rule_id]=count + rules< + \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_tabs.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_tabs.html.erb new file mode 100644 index 00000000000..bfa00de46dd --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/_tabs.html.erb @@ -0,0 +1,168 @@ + + +
+ <%= qualifier_icon(@resource) -%> <%= @resource.long_name -%> + <% if @lines %> + | raw + <% end %> +
+ + diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/extension.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/extension.html.erb new file mode 100644 index 00000000000..f45db54fb12 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/extension.html.erb @@ -0,0 +1,16 @@ +<%= render :partial => 'tabs' -%> + +<% if @extension.isGwt() %> + <%= render :partial => 'gwt/base', :locals => {:resource => @resource, :popup => false, :metric => nil} -%> + +
+ + + + + + + +<% else # ruby on rails page %> + <%= render :inline => @page.getTemplate() %> +<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/index.html.erb index 202b3fb2b4a..da557c0651c 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/index.html.erb @@ -1,149 +1,19 @@ - +<%= render :partial => 'tabs' -%> + +<%= render :partial => "browse/header_#{@extension.getId()}" -%> -
- <%= qualifier_icon(@resource) -%> <%= @resource.long_name -%> - <% if @lines %> - | raw - <% end %> -
- +<% if @display_violations && @global_violations && @global_violations.size>0 -%> + + <% @global_violations.each do |violation| %> + + + + <% end %> +
<%= render :partial => 'violation', :locals => {:violation => violation} -%>
+<% end %> -
+
<% if @scm_available %> @@ -166,19 +36,11 @@ span.rulename a:hover { onclick="submit()"/> <% end %> + + <%= render :partial => 'rules_filter' if @display_violations -%>
-<% if @display_violations && @global_violations && @global_violations.size>0 -%> - - <% @global_violations.each do |violation| %> - - - - <% end %> -
<%= render :partial => 'violation', :locals => {:violation => violation} -%>
-<% end %> - <% if @lines && @lines.size>0 %> <% diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/browse/nothing.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/nothing.html.erb new file mode 100644 index 00000000000..d22506de4cf --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/browse/nothing.html.erb @@ -0,0 +1 @@ +<%= render :partial => 'tabs' -%> \ No newline at end of file diff --git a/sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java b/sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java index b05a115d532..c1930b2e136 100644 --- a/sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java @@ -32,12 +32,16 @@ import java.util.List; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.hasItem; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ViewsTest { - private static final View[] VIEWS = {new FakePage(), new FakeResourceViewer(), new FakeWidget()}; + private static FakeResourceViewer FAKE_TAB = new FakeResourceViewer(); + private static FakeWidget FAKE_WIDGET = new FakeWidget(); + private static FakePage FAKE_PAGE = new FakePage(); + private static final View[] VIEWS = {FAKE_PAGE, FAKE_TAB, FAKE_WIDGET}; @Test public void getPageById() { @@ -63,9 +67,9 @@ public class ViewsTest { @Test public void getResourceViewers() { final Views views = new Views(VIEWS); - List> resourceViewers = views.getPages(NavigationSection.RESOURCE_TAB); - assertThat(resourceViewers.size(), is(1)); - assertThat(resourceViewers.get(0).getTarget(), is(FakeResourceViewer.class)); + List resourceViewers = views.getPages(NavigationSection.RESOURCE_TAB); + assertThat(resourceViewers.size(), is(1 + 3 /* default */)); + assertThat(resourceViewers.contains(new ViewProxy((View)FAKE_TAB)), is(true)); } @Test -- 2.39.5