summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Gageot <david@gageot.net>2012-07-04 18:02:37 +0200
committerDavid Gageot <david@gageot.net>2012-07-04 19:14:10 +0200
commitb547f32a3de0381e41b2da6cfab1545f7447b315 (patch)
tree774cd48198c1789b13fb431e2bc16bb07c6b04cd
parentd9ec502ee20a45003ab3d55884cd95e1adfc22f2 (diff)
downloadsonarqube-b547f32a3de0381e41b2da6cfab1545f7447b315.tar.gz
sonarqube-b547f32a3de0381e41b2da6cfab1545f7447b315.zip
SONAR-3529 First baby step in providing property sets
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java3
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java28
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java60
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java16
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java12
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java12
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java2
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java22
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java30
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java44
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java78
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb33
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java38
21 files changed, 323 insertions, 133 deletions
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 86cb29c81a7..a9fb669fcd1 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
@@ -19,6 +19,8 @@
*/
package org.sonar.plugins.core;
+import org.sonar.plugins.core.widgets.DemoWidget;
+
import com.google.common.collect.ImmutableList;
import org.sonar.api.CoreProperties;
import org.sonar.api.Extension;
@@ -336,6 +338,7 @@ public final class CorePlugin extends SonarPlugin {
ReviewsMetricsWidget.class,
TreemapWidget.class,
FilterWidget.class,
+ DemoWidget.class,
// dashboards
DefaultDashboard.class,
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java
index e1c8cdde530..274e77f926c 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java
@@ -21,20 +21,18 @@ package org.sonar.plugins.core.widgets;
import org.sonar.api.web.*;
-@WidgetProperties(
- {
- @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC)
- }
-)
+@WidgetProperties({
+ @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC)
+})
public class CustomMeasuresWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "custom_measures";
@@ -48,4 +46,4 @@ public class CustomMeasuresWidget extends AbstractRubyTemplate implements RubyRa
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/custom_measures.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java
new file mode 100644
index 00000000000..48549921bef
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java
@@ -0,0 +1,60 @@
+/*
+ * 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.plugins.core.widgets;
+
+import org.sonar.api.web.WidgetPropertySet;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.RubyRailsWidget;
+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.api.web.WidgetScope;
+
+import static org.sonar.api.web.WidgetScope.*;
+
+@WidgetCategory("Global")
+@WidgetScope(GLOBAL)
+@WidgetProperties(sets = {
+ @WidgetPropertySet(key = "set1",
+ value = {
+ @WidgetProperty(key = "key1", type = WidgetPropertyType.STRING),
+ @WidgetProperty(key = "key2", type = WidgetPropertyType.STRING)
+ }), @WidgetPropertySet(key = "set2",
+ value = {
+ @WidgetProperty(key = "key3", type = WidgetPropertyType.STRING)
+ })}, value = {
+ @WidgetProperty(key = "key4", type = WidgetPropertyType.STRING)
+})
+public class DemoWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "demo";
+ }
+
+ public String getTitle() {
+ return "Demo";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/core/widgets/demo.html.erb";
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java
index f69f6ad46bb..a4a447679d4 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java
@@ -26,14 +26,12 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Hotspots" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "title", type = WidgetPropertyType.STRING),
- @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5")
- }
-)
+@WidgetCategory("Hotspots")
+@WidgetProperties({
+ @WidgetProperty(key = "title", type = WidgetPropertyType.STRING),
+ @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5")
+})
public class HotspotMetricWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "hotspot_metric";
@@ -47,4 +45,4 @@ public class HotspotMetricWidget extends AbstractRubyTemplate implements RubyRai
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/hotspots/hotspot_metric.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java
index 6c78fb9c14c..383cfcecf16 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java
@@ -26,12 +26,10 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Hotspots" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5")
- }
-)
+@WidgetCategory("Hotspots")
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5")
+})
public class HotspotMostViolatedResourcesWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
@@ -46,4 +44,4 @@ public class HotspotMostViolatedResourcesWidget extends AbstractRubyTemplate imp
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/hotspots/hotspot_most_violated_resources.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java
index b58ed0cd89a..7fff488beb6 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java
@@ -22,12 +22,10 @@ package org.sonar.plugins.core.widgets;
import org.sonar.api.web.*;
@WidgetCategory("Hotspots")
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"),
- @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO")
- }
-)
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"),
+ @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO")
+})
public class HotspotMostViolatedRulesWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "hotspot_most_violated_rules";
@@ -41,4 +39,4 @@ public class HotspotMostViolatedRulesWidget extends AbstractRubyTemplate impleme
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/hotspots/hotspot_most_violated_rules.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java
index 47f2f63c562..be8a76ab3d7 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java
@@ -26,7 +26,7 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({"History"})
+@WidgetCategory("History")
@WidgetProperties({
@WidgetProperty(key = "title", type = WidgetPropertyType.STRING),
@WidgetProperty(key = "numberOfColumns", type = WidgetPropertyType.INTEGER, defaultValue = "3"),
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java
index 7ecc27de672..6a2857d7839 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java
@@ -26,17 +26,15 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "History" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "chartTitle", type = WidgetPropertyType.STRING),
- @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
- @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
- @WidgetProperty(key = "hideEvents", type = WidgetPropertyType.BOOLEAN),
- @WidgetProperty(key = "chartHeight", type = WidgetPropertyType.INTEGER, defaultValue = "80")
- }
-)
+@WidgetCategory("History")
+@WidgetProperties({
+ @WidgetProperty(key = "chartTitle", type = WidgetPropertyType.STRING),
+ @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
+ @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "hideEvents", type = WidgetPropertyType.BOOLEAN),
+ @WidgetProperty(key = "chartHeight", type = WidgetPropertyType.INTEGER, defaultValue = "80")
+})
public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "timeline";
@@ -50,4 +48,4 @@ public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWid
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/timeline.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java
index f973ab6bf36..6e527219c16 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java
@@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Reviews" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
- description="Maximum number of reviews displayed at the same time.")
- }
-)
+@WidgetCategory("Reviews")
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
+ description = "Maximum number of reviews displayed at the same time.")
+})
public class FalsePositiveReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "false_positive_reviews";
@@ -46,4 +44,4 @@ public class FalsePositiveReviewsWidget extends AbstractRubyTemplate implements
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/reviews/false_positive_reviews.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java
index 0e3a385576a..ed95dddabeb 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java
@@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Reviews" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
- description="Maximum number of reviews displayed at the same time.")
- }
-)
+@WidgetCategory("Reviews")
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
+ description = "Maximum number of reviews displayed at the same time.")
+})
public class MyReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "my_reviews";
@@ -46,4 +44,4 @@ public class MyReviewsWidget extends AbstractRubyTemplate implements RubyRailsWi
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/reviews/my_reviews.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java
index eb1abe18dc0..2b80d72572e 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java
@@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Action plans", "Reviews" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
- description="Maximum number of reviews displayed at the same time.")
- }
-)
+@WidgetCategory({"Action plans", "Reviews"})
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
+ description = "Maximum number of reviews displayed at the same time.")
+})
public class PlannedReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "planned_reviews";
@@ -46,4 +44,4 @@ public class PlannedReviewsWidget extends AbstractRubyTemplate implements RubyRa
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/reviews/planned_reviews.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java
index 1551e84233e..c0200af47ac 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java
@@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Reviews" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
- description="Maximum number of reviews displayed at the same time.")
- }
-)
+@WidgetCategory("Reviews")
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
+ description = "Maximum number of reviews displayed at the same time.")
+})
public class ProjectReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "project_reviews";
@@ -46,4 +44,4 @@ public class ProjectReviewsWidget extends AbstractRubyTemplate implements RubyRa
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/reviews/project_reviews.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java
index 83660e87af3..014fc25acf5 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java
@@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;
-@WidgetCategory({ "Action plans", "Reviews" })
-@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
- description="Maximum number of reviews displayed at the same time.")
- }
-)
+@WidgetCategory({"Action plans", "Reviews"})
+@WidgetProperties({
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5",
+ description = "Maximum number of reviews displayed at the same time.")
+})
public class UnplannedReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "unplanned_reviews";
@@ -46,4 +44,4 @@ public class UnplannedReviewsWidget extends AbstractRubyTemplate implements Ruby
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/reviews/unplanned_reviews.html.erb";
}
-} \ No newline at end of file
+}
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb
new file mode 100644
index 00000000000..197d45b202c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb
@@ -0,0 +1,4 @@
+Key1: <%= widget_properties['key1'] -%>
+Key2: <%= widget_properties['key2'] -%>
+Key3: <%= widget_properties['key3'] -%>
+Key4: <%= widget_properties['key4'] -%>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java
new file mode 100644
index 00000000000..d49158d732e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public interface PropertyValidation {
+ boolean validate(String value);
+
+ class None implements PropertyValidation {
+ public boolean validate(String value) {
+ return true;
+ }
+ };
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java
index 03b302cd395..f4fd18a874f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java
@@ -25,4 +25,6 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface WidgetProperties {
WidgetProperty[] value() default {};
+
+ WidgetPropertySet[] sets() default {};
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java
index 4fd661a7dfb..652076a1d48 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java
@@ -37,4 +37,6 @@ public @interface WidgetProperty {
String description() default "";
boolean optional() default true;
+
+ Class<? extends PropertyValidation> validation() default PropertyValidation.None.class;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java
new file mode 100644
index 00000000000..28de523e788
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java
@@ -0,0 +1,44 @@
+/*
+ * 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.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface WidgetPropertySet {
+ String key();
+
+ WidgetProperty[] value();
+
+ static WidgetPropertySet DEFAULT = new WidgetPropertySet() {
+ public Class<WidgetPropertySet> annotationType() {
+ return WidgetPropertySet.class;
+ }
+
+ public WidgetProperty[] value() {
+ return null;
+ }
+
+ public String key() {
+ return "";
+ }
+ };
+}
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 ae3c1b29798..0770add2d14 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
@@ -20,7 +20,10 @@
package org.sonar.server.ui;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
@@ -43,6 +46,7 @@ import org.sonar.api.web.WidgetLayout;
import org.sonar.api.web.WidgetLayoutType;
import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
+import org.sonar.api.web.WidgetPropertySet;
import org.sonar.api.web.WidgetScope;
import java.util.Collection;
@@ -51,7 +55,8 @@ import java.util.Map;
@SuppressWarnings("rawtypes")
public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
- private V view;
+ private final V view;
+ private final boolean isWidget;
private String[] sections = {NavigationSection.HOME};
private String[] userRoles = {};
private String[] resourceScopes = {};
@@ -60,16 +65,17 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
private String[] defaultForMetrics = {};
private String description = "";
private Map<String, WidgetProperty> widgetPropertiesByKey = Maps.newHashMap();
+ private SetMultimap<WidgetPropertySet, WidgetProperty> widgetPropertiesBySet = LinkedHashMultimap.create();
private String[] widgetCategories = {};
private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT;
private boolean isDefaultTab = false;
- private boolean isWidget = false;
private boolean isGlobal = false;
private String[] mandatoryMeasures = {};
private String[] needOneOfMeasures = {};
- public ViewProxy(final V view) {
+ public ViewProxy(V view) {
this.view = view;
+ this.isWidget = (view instanceof Widget);
initUserRoles(view);
initSections(view);
@@ -83,8 +89,6 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
initWidgetLayout(view);
initWidgetGlobal(view);
initRequiredMeasures(view);
-
- isWidget = (view instanceof Widget);
}
private void initRequiredMeasures(V view) {
@@ -95,14 +99,14 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
}
}
- private void initWidgetLayout(final V view) {
+ private void initWidgetLayout(V view) {
WidgetLayout layoutAnnotation = AnnotationUtils.getAnnotation(view, WidgetLayout.class);
if (layoutAnnotation != null) {
widgetLayout = layoutAnnotation.value();
}
}
- private void initWidgetCategory(final V view) {
+ private void initWidgetCategory(V view) {
WidgetCategory categAnnotation = AnnotationUtils.getAnnotation(view, WidgetCategory.class);
if (categAnnotation != null) {
widgetCategories = categAnnotation.value();
@@ -125,29 +129,35 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
}
}
- private void initWidgetProperties(final V view) {
+ private void initWidgetProperties(V view) {
WidgetProperties propAnnotation = AnnotationUtils.getAnnotation(view, WidgetProperties.class);
if (propAnnotation != null) {
+ for (WidgetPropertySet set : propAnnotation.sets()) {
+ for (WidgetProperty property : set.value()) {
+ widgetPropertiesBySet.put(set, property);
+ widgetPropertiesByKey.put(property.key(), property);
+ }
+ }
for (WidgetProperty property : propAnnotation.value()) {
+ widgetPropertiesBySet.put(WidgetPropertySet.DEFAULT, property);
widgetPropertiesByKey.put(property.key(), property);
}
}
}
- private void initDescription(final V view) {
+ private void initDescription(V view) {
Description descriptionAnnotation = AnnotationUtils.getAnnotation(view, Description.class);
if (descriptionAnnotation != null) {
description = descriptionAnnotation.value();
}
}
- private void initDefaultTabInfo(final V view) {
+ private void initDefaultTabInfo(V view) {
DefaultTab defaultTabAnnotation = AnnotationUtils.getAnnotation(view, DefaultTab.class);
if (defaultTabAnnotation != null) {
if (defaultTabAnnotation.metrics().length == 0) {
isDefaultTab = true;
defaultForMetrics = new String[0];
-
} else {
isDefaultTab = false;
defaultForMetrics = defaultTabAnnotation.metrics();
@@ -155,35 +165,35 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
}
}
- private void initResourceLanguage(final V view) {
+ private void initResourceLanguage(V view) {
ResourceLanguage languageAnnotation = AnnotationUtils.getAnnotation(view, ResourceLanguage.class);
if (languageAnnotation != null) {
resourceLanguages = languageAnnotation.value();
}
}
- private void initResourceQualifier(final V view) {
+ private void initResourceQualifier(V view) {
ResourceQualifier qualifierAnnotation = AnnotationUtils.getAnnotation(view, ResourceQualifier.class);
if (qualifierAnnotation != null) {
resourceQualifiers = qualifierAnnotation.value();
}
}
- private void initResourceScope(final V view) {
+ private void initResourceScope(V view) {
ResourceScope scopeAnnotation = AnnotationUtils.getAnnotation(view, ResourceScope.class);
if (scopeAnnotation != null) {
resourceScopes = scopeAnnotation.value();
}
}
- private void initSections(final V view) {
+ private void initSections(V view) {
NavigationSection sectionAnnotation = AnnotationUtils.getAnnotation(view, NavigationSection.class);
if (sectionAnnotation != null) {
sections = sectionAnnotation.value();
}
}
- private void initUserRoles(final V view) {
+ private void initUserRoles(V view) {
UserRole userRoleAnnotation = AnnotationUtils.getAnnotation(view, UserRole.class);
if (userRoleAnnotation != null) {
userRoles = userRoleAnnotation.value();
@@ -200,7 +210,7 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
public boolean isController() {
String id = view.getId();
- return id !=null && id.length()>0 && id.charAt(0)=='/';
+ return id != null && !id.isEmpty() && id.charAt(0) == '/';
}
public String getTitle() {
@@ -215,6 +225,10 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
return widgetPropertiesByKey.values();
}
+ public SetMultimap<WidgetPropertySet, WidgetProperty> getWidgetPropertiesBySet() {
+ return ImmutableSetMultimap.copyOf(widgetPropertiesBySet);
+ }
+
public WidgetProperty getWidgetProperty(String propertyKey) {
return widgetPropertiesByKey.get(propertyKey);
}
@@ -254,23 +268,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;
- }
+ }
+
+ for (String neededMeasure : needOneOfMeasures) {
+ if (ArrayUtils.contains(availableMeasures, neededMeasure)) {
+ return true;
}
- return false;
}
+ return false;
}
public boolean isWidget() {
@@ -294,13 +309,20 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
}
public boolean hasRequiredProperties() {
- boolean requires = false;
for (WidgetProperty property : getWidgetProperties()) {
if (!property.optional() && StringUtils.isEmpty(property.defaultValue())) {
- requires = true;
+ return true;
}
}
- return requires;
+ return false;
+ }
+
+ public boolean validate(WidgetProperty property, String value) {
+ try {
+ return property.validation().newInstance().validate(value);
+ } catch (Exception e) {
+ }
+ return true;
}
@Override
@@ -336,7 +358,6 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
.append("languages", resourceLanguages)
.append("metrics", defaultForMetrics)
.toString();
-
}
public int compareTo(ViewProxy other) {
@@ -344,6 +365,5 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
.append(getTitle(), other.getTitle())
.append(getId(), other.getId())
.toComparison();
-
}
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb
index 79225aa2e72..b14797cfcc3 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb
@@ -13,16 +13,29 @@
</tr>
<% end %>
- <% widget.java_definition.getWidgetProperties().sort { |w1, w2| natural_comparison(w1.key, w2.key) }.each do |property_def| %>
- <tr>
- <td class="form-key-cell"><%= property_def.key() -%><%= " *" unless property_def.optional() -%></td>
- <td class="form-val-cell" id="row_<%= property_def.key() -%>">
- <%= property_value_field(property_def, widget.property_text_value(property_def.key())) -%>
- <div class="form-val-note">
- <%= message("widget." + widget.key + ".param." + property_def.key(), :default => property_def.description()) -%>
- </div>
- </td>
- </tr>
+ <% widget.java_definition.getWidgetPropertiesBySet().asMap().sort { |(s1,p1), (s2,p2)| natural_comparison(s1.key, s2.key) }.each do |set,properties| %>
+ <% if widget.java_definition.getWidgetPropertiesBySet().asMap().size > 1 %>
+ <% if set.key().empty? %>
+ <tr>
+ <td celspan="2"><h3>other</h3></td>
+ </tr>
+ <% else %>
+ <tr>
+ <td celspan="2"><h3><%= set.key() -%></h3></td>
+ </tr>
+ <% end %>
+ <% end %>
+ <% properties.sort { |w1, w2| natural_comparison(w1.key, w2.key) }.each do |property_def| %>
+ <tr>
+ <td class="form-key-cell"><%= property_def.key() -%><%= " *" unless property_def.optional() -%></td>
+ <td class="form-val-cell" id="row_<%= property_def.key() -%>">
+ <%= property_value_field(property_def, widget.property_text_value(property_def.key())) -%>
+ <div class="form-val-note">
+ <%= message("widget." + widget.key + ".param." + property_def.key(), :default => property_def.description()) -%>
+ </div>
+ </td>
+ </tr>
+ <% end %>
<% end %>
<tr>
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 e3260630791..725592eb4ab 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,6 +19,8 @@
*/
package org.sonar.server.ui;
+import org.sonar.api.web.WidgetPropertySet;
+
import org.junit.rules.ExpectedException;
import org.junit.Rule;
@@ -140,6 +142,15 @@ public class ViewProxyTest {
}
@Test
+ public void should_support_property_sets() {
+ ViewProxy proxy = new ViewProxy<Widget>(new EditableWidgetWithSets());
+
+ assertThat(proxy.getWidgetProperties()).hasSize(4);
+ assertThat(proxy.getWidgetPropertiesBySet().keySet()).hasSize(3);
+ assertThat(proxy.getWidgetPropertiesBySet().values()).hasSize(4);
+ }
+
+ @Test
public void widget_should_not_be_global_by_default() {
ViewProxy proxy = new ViewProxy<Widget>(new EditableWidget());
@@ -158,7 +169,7 @@ public class ViewProxyTest {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("INVALID");
exception.expectMessage("WidgetWithInvalidScope");
-
+
new ViewProxy<Widget>(new WidgetWithInvalidScope());
}
@@ -256,7 +267,6 @@ class FakeView implements View {
@WidgetProperty(key = "bar", defaultValue = "30", type = WidgetPropertyType.INTEGER)
})
class EditableWidget implements Widget {
-
public String getId() {
return "w1";
}
@@ -266,6 +276,29 @@ class EditableWidget implements Widget {
}
}
+@WidgetProperties(sets = {
+ @WidgetPropertySet(key = "set1",
+ value = {
+ @WidgetProperty(key = "foo", optional = false),
+ @WidgetProperty(key = "bar", optional = false),
+ }),
+ @WidgetPropertySet(key = "set2",
+ value = {
+ @WidgetProperty(key = "qix", optional = false),
+ })},
+ value = {
+ @WidgetProperty(key = "fizz", optional = false)
+ })
+class EditableWidgetWithSets implements Widget {
+ public String getId() {
+ return "w3";
+ }
+
+ public String getTitle() {
+ return "W3";
+ }
+}
+
@WidgetScope("GLOBAL")
class GlobalWidget implements Widget {
public String getId() {
@@ -293,7 +326,6 @@ class WidgetWithInvalidScope implements Widget {
@WidgetProperty(key = "bar")
})
class WidgetWithOptionalProperties implements Widget {
-
public String getId() {
return "w2";
}