]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2218 display headers of source, coverage and violations tabs
authorsimonbrandhof <simon.brandhof@gmail.com>
Wed, 2 Mar 2011 13:49:14 +0000 (14:49 +0100)
committersimonbrandhof <simon.brandhof@gmail.com>
Wed, 2 Mar 2011 13:49:14 +0000 (14:49 +0100)
13 files changed:
sonar-server/src/main/java/org/sonar/server/ui/DefaultPages.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/java/org/sonar/server/ui/Views.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/browse_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_coverage.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_source.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/_header_violations.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/_rules_filter.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/_tabs.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/extension.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/browse/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/browse/nothing.html.erb [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java

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 (file)
index 0000000..e49af9f
--- /dev/null
@@ -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";
+    }
+  }
+}
index 06675cd00c87b0bc8f0e493a2384ed47872c09d2..d0544c0369254318afb89f285d463b8e1e6970ae 100644 (file)
@@ -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<ViewProxy<Page>> getResourceTabs(String scope, String qualifier, String language) {
+    return getContainer().getComponent(Views.class).getPages(NavigationSection.RESOURCE_TAB, scope, qualifier, language);
+  }
+
   public ViewProxy<Page> getPage(String id) {
     return getContainer().getComponent(Views.class).getPage(id);
   }
index 94b789eda4678d516da4c7e971d31853aab8e9d3..d1bf0e9c80d64452a57b417773e25aea249d9376 100644 (file)
@@ -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);
     }
   }
 
index 5021fe3eefc701fd5383855acfed7ce6bc916fb5..bacf6c73325bba3e6ca98cec6d5c8311a7d09b6e 100644 (file)
@@ -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<<tab
+          break
+        end
+      end
+    end
 
-  def render_resource
+    selected_tab_id=params[:tab]
+    @extension=@extensions.find{|extension| extension.getId()==selected_tab_id} unless selected_tab_id.blank?
+    @extension=@extensions.find{|extension| extension.isDefaultTab()} if @extension==nil
+  end
+
+  def load_sources
     @period = params[:period].to_i unless params[:period].blank?
     @expanded=(params[:expand]=='true')
 
@@ -57,19 +88,9 @@ class BrowseController < ApplicationController
         line.datetime=(date_string ? DateTime::strptime(date_string): nil)
       end
      end
-
-    if (params[:tab]=='violations')
-      load_violations_tab()
-    elsif (params[:tab]=='coverage')
-      load_coverage_tab()
-    else
-      load_source_tab()
-    end
-
-    params[:layout]='false'
   end
 
-  def load_scm()
+  def load_scm
     @scm_available=(@snapshot.measure('last_commit_datetimes_by_line')!=nil)
     @display_scm=(params[:scm]=='true')
     if @display_scm
@@ -88,7 +109,8 @@ class BrowseController < ApplicationController
     m ? m.data_as_line_distribution() : {}
   end
 
-  def load_coverage_tab
+  def render_coverage
+    load_sources()
     @display_coverage=true
     @hits_by_line=load_distribution('coverage_line_hits_data')
     @conditions_by_line=load_distribution('conditions_by_line')
@@ -104,9 +126,13 @@ class BrowseController < ApplicationController
     end
 
     filter_lines_by_date()
