From ec7f0817eed5c7824bb2145c6bd520ab480ba3b9 Mon Sep 17 00:00:00 2001 From: simonbrandhof Date: Thu, 28 Oct 2010 08:02:18 +0000 Subject: [PATCH] SONAR-1643 apply a part of the GSOC branch --- .../org/sonar/plugins/core/CorePlugin.java | 10 + .../core/widgets/DefaultAlertsWidget.java | 39 + .../widgets/DefaultCodeCoverageWidget.java | 39 + .../DefaultCommentsDuplicationsWidget.java | 39 + .../widgets/DefaultDescriptionWidget.java | 39 + .../DefaultExtendedAnalysisWidget.java | 39 + .../core/widgets/DefaultRulesWidget.java | 39 + .../widgets/DefaultStaticAnalysisWidget.java | 39 + .../plugins/core/widgets/_alerts.html.erb | 10 + .../core/widgets/_code_coverage.html.erb | 45 + .../widgets/_comments_duplications.html.erb | 37 + .../core/widgets/_description.html.erb | 27 + .../core/widgets/_extended_analysis.html.erb | 126 + .../plugins/core/widgets/_rules.html.erb | 132 + .../core/widgets/_static_analysis.html.erb | 63 + .../org/sonar/jpa/entity/SchemaMigration.java | 2 +- .../java/org/sonar/api/web/Description.java | 37 + .../org/sonar/api/web/WidgetProperties.java | 36 + .../org/sonar/api/web/WidgetProperty.java | 50 + sonar-server/pom.xml | 4 +- .../java/org/sonar/server/ui/JRubyFacade.java | 9 + .../java/org/sonar/server/ui/ViewProxy.java | 20 + .../main/java/org/sonar/server/ui/Views.java | 5 + .../admin_dashboards_controller.rb | 101 + .../app/controllers/dashboard_controller.rb | 154 + .../app/controllers/dashboards_controller.rb | 152 + .../WEB-INF/app/helpers/dashboard_helper.rb | 65 + .../WEB-INF/app/helpers/dashboards_helper.rb | 66 + .../app/helpers/widget_properties_helper.rb | 73 + .../WEB-INF/app/models/active_dashboard.rb | 59 + .../webapp/WEB-INF/app/models/dashboard.rb | 77 + .../main/webapp/WEB-INF/app/models/user.rb | 3 + .../main/webapp/WEB-INF/app/models/widget.rb | 86 + .../WEB-INF/app/models/widget_property.rb | 49 + .../app/views/admin_dashboards/index.html.erb | 72 + .../dashboard/_configure_widget.html.erb | 41 + .../app/views/dashboard/_header.html.erb | 29 + .../app/views/dashboard/_widget.html.erb | 22 + .../dashboard/_widget_definition.html.erb | 7 + .../app/views/dashboard/configure.html.erb | 82 + .../app/views/dashboard/edit_layout.html.erb | 28 + .../app/views/dashboard/index.html.erb | 26 + .../app/views/dashboards/_create.html.erb | 35 + .../app/views/dashboards/_dashboard.html.erb | 186 + .../app/views/dashboards/_edit.html.erb | 32 + .../app/views/dashboards/_editform.html.erb | 47 + .../app/views/dashboards/_layouts.html.erb | 25 + .../views/dashboards/_snapshot_title.html.erb | 12 + .../app/views/dashboards/_tabs.html.erb | 21 + .../app/views/dashboards/_widget.html.erb | 100 + .../app/views/dashboards/_widget_update.rjs | 6 + .../app/views/dashboards/index.html.erb | 62 + .../app/views/dashboards/manage.html.erb | 86 + .../WEB-INF/app/views/dashboards/new.html.erb | 6 + .../WEB-INF/app/views/layouts/_head.html.erb | 2 + .../app/views/layouts/_layout.html.erb | 5 +- .../db/migrate/151_create_dashboards.rb | 89 + .../src/main/webapp/images/layout100.png | Bin 0 -> 321 bytes .../src/main/webapp/images/layout3070.png | Bin 0 -> 369 bytes .../src/main/webapp/images/layout333333.png | Bin 0 -> 431 bytes .../src/main/webapp/images/layout5050.png | Bin 0 -> 364 bytes .../src/main/webapp/images/layout7030.png | Bin 0 -> 355 bytes .../src/main/webapp/javascripts/dashboard.js | 100 + .../main/webapp/javascripts/scriptaculous.js | 5377 ++++++++++------- .../src/main/webapp/stylesheets/dashboard.css | 216 + .../src/main/webapp/stylesheets/style.css | 4 + 66 files changed, 6313 insertions(+), 2176 deletions(-) create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultAlertsWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCodeCoverageWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCommentsDuplicationsWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultDescriptionWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultExtendedAnalysisWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultRulesWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultStaticAnalysisWidget.java create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_alerts.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_code_coverage.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_comments_duplications.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_description.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_extended_analysis.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_rules.html.erb create mode 100644 plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_static_analysis.html.erb create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/Description.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/controllers/admin_dashboards_controller.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboards_helper.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/helpers/widget_properties_helper.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/models/active_dashboard.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/models/widget.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/admin_dashboards/index.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_configure_widget.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_definition.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/edit_layout.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/index.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_create.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_dashboard.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_edit.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_editform.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_layouts.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_snapshot_title.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_tabs.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget_update.rjs create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/index.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/manage.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/new.html.erb create mode 100755 sonar-server/src/main/webapp/WEB-INF/db/migrate/151_create_dashboards.rb create mode 100644 sonar-server/src/main/webapp/images/layout100.png create mode 100644 sonar-server/src/main/webapp/images/layout3070.png create mode 100644 sonar-server/src/main/webapp/images/layout333333.png create mode 100644 sonar-server/src/main/webapp/images/layout5050.png create mode 100644 sonar-server/src/main/webapp/images/layout7030.png create mode 100644 sonar-server/src/main/webapp/javascripts/dashboard.js create mode 100644 sonar-server/src/main/webapp/stylesheets/dashboard.css 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 2fa67c2613a..e96985a92cb 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 @@ -39,6 +39,7 @@ import org.sonar.plugins.core.sensors.*; import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition; import org.sonar.plugins.core.ui.pageselector.GwtPageSelector; import org.sonar.plugins.core.violationsviewer.ViolationsViewerDefinition; +import org.sonar.plugins.core.widgets.*; import java.util.ArrayList; import java.util.List; @@ -143,6 +144,15 @@ public class CorePlugin implements Plugin { extensions.add(Clouds.class); extensions.add(Hotspots.class); + //widgets + extensions.add(DefaultAlertsWidget.class); + extensions.add(DefaultCodeCoverageWidget.class); + extensions.add(DefaultCommentsDuplicationsWidget.class); + extensions.add(DefaultDescriptionWidget.class); + extensions.add(DefaultExtendedAnalysisWidget.class); + extensions.add(DefaultRulesWidget.class); + extensions.add(DefaultStaticAnalysisWidget.class); + // chart extensions.add(XradarChart.class); extensions.add(DistributionBarChart.class); diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultAlertsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultAlertsWidget.java new file mode 100644 index 00000000000..b826b83fdd7 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultAlertsWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultAlertsWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "alerts"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Alerts"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_alerts.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCodeCoverageWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCodeCoverageWidget.java new file mode 100644 index 00000000000..88471b2f755 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCodeCoverageWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultCodeCoverageWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "code_coverage"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Code coverage"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_code_coverage.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCommentsDuplicationsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCommentsDuplicationsWidget.java new file mode 100644 index 00000000000..8d1a8da9eec --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultCommentsDuplicationsWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultCommentsDuplicationsWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "comments_duplications"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Comments duplications"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_comments_duplications.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultDescriptionWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultDescriptionWidget.java new file mode 100644 index 00000000000..455fe019dd7 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultDescriptionWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultDescriptionWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "description"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Description"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_description.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultExtendedAnalysisWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultExtendedAnalysisWidget.java new file mode 100644 index 00000000000..516b910653c --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultExtendedAnalysisWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultExtendedAnalysisWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "extended_analysis"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Extended analysis"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_extended_analysis.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultRulesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultRulesWidget.java new file mode 100644 index 00000000000..f2985fa6ccf --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultRulesWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultRulesWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "rules"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Rules"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_rules.html.erb"; + } +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultStaticAnalysisWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultStaticAnalysisWidget.java new file mode 100644 index 00000000000..9c10430c774 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DefaultStaticAnalysisWidget.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; + +public class DefaultStaticAnalysisWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "static_analysis"; + } + + public String getTitle() { + // not used for the moment by widgets. + return "Static analysis"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/_static_analysis.html.erb"; + } +} diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_alerts.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_alerts.html.erb new file mode 100644 index 00000000000..095afb59331 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_alerts.html.erb @@ -0,0 +1,10 @@ +<% m=measure(Metric::ALERT_STATUS) + if m && !m.alert_status.blank? + css_class = "widget color_#{m.alert_status}" + if m.alert_status==Metric::TYPE_LEVEL_OK + label = 'No alerts.' + else + label = "Alerts : #{h(m.alert_text)}." + end +%>
<%= format_measure(m) -%> <%= label -%>
+<% end %> diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_code_coverage.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_code_coverage.html.erb new file mode 100644 index 00000000000..02b1dc5ef5a --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_code_coverage.html.erb @@ -0,0 +1,45 @@ +<% +code_coverage_measure=measure(Metric::COVERAGE) +tests_measure=measure(Metric::TESTS) +if code_coverage_measure || tests_measure %> +
+
+
+

Code coverage

+

<%= format_measure(code_coverage_measure, :suffix => '', :url => url_for_drilldown(Metric::COVERAGE), :default => '-') %> <%= tendency_icon(code_coverage_measure, false) %>

+ <% line_coverage=measure(Metric::LINE_COVERAGE) + if line_coverage %> +

<%= format_measure(line_coverage, :suffix => ' line coverage', :url => url_for_drilldown(Metric::UNCOVERED_LINES, :highlight => Metric::LINE_COVERAGE)) %> <%= tendency_icon(line_coverage) %>

+ <% end %> + <% branch_coverage=measure(Metric::BRANCH_COVERAGE) + if branch_coverage %> +

<%= format_measure(branch_coverage, :suffix => ' branch coverage', :url => url_for_drilldown(Metric::UNCOVERED_CONDITIONS, :highlight => Metric::BRANCH_COVERAGE)) %> <%= tendency_icon(branch_coverage) %>

+ <% end %> +

<%= format_measure(tests_measure, :suffix => ' tests', :url => url_for_drilldown(Metric::TESTS)) %> <%= tendency_icon(tests_measure) %>

+ <% skipped_measure=measure(Metric::SKIPPED_TESTS) + if skipped_measure && skipped_measure.value>0 + %> +

+<%= format_measure(skipped_measure, :suffix => ' skipped', :url => url_for_drilldown(Metric::SKIPPED_TESTS)) %> <%= tendency_icon(skipped_measure) %>

+ <% end %> +

<%= format_measure(Metric::TEST_EXECUTION_TIME, :suffix => '', :url => url_for_drilldown(Metric::TEST_EXECUTION_TIME)) %> <%= tendency_icon(measure(Metric::TEST_EXECUTION_TIME)) %>

+
+
+
+ <% + success_percentage=measure(Metric::TEST_SUCCESS_DENSITY) + if success_percentage + %> +
+

Test success

+

<%= format_measure(success_percentage, :suffix => '', :url => url_for_drilldown(success_percentage)) %> <%= tendency_icon(measure(Metric::TEST_SUCCESS_DENSITY), false) %>

+

+ <%= format_measure(Metric::TEST_FAILURES, :suffix => ' failures', :url => url_for_drilldown(Metric::TEST_FAILURES)) %> <%= tendency_icon(measure(Metric::TEST_FAILURES)) %> +

+

+ <%= format_measure(Metric::TEST_ERRORS, :suffix => ' errors', :url => url_for_drilldown(Metric::TEST_ERRORS)) %> <%= tendency_icon(measure(Metric::TEST_ERRORS)) %> +

+
+ <% end %> +
+
+<% end %> diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_comments_duplications.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_comments_duplications.html.erb new file mode 100644 index 00000000000..b7b7e2f54cd --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_comments_duplications.html.erb @@ -0,0 +1,37 @@ +<% if measure(Metric::LINES) || measure(Metric::NCLOC) %> + + + + + + +
+ <% if (measure(Metric::COMMENT_LINES)) %> +
+

Comments

+

<%= format_measure(Metric::COMMENT_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::COMMENT_LINES_DENSITY)) -%> <%= tendency_icon(Metric::COMMENT_LINES_DENSITY, false) -%>

