aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2012-03-22 14:44:11 +0100
committerFabrice Bellingard <bellingard@gmail.com>2012-03-22 14:48:17 +0100
commit3475a5114e4282de534d477980acaa6b36714d39 (patch)
treee66fffd78582ad072cbf37589c8ac15d1ac379b8
parent4807b56a875275c36ad1e7aaaf5f6975fe4a4a8b (diff)
downloadsonarqube-3475a5114e4282de534d477980acaa6b36714d39.tar.gz
sonarqube-3475a5114e4282de534d477980acaa6b36714d39.zip
SONAR-3323 Display tab/page/widget only when required measures exist
=> new annotation @RequiredMeasure(mandatory={}, oneOf={}) that can be applied on any views. Tested on pages, tabs and widgets
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/RequiredMeasures.java57
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java18
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java150
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/Views.java32
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb6
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/drilldown_controller.rb2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb10
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb6
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java82
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ui/ViewsTest.java51
11 files changed, 317 insertions, 99 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/RequiredMeasures.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/RequiredMeasures.java
new file mode 100644
index 00000000000..f89cbddee4f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/RequiredMeasures.java
@@ -0,0 +1,57 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.api.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Annotation used to specify which measures should be available on a snapshot to be able to display a view (page, tab, ...).
+ * It is possible to give a list of mandatory measures (= if one is not available, the view is not displayed) and/or a list of
+ * needed measures (only one of them needs to be available). The measures are specified using the metric keys.
+ * </p>
+ * <p>
+ * Example: the DesignPage absolutely requires the "dsm" measure to be fed in order to be displayed, whatever the language.
+ * The class will define a <code>@RequiredMeasures(mandatory={"dsm"})</code> annotation.
+ * </p>
+ *
+ * @since 2.15
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface RequiredMeasures {
+
+ /**
+ * Lists all the measures that must absolutely to be available on the snapshot in order to display the view.
+ * @return the list of mandatory measures, identified by their metric key
+ */
+ String[] mandatory() default {};
+
+ /**
+ * Lists all needed measures required to display the view. If only one of them is available on the snapshot, then the view
+ * is displayed.
+ * @return the list of needed measures, identified by their metric key
+ */
+ String[] oneOf() default {};
+
+}
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 76517e30df8..be0d7388ac3 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
@@ -153,8 +153,8 @@ public final class JRubyFacade {
}
- public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage) {
- return getContainer().getComponentByType(Views.class).getWidgets(resourceScope, resourceQualifier, resourceLanguage);
+ public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage, Object[] availableMeasures) {
+ return getContainer().getComponentByType(Views.class).getWidgets(resourceScope, resourceQualifier, resourceLanguage, (String[]) availableMeasures);
}
public List<ViewProxy<Widget>> getWidgets() {
@@ -165,20 +165,20 @@ public final class JRubyFacade {
return getContainer().getComponentByType(Views.class).getWidget(id);
}
- public List<ViewProxy<Page>> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage) {
- return getContainer().getComponentByType(Views.class).getPages(section, resourceScope, resourceQualifier, resourceLanguage);
+ public List<ViewProxy<Page>> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage, Object[] availableMeasures) {
+ return getContainer().getComponentByType(Views.class).getPages(section, resourceScope, resourceQualifier, resourceLanguage, (String[]) availableMeasures);
}
public List<ViewProxy<Page>> getResourceTabs() {
- return getContainer().getComponentByType(Views.class).getPages(NavigationSection.RESOURCE_TAB, null, null, null);
+ return getContainer().getComponentByType(Views.class).getPages(NavigationSection.RESOURCE_TAB, null, null, null, null);
}
- public List<ViewProxy<Page>> getResourceTabs(String scope, String qualifier, String language) {
- return getContainer().getComponentByType(Views.class).getPages(NavigationSection.RESOURCE_TAB, scope, qualifier, language);
+ public List<ViewProxy<Page>> getResourceTabs(String scope, String qualifier, String language, Object[] availableMeasures) {
+ return getContainer().getComponentByType(Views.class).getPages(NavigationSection.RESOURCE_TAB, scope, qualifier, language, (String[]) availableMeasures);
}
- public List<ViewProxy<Page>> getResourceTabsForMetric(String scope, String qualifier, String language, String metric) {
- return getContainer().getComponentByType(Views.class).getPagesForMetric(NavigationSection.RESOURCE_TAB, scope, qualifier, language, metric);
+ public List<ViewProxy<Page>> getResourceTabsForMetric(String scope, String qualifier, String language, Object[] availableMeasures, String metric) {
+ return getContainer().getComponentByType(Views.class).getPagesForMetric(NavigationSection.RESOURCE_TAB, scope, qualifier, language, (String[]) availableMeasures, metric);
}
public ViewProxy<Page> getPage(String id) {
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java b/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java
index e7d09cbcf7d..eb8822713fa 100644
--- a/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java
+++ b/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java
@@ -31,6 +31,7 @@ import org.sonar.api.web.*;
import java.util.Collection;
import java.util.Map;
+@SuppressWarnings("rawtypes")
public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
private V view;
@@ -46,35 +47,66 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT;
private boolean isDefaultTab = false;
private boolean isWidget = false;
+ private String[] mandatoryMeasures = {};
+ private String[] needOneOfMeasures = {};
public ViewProxy(final V view) {
this.view = view;
- UserRole userRoleAnnotation = AnnotationUtils.getClassAnnotation(view, UserRole.class);
- if (userRoleAnnotation != null) {
- userRoles = userRoleAnnotation.value();
+ initUserRoles(view);
+ initSections(view);
+ initResourceScope(view);
+ initResourceQualifier(view);
+ initResourceLanguage(view);
+ initDefaultTabInfo(view);
+ initDescription(view);
+ initWidgetProperties(view);
+ initWidgetCategory(view);
+ initWidgetLayout(view);
+ initRequiredMeasures(view);
+
+ isWidget = (view instanceof Widget);
+ }
+
+ private void initRequiredMeasures(V view) {
+ RequiredMeasures requiredMeasuresAnnotation = AnnotationUtils.getClassAnnotation(view, RequiredMeasures.class);
+ if (requiredMeasuresAnnotation != null) {
+ mandatoryMeasures = requiredMeasuresAnnotation.mandatory();
+ needOneOfMeasures = requiredMeasuresAnnotation.oneOf();
}
+ }
- NavigationSection sectionAnnotation = AnnotationUtils.getClassAnnotation(view, NavigationSection.class);
- if (sectionAnnotation != null) {
- sections = sectionAnnotation.value();
+ private void initWidgetLayout(final V view) {
+ WidgetLayout layoutAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetLayout.class);
+ if (layoutAnnotation != null) {
+ widgetLayout = layoutAnnotation.value();
}
+ }
- ResourceScope scopeAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceScope.class);
- if (scopeAnnotation != null) {
- resourceScopes = scopeAnnotation.value();
+ private void initWidgetCategory(final V view) {
+ WidgetCategory categAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetCategory.class);
+ if (categAnnotation != null) {
+ widgetCategories = categAnnotation.value();
}
+ }
- ResourceQualifier qualifierAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceQualifier.class);
- if (qualifierAnnotation != null) {
- resourceQualifiers = qualifierAnnotation.value();
+ private void initWidgetProperties(final V view) {
+ WidgetProperties propAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetProperties.class);
+ if (propAnnotation != null) {
+ for (WidgetProperty property : propAnnotation.value()) {
+ widgetPropertiesByKey.put(property.key(), property);
+ }
}
+ }
- ResourceLanguage languageAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceLanguage.class);
- if (languageAnnotation != null) {
- resourceLanguages = languageAnnotation.value();
+ private void initDescription(final V view) {
+ Description descriptionAnnotation = AnnotationUtils.getClassAnnotation(view, Description.class);
+ if (descriptionAnnotation != null) {
+ description = descriptionAnnotation.value();
}
+ }
+ private void initDefaultTabInfo(final V view) {
DefaultTab defaultTabAnnotation = AnnotationUtils.getClassAnnotation(view, DefaultTab.class);
if (defaultTabAnnotation != null) {
if (defaultTabAnnotation.metrics().length == 0) {
@@ -86,30 +118,41 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
defaultForMetrics = defaultTabAnnotation.metrics();
}
}
+ }
- Description descriptionAnnotation = AnnotationUtils.getClassAnnotation(view, Description.class);
- if (descriptionAnnotation != null) {
- description = descriptionAnnotation.value();
+ private void initResourceLanguage(final V view) {
+ ResourceLanguage languageAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceLanguage.class);
+ if (languageAnnotation != null) {
+ resourceLanguages = languageAnnotation.value();
}
+ }
- WidgetProperties propAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetProperties.class);
- if (propAnnotation != null) {
- for (WidgetProperty property : propAnnotation.value()) {
- widgetPropertiesByKey.put(property.key(), property);
- }
+ private void initResourceQualifier(final V view) {
+ ResourceQualifier qualifierAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceQualifier.class);
+ if (qualifierAnnotation != null) {
+ resourceQualifiers = qualifierAnnotation.value();
}
+ }
- WidgetCategory categAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetCategory.class);
- if (categAnnotation != null) {
- widgetCategories = categAnnotation.value();
+ private void initResourceScope(final V view) {
+ ResourceScope scopeAnnotation = AnnotationUtils.getClassAnnotation(view, ResourceScope.class);
+ if (scopeAnnotation != null) {
+ resourceScopes = scopeAnnotation.value();
}
+ }
- WidgetLayout layoutAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetLayout.class);
- if (layoutAnnotation != null) {
- widgetLayout = layoutAnnotation.value();
+ private void initSections(final V view) {
+ NavigationSection sectionAnnotation = AnnotationUtils.getClassAnnotation(view, NavigationSection.class);
+ if (sectionAnnotation != null) {
+ sections = sectionAnnotation.value();
}
+ }
- isWidget = (view instanceof Widget);
+ private void initUserRoles(final V view) {
+ UserRole userRoleAnnotation = AnnotationUtils.getClassAnnotation(view, UserRole.class);
+ if (userRoleAnnotation != null) {
+ userRoles = userRoleAnnotation.value();
+ }
}
public V getTarget() {
@@ -171,6 +214,24 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
public boolean supportsMetric(String metricKey) {
return ArrayUtils.contains(defaultForMetrics, metricKey);
}
+
+ public boolean acceptsAvailableMeasures(String[] availableMeasures) {
+ for (String mandatoryMeasure : mandatoryMeasures) {
+ if (!ArrayUtils.contains(availableMeasures, mandatoryMeasure)) {
+ return false;
+ }
+ }
+ if (needOneOfMeasures.length == 0) {
+ return true;
+ } else {
+ for (String neededMeasure : needOneOfMeasures) {
+ if (ArrayUtils.contains(availableMeasures, neededMeasure)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
public boolean isWidget() {
return isWidget;
@@ -203,7 +264,6 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
return getId().hashCode();
}
-
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -217,29 +277,29 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
}
ViewProxy rhs = (ViewProxy) obj;
return new EqualsBuilder()
- .append(getId(), rhs.getId())
- .isEquals();
+ .append(getId(), rhs.getId())
+ .isEquals();
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .append("id", view.getId())
- .append("sections", sections)
- .append("userRoles", userRoles)
- .append("scopes", resourceScopes)
- .append("qualifiers", resourceQualifiers)
- .append("languages", resourceLanguages)
- .append("metrics", defaultForMetrics)
- .toString();
+ .append("id", view.getId())
+ .append("sections", sections)
+ .append("userRoles", userRoles)
+ .append("scopes", resourceScopes)
+ .append("qualifiers", resourceQualifiers)
+ .append("languages", resourceLanguages)
+ .append("metrics", defaultForMetrics)
+ .toString();
}
public int compareTo(ViewProxy other) {
return new CompareToBuilder()
- .append(getTitle(), other.getTitle())
- .append(getId(), other.getId())
- .toComparison();
+ .append(getTitle(), other.getTitle())
+ .append(getId(), other.getId())
+ .toComparison();
}
-} \ No newline at end of file
+}
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 4e95904d529..305daa0a471 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
@@ -19,14 +19,19 @@
*/
package org.sonar.server.ui;
-import com.google.common.collect.Lists;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
import org.apache.commons.lang.ArrayUtils;
import org.sonar.api.ServerComponent;
import org.sonar.api.web.Page;
import org.sonar.api.web.View;
import org.sonar.api.web.Widget;
-import java.util.*;
+import com.google.common.collect.Lists;
public class Views implements ServerComponent {
@@ -66,23 +71,23 @@ public class Views implements ServerComponent {
}
public List<ViewProxy<Page>> getPages(String section) {
- return getPages(section, null, null, null);
+ return getPages(section, null, null, null, null);
}
- public List<ViewProxy<Page>> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage) {
+ public List<ViewProxy<Page>> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage, String[] availableMeasures) {
List<ViewProxy<Page>> result = Lists.newArrayList();
for (ViewProxy<Page> proxy : pages) {
- if (accept(proxy, section, resourceScope, resourceQualifier, resourceLanguage)) {
+ if (accept(proxy, section, resourceScope, resourceQualifier, resourceLanguage, availableMeasures)) {
result.add(proxy);
}
}
return result;
}
- public List<ViewProxy<Page>> getPagesForMetric(String section, String resourceScope, String resourceQualifier, String resourceLanguage, String metric) {
+ public List<ViewProxy<Page>> getPagesForMetric(String section, String resourceScope, String resourceQualifier, String resourceLanguage, String[] availableMeasures, String metric) {
List<ViewProxy<Page>> result = Lists.newArrayList();
for (ViewProxy<Page> proxy : pages) {
- if (accept(proxy, section, resourceScope, resourceQualifier, resourceLanguage) && proxy.supportsMetric(metric)) {
+ if (accept(proxy, section, resourceScope, resourceQualifier, resourceLanguage, availableMeasures) && proxy.supportsMetric(metric)) {
result.add(proxy);
}
}
@@ -93,10 +98,10 @@ public class Views implements ServerComponent {
return widgetsPerId.get(id);
}
- public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage) {
+ public List<ViewProxy<Widget>> getWidgets(String resourceScope, String resourceQualifier, String resourceLanguage, String[] availableMeasures) {
List<ViewProxy<Widget>> result = Lists.newArrayList();
for (ViewProxy<Widget> proxy : widgets) {
- if (accept(proxy, null, resourceScope, resourceQualifier, resourceLanguage)) {
+ if (accept(proxy, null, resourceScope, resourceQualifier, resourceLanguage, availableMeasures)) {
result.add(proxy);
}
}
@@ -107,11 +112,12 @@ public class Views implements ServerComponent {
return Lists.newArrayList(widgets);
}
- private static boolean accept(ViewProxy proxy, String section, String resourceScope, String resourceQualifier, String resourceLanguage) {
+ protected static boolean accept(ViewProxy proxy, String section, String resourceScope, String resourceQualifier, String resourceLanguage, String[] availableMeasures) {
return acceptNavigationSection(proxy, section)
&& acceptResourceScope(proxy, resourceScope)
&& acceptResourceQualifier(proxy, resourceQualifier)
- && acceptResourceLanguage(proxy, resourceLanguage);
+ && acceptResourceLanguage(proxy, resourceLanguage)
+ && acceptAvailableMeasures(proxy, availableMeasures);
}
protected static boolean acceptResourceLanguage(ViewProxy proxy, String resourceLanguage) {
@@ -129,4 +135,8 @@ public class Views implements ServerComponent {
protected static boolean acceptNavigationSection(ViewProxy proxy, String section) {
return proxy.isWidget() || section == null || ArrayUtils.contains(proxy.getSections(), section);
}
+
+ protected static boolean acceptAvailableMeasures(ViewProxy proxy, String[] availableMeasures) {
+ return availableMeasures == null || proxy.acceptsAvailableMeasures(availableMeasures);
+ }
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
index ad1ce1fa10d..674d6509076 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
@@ -35,9 +35,9 @@ class DashboardController < ApplicationController
end
else
# display the layout of the parent, usually the directory, but display the file viewers
- if @resource.last_snapshot
+ if @snapshot
@file = @resource
- @project = @resource.last_snapshot.parent.project
+ @project = @snapshot.parent.project
render :action => 'no_dashboard'
else
redirect_to home_path
@@ -186,7 +186,7 @@ class DashboardController < ApplicationController
def load_authorized_widget_definitions
if @resource
- @authorized_widget_definitions = java_facade.getWidgets(@resource.scope, @resource.qualifier, @resource.language)
+ @authorized_widget_definitions = java_facade.getWidgets(@resource.scope, @resource.qualifier, @resource.language, @snapshot.available_measures)
@authorized_widget_definitions=@authorized_widget_definitions.select do |widget|
authorized=widget.getUserRoles().size==0
unless authorized
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/drilldown_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/drilldown_controller.rb
index 848e046f371..feb4f8e64eb 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/drilldown_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/drilldown_controller.rb
@@ -173,7 +173,7 @@ class DrilldownController < ApplicationController
def display_metric_viewers?(resource, metric_key)
return true if resource.file?
- java_facade.getResourceTabsForMetric(resource.scope, resource.qualifier, resource.language, metric_key).each do |tab|
+ java_facade.getResourceTabsForMetric(resource.scope, resource.qualifier, resource.language, resource.last_snapshot.available_measures, metric_key).each do |tab|
tab.getUserRoles().each do |role|
if has_role?(role, resource)
return true
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 9c5b1cfcfc2..bf970cf2a96 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
@@ -113,7 +113,7 @@ class ResourceController < ApplicationController
def load_extensions
@extensions=[]
- java_facade.getResourceTabs(@resource.scope, @resource.qualifier, @resource.language).each do |tab|
+ java_facade.getResourceTabs(@resource.scope, @resource.qualifier, @resource.language, @snapshot.available_measures).each do |tab|
if tab.getUserRoles().empty?
@extensions<<tab
else
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb
index d600cf6ef3c..6d7f8b6e25c 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb
@@ -248,6 +248,16 @@ class Snapshot < ActiveRecord::Base
project_snapshot.send "period#{period_index}_date"
end
+ # IMPORTANT: this is method must used only to pass arguments to the java_facade, as it returns
+ # an array of java.lang.String objects.
+ # It is used to know which views (page, tab, widget, ...) to display on for a given snapshot
+ def available_measures
+ if @available_measures.nil?
+ @available_measures = metrics.map {|m| m.name}.to_java(java.lang.String)
+ end
+ @available_measures
+ end
+
private
def measures_hash
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
index ab826232405..e7bc7ca6d6a 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
@@ -39,7 +39,7 @@
<li class="<%= 'selected' if controller.controller_path=='dependencies' -%>">
<a href="<%= ApplicationController.root_context -%>/dependencies/index"><%= message('dependencies.page') -%></a></li>
- <% controller.java_facade.getPages(Navigation::SECTION_HOME, nil, nil, nil).each do |page|
+ <% controller.java_facade.getPages(Navigation::SECTION_HOME, nil, nil, nil, nil).each do |page|
selected=request.request_uri.include?("/plugins/home/#{page.getId()}") %>
<li class="<%= 'selected' if selected -%>">
<a href="<%= ApplicationController.root_context -%>/plugins/home/<%= page.getId() -%>"><%= message(page.getId() + '.page', :default => page.getTitle()) -%></a></li>
@@ -57,7 +57,7 @@
<a href="<%= ApplicationController.root_context -%>/drilldown/violations/<%= @project.id -%><%= "?"+period_param if period_param -%>"><%= message('violations_drilldown.page') -%></a></li>
<li class="<%= 'selected' if request.request_uri.include?('/cloud/index') -%>">
<a href="<%= ApplicationController.root_context -%>/cloud/index/<%= @project.id -%>"><%= message('clouds.page') -%></a></li>
- <% controller.java_facade.getPages(Navigation::SECTION_RESOURCE, @project.scope, @project.qualifier, @project.language).each do |page| %>
+ <% controller.java_facade.getPages(Navigation::SECTION_RESOURCE, @project.scope, @project.qualifier, @project.language, @project.last_snapshot.available_measures).each do |page| %>
<li class="<%= 'selected' if request.request_uri.include?("page=#{page.getId()}") -%>">
<a href="<%= ApplicationController.root_context -%>/plugins/resource/<%= @project.id -%>?page=<%= page.getId() -%>"><%= message(page.getId() + '.page', :default => page.getTitle()) %></a>
</li>
@@ -111,7 +111,7 @@
<a href="<%= ApplicationController.root_context -%>/admin_filters/index"><%= message('default_filters.page') -%></a></li>
<li class="<%= 'selected' if controller.controller_path=='admin_dashboards' -%>">
<a href="<%= ApplicationController.root_context -%>/admin_dashboards/index"><%= message('default_dashboards.page') -%></a></li>
- <% controller.java_facade.getPages(Navigation::SECTION_CONFIGURATION, nil, nil, nil).each do |page| %>
+ <% controller.java_facade.getPages(Navigation::SECTION_CONFIGURATION, nil, nil, nil, nil).each do |page| %>
<li class="<%= 'selected' if request.request_uri.include?("plugins/configuration/#{page.getId()}") -%>">
<a href="<%= ApplicationController.root_context -%>/plugins/configuration/<%= page.getId() -%>"><%= message(page.getId() + '.page', :default => page.getTitle()) %></a>
</li>
diff --git a/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java b/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java
index 460832cc7d5..c2ae3c9d7ed 100644
--- a/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java
@@ -19,15 +19,23 @@
*/
package org.sonar.server.ui;
-import org.junit.Test;
-import org.sonar.api.web.*;
-
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.number.OrderingComparisons.greaterThan;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
+import org.junit.Test;
+import org.sonar.api.web.DefaultTab;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.RequiredMeasures;
+import org.sonar.api.web.UserRole;
+import org.sonar.api.web.View;
+import org.sonar.api.web.Widget;
+import org.sonar.api.web.WidgetProperties;
+import org.sonar.api.web.WidgetProperty;
+import org.sonar.api.web.WidgetPropertyType;
+
public class ViewProxyTest {
@Test
@@ -52,8 +60,8 @@ public class ViewProxyTest {
ViewProxy proxy = new ViewProxy(view);
assertThat(proxy.getTarget(), is(view));
- assertArrayEquals(proxy.getSections(), new String[]{NavigationSection.RESOURCE});
- assertArrayEquals(proxy.getUserRoles(), new String[]{UserRole.ADMIN});
+ assertArrayEquals(proxy.getSections(), new String[] {NavigationSection.RESOURCE});
+ assertArrayEquals(proxy.getUserRoles(), new String[] {UserRole.ADMIN});
}
@Test
@@ -68,11 +76,10 @@ public class ViewProxyTest {
ViewProxy proxy = new ViewProxy(view);
assertThat(proxy.getTarget(), is(view));
- assertArrayEquals(proxy.getSections(), new String[]{NavigationSection.HOME});
+ assertArrayEquals(proxy.getSections(), new String[] {NavigationSection.HOME});
assertThat(proxy.getUserRoles().length, org.hamcrest.Matchers.is(0));
}
-
@Test
public void isDefaultTab() {
@DefaultTab
@@ -112,7 +119,7 @@ public class ViewProxyTest {
ViewProxy proxy = new ViewProxy<MyView>(new MyView());
assertThat(proxy.isDefaultTab(), is(false));
- assertThat(proxy.getDefaultTabForMetrics(), is(new String[]{"ncloc", "coverage"}));
+ assertThat(proxy.getDefaultTabForMetrics(), is(new String[] {"ncloc", "coverage"}));
}
@Test
@@ -133,6 +140,65 @@ public class ViewProxyTest {
ViewProxy proxy = new ViewProxy<Widget>(new WidgetWithOptionalProperties());
assertThat(proxy.hasRequiredProperties(), is(false));
}
+
+ @Test
+ public void shouldAcceptAvailableMeasuresForNoRequiredMeasures() throws Exception {
+ class MyView extends FakeView {
+ MyView() {
+ super("fake");
+ }
+ }
+ ViewProxy proxy = new ViewProxy<MyView>(new MyView());
+
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "ncloc", "coverage"}), is(true));
+ }
+
+ @Test
+ public void shouldAcceptAvailableMeasuresForMandatoryMeasures() throws Exception {
+ @RequiredMeasures(mandatory = {"lines", "ncloc"})
+ class MyView extends FakeView {
+ MyView() {
+ super("fake");
+ }
+ }
+ ViewProxy proxy = new ViewProxy<MyView>(new MyView());
+
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "ncloc", "coverage"}), is(true));
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "coverage"}), is(false));
+ }
+
+ @Test
+ public void shouldAcceptAvailableMeasuresForOneOfNeededMeasures() throws Exception {
+ @RequiredMeasures(oneOf = {"lines", "ncloc"})
+ class MyView extends FakeView {
+ MyView() {
+ super("fake");
+ }
+ }
+ ViewProxy proxy = new ViewProxy<MyView>(new MyView());
+
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "coverage"}), is(true));
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"complexity", "coverage"}), is(false));
+ }
+
+ @Test
+ public void shouldAcceptAvailableMeasuresForMandatoryAndOneOfNeededMeasures() throws Exception {
+ @RequiredMeasures(mandatory = {"lines", "ncloc"}, oneOf = {"duplications", "duplictated_blocks"})
+ class MyView extends FakeView {
+ MyView() {
+ super("fake");
+ }
+ }
+ ViewProxy proxy = new ViewProxy<MyView>(new MyView());
+
+ // ok, mandatory measures and 1 needed measure
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "ncloc", "coverage", "duplications"}), is(true));
+ // ko, one of the needed measures but not all of the mandatory ones
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "coverage", "duplications"}), is(false));
+ // ko, mandatory measures but no one of the needed measures
+ assertThat(proxy.acceptsAvailableMeasures(new String[] {"lines", "nloc", "coverage", "dsm"}), is(false));
+ }
+
}
class FakeView implements View {
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 fa4bded9aa2..eb83b805941 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
@@ -19,6 +19,14 @@
*/
package org.sonar.server.ui;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
import org.junit.Test;
import org.sonar.api.resources.Java;
import org.sonar.api.resources.Qualifiers;
@@ -28,14 +36,6 @@ import org.sonar.api.web.Page;
import org.sonar.api.web.View;
import org.sonar.api.web.Widget;
-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.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class ViewsTest {
private static FakeResourceViewer FAKE_TAB = new FakeResourceViewer();
@@ -75,15 +75,15 @@ public class ViewsTest {
@Test
public void getWidgets() {
final Views views = new Views(VIEWS);
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, null);
assertThat(widgets.size(), is(1));
assertThat(widgets.get(0).getTarget(), is(FakeWidget.class));
}
@Test
public void sortViewsByTitle() {
- final Views views = new Views(new View[]{new FakeWidget("ccc", "ccc"), new FakeWidget("aaa", "aaa"), new FakeWidget("bbb", "bbb")});
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ final Views views = new Views(new View[] {new FakeWidget("ccc", "ccc"), new FakeWidget("aaa", "aaa"), new FakeWidget("bbb", "bbb")});
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, null);
assertThat(widgets.size(), is(3));
assertThat(widgets.get(0).getId(), is("aaa"));
assertThat(widgets.get(1).getId(), is("bbb"));
@@ -92,8 +92,8 @@ public class ViewsTest {
@Test
public void prefixTitleByNumberToDisplayFirst() {
- final Views views = new Views(new View[]{new FakeWidget("other", "Other"), new FakeWidget("1id", "1widget"), new FakeWidget("2id", "2widget")});
- List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null);
+ final Views views = new Views(new View[] {new FakeWidget("other", "Other"), new FakeWidget("1id", "1widget"), new FakeWidget("2id", "2widget")});
+ List<ViewProxy<Widget>> widgets = views.getWidgets(null, null, null, null);
assertThat(widgets.size(), is(3));
assertThat(widgets.get(0).getId(), is("1id"));
assertThat(widgets.get(1).getId(), is("2id"));
@@ -103,7 +103,7 @@ public class ViewsTest {
@Test
public void acceptNavigationSection() {
ViewProxy proxy = mock(ViewProxy.class);
- when(proxy.getSections()).thenReturn(new String[]{NavigationSection.RESOURCE});
+ when(proxy.getSections()).thenReturn(new String[] {NavigationSection.RESOURCE});
when(proxy.isWidget()).thenReturn(false);
assertThat(Views.acceptNavigationSection(proxy, NavigationSection.RESOURCE), is(true));
@@ -113,6 +113,21 @@ public class ViewsTest {
}
@Test
+ public void acceptAvailableMeasures() {
+ ViewProxy proxy = mock(ViewProxy.class);
+ when(proxy.acceptsAvailableMeasures(new String[] {"lines"})).thenReturn(true);
+ when(proxy.acceptsAvailableMeasures(new String[] {"ncloc"})).thenReturn(false);
+
+ assertThat(Views.acceptAvailableMeasures(proxy, null), is(true));
+ assertThat(Views.acceptAvailableMeasures(proxy, new String[] {"lines"}), is(true));
+ assertThat(Views.acceptAvailableMeasures(proxy, new String[] {"ncloc"}), is(false));
+
+ assertThat(Views.accept(proxy, null, null, null, null, null), is(true));
+ assertThat(Views.accept(proxy, null, null, null, null, new String[] {"lines"}), is(true));
+ assertThat(Views.accept(proxy, null, null, null, null, new String[] {"ncloc"}), is(false));
+ }
+
+ @Test
public void doNotCheckNavigationSectionOnWidgets() {
ViewProxy proxy = mock(ViewProxy.class);
when(proxy.isWidget()).thenReturn(true);
@@ -128,7 +143,7 @@ public class ViewsTest {
ViewProxy proxy = mock(ViewProxy.class);
assertThat(Views.acceptResourceLanguage(proxy, Java.KEY), is(true));
- when(proxy.getResourceLanguages()).thenReturn(new String[]{"foo"});
+ when(proxy.getResourceLanguages()).thenReturn(new String[] {"foo"});
assertThat(Views.acceptResourceLanguage(proxy, Java.KEY), is(false));
assertThat(Views.acceptResourceLanguage(proxy, "foo"), is(true));
}
@@ -138,7 +153,7 @@ public class ViewsTest {
ViewProxy proxy = mock(ViewProxy.class);
assertThat(Views.acceptResourceScope(proxy, Scopes.FILE), is(true));
- when(proxy.getResourceScopes()).thenReturn(new String[]{Scopes.PROJECT, Scopes.FILE});
+ when(proxy.getResourceScopes()).thenReturn(new String[] {Scopes.PROJECT, Scopes.FILE});
assertThat(Views.acceptResourceScope(proxy, Scopes.FILE), is(true));
assertThat(Views.acceptResourceScope(proxy, Scopes.DIRECTORY), is(false));
}
@@ -148,8 +163,8 @@ public class ViewsTest {
ViewProxy proxy = mock(ViewProxy.class);
assertThat(Views.acceptResourceQualifier(proxy, Scopes.FILE), is(true));
- when(proxy.getResourceQualifiers()).thenReturn(new String[]{Qualifiers.CLASS, Qualifiers.FILE});
+ when(proxy.getResourceQualifiers()).thenReturn(new String[] {Qualifiers.CLASS, Qualifiers.FILE});
assertThat(Views.acceptResourceQualifier(proxy, Qualifiers.FILE), is(true));
assertThat(Views.acceptResourceQualifier(proxy, Qualifiers.PACKAGE), is(false));
}
-} \ No newline at end of file
+}