+    render :action => '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 (file)
index 0000000..696f331
--- /dev/null
@@ -0,0 +1,40 @@
+<div id="coverage_header" class="tab_header">
+  <table class="col">
+    <tr>
+      <td class="big"><%= format_measure('coverage', :default => '-') -%></td>
+    </tr>
+  </table>
+
+  <table class="col">
+    <% if m=measure('line_coverage') %>
+      <tr>
+        <td class="name">Line coverage:</td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+    <% end %>
+    <% if m=measure('uncovered_lines') %>
+      <tr>
+        <td class="name">Uncovered lines:</td>
+        <td class="value"><%= format_measure(m) -%> / <%= format_measure('lines_to_cover') -%></td>
+      </tr>
+    <% end %>
+  </table>
+
+  <table class="col">
+    <% if m=measure('branch_coverage') %>
+      <tr>
+        <td class="name">Branch coverage:</td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+    <% end %>
+    <% if m=measure('uncovered_conditions') %>
+      <tr>
+        <td class="name">Uncovered conditions:</td>
+        <td class="value"><%= format_measure(m) -%> / <%= format_measure('conditions_to_cover') -%></td>
+      </tr>
+    <% end %>
+  </table>
+
+  <div class="clear"></div>
+</div>
+
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 (file)
index 0000000..dd0e808
--- /dev/null
@@ -0,0 +1,139 @@
+<div id="source_header" class="tab_header">
+  <table class="col">
+      <% if m=measure('lines') %>
+      <tr>
+        <td class="name">Lines: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('ncloc') %>
+      <tr>
+        <td class="name">Lines of code: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('functions') %>
+      <tr>
+        <td class="name">Methods: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('accessors') %>
+      <tr>
+        <td class="name">Accessors: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('paragraphs') %>
+      <tr>
+        <td class="name">Paragraphs: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+    </table>
+
+  <table class="col">
+     <% if m=measure('statements') %>
+      <tr>
+        <td class="name">Statements: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('complexity') %>
+      <tr>
+        <td class="name">Complexity: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('function_complexity') %>
+      <tr>
+        <td class="name">Complexity/method: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('paragraph_complexity') %>
+      <tr>
+        <td class="name">Complexity/paragraph: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+    </table>
+  
+  <table class="col">
+     <% if m=measure('comment_lines_density') %>
+      <tr>
+        <td class="name">Comments: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('comment_lines') %>
+      <tr>
+        <td class="name">Comment lines: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('commented_out_code_lines') %>
+      <tr>
+        <td class="name">Commented-out LOC: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('comment_blank_lines') %>
+      <tr>
+        <td class="name">Blank comments: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+    </table>
+
+  <table class="col">
+     <% if m=measure('public_documented_api_density') %>
+      <tr>
+        <td class="name">Public documented API: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('public_undocumented_api') %>
+      <tr>
+        <td class="name">Public undocumented API: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('public_api') %>
+      <tr>
+        <td class="name">Public API: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+    </table>
+
+  <table class="col">
+     <% if m=measure('classes') %>
+      <tr>
+        <td class="name">Classes: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('noc') %>
+      <tr>
+        <td class="name">Number of Children: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('dit') %>
+      <tr>
+        <td class="name">Depth in Tree: </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+      <% if m=measure('rfc') %>
+      <tr>
+        <td class="name">Response for Class (RFC): </td>
+        <td class="value"><%= format_measure(m) -%></td>
+      </tr>
+      <% end %>
+    </table>
+  
+  <div class="clear"></div>
+</div>
+
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 (file)
index 0000000..ad5aade
--- /dev/null
@@ -0,0 +1,38 @@
+<div id="violations_header" class="tab_header">
+  <table class="col">
+    <tr>
+      <td><span class="big"><%= format_measure('violations', :default => '-') -%></span> violations</td>
+    </tr>
+  </table>
+
+  <table class="col">
+    <tr>
+      <td><%= image_tag 'priority/BLOCKER.png'-%></td>
+      <td class="name">Blocker:</td>
+      <td class="value"><%= format_measure('blocker_violations', :default => 0) -%></td>
+    </tr>
+    <tr>
+      <td><%= image_tag 'priority/CRITICAL.png'-%></td>
+      <td class="name">Critical:</td>
+      <td class="value"><%= format_measure('critical_violations', :default => 0) -%></td>
+    </tr>
+    <tr>
+      <td><%= image_tag 'priority/MAJOR.png'-%></td>
+      <td class="name">Major:</td>
+      <td class="value"><%= format_measure('major_violations', :default => 0) -%></td>
+    </tr>
+    <tr>
+      <td><%= image_tag 'priority/MINOR.png'-%></td>
+      <td class="name">Minor:</td>
+      <td class="value"><%= format_measure('minor_violations', :default => 0) -%></td>
+    </tr>
+    <tr>
+      <td><%= image_tag 'priority/INFO.png'-%></td>
+      <td class="name">Info:</td>
+      <td class="value"><%= format_measure('info_violations', :default => 0) -%></td>
+    </tr>
+  </table>
+
+  <div class="clear"></div>
+</div>
+
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 (file)
index 0000000..cd37ea4
--- /dev/null
@@ -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<<violation.rule
+   end
+
+
+   rule_options=[]
+   rules.uniq.sort_by{|rule| rule.name}.each do |rule|
+     rule_options<<["#{h rule.name} (#{rule_counts[rule.id]})", rule.key]
+   end
+%>
+<select id="rule" name="rule" class="small" onchange="submit()">
+  <option value="">No filters</option>
+  <optgroup label="Severity">
+    <% if blocker_violations && blocker_violations.value>0 %>
+      <option value="<%= Sonar::RulePriority::PRIORITY_BLOCKER-%>" <%= 'selected' if params[:rule]==Sonar::RulePriority::PRIORITY_BLOCKER.to_s -%>>Blocker (<%= blocker_violations.formatted_value -%>)</option>
+    <% end %>
+    <% if critical_violations && critical_violations.value>0 %>
+      <option value="<%= Sonar::RulePriority::PRIORITY_CRITICAL -%>" <%= 'selected' if params[:rule]==Sonar::RulePriority::PRIORITY_CRITICAL.to_s -%>>Critical (<%= critical_violations.formatted_value -%>)</option>
+    <% end %>
+    <% if major_violations && major_violations.value>0 %>
+      <option value="<%= Sonar::RulePriority::PRIORITY_MAJOR -%>" <%= 'selected' if params[:rule]==Sonar::RulePriority::PRIORITY_MAJOR.to_s -%>>Major (<%= major_violations.formatted_value -%>)</option>
+    <% end %>
+    <% if minor_violations && minor_violations.value>0 %>
+      <option value="<%= Sonar::RulePriority::PRIORITY_MINOR -%>" <%= 'selected' if params[:rule]==Sonar::RulePriority::PRIORITY_MINOR.to_s -%>>Minor (<%= minor_violations.formatted_value -%>)</option>
+    <% end %>
+    <% if info_violations && info_violations.value>0 %>
+      <option value="<%= Sonar::RulePriority::PRIORITY_INFO -%>" <%= 'selected' if params[:rule]==Sonar::RulePriority::PRIORITY_INFO.to_s -%>>Info (<%= info_violations.formatted_value -%>)</option>
+    <% end %>
+  </optgroup>
+
+
+  <optgroup label="Rule">
+    <%= options_for_select(rule_options, params[:rule]) -%>
+  </optgroup>
+</select>
\ 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 (file)
index 0000000..bfa00de
--- /dev/null
@@ -0,0 +1,168 @@
+<style>
+#source_metrics {
+  width: 100%;
+}
+#source_metrics {
+  width: 100%;
+}
+.sources2 {
+  width: 100%;
+  border-top: 1px solid #DDD;
+  border-bottom: 1px solid #DDD;
+  margin: 0;
+}
+.sources2 td.lid {
+  background-color: #ECECEC;
+  border-right: 1px solid #DDDDDD;
+  border-left: 1px solid #DDDDDD;
+  text-align: right;
+  padding: 2px 0.5em 0 0.5em;
+  vertical-align: top;
+  font-size: 11px;
+}
+.sources2 td.lid a {
+  text-decoration: none;
+  color: #AAA;
+}
+.sources2 td.scm {
+  border-right: 1px solid #DDD;
+  border-left: 1px solid #DDD;
+  background-color: #ECECEC;
+}
+.sources2 td.revision {
+  border-top: 1px solid #DDD;
+  vertical-align: top;
+  padding: 2px 0.3em;
+  white-space: nowrap;
+}
+.sources2 span.date, .sources2 span.date a {
+  color: #AAA;
+  font-size: 11px;
+  text-decoration: none;
+}
+.sources2 span.author, .sources2 span.author a {
+  font-size: 11px;
+}
+.sources2 td.rule {
+  padding: 1px 0.5em;
+  border-left: 1px solid #DDDDDD;
+}
+.sources2 td.violations {
+  background-color: #ECECEC;
+  min-width: 450px;
+}
+span.rulename, span.rulename a {
+  color: #4183C4;
+  text-decoration: none;
+}
+span.violation_date {
+  color: #AAA;
+  font-size: 11px;
+}
+span.rulename a:hover {
+  text-decoration: underline;
+}
+.sources2 td.violations img {
+  vertical-align: sub;
+}
+.sources2 td.line {
+  padding-left: 1em;
+  width: 100%;
+  white-space: pre;
+  font-size: 12px;
+  font-family: monospace;
+  border-right: 1px solid #DDD;
+}
+.sources2 td.section {
+  border-top: 1px solid #DDD;
+  border-bottom: 1px solid #DDD;
+}
+.sources2 td.ind {
+  border-right: 1px solid #DDD;
+  min-width: 1.5em;
+  padding: 0 0.3em;
+  text-align: center;
+  vertical-align: middle;
+}
+.sources2 td.ok {
+  background-color: #ACE97C;
+  border-top: 1px solid #6EC563;
+  border-bottom: 1px solid #6EC563;
+}
+.sources2 td.sev0, .sources2 td.sev1, .sources2 td.warn {
+  /* info, minor */
+  background-color: #FFF6BF;
+  border-top: 1px solid #FFD324;
+  border-bottom: 1px solid #FFD324;
+}
+.sources2 td.sev2, .sources2 td.sev3, .sources2 td.sev4, .sources2 td.ko {
+  /* major, critical, blocker */
+  background-color: #FF9090;
+  border-top: 1px solid #FF5252;
+  border-bottom: 1px solid #FF5252;
+}
+.sources2 td.new_section {
+  border-top: 1px solid #DDD;
+  border-bottom: 1px solid #DDD;
+  height: 40px;
+}
+#source_title {
+  padding: 10px 0;
+}
+#source_title span.h1 {
+  font-size: 16px;
+  margin-right: 10px;
+}
+.source_links {
+  font-size: 11px;
+}
+#global_violations {
+  width: 100%;
+  border: 1px solid #DDD;
+  margin-bottom: 10px;
+}
+#global_violations td {
+  background-color: #ECECEC;
+  padding: 3px 0.5em;
+}
+#global_violations td img, #source_title img {
+  vertical-align: sub;
+}
+.tab_header {
+  width: 100%;
+  border: 1px solid #DDD;
+  border-top-width: 0;
+  background-color: #ECECEC;
+  padding: 5px 10px;
+  margin-bottom: 10px;
+  color: #555555;
+}
+
+.tab_header table.col {
+  margin-right: 30px;
+  float: left;
+}
+.tab_header td {
+  padding-right: 7px;
+}
+.tab_header td.name {
+  font-weight: bold;
+  text-align: left;
+}
+.tab_header td.value {
+  text-align: right;
+}
+</style>
+
+<div id="source_title">
+  <span class="h1"><%= qualifier_icon(@resource) -%> <%= @resource.long_name -%></span>
+  <% if @lines  %>
+    | <span class="source_link"><a href="<%= ApplicationController.root_context -%>/api/sources?resource=<%= @resource.key -%>&format=txt">raw</a></span>
+  <% end %>
+</div>
+
+<ul id="source_tabs" class="tabs">
+  <% @extensions.each do |extension| %>
+    <li><a href="<%= url_for(:overwrite_params => {:tab => extension.getId()}) -%>" class="<%= 'selected' if @extension.getId()==extension.getId() -%>"><%= extension.getTitle() -%></a></li>
+  <% end %>
+</ul>
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 (file)
index 0000000..f45db54
--- /dev/null
@@ -0,0 +1,16 @@
+<%= render :partial => 'tabs' -%>
+
+<% if @extension.isGwt() %>
+  <%= render :partial => 'gwt/base', :locals => {:resource => @resource, :popup => false, :metric => nil} -%>
+
+  <div id="gwtpage"> </div>
+
+  <!-- for SmartGWT -->
+  <script> var isomorphicDir = "<%= "#{ApplicationController.root_context}/deploy/gwt/#{@extension.getId()}" -%>/sc/";</script>
+
+
+  <script src="<%= ApplicationController.root_context -%>/deploy/gwt/<%= @extension.getId() -%>/<%= @extension.getId() -%>.nocache.js?<%= sonar_version -%>"></script>
+  
+<% else # ruby on rails page %>
+  <%= render :inline => @page.getTemplate() %>
+<% end %>
\ No newline at end of file
index 202b3fb2b4abaf7e7d98248e54e0d4be5d7763d4..da557c0651c8fa3a24dde535b32717b34fb48d66 100644 (file)
-<style>
-#source_metrics {
-  width: 100%;
-}
-#source_metrics {
-  width: 100%;
-}
-.sources2 {
-  width: 100%;
-  border-top: 1px solid #DDD;
-  border-bottom: 1px solid #DDD;
-  margin: 0;
-}
-.sources2 td.lid {
-  background-color: #ECECEC;
-  border-right: 1px solid #DDDDDD;
-  border-left: 1px solid #DDDDDD;
-  text-align: right;
-  padding: 2px 0.5em 0 0.5em;
-  vertical-align: top;
-  font-size: 11px;
-}
-.sources2 td.lid a {
-  text-decoration: none;
-  color: #AAA;
-}
-.sources2 td.scm {
-  border-right: 1px solid #DDD;
-  border-left: 1px solid #DDD;
-  background-color: #ECECEC;
-}
-.sources2 td.revision {
-  border-top: 1px solid #DDD;
-  vertical-align: top;
-  padding: 2px 0.3em;
-  white-space: nowrap;
-}
-.sources2 span.date, .sources2 span.date a {
-  color: #AAA;
-  font-size: 11px;
-  text-decoration: none;
-}
-.sources2 span.author, .sources2 span.author a {
-  font-size: 11px;
-}
-.sources2 td.rule {
-  padding: 1px 0.5em;
-  border-left: 1px solid #DDDDDD;
-}
-.sources2 td.violations {
-  background-color: #ECECEC;
-  min-width: 450px;
-}
-span.rulename, span.rulename a {
-  color: #4183C4;
-  text-decoration: none;
-}
-span.violation_date {
-  color: #AAA;
-  font-size: 11px;
-}
-span.rulename a:hover {
-  text-decoration: underline;
-}
-.sources2 td.violations img {
-  vertical-align: sub;
-}
-.sources2 td.line {
-  padding-left: 1em;
-  width: 100%;
-  white-space: pre;
-  font-size: 12px;
-  font-family: monospace;
-  border-right: 1px solid #DDD;
-}
-.sources2 td.section {
-  border-top: 1px solid #DDD;
-  border-bottom: 1px solid #DDD;
-}
-.sources2 td.ind {
-  border-right: 1px solid #DDD;
-  min-width: 1.5em;
-  padding: 0 0.3em;
-  text-align: center;
-  vertical-align: middle;
-}
-.sources2 td.ok {
-  background-color: #ACE97C;
-  border-top: 1px solid #6EC563;
-  border-bottom: 1px solid #6EC563;
-}
-.sources2 td.sev0, .sources2 td.sev1, .sources2 td.warn {
-  /* info, minor */
-  background-color: #FFF6BF;
-  border-top: 1px solid #FFD324;
-  border-bottom: 1px solid #FFD324;
-}
-.sources2 td.sev2, .sources2 td.sev3, .sources2 td.sev4, .sources2 td.ko {
-  /* major, critical, blocker */
-  background-color: #FF9090;
-  border-top: 1px solid #FF5252;
-  border-bottom: 1px solid #FF5252;
-}
-.sources2 td.new_section {
-  border-top: 1px solid #DDD;
-  border-bottom: 1px solid #DDD;
-  height: 40px;
-}
-#source_title {
-  padding: 10px 0;
-}
-#source_title span.h1 {
-  font-size: 16px;
-  margin-right: 10px;
-}
-.source_links {
-  font-size: 11px;
-}
-#global_violations {
-  width: 100%;
-  border: 1px solid #DDD;
-  margin-bottom: 10px;
-}
-#global_violations td {
-  background-color: #ECECEC;
-  padding: 3px 0.5em;
-}
-#global_violations td img, #source_title img {
-  vertical-align: sub;
-}
-</style>
+<%= render :partial => 'tabs' -%>
+
+<%= render :partial => "browse/header_#{@extension.getId()}" -%>
 