+

<%= format_measure(Metric::COMMENT_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::COMMENT_LINES)) -%> <%= tendency_icon(Metric::COMMENT_LINES) -%>

+ <% + comment_blank_lines=measure('comment_blank_lines') + if comment_blank_lines && comment_blank_lines.value>0 + %> +

+<%= format_measure(comment_blank_lines, :suffix => ' blank', :url => url_for_drilldown(comment_blank_lines)) -%> <%= tendency_icon(comment_blank_lines) -%>

+ <% end %> +

<%= format_measure(Metric::PUBLIC_DOCUMENTED_API_DENSITY, :suffix => ' docu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_DOCUMENTED_API_DENSITY)) -%> <%= tendency_icon(Metric::PUBLIC_DOCUMENTED_API_DENSITY) -%>

+

<%= format_measure(Metric::PUBLIC_UNDOCUMENTED_API, :suffix => ' undocu. API', :url => url_for_drilldown(Metric::PUBLIC_UNDOCUMENTED_API, :highlight => Metric::PUBLIC_UNDOCUMENTED_API)) -%> <%= tendency_icon(Metric::PUBLIC_UNDOCUMENTED_API) -%>

+

<%= format_measure(Metric::COMMENTED_OUT_CODE_LINES, :suffix => ' commented LOCs', :url => url_for_drilldown(Metric::COMMENTED_OUT_CODE_LINES, :highlight => Metric::COMMENTED_OUT_CODE_LINES)) -%> <%= tendency_icon(Metric::COMMENTED_OUT_CODE_LINES) -%>

+
+ <% end %> +
+ <% if (measure(Metric::DUPLICATED_LINES_DENSITY)) %> +
+

Duplications

+

<%= format_measure(Metric::DUPLICATED_LINES_DENSITY, :suffix => '', :url => url_for_drilldown(Metric::DUPLICATED_LINES, :highlight => Metric::DUPLICATED_LINES_DENSITY)) -%> <%= tendency_icon(Metric::DUPLICATED_LINES_DENSITY, false) -%>

+

<%= format_measure(Metric::DUPLICATED_LINES, :suffix => ' lines', :url => url_for_drilldown(Metric::DUPLICATED_LINES)) -%> <%= tendency_icon(Metric::DUPLICATED_LINES) -%>

+

<%= format_measure(Metric::DUPLICATED_BLOCKS, :suffix => ' blocks', :url => url_for_drilldown(Metric::DUPLICATED_BLOCKS)) -%> <%= tendency_icon(Metric::DUPLICATED_BLOCKS) -%>

+

<%= format_measure(Metric::DUPLICATED_FILES, :suffix => ' files', :url => url_for_drilldown(Metric::DUPLICATED_FILES)) -%> <%= tendency_icon(Metric::DUPLICATED_FILES) -%>

+
+ <% end %> + +
+<% end %> \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_description.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_description.html.erb new file mode 100644 index 00000000000..8ae52acc63b --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_description.html.erb @@ -0,0 +1,27 @@ + <% if not @project.description.blank? %> + <%= h @project.description %>
+ <% end %> + + Key : <%= @project.key %>
+<% if @project.language %> + Language : <%= @project.language %>
+<% end %> + + + <% links_count=@project.project_links.size + if links_count > 0 %> + + <% end %> + <% if Project::SCOPE_SET==@project.scope %> + + <% end %> +
+ <% @project.project_links.sort.each_with_index do |link, index| %> + <% if index==links_count/2 %><% end %> + <%= link_to(image_tag(link.icon, :alt => link.name), link.href , :popup => true, :class => 'nolink') -%> + <%= link_to(h(link.name), link.href, :popup => true) -%>
+ <% end %> +
+ <%= image_tag 'feed-icon-14x14.png' %> + Alerts feed +
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_extended_analysis.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_extended_analysis.html.erb new file mode 100644 index 00000000000..230633d4e24 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_extended_analysis.html.erb @@ -0,0 +1,126 @@ +<% + file_complexity=measure('file_complexity') + function_complexity=measure('function_complexity') + class_complexity=measure('class_complexity') + paragraph_complexity=measure('paragraph_complexity') + + if file_complexity || function_complexity || class_complexity || paragraph_complexity + complexity=measure('complexity') +%> +
+

Complexity

+ <% if function_complexity %> +

+ <%= format_measure(function_complexity, :suffix => '', :url => url_for_drilldown(function_complexity)) -%> <%= tendency_icon(function_complexity, false) -%>/ method +

+ <% end %> + <% if paragraph_complexity %> +

+ <%= format_measure(paragraph_complexity, :suffix => '', :url => url_for_drilldown(paragraph_complexity)) -%> <%= tendency_icon(paragraph_complexity, false) -%>/ paragraph +

+ <% end %> + <% if class_complexity %> +

+ <%= format_measure(class_complexity, :suffix => '', :url => url_for_drilldown(class_complexity)) -%> <%= tendency_icon(class_complexity, false) -%>/ class +

+ <% end %> + <% if file_complexity %> +

+ <%= format_measure(file_complexity, :suffix => '', :url => url_for_drilldown(file_complexity)) -%> <%= tendency_icon(file_complexity, false) -%>/ file +

+ <% end %> + <% if complexity %> +

+ Total: <%= format_measure(complexity, :url => url_for_drilldown(complexity)) -%> <%= tendency_icon(complexity) -%> +

+ <% end %> +
+ + +<% + function_distribution=measure('function_complexity_distribution') + paragraph_distribution=measure('paragraph_complexity_distribution') + class_distribution=measure('class_complexity_distribution') + file_distribution=measure('file_complexity_distribution') + distributions=[function_distribution,paragraph_distribution,class_distribution,file_distribution].compact + selected_distribution=nil + if distributions.size>0 + selected_distribution=distributions.first + end + if selected_distribution +%> +
+ + + <% distributions.each do |distribution_measure| %> + <% metric = distribution_measure.metric.key + dist_measure = measure(metric) + title = distribution_measure.metric.description + visible = (selected_distribution==distribution_measure) + if dist_measure && !dist_measure.data.blank? + %> +
"> + <% + query="ck=distbar&c=777777&v=" + u(dist_measure.data) + small_size_query=query + '&w=220&h=100&fs=8&bgc=ffffff' + big_size_query=query + '&w=300&h=150&fs=12&bgc=CAE3F2' + %><%= chart(small_size_query, :id => 'chart_img_' + metric, :alt => title) -%> + + +
+ <% end %> + <% end %> + +
+ <% + count_dist=0 + if function_distribution + count_dist+=1 + %> + > + <% + end + if paragraph_distribution + count_dist+=1 + %> + > <%= '
' if count_dist==2 %> + <% + end + if class_distribution + count_dist+=1 + %> + > <%= '
' if count_dist==2 %> + <% + end + if file_distribution + count_dist+=1 + %> + > + <% end %> + +
+ +
+<% end %> +<% end %> + diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_rules.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_rules.html.erb new file mode 100644 index 00000000000..7befce2e792 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_rules.html.erb @@ -0,0 +1,132 @@ +<% if measure(Metric::LINES) %> + + + + + + +
+
+

Rules compliance

+
+ <%= format_measure(Metric::VIOLATIONS_DENSITY, :url => url_for_drilldown(Metric::WEIGHTED_VIOLATIONS, {:highlight => Metric::WEIGHTED_VIOLATIONS})) -%> <%= tendency_icon(Metric::VIOLATIONS_DENSITY) -%> +
+ <% + maintainability=@snapshot.measure(Metric::MAINTAINABILITY) + efficiency=@snapshot.measure(Metric::EFFICIENCY) + usability=@snapshot.measure(Metric::USABILITY) + reliability=@snapshot.measure(Metric::RELIABILITY) + portability=@snapshot.measure(Metric::PORTABILITY) + %> + +
+
+
+

Violations

+
+ <%= format_measure(Metric::VIOLATIONS, :url => url_for(:controller => 'drilldown', :action => 'violations', :id => @project.key)) -%> <%= tendency_icon(Metric::VIOLATIONS) -%> +
+ <% + blocker_violations = @snapshot.measure(Metric::BLOCKER_VIOLATIONS) + critical_violations = @snapshot.measure(Metric::CRITICAL_VIOLATIONS) + major_violations = @snapshot.measure(Metric::MAJOR_VIOLATIONS) + minor_violations = @snapshot.measure(Metric::MINOR_VIOLATIONS) + info_violations = @snapshot.measure(Metric::INFO_VIOLATIONS) + max = 0 + [blocker_violations,critical_violations,major_violations,minor_violations,info_violations].each do |m| + max = m.value if m and m.value and m.value>max + end + %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
<%= image_tag 'priority/BLOCKER.png'%>  <%= link_to 'Blocker', {:controller => 'drilldown', :action => 'violations', :id => @project.key, :priority => 'BLOCKER'} %> + <%= format_measure(blocker_violations) -%> + <%= tendency_icon(blocker_violations) -%> + <%= barchart(:width => 60, :percent => (blocker_violations ? (100 * blocker_violations.value / max).to_i : 0), :color => '#777777') if max>0 %> +
<%= image_tag 'priority/CRITICAL.png' %>  <%= link_to 'Critical', {:controller => 'drilldown', :action => 'violations', :id => @project.key, :priority => 'CRITICAL'} %> + <%= format_measure(critical_violations) -%> + <%= tendency_icon(critical_violations) -%> + <%= barchart(:width => 60, :percent => (critical_violations ? (100 * critical_violations.value / max).to_i : 0), :color => '#777777') if max>0 %> +
<%= image_tag 'priority/MAJOR.png' %>  <%= link_to 'Major', {:controller => 'drilldown', :action => 'violations', :id => @project.key, :priority => 'MAJOR'} %> + <%= format_measure(major_violations) -%> + <%= tendency_icon(major_violations) -%> + <%= barchart(:width => 60, :percent => (major_violations ? (100 * major_violations.value / max).to_i : 0), :color => '#777777') if max>0 %> +
<%= image_tag 'priority/MINOR.png' %>  <%= link_to 'Minor', {:controller => 'drilldown', :action => 'violations', :id => @project.key, :priority => 'MINOR'} %> + <%= format_measure(minor_violations) -%> + <%= tendency_icon(minor_violations) -%> + <%= barchart(:width => 60, :percent => (minor_violations ? (100 * minor_violations.value / max).to_i : 0), :color => '#777777') if max>0 %> +
<%= image_tag 'priority/INFO.png' %>  <%= link_to 'Info', {:controller => 'drilldown', :action => 'violations', :id => @project.key, :priority => 'INFO'} %> + <%= format_measure(info_violations) -%> + <%= tendency_icon(info_violations) -%> + <%= barchart(:width => 60, :percent => (info_violations ? (100 * info_violations.value / max).to_i : 0), :color => '#777777') if max>0 %> +
+
+
+<% end %> \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_static_analysis.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_static_analysis.html.erb new file mode 100644 index 00000000000..6ada91d46ae --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/_static_analysis.html.erb @@ -0,0 +1,63 @@ +<% +if measure('lines') || measure('ncloc') + files=measure('files') + statements=measure('statements') +%> + + + + + + +
+
+

Lines of code

+ <% if measure('ncloc') %> +

+ <%= format_measure('ncloc', :suffix => '', :url => url_for_drilldown('ncloc')) -%> <%= tendency_icon('ncloc', false) -%>

+ <% + generated_ncloc=measure('generated_ncloc') + if generated_ncloc && generated_ncloc.value>0 + %> +

+<%= format_measure(generated_ncloc, :suffix => ' generated', :url => url_for_drilldown(generated_ncloc)) -%> <%= tendency_icon(generated_ncloc) -%>

+ <% end %> +

<%= format_measure('lines', :suffix => ' lines', :url => url_for_drilldown('lines')) -%> <%= tendency_icon('lines') -%>

+ <% else%> +

<%= format_measure('lines', :suffix => '', :url => url_for_drilldown('lines')) -%> <%= tendency_icon('lines', false) -%>

+ <% end %> + <% + generated_lines=measure('generated_lines') + if generated_lines && generated_lines.value>0 + %> +

incl. <%= format_measure(generated_lines, :suffix => ' generated', :url => url_for_drilldown(generated_lines)) -%> <%= tendency_icon(generated_lines) -%>

+ <% end %> + <% if statements %> +

+ <%= format_measure(statements, :suffix => ' statements', :url => url_for_drilldown(statements)) -%> <%= tendency_icon(statements) -%> +

+ <% end %> + <% if files && measure('classes') %> +

<%= format_measure(files, :suffix => ' files', :url => url_for_drilldown(files)) -%> <%= tendency_icon(files) -%>

+ <% end %> +
+
+
+ <% if measure('classes') %> +

Classes

+

<%= format_measure('classes', :url => url_for_drilldown('classes')) -%> <%= tendency_icon('classes') -%>

+

<%= format_measure('packages', :suffix => ' packages', :url => url_for_drilldown('packages')) -%> <%= tendency_icon('packages') -%>

+ <% else %> +

Files

+

<%= format_measure('files', :url => url_for_drilldown('files')) -%> <%= tendency_icon('files') -%>

+

<%= format_measure('directories', :suffix => ' directories', :url => url_for_drilldown('directories')) -%> <%= tendency_icon('directories') -%>

+ <% end %> +

<%= format_measure('functions', :suffix => ' methods', :url => url_for_drilldown('functions')) -%> <%= tendency_icon('functions') -%>

+ <% if (measure('accessors')) %> +

<%= format_measure('accessors', :prefix => '+', :suffix => ' accessors', :url => url_for_drilldown('accessors')) -%> <%= tendency_icon('accessors') -%>

+ <% end %> + <% if measure('paragraphs') %> +

<%= format_measure('paragraphs', :suffix => ' paragraphs', :url => url_for_drilldown('paragraphs')) -%> <%= tendency_icon('paragraphs') -%>

+ <% end %> +
+
+<% end %> \ No newline at end of file diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java index 60f57295f39..a7920936481 100644 --- a/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java +++ b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java @@ -30,7 +30,7 @@ import java.sql.Statement; public class SchemaMigration { public final static int VERSION_UNKNOWN = -1; - public static final int LAST_VERSION = 150; + public static final int LAST_VERSION = 151; public final static String TABLE_NAME = "schema_migrations"; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Description.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Description.java new file mode 100644 index 00000000000..34d44a6f341 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Description.java @@ -0,0 +1,37 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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; + +/** + * Created by IntelliJ IDEA. + * User: dreik + * Date: 02.08.2010 + * Time: 13:00:45 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Description { + String value(); +} 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 new file mode 100644 index 00000000000..86d6d7c7706 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java @@ -0,0 +1,36 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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 org.sonar.api.Property; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Created by IntelliJ IDEA. + * User: dreik + * Date: 09.08.2010 + * Time: 10:40:09 + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface WidgetProperties { + WidgetProperty[] value() 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 new file mode 100644 index 00000000000..7ec6bf86f8d --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java @@ -0,0 +1,50 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * 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; + +/** + * Created by IntelliJ IDEA. + * User: dreik + * Date: 09.08.2010 + * Time: 10:33:35 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface WidgetProperty { + + String key(); + + String defaultValue() default ""; + + String name(); + + String description() default ""; + + String type() default "STRING"; + + String parameter() default ""; + + boolean optional() default true; +} diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml index 3427d048c85..be35e82f60f 100644 --- a/sonar-server/pom.xml +++ b/sonar-server/pom.xml @@ -251,6 +251,8 @@ **/tablekit-min.js **/prototip-min.js **/tooltip-min.js + **/dashboard-min.js + ${project.build.directory}/${project.build.finalName}/javascripts/sonar.js @@ -260,6 +262,7 @@ **/calendar-min.css **/style-min.css **/sonar-colorizer-min.css + **/dashboard-min.css ${project.build.directory}/${project.build.finalName}/stylesheets/sonar.css @@ -336,7 +339,6 @@ true true true - org.codehaus.sonar.plugins ${project.build.directory}/sonar-dev-home/extensions/plugins/ 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 e9b556c55d8..f5dc477cd43 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 @@ -108,6 +108,15 @@ public final class JRubyFacade implements ServerComponent { return getContainer().getComponent(Views.class).getWidgets(resourceScope, resourceQualifier, resourceLanguage); } + public List> getWidgets() { + return getContainer().getComponent(Views.class).getWidgets(); + } + + public ViewProxy getWidget(String id) { + return getContainer().getComponent(Views.class).getWidget(id); + } + + public List> getPages(String section, String resourceScope, String resourceQualifier, String resourceLanguage) { return getContainer().getComponent(Views.class).getPages(section, resourceScope, resourceQualifier, resourceLanguage); } 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 d224d790a07..17582d0703a 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 @@ -34,6 +34,8 @@ public class ViewProxy implements Comparable { private String[] resourceQualifiers = {}; private String[] resourceLanguages = {}; private String[] defaultForMetrics = {}; + private String description = ""; + private WidgetProperty[] properties = {}; private boolean isDefaultTab=false; private boolean isWidget = false; @@ -77,6 +79,16 @@ public class ViewProxy implements Comparable { } } + Description descriptionAnnotation = AnnotationUtils.getClassAnnotation(view, Description.class); + if (descriptionAnnotation != null) { + description = descriptionAnnotation.value(); + } + + WidgetProperties widgetProperties = AnnotationUtils.getClassAnnotation(view, WidgetProperties.class); + if (widgetProperties != null) { + properties = widgetProperties.value(); + } + isWidget = (view instanceof Widget); } @@ -92,6 +104,14 @@ public class ViewProxy implements Comparable { return view.getTitle(); } + public String getDescription() { + return description; + } + + public WidgetProperty[] getProperties() { + return properties; + } + public String[] getSections() { return sections; } 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 ebc666a8f4f..6addb165827 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,6 +19,7 @@ */ package org.sonar.server.ui; +import com.google.common.collect.Lists; import org.apache.commons.lang.ArrayUtils; import org.sonar.api.ServerComponent; import org.sonar.api.web.Page; @@ -85,6 +86,10 @@ public class Views implements ServerComponent { return result; } + public List> getWidgets() { + return new ArrayList>(widgets); + } + private static boolean accept(ViewProxy proxy, String section, String resourceScope, String resourceQualifier, String resourceLanguage) { return acceptNavigationSection(proxy, section) && acceptResourceScope(proxy, resourceScope) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/admin_dashboards_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/admin_dashboards_controller.rb new file mode 100644 index 00000000000..7824a235adc --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/admin_dashboards_controller.rb @@ -0,0 +1,101 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class AdminDashboardsController < ApplicationController + + SECTION=Navigation::SECTION_CONFIGURATION + + verify :method => :post, :only => [:up, :down, :remove, :add], :redirect_to => {:action => :index} + before_filter :admin_required + before_filter :load_active_dashboards + + def index + @default_dashboards=::Dashboard.find(:all, :conditions => {:shared => true}) + ids=@actives.map{|af| af.dashboard_id} + if !ids.nil? && !ids.empty? + @default_dashboards=@default_dashboards.reject!{|f| ids.include?(f.id) } + end + end + + def up + dashboard_index=-1 + dashboard=nil + @actives.each_index do |index| + if @actives[index].id==params[:id].to_i + dashboard_index=index + dashboard=@actives[index] + end + end + if dashboard && dashboard_index>0 + @actives[dashboard_index]=@actives[dashboard_index-1] + @actives[dashboard_index-1]=dashboard + + @actives.each_index do |index| + @actives[index].order_index=index+1 + @actives[index].save + end + end + redirect_to :action => 'index' + end + + def down + dashboard_index=-1 + dashboard=nil + @actives.each_index do |index| + if @actives[index].id==params[:id].to_i + dashboard_index=index + dashboard=@actives[index] + end + end + if dashboard && dashboard_index<@actives.size-1 + @actives[dashboard_index]=@actives[dashboard_index+1] + @actives[dashboard_index+1]=dashboard + + @actives.each_index do |index| + @actives[index].order_index=index+1 + @actives[index].save + end + end + redirect_to :action => 'index' + end + + def add + dashboard=::Dashboard.find(:first, :conditions => ['shared=? and id=?', true, params[:id].to_i()]) + if dashboard + ActiveDashboard.create(:dashboard => dashboard, :user => nil, :order_index => @actives.size+1) + flash[:notice]='Default dashboard added.' + end + redirect_to :action => 'index' + end + + def remove + active=@actives.to_a.find{|af| af.id==params[:id].to_i} + if active + active.destroy + flash[:notice]='Dashboard removed from default dashboards.' + end + redirect_to :action => 'index' + end + + private + + def load_active_dashboards + @actives=ActiveDashboard.default_active_dashboards + end +end 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 new file mode 100644 index 00000000000..61f4c54f61e --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb @@ -0,0 +1,154 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class DashboardController < ApplicationController + + SECTION=Navigation::SECTION_RESOURCE + + verify :method => :post, :only => [:set_layout, :add_widget, :set_dashboard], :redirect_to => {:action => :index} + before_filter :login_required, :except => [:index] + + def index + # TODO display error page if no dashboard or no resource + load_dashboard() + load_resource() + load_widget_definitions() + end + + def configure + # TODO display error page if no dashboard or no resource + load_dashboard() + load_resource() + load_widget_definitions() + end + + def edit_layout + load_dashboard() + load_resource() + end + + def set_layout + dashboard=Dashboard.find(params[:id].to_i) + if dashboard.editable_by?(current_user) + dashboard.column_layout=params[:layout] + if dashboard.save + columns=dashboard.column_layout.split('-') + dashboard.widgets.find(:all, :conditions => ["column_index > ?",columns.size()]).each do |widget| + widget.column_index=columns.size() + widget.save + end + end + end + redirect_to :action => 'index', :id => dashboard.id, :resource => params[:resource] + end + + def set_dashboard + load_dashboard() + + dashboardstate=params[:dashboardstate] + + columns=dashboardstate.split(";") + all_ids=[] + columns.each_with_index do |col, index| + ids=col.split(",") + ids.each_with_index do |id, order| + widget=@dashboard.widgets.to_a.find { |i| i.id==id.to_i() } + if widget + widget.column_index=index+1 + widget.order_index=order+1 + widget.save! + all_ids< {:status => 'ok'} + end + + def add_widget + dashboard=Dashboard.find(params[:id].to_i) + widget_id=nil + if dashboard.editable_by?(current_user) + definition=java_facade.getWidget(params[:widget]) + if definition + new_widget=dashboard.widgets.create(:widget_key => definition.getId(), + :name => definition.getTitle(), + :column_index => dashboard.number_of_columns, + :order_index => dashboard.column_size(dashboard.number_of_columns) + 1, + :state => Widget::STATE_ACTIVE) + widget_id=new_widget.id + end + end + redirect_to :action => 'configure', :id => dashboard.id, :resource => params[:resource], :highlight => widget_id + end + + private + + def load_dashboard + @active=nil + if logged_in? + if params[:id] + @active=ActiveDashboard.find(:first, :include => 'dashboard', :conditions => ['active_dashboards.dashboard_id=? AND active_dashboards.user_id=?', params[:id].to_i, current_user.id]) + elsif params[:name] + @active=ActiveDashboard.find(:first, :include => 'dashboard', :conditions => ['dashboards.name=? AND active_dashboards.user_id=?', params[:name], current_user.id]) + end + end + + if @active.nil? + # anonymous or not found in user dashboards + if params[:id] + @active=ActiveDashboard.find(:first, :include => 'dashboard', :conditions => ['active_dashboards.dashboard_id=? AND active_dashboards.user_id IS NULL', params[:id].to_i]) + elsif params[:name] + @active=ActiveDashboard.find(:first, :include => 'dashboard', :conditions => ['dashboards.name=? AND active_dashboards.user_id IS NULL', params[:name]]) + else + @active=ActiveDashboard.find(:first, :include => 'dashboard', :conditions => ['active_dashboards.user_id IS NULL'], :order => 'order_index ASC') + end + end + @dashboard=(@active ? @active.dashboard : nil) + end + + def load_resource + @resource=Project.by_key(params[:resource]) + if @resource.nil? + # TODO display error page + redirect_to home_path + return false + end + return access_denied unless has_role?(:user, @resource) + @snapshot = @resource.last_snapshot + @project=@resource # variable name used in old widgets + end + + # TODO display unauthorized widgets instead of hiding them + def load_widget_definitions() + @widget_definitions = java_facade.getWidgets(@resource.scope, @resource.qualifier, @resource.language) + @widget_definitions=@widget_definitions.select do |widget| + authorized=widget.getUserRoles().size==0 + unless authorized + widget.getUserRoles().each do |role| + authorized=(role=='user') || (role=='viewer') || has_role?(role, @resource) + break if authorized + end + end + authorized + end + end +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb new file mode 100644 index 00000000000..670f637e425 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb @@ -0,0 +1,152 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class DashboardsController < ApplicationController + + SECTION=Navigation::SECTION_RESOURCE + + verify :method => :post, :only => [:create, :update, :delete, :up, :down], :redirect_to => {:action => :index} + before_filter :login_required + + def index + @actives=ActiveDashboard.user_dashboards(current_user) + + @resource=Project.by_key(params[:resource]) + if @resource.nil? + # TODO display error page + redirect_to home_path + return false + end + return access_denied unless has_role?(:user, @resource) + @snapshot = @resource.last_snapshot + @project=@resource # variable name used in old widgets + end + + def create + @dashboard=Dashboard.new() + load_dashboard_from_params(@dashboard) + if @dashboard.valid? + @dashboard.save + + add_default_dashboards_if_first_user_dashboard + last_active_dashboard=current_user.active_dashboards.max{|x,y| x.order_index<=>y.order_index} + current_user.active_dashboards.create(:dashboard => @dashboard, :user_id => current_user.id, :order_index => (last_active_dashboard ? last_active_dashboard.order_index+1: 1)) + redirect_to :controller => 'dashboard', :action => 'configure', :id => @dashboard.id, :resource => params[:resource] + else + flash[:error]=@dashboard.errors.full_messages.join('
') + redirect_to :controller => 'dashboards', :action => 'index', :resource => params[:resource] + end + end + + def edit + # TODO check ownership + @dashboard=Dashboard.find(params[:id]) + render :partial => "edit" + end + + def update + dashboard=Dashboard.find(params[:id]) + if dashboard.owner?(current_user) + load_dashboard_from_params(dashboard) + + if dashboard.save + if !dashboard.shared? + ActiveDashboard.destroy_all(["dashboard_id = ? and (user_id<>? OR user_id IS NULL)", dashboard.id, current_user.id]) + end + else + flash[:error]=dashboard.errors.full_messages.join('
') + end + else + # TODO explicit error + end + redirect :action => 'index', :resource => params[:resource] + end + + def delete + dashboard=Dashboard.find(params[:id]) + if dashboard.owner?(current_user) + dashboard.destroy + flash[:notice]='Dashboard deleted' + redirect_to :action => 'index', :resource => params[:resource] + else + # TODO explicit error + redirect_to home_path + end + end + + def down + add_default_dashboards_if_first_user_dashboard + dashboard_index=-1 + current_user.active_dashboards.each_with_index do |ad, index| + ad.order_index=index+1 + if ad.dashboard_id==params[:id].to_i + dashboard_index=index + end + end + if dashboard_index>-1 && dashboard_index 'index', :resource => params[:resource] + end + + def up + add_default_dashboards_if_first_user_dashboard + dashboard_index=-1 + current_user.active_dashboards.each_with_index do |ad, index| + ad.order_index=index+1 + dashboard_index=index if ad.dashboard_id==params[:id].to_i + end + if dashboard_index>0 + current_user.active_dashboards[dashboard_index].order_index-=1 + current_user.active_dashboards[dashboard_index-1].order_index+=1 + end + current_user.active_dashboards.each do |ad| + ad.save + end + redirect_to :action => 'index', :resource => params[:resource] + end + + + + private + + def load_dashboard_from_params(dashboard) + dashboard.name=params[:name] + dashboard.description=params[:description] + dashboard.shared=(params[:shared].present? && is_admin?) + dashboard.user_id=current_user.id + dashboard.column_layout='50-50' if !dashboard.column_layout + end + + def add_default_dashboards_if_first_user_dashboard + if current_user.active_dashboards.empty? + defaults=ActiveDashboard.default_dashboards + defaults.each do |default_active| + current_user.active_dashboards.create(:dashboard => default_active.dashboard, :user => current_user, :order_index => current_user.active_dashboards.size+1) + end + end + end + + + +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb new file mode 100644 index 00000000000..e34f593a533 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb @@ -0,0 +1,65 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +module DashboardHelper + + def item_by_metric_id(items, metric_id) + return nil if items.nil? + items.each do |item| + return item if (item.metric.id==metric_id and item.rules_category_id.nil?) + end + nil + end + + def active_widgets_ids_formatted(column) + active_widget_ids=[] + @dashboard.widgets.find(:all, :conditions => {:column_index => column}, :order => :order_index).each do |widget| + widget_view=nil + found_index=-1 + @widgets.each_with_index {|item, index| + if item.getId()==widget.widget_key + found_index=index + end + } + if found_index>-1 + active_widget_ids=active_widget_ids << (widget.widget_key+"_"+found_index.to_s()) + end + end + return "\'"+active_widget_ids.join("\',\'")+"\'" + end + + def item_by_metric_name_and_categ_id(items, metric_name, rules_category_id) + return nil if items.nil? + items.each do |item| + return item if (item.metric.name==metric_name and + item.rules_category_id == rules_category_id and + item.rule_id.nil?) + end + nil + end + + def formatted_value(measure, default='') + measure ? measure.formatted_value : default + end + + def measure(metric_key) + @snapshot.measure(metric_key) + end + +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboards_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboards_helper.rb new file mode 100644 index 00000000000..4893c32db0d --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboards_helper.rb @@ -0,0 +1,66 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +module DashboardsHelper + include ActionView::Helpers::UrlHelper + include WidgetPropertiesHelper + + def item_by_metric_id(items, metric_id) + return nil if items.nil? + items.each do |item| + return item if (item.metric.id==metric_id and item.rules_category_id.nil?) + end + nil + end + + def active_widgets_ids_formatted(column) + active_widget_ids=[] + @dashboard.widgets.find(:all, :conditions => {:column_index => column}, :order => :order_index).each do |widget| + widget_view=nil + found_index=-1 + @widgets.each_with_index {|item, index| + if item.getId()==widget.widget_key + found_index=index + end + } + if found_index>-1 + active_widget_ids=active_widget_ids << (widget.widget_key+"_"+found_index.to_s()) + end + end + return "\'"+active_widget_ids.join("\',\'")+"\'" + end + + def item_by_metric_name_and_categ_id(items, metric_name, rules_category_id) + return nil if items.nil? + items.each do |item| + return item if (item.metric.name==metric_name and + item.rules_category_id == rules_category_id and + item.rule_id.nil?) + end + nil + end + + def formatted_value(measure, default='') + measure ? measure.formatted_value : default + end + + def measure(metric_key) + @snapshot.measure(metric_key) + end +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/widget_properties_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/widget_properties_helper.rb new file mode 100644 index 00000000000..543a2864f63 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/widget_properties_helper.rb @@ -0,0 +1,73 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +module WidgetPropertiesHelper + VALUE_TYPE_INT = 'INT' + VALUE_TYPE_BOOLEAN = 'BOOL' + VALUE_TYPE_FLOAT = 'FLOAT' + VALUE_TYPE_STRING = 'STRING' + VALUE_TYPE_REGEXP = 'REGEXP' + + def valid_property_value?(type, value, parameter="") + if type==VALUE_TYPE_INT + value.to_i.to_s == value + + elsif type==VALUE_TYPE_FLOAT + true if Float(value) rescue false + + elsif type==VALUE_TYPE_BOOLEAN + value=="1" || value=="0" + + elsif type==VALUE_TYPE_STRING + true + + elsif type==VALUE_TYPE_REGEXP + value.to_s.match(parameter) == nil ? false : true + + else + false + end + end + + def property_value_field(type, fieldname, value, param_value="") + val= param_value ? param_value : value + + if type==VALUE_TYPE_INT + text_field_tag fieldname, val , :size => 10 + + elsif type==VALUE_TYPE_FLOAT + text_field_tag fieldname, val, :size => 10 + + elsif type==VALUE_TYPE_BOOLEAN + opts="" + opts+="" + opts+="" + select_tag fieldname, opts + + elsif type==VALUE_TYPE_STRING + text_field_tag fieldname, val, :size => 10 + + elsif type==VALUE_TYPE_REGEXP + text_field_tag fieldname, val, :size => 10 + else + hidden_field_tag fieldname + end + end + +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/active_dashboard.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/active_dashboard.rb new file mode 100644 index 00000000000..3ca3383aa8d --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/active_dashboard.rb @@ -0,0 +1,59 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class ActiveDashboard < ActiveRecord::Base + + belongs_to :user + belongs_to :dashboard + + def name + dashboard.name + end + + def order_index + read_attribute(:order_index) || 1 + end + + def shared? + dashboard.shared + end + + def owner?(user) + dashboard.owner?(user) + end + + def follower?(user) + self.user.nil? || self.user_id==user.id + end + + def self.user_dashboards(user) + result=nil + if user && user.id + result=find(:all, :include => 'dashboard', :conditions => ['user_id=?', user.id], :order => 'order_index') + end + if result.nil? || result.empty? + result=default_dashboards + end + result + end + + def self.default_dashboards + find(:all, :include => 'dashboard', :conditions => ['user_id IS NULL'], :order => 'order_index') + end +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb new file mode 100644 index 00000000000..a36f6aa1066 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb @@ -0,0 +1,77 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class Dashboard < ActiveRecord::Base + belongs_to :user + + has_many :widgets, :include => 'widget_properties', :dependent => :delete_all + has_many :active_dashboards, :dependent => :delete_all + + validates_length_of :name, :within => 1..256 + validates_length_of :description, :maximum => 1000, :allow_blank => true, :allow_nil => true + validates_length_of :column_layout, :maximum => 10, :allow_blank => false, :allow_nil => false + validates_uniqueness_of :name, :scope => :user_id + + def shared? + read_attribute(:shared) || false + end + + def author + dashboard.user + end + + def author_name + author ? author.name : nil + end + + def editable_by?(user) + (user && user_id==user.id) || (user_id.nil? && user.has_role?(:admin)) + end + + def owner?(user) + self.user_id==user.id + end + + def number_of_columns + column_layout.split('-').size + end + + def column_size(column_index) + last_widget=widgets.select{|w| w.column_index==column_index}.max{|x,y| x.order_index <=> y.order_index} + last_widget ? last_widget.order_index : 0 + end + + def deep_copy() + dashboard=Dashboard.new(attributes) + dashboard.shared=false + self.widgets.each do |child| + new_widget = Widget.create(child.attributes) + + child.widget_properties.each do |prop| + widget_prop = WidgetProperty.create(prop.attributes) + new_widget.widget_properties << widget_prop + end + + new_widget.save + dashboard.widgets << new_widget + end + dashboard.save + dashboard + end +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb index 867dd27a6c5..6e08e3b17fd 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb @@ -28,6 +28,9 @@ class User < ActiveRecord::Base has_many :properties, :foreign_key => 'user_id', :dependent => :delete_all has_many :active_filters, :include => 'filter', :order => 'order_index' has_many :filters, :dependent => :delete_all + + has_many :active_dashboards, :dependent => :delete_all, :order => 'order_index' + has_many :dashboards, :dependent => :delete_all include Authentication include Authentication::ByPassword diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/widget.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/widget.rb new file mode 100644 index 00000000000..62e04d842cc --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/widget.rb @@ -0,0 +1,86 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2009 SonarSource SA +# 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 +# +class Widget < ActiveRecord::Base + STATE_ACTIVE='A' + STATE_INACTIVE='I' + + has_many :widget_properties, :dependent => :delete_all + belongs_to :dashboards + + validates_presence_of :name + validates_length_of :name, :within => 1..256 + + validates_presence_of :widget_key + validates_length_of :widget_key, :within => 1..256 + + validates_length_of :description, :maximum => 1000, :allow_blank => true, :allow_nil => true + + def state + read_attribute(:state) || 'V' + end + + #--------------------------------------------------------------------- + # WIDGET PROPERTIES + #--------------------------------------------------------------------- + def properties + widget_properties + end + + def widget_property(key) + widget_properties().each do |p| + return p if (p.key==key) + end + nil + end + + def widget_property_value(key) + prop=widget_property(key) + prop ? prop.value : nil + end + + def set_widget_property(options) + key=options[:kee] + prop=widget_property(key) + if prop + prop.attributes=options + prop.widget_id=id + prop.save! + else + prop=WidgetProperty.new(options) + prop.widget_id=id + widget_properties< 1..100 + validates_length_of :description, :maximum => 4000, :allow_blank => true, :allow_nil => true + validates_length_of :text_value, :maximum => 4000, :allow_blank => true, :allow_nil => true + + def key + kee + end + + def value + text_value + end + + def to_hash_json + {:key => key, :value => value.to_s} + end + + def to_xml(xml=Builder::XmlMarkup.new(:indent => 0)) + xml.property do + xml.key(prop_key) + xml.value {xml.cdata!(text_value.to_s)} + end + xml + end + +end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/admin_dashboards/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/admin_dashboards/index.html.erb new file mode 100644 index 00000000000..aade1013455 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/admin_dashboards/index.html.erb @@ -0,0 +1,72 @@ +

Default dashboards

+

These dashboards are displayed to anonymous users or users that haven't customized their dashboards.

+
+ + + + + + + + + + + <% if @actives.empty? %> + + <% else %> + <% @actives.each_with_index do |active,index| %> + + + + + + + <% end %> + <% end %> + +
NameShared byOrderOperations
No results.
+ <%= h(active.name) %>
+ <%= active.dashboard.description %> +
<%= h(active.dashboard.user.name) if active.dashboard.user %> + <% if index>0 %> + <%= link_to image_tag('blue-up.png'), {:action => :up, :id => active.id}, :method => :post, :id => "up-#{u active.name}" %> + <% else %> + <%= image_tag('transparent_16.gif') %> + <% end %> + <% if index<@actives.size-1 %> + <%= link_to image_tag('blue-down.png'), {:action => :down, :id => active.id}, :method => :post, :id => "down-#{u active.name}" %> + <% end %> + + <%= link_to 'Remove', {:action => 'remove', :id => active.id}, :confirm => 'Are you sure to remove it from default dashboards ?', :method => :post, :id => "remove-#{u active.name}" %> +
+ +

+

Admin dashboards

+

These dashboards can be added to default dashboards.

+
+ + + + + + + + + + <% if @default_dashboards.nil? || @default_dashboards.empty? %> + + <% else %> + <% @default_dashboards.each do |dashboard| %> + + + + + + <% end %> + <% end %> + +
NameShared byOperations
No results.
+ <%= h(dashboard.name) -%>
+ <%= dashboard.description %> +
<%= h(dashboard.user.name) if dashboard.user %><%= link_to 'Add to defaults', {:action => 'add', :id => dashboard.id}, :method => :post, :id => "add-#{u dashboard.name}" %>
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_configure_widget.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_configure_widget.html.erb new file mode 100644 index 00000000000..bf0c1ce7280 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_configure_widget.html.erb @@ -0,0 +1,41 @@ +<% + begin + widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash} + rescue => error + logger.error("Can not render widget #{definition.getId()}: " + error) + logger.error(error.backtrace.join("\n")) + widget_body="" + end +%> + +
+ <%= definition.getTitle() -%> + Delete + Edit   +
+<% if defined?(validation_result) %> +
clear:both;margin: 0;border-bottom:0;"> + <%= validation_result["errormsg"] if validation_result["errormsg"] %>   [hide] +
+
clear:both;margin: 0;border-bottom:0;"> + <%= validation_result["infomsg"] if validation_result["infomsg"] %>   [hide] +
+<% end %> + +
+ +
+ <% if widget_body.include? '<' %> + <%= widget_body %> + <% else %> +

Can not render widget <%= definition.getTitle() %>.

+ <% end %> +
+
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb new file mode 100644 index 00000000000..a3ee6996874 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb @@ -0,0 +1,29 @@ +
+<% if logged_in? %> +
    + <% if back %> +
  • <%= link_to 'Back to dashboard', {:action => 'index', :id => @dashboard.id, :resource => @resource.id } -%>
  • + <% else %> +
  • <%= link_to 'Configure', {:action => 'configure', :id => @dashboard.id, :resource => @resource.id } -%>
  • +
  • <%= link_to 'Edit layout', {:action => 'edit_layout', :id => @dashboard.id, :resource => @resource.id } -%>
  • +
  • <%= link_to 'Manage dashboards', {:controller => 'dashboards', :action => 'index', :resource => @resource.id } -%>
  • + <% end %> +
+<% end %> + + <% if @snapshot %> +
+

+ <% + version_event=@snapshot.event(EventCategory::KEY_VERSION) + profile_measure=@snapshot.measure(Metric::PROFILE) + %> + <%= link_to_favourite(@project) -%> <%= "#{version_event.fullname} - " if version_event %> <%= l @snapshot.created_at %> + <% if profile_measure %> - profile <%= link_to profile_measure.data, :controller => '/rules_configuration', :action => 'index', :id => profile_measure.value.to_i %><% end %> +

+
+ <% end %> +
+ + + diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb new file mode 100644 index 00000000000..748a16b8e99 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb @@ -0,0 +1,22 @@ +
+<% + begin + widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash} + rescue => error + logger.error("Can not render widget #{definition.getId()}: " + error) + logger.error(error.backtrace.join("\n")) + widget_body="" + end + + if widget_body.include?('<') +%> + <%= widget_body %> +<% + else +%> +

