*/
package org.sonar.plugins.core;
-import com.google.common.collect.Lists;
+import java.util.List;
+
import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.plugins.core.hotspots.Hotspots;
import org.sonar.plugins.core.metrics.UserManagedMetrics;
import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
-import org.sonar.plugins.core.sensors.*;
+import org.sonar.plugins.core.sensors.BranchCoverageDecorator;
+import org.sonar.plugins.core.sensors.CheckAlertThresholds;
+import org.sonar.plugins.core.sensors.CloseReviewsDecorator;
+import org.sonar.plugins.core.sensors.CommentDensityDecorator;
+import org.sonar.plugins.core.sensors.CoverageDecorator;
+import org.sonar.plugins.core.sensors.DirectoriesDecorator;
+import org.sonar.plugins.core.sensors.FilesDecorator;
+import org.sonar.plugins.core.sensors.GenerateAlertEvents;
+import org.sonar.plugins.core.sensors.LineCoverageDecorator;
+import org.sonar.plugins.core.sensors.ManualMeasureDecorator;
+import org.sonar.plugins.core.sensors.ProfileEventsSensor;
+import org.sonar.plugins.core.sensors.ProfileSensor;
+import org.sonar.plugins.core.sensors.ProjectLinksSensor;
+import org.sonar.plugins.core.sensors.UnitTestDecorator;
+import org.sonar.plugins.core.sensors.VersionEventsSensor;
+import org.sonar.plugins.core.sensors.ViolationsDecorator;
+import org.sonar.plugins.core.sensors.ViolationsDensityDecorator;
+import org.sonar.plugins.core.sensors.WeightedViolationsDecorator;
import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition;
-import org.sonar.plugins.core.timemachine.*;
-import org.sonar.plugins.core.widgets.*;
+import org.sonar.plugins.core.timemachine.NewCoverageAggregator;
+import org.sonar.plugins.core.timemachine.NewCoverageFileAnalyzer;
+import org.sonar.plugins.core.timemachine.NewViolationsDecorator;
+import org.sonar.plugins.core.timemachine.ReferenceAnalysis;
+import org.sonar.plugins.core.timemachine.TendencyDecorator;
+import org.sonar.plugins.core.timemachine.TimeMachineConfigurationPersister;
+import org.sonar.plugins.core.timemachine.VariationDecorator;
+import org.sonar.plugins.core.timemachine.ViolationPersisterDecorator;
+import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator;
+import org.sonar.plugins.core.widgets.AlertsWidget;
+import org.sonar.plugins.core.widgets.CodeCoverageWidget;
+import org.sonar.plugins.core.widgets.CommentsDuplicationsWidget;
+import org.sonar.plugins.core.widgets.ComplexityWidget;
+import org.sonar.plugins.core.widgets.CustomMeasuresWidget;
+import org.sonar.plugins.core.widgets.DescriptionWidget;
+import org.sonar.plugins.core.widgets.EventsWidget;
+import org.sonar.plugins.core.widgets.HotspotMetricWidget;
+import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget;
+import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget;
+import org.sonar.plugins.core.widgets.RulesWidget;
+import org.sonar.plugins.core.widgets.SizeWidget;
+import org.sonar.plugins.core.widgets.TimeMachineWidget;
+import org.sonar.plugins.core.widgets.TimelineWidget;
-import java.util.List;
+import com.google.common.collect.Lists;
@Properties({
@Property(
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
description = "HTTP URL of this Sonar server, such as <i>http://yourhost.yourdomain/sonar</i>. This value is used i.e. to create links in emails.",
- project = false,
- global = true,
- category = CoreProperties.CATEGORY_GENERAL),
- @Property(
- key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY,
- defaultValue = "cobertura",
- name = "Code coverage plugin",
- description = "Key of the code coverage plugin to use.",
- project = true,
- global = true,
+ project = false, global = true, category = CoreProperties.CATEGORY_GENERAL),
+ @Property(key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY, defaultValue = "cobertura", name = "Code coverage plugin",
+ description = "Key of the code coverage plugin to use.", project = true, global = true,
category = CoreProperties.CATEGORY_CODE_COVERAGE),
- @Property(
- key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
- defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
- name = "Import sources",
- description = "Set to false if sources should not be displayed, e.g. for security reasons.",
- project = true,
- module = true,
- global = true,
- category = CoreProperties.CATEGORY_SECURITY),
- @Property(
- key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY,
- defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE,
- name = "Tendency period",
- description = TendencyDecorator.PROP_DAYS_DESCRIPTION,
- project = false,
- global = true,
+ @Property(key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY, defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
+ name = "Import sources", description = "Set to false if sources should not be displayed, e.g. for security reasons.",
+ project = true, module = true, global = true, category = CoreProperties.CATEGORY_SECURITY),
+ @Property(key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY, defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE,
+ name = "Tendency period", description = TendencyDecorator.PROP_DAYS_DESCRIPTION, project = false, global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
- @Property(
- key = CoreProperties.SKIP_TENDENCIES_PROPERTY,
- defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE,
- name = "Skip tendencies",
- description = "Skip calculation of measure tendencies",
- project = true,
- module = false,
- global = true,
+ @Property(key = CoreProperties.SKIP_TENDENCIES_PROPERTY, defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE,
+ name = "Skip tendencies", description = "Skip calculation of measure tendencies", project = true, module = false, global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
- @Property(
- key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY,
- name = "Exclude modules",
- description = "Maven artifact ids of modules to exclude (comma-separated).",
- project = true,
- global = false,
+ @Property(key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY, name = "Exclude modules",
+ description = "Maven artifact ids of modules to exclude (comma-separated).", project = true, global = false,
category = CoreProperties.CATEGORY_GENERAL),
- @Property(
- key = CoreProperties.CORE_RULE_WEIGHTS_PROPERTY,
- defaultValue = CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE,
- name = "Rules weight",
- description = "A weight is associated to each priority to calculate the Rules Compliance Index.",
- project = false,
- global = true,
- category = CoreProperties.CATEGORY_GENERAL),
- @Property(
- key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY,
- defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE,
- name = "Force user authentication",
- description = "Forcing user authentication stops un-logged users to access Sonar.",
- project = false,
- global = true,
+ @Property(key = CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, defaultValue = CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE,
+ name = "Rules weight", description = "A weight is associated to each priority to calculate the Rules Compliance Index.",
+ project = false, global = true, category = CoreProperties.CATEGORY_GENERAL),
+ @Property(key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, defaultValue = ""
+ + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE, name = "Force user authentication",
+ description = "Forcing user authentication stops un-logged users to access Sonar.", project = false, global = true,
category = CoreProperties.CATEGORY_SECURITY),
- @Property(
- key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY,
- defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE,
- name = "Allow users to sign up online",
- description = "Users can sign up online.",
- project = false,
- global = true,
- category = CoreProperties.CATEGORY_SECURITY),
- @Property(
- key = CoreProperties.CORE_DEFAULT_GROUP,
- defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE,
- name = "Default user group",
- description = "Any new users will automatically join this group.",
- project = false,
- global = true,
+ @Property(key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY, defaultValue = ""
+ + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE, name = "Allow users to sign up online",
+ description = "Users can sign up online.", project = false, global = true, category = CoreProperties.CATEGORY_SECURITY),
+ @Property(key = CoreProperties.CORE_DEFAULT_GROUP, defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE,
+ name = "Default user group", description = "Any new users will automatically join this group.", project = false, global = true,
category = CoreProperties.CATEGORY_SECURITY),
@Property(
key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY,
defaultValue = "en",
name = "Locale used for violation messages",
description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property",
- project = true,
- global = true,
- category = CoreProperties.CATEGORY_L10N),
+ project = true, global = true, category = CoreProperties.CATEGORY_L10N),
@Property(
key = "sonar.timemachine.period1",
name = "Period 1",
- description = "Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
- "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
- "compare to previous analysis</li><li>A version, for example 1.2</li></ul>",
- project = false,
- global = true,
- defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1,
- category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
- @Property(
- key = "sonar.timemachine.period2",
- name = "Period 2",
- description = "See the property 'Period 1'",
- project = false,
- global = true,
- defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2,
- category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
- @Property(
- key = "sonar.timemachine.period3",
- name = "Period 3",
- description = "See the property 'Period 1'",
- project = false,
- global = true,
- defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3,
- category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
+ description = "Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before "
+ + "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to "
+ + "compare to previous analysis</li><li>A version, for example 1.2</li></ul>", project = false, global = true,
+ defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1, category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
+ @Property(key = "sonar.timemachine.period2", name = "Period 2", description = "See the property 'Period 1'", project = false,
+ global = true, defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2, category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
+ @Property(key = "sonar.timemachine.period3", name = "Period 3", description = "See the property 'Period 1'", project = false,
+ global = true, defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3, category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period4",
name = "Period 4",
- description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
- "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
- "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li><li>A version, for example 1.2</li></ul>",
- project = true,
- global = false,
- defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
- category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
- @Property(
- key = "sonar.timemachine.period5",
- name = "Period 5",
- description = "See the property 'Period 4'",
- project = true,
- global = false,
- defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5,
+ description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : "
+ + "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, "
+ + "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li><li>A version, for example 1.2</li></ul>",
+ project = true, global = false, defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
-
+ @Property(key = "sonar.timemachine.period5", name = "Period 5", description = "See the property 'Period 4'", project = true,
+ global = false, defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5, category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
// SERVER-SIDE TECHNICAL PROPERTIES
- @Property(
- key = "sonar.useStructureDump",
- name = "Use Structure Dump",
- description = "Used when creating database schema",
- project = false,
- global = false,
- defaultValue = "true"),
- @Property(
- key = "sonar.authenticator.downcase",
- name = "Downcase login",
- description = "Downcase login during user authentication, typically for Active Directory",
- project = false,
- global = false,
+ @Property(key = "sonar.useStructureDump", name = "Use Structure Dump", description = "Used when creating database schema",
+ project = false, global = false, defaultValue = "true"),
+ @Property(key = "sonar.authenticator.downcase", name = "Downcase login",
+ description = "Downcase login during user authentication, typically for Active Directory", project = false, global = false,
defaultValue = "false"),
- @Property(
- key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS,
- name = "Create user accounts",
- description = "Create accounts when authenticating users via an external system",
- project = false,
- global = false,
+ @Property(key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS, name = "Create user accounts",
+ description = "Create accounts when authenticating users via an external system", project = false, global = false,
defaultValue = "false"),
- @Property(
- key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE,
- name = "Ignore failures during authenticator startup",
- defaultValue = "false",
- project = false,
- global = false)
-})
+ @Property(key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE, name = "Ignore failures during authenticator startup",
+ defaultValue = "false", project = false, global = false) })
public class CorePlugin extends SonarPlugin {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
public List getExtensions() {
List extensions = Lists.newLinkedList();
extensions.add(CustomMeasuresWidget.class);
extensions.add(TimelineWidget.class);
extensions.add(TimeMachineWidget.class);
+ extensions.add(HotspotMetricWidget.class);
+ extensions.add(HotspotMostViolatedResourcesWidget.class);
+ extensions.add(HotspotMostViolatedRulesWidget.class);
// chart
extensions.add(XradarChart.class);
--- /dev/null
+<%
+ violations_metric = Metric.find(:first, :conditions => "name = 'violations'")
+ limit = widget_properties["numberOfLines"]
+ unless limit
+ limit = 5
+ end
+ defaultSeverity = widget_properties["defaultSeverity"]
+ if defaultSeverity
+ # we try to figure out if the user has specified a prefered severity, may it be an integer (0->4) or a text value ("blocker", ...)
+ defaultSeverityIdFromText = Sonar::RulePriority.id(widget_properties["defaultSeverity"].upcase)
+ defaultSeverity = defaultSeverityIdFromText.to_s if defaultSeverityIdFromText
+ end
+ defaultSeverity = "" unless defaultSeverity=="0" || defaultSeverity=="1" || defaultSeverity=="2" || defaultSeverity=="3" || defaultSeverity=="4"
+
+ measures_conditions = ["rule_id IS NOT NULL", "characteristic_id IS NULL"]
+ measures_values = {}
+ measures_conditions << "snapshot_id = :s_id"
+ measures_values[:s_id] = @snapshot.id
+ measures_conditions << "metric_id = :m_id"
+ measures_values[:m_id] = violations_metric.id
+ measures_conditions << "metric_id = :m_id"
+ measures_values[:m_id] = violations_metric.id
+
+ measures_by_priority = {}
+ measures_by_priority[""] = ProjectMeasure.find(:all,
+ :conditions => [measures_conditions.join(' AND '), measures_values],
+ :include => 'rule',
+ :order => 'value DESC',
+ :limit => limit)
+ (0..4).each do |priority|
+ measures_by_priority[priority.to_s] = ProjectMeasure.find(:all,
+ :conditions => [measures_conditions.join(' AND ') + " AND rule_priority = " + priority.to_s, measures_values],
+ :include => 'rule',
+ :order => 'value DESC',
+ :limit => limit)
+ end
+%>
+
+<script type="text/javascript">
+
+ function showCorrespondingDiv(severity) {
+ divs = $$('#widget-<%= widget.id-%> div.hotspot');
+ for (i=0; i<divs.size(); i++) {
+ divs[i].hide();
+ }
+ $('most-violated-rules-<%= widget.id -%>-' + severity).show();
+ }
+
+</script>
+
+<div class="line-block">
+ <div style="float:right">
+ <a href="<%= url_for(:controller => 'drilldown', :action => 'violations', :id => @resource.key) -%>"><%= message('widget.hotspot_metric.more') -%></a>
+ </div>
+ <h3><%= message('widget.hotspot_most_violated_rules.name') -%>
+ <select class="small" style="margin-left: 20px" onchange="showCorrespondingDiv(this.value);">
+ <option value="" <%= 'selected' if defaultSeverity=="" -%>><%= message('widget.hotspot_most_violated_rules.any_severity') -%></option>
+ <option value="4" <%= 'selected' if defaultSeverity=="4" -%>><%= message('severity.BLOCKER') -%></option>
+ <option value="3" <%= 'selected' if defaultSeverity=="3" -%>><%= message('severity.CRITICAL') -%></option>
+ <option value="2" <%= 'selected' if defaultSeverity=="2" -%>><%= message('severity.MAJOR') -%></option>
+ <option value="1" <%= 'selected' if defaultSeverity=="1" -%>><%= message('severity.MINOR') -%></option>
+ <option value="0" <%= 'selected' if defaultSeverity=="0" -%>><%= message('severity.INFO') -%></option>
+ </select>
+ </h3>
+</div>
+
+
+<div id="widget-<%= widget.id-%>">
+<%
+ measures_by_priority.keys.each do |priority|
+ measures = measures_by_priority[priority]
+ if measures.empty?
+%>
+
+ <div id="most-violated-rules-<%= widget.id -%>-<%= priority -%>" class="hotspot" style="padding-top:10px">
+ <span style="color: #777777; font-size: 93%; font-style:italic"><%= message('widget.hotspot_most_violated_rules.no_violation_for_severity') -%></span>
+ </div>
+
+<%
+ else
+%>
+
+ <div id="most-violated-rules-<%= widget.id -%>-<%= priority -%>" class="hotspot">
+ <table class="data">
+ <thead><tr><th colspan="3"/></tr></thead>
+ <tbody>
+ <%
+ violations_max_value = measures.first.value
+ measures.each do |m|
+ rule = m.rule
+ %>
+ <tr class="<%= cycle 'even','odd' -%>">
+ <td>
+ <a href="<%= url_for(:controller => 'drilldown', :action => 'violations', :id => @resource.key, :priority => Sonar::RulePriority.to_s(m.rule_priority)) -%>">
+ <%= image_tag('priority/' + m.rule_priority.to_s + '.png') -%>
+ </a>
+ </td>
+ <td>
+ <a href="<%= url_for(:controller => 'drilldown', :action => 'violations', :id => @resource.key, :rule => rule.key) -%>"><%= rule.name -%></a>
+ </td>
+ <td class="right">
+ <%= m.formatted_value -%>
+ </td>
+ <td class="barchart">
+ <div class="barchart" style="width: <%= (m.value*100/violations_max_value).round.to_i -%>%">
+ <div style="width: 100%;background-color:#777;"></div>
+ </div>
+ </td>
+ </tr>
+ <%
+ end
+ %>
+ </tbody>
+ </table>
+ </div>
+
+<%
+ end
+ end
+%>
+</div>
+
+<script type="text/javascript">
+ showCorrespondingDiv("<%= defaultSeverity -%>");
+</script>
\ No newline at end of file