-<div id="source_title">
-  <span class="h1"><%= qualifier_icon(@resource) -%> <%= @resource.long_name -%></span>
-  <% if @lines  %>
-    | <span class="source_link"><a href="<%= ApplicationController.root_context -%>/api/sources?resource=<%= @resource.key -%>&format=txt">raw</a></span>
-  <% end %>
-</div>
 
-<ul id="source_tabs" class="tabs">
-  <li><a href="<%= url_for(:overwrite_params => {:tab => 'source'}) -%>" class="<%= 'selected' if params[:tab]=='source' || params[:tab].blank? -%>">source</a></li>
-  <li><a href="<%= url_for(:overwrite_params => {:tab => 'coverage'}) -%>"  class="<%= 'selected' if params[:tab]=='coverage' -%>">coverage</a></li>
-  <li><a href="<%= url_for(:overwrite_params => {:tab => 'violations'}) -%>" class="<%= 'selected' if params[:tab]=='violations' -%>">violations</a></li>
-</ul>
+<% if @display_violations && @global_violations && @global_violations.size>0 -%>
+  <table id="global_violations" cellpadding="0" cellspacing="0" border="0">
+    <% @global_violations.each do |violation| %>
+      <tr>
+        <td><%= render :partial => 'violation', :locals => {:violation => violation} -%></td>
+      </tr>
+    <% end %>
+  </table>
+<% end %>
 