Can not render widget <%= definition.getTitle() %>.

+<% + end +%> +
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_definition.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_definition.html.erb new file mode 100644 index 00000000000..ee7ca54dc24 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_definition.html.erb @@ -0,0 +1,7 @@ +
+

<%= h definition.getTitle() -%>

+

<%= h definition.getDescription() -%>

+<%= form_tag :action => 'add_widget', :id => @dashboard.id, :resource => params[:resource], :widget => definition.getId() %> + + +
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb new file mode 100644 index 00000000000..5605bda6cff --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb @@ -0,0 +1,82 @@ + + + +
+ <%= render :partial => 'dashboard/header', :locals => {:back => true} %> + + + + + <% + columns=@dashboard.column_layout.split('-') + for index in 1..columns.size() + %> +
+
0px <%= index>1 ? "5px" : "0px" %>;"> + + + <% + @dashboard.widgets.select{|widget| widget.column_index==index}.sort_by{|widget| widget.order_index}.each do |widget| + widget_definition=@widget_definitions.find{|wd| wd.getId()==widget.widget_key } + if widget_definition + %> +
+ <%= render :partial => 'dashboard/configure_widget', :locals => {:widget => widget, :definition => widget_definition} %> +
+ <% + end + end + %> +
+
+ <% end %> +
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/edit_layout.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/edit_layout.html.erb new file mode 100644 index 00000000000..9d906e9b323 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/edit_layout.html.erb @@ -0,0 +1,28 @@ +
+ <%= render :partial => 'dashboard/header', :locals => {:back => true} %> + +
+

Click to choose the layout:


+ +
+ <%= link_to image_tag('layout100.png'), {:action => 'set_layout', :id => @dashboard.id, :resource => @resource.id, :layout => "100"}, :method => "post" %> +
+ +
+ <%= link_to image_tag('layout5050.png'), {:action => 'set_layout', :id => @dashboard.id, :resource => @resource.id, :layout => "50-50"}, :method => "post" %> +
+ +
+ <%= link_to image_tag('layout3070.png'), {:action => 'set_layout', :id => @dashboard.id, :resource => @resource.id, :layout => "30-70"}, :method => "post" %> +
+ +
+ <%= link_to image_tag('layout7030.png'), {:action => 'set_layout', :id => @dashboard.id, :resource => @resource.id, :layout => "70-30"}, :method => "post" %> +
+ +
+ <%= link_to image_tag('layout333333.png'), {:action => 'set_layout', :id => @dashboard.id, :resource => @resource.id, :layout => "33-33-33"}, :method => "post" %> +
+
+
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/index.html.erb new file mode 100644 index 00000000000..e7a6d569daa --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/index.html.erb @@ -0,0 +1,26 @@ +
+ <%= render :partial => 'dashboard/header', :locals => {:back => false} %> + + <% + columns=@dashboard.column_layout.split('-') + for index in 1..columns.size() + %> +
+
0px <%= index>1 ? "5px" : "0px" %>;"> + <% + @dashboard.widgets.select{|widget| widget.column_index==index}.sort_by{|widget| widget.order_index}.each do |widget| + widget_definition=@widget_definitions.find{|wd| wd.getId()==widget.widget_key } + if widget_definition + %> +
+ <%= render :partial => 'dashboard/widget', :locals => {:widget => widget, :definition => widget_definition} %> +
+ <% + end + end + %> +
+
+ <% end %> +
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_create.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_create.html.erb new file mode 100644 index 00000000000..72f6133a23f --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_create.html.erb @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + <% if is_admin? %> + + + + <% end %> + + + + + +