-<div id="source_header">
+<div id="source_options">
   <form method="GET" action="<%= url_for :controller => 'browse', :id => @resource.key -%>">
     <input type="hidden" name="tab" value="<%= params[:tab] -%>"/>
     <% if @scm_available %>
@@ -166,19 +36,11 @@ span.rulename a:hover {
       <input type="checkbox" value="true" name="expand" id="expand" <%= 'checked' if @expanded -%> onclick="submit()"/>
       <label for="expand">Expand</label>
     <% end %>
+
+    <%= render :partial => 'rules_filter' if @display_violations -%>
   </form>
 </div>
 
-<% if @display_violations && @global_violations && @global_violations.size>0 -%>
-  <table id="global_violations" cellpadding="0" cellspacing="0" border="0">
-    <% @global_violations.each do |violation| %>
-      <tr>
-        <td><%= render :partial => 'violation', :locals => {:violation => violation} -%></td>
-      </tr>
-    <% end %>
-  </table>
-<% end %>
-
 <% if @lines && @lines.size>0 %>
 <table id="sources" class="sources2 code" cellpadding="0" cellspacing="0" border="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 (file)
index 0000000..d22506d
--- /dev/null
@@ -0,0 +1 @@
+<%= render :partial => 'tabs' -%>
\ No newline at end of file
index b05a115d532e02075870a1a3412d1ca190d6e50c..c1930b2e1366c09b19c3e6055e2d56c97f790495 100644 (file)
@@ -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<ViewProxy<Page>> 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