Create dashboard

+ Name:
+
+ Description:
+
+ Shared:
+
+ +
+ \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_dashboard.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_dashboard.html.erb new file mode 100644 index 00000000000..8835f7c85ff --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_dashboard.html.erb @@ -0,0 +1,186 @@ +<% if @dashboard && ((@dashboard.widgets.size > 0 && @dashboard.column_layout.split("-").size() > 0)||dashboard_edit) %> + + +
+ <% if dashboard_edit %> + + + + <%= render :partial => 'dashboards/editform' %> + + <%= render :partial => 'dashboards/layouts' %> + + + + <% end %> + + <% columns=Array.new + if @dashboard && @dashboard.column_layout + columns=@dashboard.column_layout.split("-") + end + for index in 1..columns.size() %> +
+
0px <%= index>1 ? "5px" : "0px" %>;"> + <% if dashboard_edit %> +
+
{:column_index => index}).size()>0 %>>Drop widgets here
+ <% end %> + + <% @dashboard.widgets.find(:all, :conditions => {:column_index => index}, :order => :order_index).each do |widget| + widget_view=@widgets.find { |w| w.getId()== widget.widget_key } + if widget_view %> +
+ + <%= render :partial => 'dashboards/widget', + :locals => {:dashboard_edit => dashboard_edit, + :widget => widget, + :widget_view => widget_view, + :edit_mode => false} %> + +
+ <% end %> + <% end %> + +
+
+ <% end %> +
+
+<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_edit.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_edit.html.erb new file mode 100644 index 00000000000..14b734c41b7 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_edit.html.erb @@ -0,0 +1,32 @@ +
+ + + + + + + + + + + + <% if is_admin? %> + + + + <% end %> + + + + +

Edit dashboard

+ Name:
+
+ Description:
+
+ Shared:
> +
+ + Cancel +
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_editform.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_editform.html.erb new file mode 100644 index 00000000000..f330d0f5bc7 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_editform.html.erb @@ -0,0 +1,47 @@ +
> +
+ + + + + + + + <% if is_admin? %> + + + + + + + <% end %> + + + + + + + + + + + +
Name: + +
Shared: + /> +
Description: + +
+ + + <% if @dashboard.id %> + <%= link_to "Delete", {:action => 'delete', :id => @project.id, :dashboard => @dashboard.id}, :method => :post, :confirm => 'Do you want to delete this dashboard ?' %> + + Cancel + <% else %> + Cancel + <% end %> +
+
+
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_layouts.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_layouts.html.erb new file mode 100644 index 00000000000..9fdacdb52cd --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_layouts.html.erb @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_snapshot_title.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_snapshot_title.html.erb new file mode 100644 index 00000000000..c5a57fb1533 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_snapshot_title.html.erb @@ -0,0 +1,12 @@ +<% if @snapshot %> +
+

+<% + version_event=@snapshot.event(EventCategory::KEY_VERSION) + profile_measure=@snapshot.measure(Metric::PROFILE) + %> +<%= link_to_favourite(@project) -%> <%= "#{version_event.fullname} - " if version_event %> <%= l @snapshot.created_at %> +<% if profile_measure %> - profile <%= link_to profile_measure.data, :controller => '/rules_configuration', :action => 'index', :id => profile_measure.value.to_i %><% end %> +

+
+<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_tabs.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_tabs.html.erb new file mode 100644 index 00000000000..1e594e2730d --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_tabs.html.erb @@ -0,0 +1,21 @@ +<% if logged_in? %> +
+
    +
  • <%= link_to 'Add dashboard', {:action => 'new', :id => @project.id } -%>
  • + <% if selected_tab && @dashboard %> +
  • <%= link_to 'Edit dashboard', {:action => 'edit', :id => @project.id, :dashboard => @dashboard.id } -%>
  • + <% end %> +
  • <%= link_to 'Manage dashboards', {:action => 'manage', :id => @project.id } -%>
  • +
+
+<% end %> + +<% if @actives && !@actives.empty? %> + +<% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget.html.erb new file mode 100644 index 00000000000..d7c834d0f50 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget.html.erb @@ -0,0 +1,100 @@ +<% + properties_available=widget_view.getProperties() && widget_view.getProperties().size() != 0 + + if !properties_available || (widget && widget.state==Widget::STATE_ACTIVE) + begin + widget_properties=Hash.new + if widget + widget.widget_properties.each do |property| + widget_properties[property.key]=property.value + end + end + widget_body=render :inline => widget_view.getTarget().getTemplate(), :locals => {:widget_properties => widget_properties} + rescue => error + logger.error("Can not render widget #{widget_view.getId()}: " + error) + logger.error(error.backtrace.join("\n")) + widget_body="" + end + end +%> + +<% if dashboard_edit || (widget.state==Widget::STATE_ACTIVE && widget_body.include?('<')) %> + +
"> + <%= (widget) ? widget.name : widget_view.getTitle() -%> + <% if dashboard_edit %> + [x] + <% if properties_available && widget && widget.state==Widget::STATE_ACTIVE %> + [<%= edit_mode ? "view" : "edit" %>]   + <% end %> + <% end %> +
+ <% if defined?(validation_result) %> +
clear:both;margin: 0;border-bottom:0;"> + <%= validation_result["errormsg"] if validation_result["errormsg"] %>   [hide] +
+
clear:both;margin: 0;border-bottom:0;"> + <%= validation_result["infomsg"] if validation_result["infomsg"] %>   [hide] +
+ <% end %> + +
+
<%= widget_view.getDescription() -%>
+ +
<%= button_to_function "Add", "portal.addNewWidget($(this).up('.block'),$$('.column-handle')[0]);", :style => "width:40px;"%>
+
+ + <% if properties_available && dashboard_edit %> +
"> + <% form_remote_tag :url => {:action => 'savewidget', :id => @project.id, :dashboard => @dashboard.id}, + :method => "post", + :before => "$(this).down('.widgetid').value=$(this).up('.block').identify().split('_').pop();hide_block_info($(this).up('.block'))" do -%> + + + <% widget_view.getProperties().each do |property| + db_property_value=widget.widget_property_value(property.key()) if widget + entered_value=params[property.key()] + entered_value=property.defaultValue() if !entered_value || entered_value.empty? + entered_value=nil unless edit_mode + editrow_class="" + if defined?(validation_result) + editrow_class= validation_result[property.key()]=="valid" ? "valid-editrow" : "invalid-editrow" + end + %> + + + + + <% end %> + +
<%= property.name() -%>:
+ [<%= property.type()+" "+property.key() -%>]
<%= property_value_field(property.type(), property.key(), db_property_value, entered_value) %><%= " *" unless property.optional() %> +
+ [<%= property.description() -%>]
+ <%= hidden_field_tag "widgetid", "", :class => "widgetid" %> +
<%= submit_tag 'Save' %>
+ <% end -%> +
+ <% end %> + + <% if (dashboard_edit && (!properties_available || (widget && widget.state==Widget::STATE_ACTIVE))) || (widget_body && widget_body.include?('<')) %> +
"> + <% if dashboard_edit %> + +
+ <% end %> + <% if widget_body.include? '<' %> + <%= widget_body %> + <% else %> +

Can not render widget <%= widget_view.getTitle() %>. + <% end %> +

+
+ <% end %> +<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget_update.rjs b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget_update.rjs new file mode 100644 index 00000000000..39b20ce7278 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_widget_update.rjs @@ -0,0 +1,6 @@ +page.replace_html 'block_'+widget.id.to_s(), :partial => 'dashboards/widget', + :locals => {:dashboard_edit => true, + :widget => widget, + :widget_view => widget_view, + :edit_mode => result["saved"]!="true", + :validation_result => result} diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/index.html.erb new file mode 100644 index 00000000000..e2244e56b5f --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/index.html.erb @@ -0,0 +1,62 @@ +<% is_admin=is_admin? %> + + + + + + +
+ +

My dashboards

+ + + + + <% if is_admin %><% end %> + + + + + + <% if @actives.nil? || @actives.empty? %> + + <% + else + @actives.each_with_index do |active,index| %> + + + <% if is_admin %> + + <% end %> + + + + <% end + end + %> + +
NameSharedOrderOperations
No dashboards
<%= active.name %> + <%= boolean_icon(active.dashboard.shared, {:display_false => false}) -%> + + <% if index>0 %> + <%= link_to image_tag('blue-up.png'), {:action => 'up', :id => active.dashboard_id, :resource => params[:resource]}, :method => :post, :id => "up-#{u active.name}" %> + <% else %> + <%= image_tag('transparent_16.gif') %> + <% end %> + <% if index<@actives.size-1 %> + <%= link_to image_tag('blue-down.png'), {:action => 'down', :id => active.dashboard.id, :resource => params[:resource]}, :method => :post, :id => "down-#{u active.name}" %> + <% end %> + + <% if !active.shared? || is_admin %> + <%= link_to_remote "Edit", :update => "admin_form", :url => { :action => "edit", :id => active.dashboard_id, :resource => params[:resource] }, :id => "edit-#{u active.name}", :method => :get %> + <%= link_to 'Delete', {:action => 'delete', :id => active.dashboard_id, :resource => params[:resource]}, :method => :post, :confirm => 'Do you want to delete this dashboard ?', :id => "delete-#{u active.name}" %> + <% else %> + <%= link_to 'Unfollow', {:action => 'unfollow', :id => active.dashboard_id}, :method => :post, :id => "hide-#{u active.name}" %> + <% end %> +
+ +
+
+ <%= render :partial => 'dashboards/create' %> +
+
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/manage.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/manage.html.erb new file mode 100644 index 00000000000..2dfc964f2b3 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/manage.html.erb @@ -0,0 +1,86 @@ +<%= render :partial => 'dashboards/tabs', :locals => {:selected_tab => nil} %> + +

My dashboards

+

These dashboards are active and visible.

+
+ + + + + + + + + + + <% if @actives.nil? || @actives.empty? %> + + + <% + else + @actives.each_with_index do |active,index| %> + + + + + + + <% end + end + %> + +
NameDefaultOrderOperations
No dashboards
<%= active.name %> + <%= boolean_icon(active.dashboard.shared, {:display_false => false}) -%> + + <% if index>0 %> + <%= link_to image_tag('blue-up.png'), {:action => 'up', :id => @project.id, :dashboard => active.dashboard.id}, :method => :post, :id => "up-#{u active.name}" %> + <% else %> + <%= image_tag('transparent_16.gif') %> + <% end %> + <% if index<@actives.size-1 %> + <%= link_to image_tag('blue-down.png'), {:action => 'down', :id => @project.id, :dashboard => active.dashboard.id}, :method => :post, :id => "down-#{u active.name}" %> + <% end %> + + <% if !active.shared? || is_admin? %> + <%= link_to 'Edit', {:action => 'edit', :id => @project.id, :dashboard => active.dashboard_id}, :method => :post, :id => "edit-#{u active.name}" %> | + <%= link_to 'Delete', {:action => 'delete', :id => @project.id, :dashboard => active.dashboard_id, :manage => true}, :method => :post, :confirm => 'Do you want to delete this dashboard ?', :id => "delete-#{u active.name}" %> + <% else %> + <%= link_to 'Edit', {:action => 'edit', :id => @project.id, :dashboard => active.dashboard_id}, :method => :post, :id => "edit-#{u active.name}" %> | + <%= link_to 'Hide', {:action => 'hide', :id => @project.id, :dashboard => active.dashboard_id}, :method => :post, :id => "hide-#{u active.name}" %> + <% end %> +
+ +


+

Disabled dashboards

+

These dashboards are hided.

+
+ + + + + + + + + + <% if @disabled_dashboards.nil? || @disabled_dashboards.empty? %> + + + <% + else + @disabled_dashboards.each_with_index do |disabled,index| + %> + + + + + + <% end + end + %> + +
NameDefaultOperations
No dashboards
<%= disabled.name %> + <%= boolean_icon(disabled.dashboard.shared, {:display_false => false}) -%> + + <%= link_to 'Show', {:action => 'show', :id => @project.id, :dashboard => disabled.dashboard_id}, :method => :post, :id => "show-#{u disabled.name}" %> +
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/new.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/new.html.erb new file mode 100644 index 00000000000..4158e623378 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/new.html.erb @@ -0,0 +1,6 @@ +<%= render :partial => 'dashboards/tabs', :locals => {:selected_tab => (@dashboard ? @dashboard.id : 'new') } %> +<% if @dashboard.id %> + <%= render :partial => 'dashboards/dashboard', :locals => {:dashboard_edit => true } %> +<% else %> + <%= render :partial => 'dashboards/editform' %> +<% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb index 44741eea52d..4c6de8ef374 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb @@ -18,6 +18,7 @@ <%= stylesheet_link_tag 'calendar', :media => 'all' %> <%= stylesheet_link_tag 'style', :media => 'all' %> <%= stylesheet_link_tag 'sonar-colorizer', :media => 'all' %> +<%= stylesheet_link_tag 'dashboard', :media => 'all' %> <%= javascript_include_tag 'calendar/yahoo-dom-event.js' %> <%= javascript_include_tag 'calendar/calendar.js' %> <%= javascript_include_tag 'application' %> @@ -25,6 +26,7 @@ <%= javascript_include_tag 'scriptaculous' %> <%= javascript_include_tag 'tablekit' %> <%= javascript_include_tag 'prototip' %> +<%= javascript_include_tag 'dashboard' %> <% end %>