The column WIDGET_PROPERTIES.VALUE_TYPE is removed because it duplicates the Java extensions.
import org.sonar.api.web.*;
-@WidgetCategory({"Hotspots"})
+@WidgetCategory("Hotspots")
@WidgetProperties(
- {
- @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"),
- @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO")
- }
+ {
+ @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"),
+ @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO")
+ }
)
public class HotspotMostViolatedRulesWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
<%
metric = widget_properties["metric"]
metric = Metric.by_key('ncloc') unless metric
- limit = widget_properties["numberOfLines"] || 5
+ limit = widget_properties["numberOfLines"]
title = widget_properties["title"]
title = message('widget.hotspot_metric.hotspots_by_x', :params => metric.short_name) if title.blank?
<%
- limit = widget_properties["numberOfLines"] || 5
-
- metric = Metric.by_key('weighted_violations')
-
- snapshots_conditions=["snapshots.scope = 'FIL'", "snapshots.islast=:islast", "snapshots.status = 'P'"]
- snapshots_values={:islast => true}
- snapshots_conditions << "(snapshots.qualifier = 'CLA' OR snapshots.qualifier = 'FIL' OR snapshots.qualifier = 'TRK')"
- snapshots_conditions << '(snapshots.id=:sid OR (snapshots.root_snapshot_id=:root_sid AND snapshots.path LIKE :path))'
- snapshots_values[:sid]=@snapshot.id
- snapshots_values[:root_sid] = (@snapshot.root_snapshot_id || @snapshot.id)
- snapshots_values[:path]="#{@snapshot.path}#{@snapshot.id}.%"
-
- measures_conditions = ["project_measures.rule_id IS NULL", "project_measures.characteristic_id IS NULL"]
- measures_values = {}
- measures_conditions << "project_measures.metric_id = :m_id"
- measures_values[:m_id] = metric.id
-
- measures=ProjectMeasure.find(:all,
- :joins => :snapshot,
- :conditions => [ (snapshots_conditions + measures_conditions).join(' AND '), snapshots_values.merge(measures_values)],
- :order => "project_measures.value #{'DESC' if metric.direction<0}",
- :limit => limit)
-
- snapshots=Snapshot.find(measures.map {|m| m.snapshot_id}, :include => 'project')
- snapshots_by_id = {}
- snapshots.each do |s|
- snapshots_by_id[s.id]=s
- end
+ limit = widget_properties["numberOfLines"]
+ metric = Metric.by_key('weighted_violations')
+
+ snapshots_conditions=["snapshots.scope = 'FIL'", "snapshots.islast=:islast", "snapshots.status = 'P'"]
+ snapshots_values={:islast => true}
+ snapshots_conditions << "(snapshots.qualifier = 'CLA' OR snapshots.qualifier = 'FIL' OR snapshots.qualifier = 'TRK')"
+ snapshots_conditions << '(snapshots.id=:sid OR (snapshots.root_snapshot_id=:root_sid AND snapshots.path LIKE :path))'
+ snapshots_values[:sid]=@snapshot.id
+ snapshots_values[:root_sid] = (@snapshot.root_snapshot_id || @snapshot.id)
+ snapshots_values[:path]="#{@snapshot.path}#{@snapshot.id}.%"
+
+ measures_conditions = ["project_measures.rule_id IS NULL", "project_measures.characteristic_id IS NULL"]
+ measures_values = {}
+ measures_conditions << "project_measures.metric_id = :m_id"
+ measures_values[:m_id] = metric.id
+
+ measures=ProjectMeasure.find(:all,
+ :joins => :snapshot,
+ :conditions => [(snapshots_conditions + measures_conditions).join(' AND '), snapshots_values.merge(measures_values)],
+ :order => "project_measures.value #{'DESC' if metric.direction<0}",
+ :limit => limit)
+
+ snapshots=Snapshot.find(measures.map { |m| m.snapshot_id }, :include => 'project')
+ snapshots_by_id = {}
+ snapshots.each do |s|
+ snapshots_by_id[s.id]=s
+ end
%>
<div class="line-block">
</div>
<table id="most-violated-resources-<%= widget.id -%>" class="data">
- <thead><tr><th colspan="11"></th></tr></thead>
+ <thead>
+ <tr>
+ <th colspan="11"></th>
+ </tr>
+ </thead>
<tbody>
-<%
- measures.each do |measure|
- resource = snapshots_by_id[measure.snapshot_id].resource
- violations_per_severity={}
- measure.text_value.split(';').each do |part|
- fields=part.split('=')
- violations_per_severity[fields[0]]=fields[1]
- end
-%>
- <tr class="<%= cycle 'even','odd', :name => ('hotspot_most_violated_resources' + widget.id.to_s) -%>">
+ <%
+ measures.each do |measure|
+ resource = snapshots_by_id[measure.snapshot_id].resource
+ violations_per_severity={}
+ measure.text_value.split(';').each do |part|
+ fields=part.split('=')
+ violations_per_severity[fields[0]]=fields[1]
+ end
+ %>
+ <tr class="<%= cycle 'even', 'odd', :name => ('hotspot_most_violated_resources' + widget.id.to_s) -%>">
<td>
<%= link_to_resource(resource, resource.name, {:tab => :violations}) -%>
</td>
</td>
<td class="small left">
<%= violations_per_severity["INFO"] ? violations_per_severity["INFO"].to_s : "0" -%>
- </td>
+ </td>
</tr>
-<%
- end
-%>
+ <%
+ end
+ %>
</tbody>
</table>
\ No newline at end of file
<%
- limit = widget_properties["numberOfLines"] || 5
+ limit = widget_properties["numberOfLines"]
metric_prefix = dashboard_configuration.selected_period? ? 'new_' : ''
value_column = (dashboard_configuration.selected_period? ? "variation_value_#{dashboard_configuration.period_index}" : 'value')
measures_by_severity = {}
end
measures_by_severity['']=all_measures.sort { |a, b| b.send(value_column) <=> a.send(value_column) }[0...limit]
- default_severity = widget_properties['defaultSeverity']||''
+ default_severity = widget_properties['defaultSeverity']
default_severity = '' unless Severity::KEYS.include?(default_severity)
%>
-<%
- if has_role?(:user, @project)
- limit = widget_properties["numberOfLines"].to_i == 0 ? 5 : widget_properties["numberOfLines"].to_i
-
- if @dashboard_configuration.selected_period?
- from_date = @dashboard_configuration.from_datetime
- to_date = DateTime.now
- end
+<%
+ if has_role?(:user, @project)
+ limit = widget_properties["numberOfLines"]
+
+ if @dashboard_configuration.selected_period?
+ from_date = @dashboard_configuration.from_datetime
+ to_date = DateTime.now
+ end
%>
-<div class="line-block">
- <div style="float:right">
- <a href="<%= url_for :controller => 'reviews', :action => 'index', :projects => [@project.id], :false_positives => 'only',
- :assignee_login => '', :statuses => [''], :from => from_date, :to => to_date -%>">
- <%= message('widgets.more') -%>
- </a>
+ <div class="line-block">
+ <div style="float:right">
+ <a href="<%= url_for :controller => 'reviews', :action => 'index', :projects => [@project.id], :false_positives => 'only',
+ :assignee_login => '', :statuses => [''], :from => from_date, :to => to_date -%>">
+ <%= message('widgets.more') -%>
+ </a>
+ </div>
+ <h3><%= message('widget.false_positive_reviews.name') -%></h3>
</div>
- <h3><%= message('widget.false_positive_reviews.name') -%></h3>
-</div>
-<div id="reviews-widget-<%= widget.id -%>">
- <%= render :partial => 'project/widgets/reviews/reviews_list',
- :locals => {:assignee_login => '',
- :project_key => @project.key,
- :statuses => 'RESOLVED',
- :resolution => 'FALSE-POSITIVE',
- :limit => limit,
- :widget_id => widget.id.to_s} %>
-</div>
+ <div id="reviews-widget-<%= widget.id -%>">
+ <%= render :partial => 'project/widgets/reviews/reviews_list',
+ :locals => {:assignee_login => '',
+ :project_key => @project.key,
+ :statuses => 'RESOLVED',
+ :resolution => 'FALSE-POSITIVE',
+ :limit => limit,
+ :widget_id => widget.id.to_s} %>
+ </div>
<% end %>
-<%
- if current_user && has_role?(:user, @project)
- limit = widget_properties["numberOfLines"].to_i == 0 ? 5 : widget_properties["numberOfLines"].to_i
-
- if @dashboard_configuration.selected_period?
- from_date = @dashboard_configuration.from_datetime
- to_date = DateTime.now
- end
+<%
+ if current_user && has_role?(:user, @project)
+ limit = widget_properties["numberOfLines"]
+
+ if @dashboard_configuration.selected_period?
+ from_date = @dashboard_configuration.from_datetime
+ to_date = DateTime.now
+ end
%>
-<div class="line-block">
- <div style="float:right">
- <a href="<%= url_for :controller => 'reviews', :action => 'index', :projects => [@project.id], :from => from_date, :to => to_date -%>">
- <%= message('widgets.more') -%>
- </a>
+ <div class="line-block">
+ <div style="float:right">
+ <a href="<%= url_for :controller => 'reviews', :action => 'index', :projects => [@project.id], :from => from_date, :to => to_date -%>">
+ <%= message('widgets.more') -%>
+ </a>
+ </div>
+ <h3><%= message('widget.my_reviews.name') -%></h3>
</div>
- <h3><%= message('widget.my_reviews.name') -%></h3>
-</div>
-<div id="reviews-widget-<%= widget.id -%>">
- <%= render :partial => 'project/widgets/reviews/reviews_list',
- :locals => {:assignee_login => current_user.login,
- :project_key => @project.key,
- :statuses => 'OPEN,REOPENED',
- :resolution => '',
- :limit => limit,
- :widget_id => widget.id.to_s} %>
-</div>
+ <div id="reviews-widget-<%= widget.id -%>">
+ <%= render :partial => 'project/widgets/reviews/reviews_list',
+ :locals => {:assignee_login => current_user.login,
+ :project_key => @project.key,
+ :statuses => 'OPEN,REOPENED',
+ :resolution => '',
+ :limit => limit,
+ :widget_id => widget.id.to_s} %>
+ </div>
<% end %>
\ No newline at end of file
<%
if has_role?(:user, @project)
- limit = widget_properties["numberOfLines"].to_i == 0 ? 5 : widget_properties["numberOfLines"].to_i
+ limit = widget_properties["numberOfLines"]
from_date=nil
to_date=nil
<%
- # Retrieve widget settings
- metric_ids = []
- (1..10).each do |index|
- metric=widget_properties["metric#{index}"]
- if metric
- metric_ids << metric.id
- end
- end
- if metric_ids.empty?
- # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
- ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
- metric_ids << ncloc.id
- end
- numberOfColumns = widget_properties["numberOfColumns"].to_i == 0 ? 4 : widget_properties["numberOfColumns"].to_i
-
- # Retrieve the measures for each metric on each snapshot
- options = {}
- from_date = dashboard_configuration.from_datetime
- if from_date
- options[:from] = from_date
- end
- snapshots=Snapshot.for_timemachine_widget(@resource, numberOfColumns, options)
- sids = snapshots.collect{|s| s.id}.uniq
- measures=ProjectMeasure.find(:all,
- :conditions => ["snapshot_id IN (:snapshot_id) AND metric_id IN (:metric_id) AND rule_id IS NULL AND characteristic_id IS NULL",
- {:snapshot_id => sids, :metric_id => metric_ids}]
- )
+ # Retrieve widget settings
+ metric_ids = []
+ (1..10).each do |index|
+ metric=widget_properties["metric#{index}"]
+ if metric
+ metric_ids << metric.id
+ end
+ end
+ if metric_ids.empty?
+ # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
+ ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
+ metric_ids << ncloc.id
+ end
+ number_of_columns = widget_properties["numberOfColumns"]
- # And prepare the rows to display
- snapshot_by_id={}
- snapshots.each do |s|
- snapshot_by_id[s.id]=s
- end
- rows_by_metric_id={}
- measures.each do |measure|
- next unless measure.metric
+ # Retrieve the measures for each metric on each snapshot
+ options = {}
+ from_date = dashboard_configuration.from_datetime
+ if from_date
+ options[:from] = from_date
+ end
+ snapshots=Snapshot.for_timemachine_widget(@resource, number_of_columns, options)
+ sids = snapshots.collect { |s| s.id }.uniq
+ measures=ProjectMeasure.find(:all,
+ :conditions => ["snapshot_id IN (:snapshot_id) AND metric_id IN (:metric_id) AND rule_id IS NULL AND characteristic_id IS NULL",
+ {:snapshot_id => sids, :metric_id => metric_ids}]
+ )
- if measure.metric.timemachine? && (measure.value || measure.text_value)
- row=rows_by_metric_id[measure.metric_id]
- unless row
- row=Sonar::TimemachineRow.new(measure.metric)
- rows_by_metric_id[measure.metric_id]=row
- end
+ # And prepare the rows to display
+ snapshot_by_id={}
+ snapshots.each do |s|
+ snapshot_by_id[s.id]=s
+ end
+ rows_by_metric_id={}
+ measures.each do |measure|
+ next unless measure.metric
- #optimization : avoid eager loading of snapshots
- measure.snapshot=snapshot_by_id[measure.snapshot_id]
- row.add_measure(measure)
- end
- end
-
- # Create the list of rows to display in the same order as defined by the user
- rows=[]
- metric_ids.each do |metric_id|
- row = rows_by_metric_id[metric_id]
- if row
- rows<<row
- end
- end
-
- # Should display the sparkline?
- displaySparkLine = widget_properties["displaySparkLine"] if snapshot_by_id.size > 1
+ if measure.metric.timemachine? && (measure.value || measure.text_value)
+ row=rows_by_metric_id[measure.metric_id]
+ unless row
+ row=Sonar::TimemachineRow.new(measure.metric)
+ rows_by_metric_id[measure.metric_id]=row
+ end
+
+ #optimization : avoid eager loading of snapshots
+ measure.snapshot=snapshot_by_id[measure.snapshot_id]
+ row.add_measure(measure)
+ end
+ end
+
+ # Create the list of rows to display in the same order as defined by the user
+ rows=[]
+ metric_ids.each do |metric_id|
+ row = rows_by_metric_id[metric_id]
+ if row
+ rows<<row
+ end
+ end
+
+ # Should display the sparkline?
+ displaySparkLine = widget_properties["displaySparkLine"] if snapshot_by_id.size > 1
%>
<div class="widget-matrix">
-<table class="data">
+ <table class="data">
- <thead>
+ <thead>
<tr>
- <th> </th>
- <%
- snapshots.each do |snapshot|
- event = snapshot.event('Version')
+ <th></th>
+ <%
+ snapshots.each do |snapshot|
+ event = snapshot.event('Version')
%>
<th nowrap="nowrap" style="vertical-align:top">
- <%= l snapshot.created_at.to_date -%>
- <br/>
- <%= event.name unless event==nil -%>
+ <%= l snapshot.created_at.to_date -%>
+ <br/>
+ <%= event.name unless event==nil -%>
</th>
<% end %>
<% if displaySparkLine %>
- <th> </th>
+ <th></th>
<% end %>
- </tr>
- </thead>
+ </tr>
+ </thead>
- <tbody>
- <%
- rows.select{|row| row.metric.val_type != Metric::VALUE_TYPE_DISTRIB}.each do |row|
- %>
- <tr class="<%= cycle 'even','odd', :name => ('time_machine' + widget.id.to_s) -%>">
- <td width="1%" nowrap="nowrap" class="left text">
- <%= row.metric.short_name %>
- </td>
- <%
- snapshots.each do |snapshot|
- measure=row.measure(snapshot)
- %>
- <td width="1%" nowrap="nowrap" class="right"><%= format_measure(measure, :skip_span_id => true) %></td>
- <% end %>
+ <tbody>
<%
- sparkline_url=row.sparkline_url
- if displaySparkLine && sparkline_url
+ rows.select { |row| row.metric.val_type != Metric::VALUE_TYPE_DISTRIB }.each do |row|
%>
- <td width="1%" >
- <%= image_tag(sparkline_url) %>
- </td>
- <% end %>
- </tr>
+ <tr class="<%= cycle 'even', 'odd', :name => ('time_machine' + widget.id.to_s) -%>">
+ <td width="1%" nowrap="nowrap" class="left text">
+ <%= row.metric.short_name %>
+ </td>
+ <%
+ snapshots.each do |snapshot|
+ measure=row.measure(snapshot)
+ %>
+ <td width="1%" nowrap="nowrap" class="right"><%= format_measure(measure, :skip_span_id => true) %></td>
+ <% end %>
+ <%
+ sparkline_url=row.sparkline_url
+ if displaySparkLine && sparkline_url
+ %>
+ <td width="1%">
+ <%= image_tag(sparkline_url) %>
+ </td>
+ <% end %>
+ </tr>
<% end %>
- </tbody>
+ </tbody>
-</table>
+ </table>
</div>
\ No newline at end of file
<%
- # Retrieve widget settings
- metric_data_map = {}
- metric_name_map = {}
- (1..3).each do |index|
- metric=widget_properties["metric#{index}"]
- if metric
- metric_data_map[metric.id] = []
- metric_name_map[metric.id] = metric.short_name
- end
- end
- if metric_data_map.empty?
- # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
- ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
- metric_data_map[ncloc.id] = []
- metric_name_map[ncloc.id] = message('metric.ncloc.name')
- end
- chartHeight = widget_properties["chartHeight"].to_i == 0 ? "null" : widget_properties["chartHeight"]
-
- # Retrieve metric trend information
- options = {}
- from_date = dashboard_configuration.from_datetime
- if from_date
- options[:from] = from_date
- end
- metric_count_per_snapshot_id = {}
- TrendsChart.time_machine_measures(@resource, metric_data_map.keys, options).each() do |trend_item|
- sid = trend_item["sid"]
- if metric_count_per_snapshot_id[sid]
- metric_count_per_snapshot_id[sid] += 1
- else
- metric_count_per_snapshot_id[sid] = 1
- end
- metric_data_map[trend_item["metric_id"].to_i] << {:date => trend_item["created_at"], :value => trend_item["value"], :sid => trend_item["sid"]}
- end
+ # Retrieve widget settings
+ metric_data_map = {}
+ metric_name_map = {}
+ (1..3).each do |index|
+ metric=widget_properties["metric#{index}"]
+ if metric
+ metric_data_map[metric.id] = []
+ metric_name_map[metric.id] = metric.short_name
+ end
+ end
+ if metric_data_map.empty?
+ # No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
+ ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
+ metric_data_map[ncloc.id] = []
+ metric_name_map[ncloc.id] = message('metric.ncloc.name')
+ end
+ chartHeight = widget_properties["chartHeight"]
+
+ # Retrieve metric trend information
+ options = {}
+ from_date = dashboard_configuration.from_datetime
+ if from_date
+ options[:from] = from_date
+ end
+ metric_count_per_snapshot_id = {}
+ TrendsChart.time_machine_measures(@resource, metric_data_map.keys, options).each() do |trend_item|
+ sid = trend_item["sid"]
+ if metric_count_per_snapshot_id[sid]
+ metric_count_per_snapshot_id[sid] += 1
+ else
+ metric_count_per_snapshot_id[sid] = 1
+ end
+ metric_data_map[trend_item["metric_id"].to_i] << {:date => trend_item["created_at"], :value => trend_item["value"], :sid => trend_item["sid"]}
+ end
+
+ # Create JS structures to print out in the HTML page
+ js_data = "["
+ js_snapshots = "["
+ js_metrics = "["
+ total_number_of_metrics = metric_name_map.keys.size()
+ metric_data_map.keys.each_with_index() do |metric_id, index|
+ unless metric_data_map[metric_id].empty?
+ js_metrics += "\"" + metric_name_map[metric_id] + "\","
+ js_data += "["
+ metric_data_map[metric_id].each() do |metric_data|
+ # for every metric value, we need to check that the corresponding snapshot has values for each metric (if not, Protovis won't be able to display)
+ if metric_count_per_snapshot_id[metric_data[:sid]]==total_number_of_metrics
+ m_date = metric_data[:date]
+ # Only Oracle returns a Time object, so let's parse this string if it's not a Time instance
+ m_date = Time.parse(metric_data[:date]) unless m_date.is_a? Time
+ m_value = sprintf("%0.02f", metric_data[:value])
+ m_value_localized = (m_value.end_with? '.00') ? number_with_precision(metric_data[:value], :precision => 0).to_s : number_with_precision(metric_data[:value], :precision => 2).to_s
+ js_data += "{x:d("
+ js_data += m_date.year.to_s
+ js_data += ","
+ # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
+ js_data += (m_date.month - 1).to_s
+ js_data += ","
+ js_data += m_date.day.to_s
+ js_data += ","
+ js_data += m_date.hour.to_s
+ js_data += ","
+ js_data += m_date.min.to_s
+ js_data += ","
+ js_data += m_date.sec.to_s
+ js_data += "),y:"
+ js_data += m_value
+ js_data += ",yl:\""
+ js_data += m_value_localized
+ js_data += "\"},"
+ if index == 0
+ # we fill the js_snapshots array (no need to do this more than once)
+ js_snapshots += "{sid:"
+ js_snapshots += metric_data[:sid].to_s
+ js_snapshots += ",d:\""
+ js_snapshots += human_short_date m_date
+ js_snapshots += "\"},"
+ end
+ end
+ end
+ js_data = js_data.chomp(',') + "],"
+ end
+ end
+ js_data = js_data.chomp(',') + "]"
+ js_snapshots = js_snapshots.chomp(',') + "]"
+ js_metrics = js_metrics.chomp(',') + "]"
+
+ # Prepare also event structure if required
+ unless widget_properties["hideEvents"]
+ events = {}
+ unless from_date
+ # find the oldest date
+ metric_data_map.values.each() do |metric_data_array|
+ first_date = metric_data_array[0][:date]
+ # Only Oracle returns a Time object, so let's parse this string if it's not a Time instance
+ first_date = Time.parse(metric_data_array[0][:date]) unless first_date.is_a? Time
+ from_date = first_date if !from_date || from_date > first_date
+ end
+ end
+ Event.find(:all, :conditions => ["resource_id=? AND event_date>=?", @resource.id, from_date], :order => 'event_date').each() do |event|
+ if events[event.event_date]
+ events[event.event_date] << event
+ else
+ date_entry = [event]
+ events[event.event_date] = date_entry
+ end
+ end
+ js_events = "["
+ events.keys().sort.each() do |e_date|
+ e_details = events[e_date]
+ js_events += "{sid:"
+ js_events += e_details[0].snapshot_id.to_s
+ js_events += ",d:d("
+ js_events += e_date.year.to_s
+ js_events += ","
+ # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
+ js_events += (e_date.month - 1).to_s
+ js_events += ","
+ js_events += e_date.day.to_s
+ js_events += ","
+ js_events += e_date.hour.to_s
+ js_events += ","
+ js_events += e_date.min.to_s
+ js_events += ","
+ js_events += e_date.sec.to_s
+ js_events += "),l:["
+ e_details.each() do |e|
+ js_events += "{n:\""
+ js_events += e.name
+ js_events += "\"},"
+ end
+ js_events = js_events.chomp(',') + "]},"
+ end
+ js_events = js_events.chomp(',') + "]"
+ end
- # Create JS structures to print out in the HTML page
- js_data = "["
- js_snapshots = "["
- js_metrics = "["
- total_number_of_metrics = metric_name_map.keys.size()
- metric_data_map.keys.each_with_index() do |metric_id, index|
- unless metric_data_map[metric_id].empty?
- js_metrics += "\"" + metric_name_map[metric_id] + "\","
- js_data += "["
- metric_data_map[metric_id].each() do |metric_data|
- # for every metric value, we need to check that the corresponding snapshot has values for each metric (if not, Protovis won't be able to display)
- if metric_count_per_snapshot_id[metric_data[:sid]]==total_number_of_metrics
- m_date = metric_data[:date]
- # Only Oracle returns a Time object, so let's parse this string if it's not a Time instance
- m_date = Time.parse(metric_data[:date]) unless m_date.is_a? Time
- m_value = sprintf( "%0.02f", metric_data[:value])
- m_value_localized = (m_value.end_with? '.00') ? number_with_precision(metric_data[:value], :precision => 0).to_s : number_with_precision(metric_data[:value], :precision => 2).to_s
- js_data += "{x:d("
- js_data += m_date.year.to_s
- js_data += ","
- # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
- js_data += (m_date.month - 1).to_s
- js_data += ","
- js_data += m_date.day.to_s
- js_data += ","
- js_data += m_date.hour.to_s
- js_data += ","
- js_data += m_date.min.to_s
- js_data += ","
- js_data += m_date.sec.to_s
- js_data += "),y:"
- js_data += m_value
- js_data += ",yl:\""
- js_data += m_value_localized
- js_data += "\"},"
- if index == 0
- # we fill the js_snapshots array (no need to do this more than once)
- js_snapshots += "{sid:"
- js_snapshots += metric_data[:sid].to_s
- js_snapshots += ",d:\""
- js_snapshots += human_short_date m_date
- js_snapshots += "\"},"
- end
- end
- end
- js_data = js_data.chomp(',') + "],"
- end
- end
- js_data = js_data.chomp(',') + "]"
- js_snapshots = js_snapshots.chomp(',') + "]"
- js_metrics = js_metrics.chomp(',') + "]"
-
- # Prepare also event structure if required
- unless widget_properties["hideEvents"]
- events = {}
- unless from_date
- # find the oldest date
- metric_data_map.values.each() do |metric_data_array|
- first_date = metric_data_array[0][:date]
- # Only Oracle returns a Time object, so let's parse this string if it's not a Time instance
- first_date = Time.parse(metric_data_array[0][:date]) unless first_date.is_a? Time
- from_date = first_date if !from_date || from_date > first_date
- end
- end
- Event.find(:all, :conditions => ["resource_id=? AND event_date>=?", @resource.id, from_date], :order => 'event_date').each() do |event|
- if events[event.event_date]
- events[event.event_date] << event
- else
- date_entry = [event]
- events[event.event_date] = date_entry
- end
- end
- js_events = "["
- events.keys().sort.each() do |e_date|
- e_details = events[e_date]
- js_events += "{sid:"
- js_events += e_details[0].snapshot_id.to_s
- js_events += ",d:d("
- js_events += e_date.year.to_s
- js_events += ","
- # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
- js_events += (e_date.month - 1).to_s
- js_events += ","
- js_events += e_date.day.to_s
- js_events += ","
- js_events += e_date.hour.to_s
- js_events += ","
- js_events += e_date.min.to_s
- js_events += ","
- js_events += e_date.sec.to_s
- js_events += "),l:["
- e_details.each() do |e|
- js_events += "{n:\""
- js_events += e.name
- js_events += "\"},"
- end
- js_events = js_events.chomp(',') + "]},"
- end
- js_events = js_events.chomp(',') + "]"
- end
-
%>
<% if widget_properties["chartTitle"] %>
-<h3 style="text-align: center; margin-bottom: 10px"><%= h(widget_properties["chartTitle"]) -%></h3>
+ <h3 style="text-align: center; margin-bottom: 10px"><%= h(widget_properties["chartTitle"]) -%></h3>
<% end %>
<% if metric_data_map.values[0].size == 1 %>
- <span class="empty_widget"><%= message('widget.timeline.timeline_not_displayed') -%></span>
+ <span class="empty_widget"><%= message('widget.timeline.timeline_not_displayed') -%></span>
<% else %>
- <div id="timeline-chart-<%= widget.id -%>"></div>
- <script type="text/javascript+protovis">
- function d(y,m,d,h,min,s) {
- return new Date(y,m,d,h,min,s);
- }
- var data = <%= js_data -%>;
- var snapshots = <%= js_snapshots -%>;
- var metrics = <%= js_metrics -%>;
- var events = <%= js_events ? js_events : "null" -%>;
- var timeline = new SonarWidgets.Timeline('timeline-chart-<%= widget.id -%>')
- .height(<%= chartHeight -%>)
- .data(data)
- .snapshots(snapshots)
- .metrics(metrics)
- .events(events);
- timeline.render();
-
- </script>
+ <div id="timeline-chart-<%= widget.id -%>"></div>
+ <script type="text/javascript+protovis">
+ function d(y, m, d, h, min, s) {
+ return new Date(y, m, d, h, min, s);
+ }
+ var data = <%= js_data -%>;
+ var snapshots = <%= js_snapshots -%>;
+ var metrics = <%= js_metrics -%>;
+ var events = <%= js_events ? js_events : "null" -%>;
+ var timeline = new SonarWidgets.Timeline('timeline-chart-<%= widget.id -%>')
+ .height(<%= chartHeight -%>)
+ .data(data)
+ .snapshots(snapshots)
+ .metrics(metrics)
+ .events(events);
+ timeline.render();
+
+ </script>
<% end %>
\ No newline at end of file
- complete the Derby DDL file used for unit tests : sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl
*/
- public static final int LAST_VERSION = 236;
+ public static final int LAST_VERSION = 237;
public final static String TABLE_NAME = "schema_migrations";
private Long widgetId;
private String key;
private String value;
- private String valueType;
/**
* @return the id
}
/**
- * @param id
- * the id to set
+ * @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
- * @param widgetId
- * the widgetId to set
+ * @param widgetId the widgetId to set
*/
public void setWidgetId(Long widgetId) {
this.widgetId = widgetId;
}
/**
- * @param key
- * the key to set
+ * @param key the key to set
*/
public void setKey(String key) {
this.key = key;
}
/**
- * @param value
- * the value to set
+ * @param value the value to set
*/
public void setValue(String value) {
this.value = value;
}
- /**
- * @return the valueType
- */
- public String getValueType() {
- return valueType;
- }
-
- /**
- * @param valueType
- * the valueType to set
- */
- public void setValueType(String valueType) {
- this.valueType = valueType;
- }
-
}
<mapper namespace="org.sonar.persistence.dashboard.WidgetPropertyMapper">
- <insert id="insert" parameterType="WidgetProperty" keyColumn="id" useGeneratedKeys="false" keyProperty ="id">
- INSERT INTO widget_properties (id, widget_id, kee, text_value, value_type)
- VALUES (widget_properties_seq.NEXTVAL, #{widgetId}, #{key, jdbcType=VARCHAR}, #{value, jdbcType=VARCHAR}, #{valueType, jdbcType=VARCHAR})
+ <insert id="insert" parameterType="WidgetProperty" keyColumn="id" useGeneratedKeys="false" keyProperty="id">
+ INSERT INTO widget_properties (id, widget_id, kee, text_value)
+ VALUES (widget_properties_seq.NEXTVAL, #{widgetId}, #{key, jdbcType=VARCHAR}, #{value, jdbcType=VARCHAR})
</insert>
</mapper>
<mapper namespace="org.sonar.persistence.dashboard.WidgetPropertyMapper">
- <insert id="insert" parameterType="WidgetProperty" useGeneratedKeys="true" keyProperty ="id">
- INSERT INTO widget_properties (widget_id, kee, text_value, value_type)
- VALUES (#{widgetId}, #{key, jdbcType=VARCHAR}, #{value, jdbcType=VARCHAR}, #{valueType, jdbcType=VARCHAR})
+ <insert id="insert" parameterType="WidgetProperty" useGeneratedKeys="true" keyProperty="id">
+ INSERT INTO widget_properties (widget_id, kee, text_value)
+ VALUES (#{widgetId}, #{key, jdbcType=VARCHAR}, #{value, jdbcType=VARCHAR})
</insert>
</mapper>
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('234');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('235');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('236');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('237');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"WIDGET_ID" INTEGER NOT NULL,
"KEE" VARCHAR(100),
- "TEXT_VALUE" VARCHAR(4000),
- "VALUE_TYPE" VARCHAR(20)
+ "TEXT_VALUE" VARCHAR(4000)
);
CREATE TABLE "EVENTS" (
*/
package org.sonar.persistence.dashboard;
-import java.util.Date;
-
import org.junit.Before;
import org.junit.Test;
import org.sonar.persistence.DaoTestCase;
-import org.sonar.persistence.dashboard.DashboardDao;
-import org.sonar.persistence.dashboard.DashboardDto;
-import org.sonar.persistence.dashboard.WidgetDto;
-import org.sonar.persistence.dashboard.WidgetPropertyDto;
+
+import java.util.Date;
public class DashboardDaoTest extends DaoTestCase {
WidgetPropertyDto property = new WidgetPropertyDto();
property.setKey("displayITs");
property.setValue("true");
- property.setValueType("BOOLEAN");
widgetDto.addWidgetProperty(property);
dao.insert(dashboardDto);
- checkTables("shouldInsert", new String[] { "created_at", "updated_at" }, "dashboards", "widgets", "widget_properties");
+ checkTables("shouldInsert", new String[]{"created_at", "updated_at"}, "dashboards", "widgets", "widget_properties");
}
@Test
WidgetPropertyDto property = new WidgetPropertyDto();
property.setKey(null);
property.setValue(null);
- property.setValueType(null);
widgetDto.addWidgetProperty(property);
dao.insert(dashboardDto);
<dataset>
<dashboards
- id="1"
- kee="d-key"
- user_id="6"
- name="My Dashboard"
- description="This is a dashboard"
- column_layout="100%"
- shared="1"/>
+ id="1"
+ kee="d-key"
+ user_id="6"
+ name="My Dashboard"
+ description="This is a dashboard"
+ column_layout="100%"
+ shared="[true]"/>
<widgets
- id="1"
- dashboard_id="1"
- widget_key="code_coverage"
- name="Code coverage"
- description="Widget for code coverage"
- column_index="13"
- row_index="14"
- configured="1"/>
+ id="1"
+ dashboard_id="1"
+ widget_key="code_coverage"
+ name="Code coverage"
+ description="Widget for code coverage"
+ column_index="13"
+ row_index="14"
+ configured="[true]"/>
<widget_properties
- id="1"
- widget_id="1"
- kee="displayITs"
- text_value="true"
- value_type="BOOLEAN"/>
+ id="1"
+ widget_id="1"
+ kee="displayITs"
+ text_value="true"/>
</dataset>
<dataset>
<dashboards
- id="1"
- kee="d-key"
- user_id="[null]"
- name="[null]"
- description="[null]"
- column_layout="[null]"
- shared="1"
- created_at="[null]"
- updated_at="[null]"/>
+ id="1"
+ kee="d-key"
+ user_id="[null]"
+ name="[null]"
+ description="[null]"
+ column_layout="[null]"
+ shared="[true]"
+ created_at="[null]"
+ updated_at="[null]"/>
<widgets
- id="1"
- dashboard_id="1"
- widget_key="code_coverage"
- name="[null]"
- description="[null]"
- column_index="[null]"
- row_index="[null]"
- configured="1"
- created_at="[null]"
- updated_at="[null]"/>
+ id="1"
+ dashboard_id="1"
+ widget_key="code_coverage"
+ name="[null]"
+ description="[null]"
+ column_index="[null]"
+ row_index="[null]"
+ configured="[true]"
+ created_at="[null]"
+ updated_at="[null]"/>
<widget_properties
- id="1"
- widget_id="1"
- kee="[null]"
- text_value="[null]"
- value_type="[null]"/>
+ id="1"
+ widget_id="1"
+ kee="[null]"
+ text_value="[null]"/>
</dataset>
*/
package org.sonar.server.ui;
+import com.google.common.collect.Maps;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.sonar.api.utils.AnnotationUtils;
import org.sonar.api.web.*;
+import java.util.Collection;
+import java.util.Map;
+
public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
private V view;
private String[] resourceLanguages = {};
private String[] defaultForMetrics = {};
private String description = "";
- private WidgetProperty[] widgetProperties = {};
+ private Map<String, WidgetProperty> widgetPropertiesByKey = Maps.newHashMap();
private String[] widgetCategories = {};
private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT;
private boolean isDefaultTab = false;
WidgetProperties propAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetProperties.class);
if (propAnnotation != null) {
- this.widgetProperties = propAnnotation.value();
+ for (WidgetProperty property : propAnnotation.value()) {
+ widgetPropertiesByKey.put(property.key(), property);
+ }
}
WidgetCategory categAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetCategory.class);
return description;
}
- public WidgetProperty[] getWidgetProperties() {
- return widgetProperties;
+ public Collection<WidgetProperty> getWidgetProperties() {
+ return widgetPropertiesByKey.values();
+ }
+
+ public WidgetProperty getWidgetProperty(String propertyKey) {
+ return widgetPropertiesByKey.get(propertyKey);
}
public String[] getWidgetCategories() {
}
public boolean isEditable() {
- return !ArrayUtils.isEmpty(widgetProperties);
+ return !widgetPropertiesByKey.isEmpty();
}
public boolean hasRequiredProperties() {
boolean requires = false;
- for (WidgetProperty property : widgetProperties) {
+ for (WidgetProperty property : getWidgetProperties()) {
if (!property.optional() && StringUtils.isEmpty(property.defaultValue())) {
requires = true;
}
}
ViewProxy rhs = (ViewProxy) obj;
return new EqualsBuilder()
- .append(getId(), rhs.getId())
- .isEquals();
+ .append(getId(), rhs.getId())
+ .isEquals();
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .append("id", view.getId())
- .append("sections", sections)
- .append("userRoles", userRoles)
- .append("scopes", resourceScopes)
- .append("qualifiers", resourceQualifiers)
- .append("languages", resourceLanguages)
- .append("metrics", defaultForMetrics)
- .toString();
+ .append("id", view.getId())
+ .append("sections", sections)
+ .append("userRoles", userRoles)
+ .append("scopes", resourceScopes)
+ .append("qualifiers", resourceQualifiers)
+ .append("languages", resourceLanguages)
+ .append("metrics", defaultForMetrics)
+ .toString();
}
public int compareTo(ViewProxy other) {
return new CompareToBuilder()
- .append(getTitle(), other.getTitle())
- .append(getId(), other.getId())
- .toComparison();
+ .append(getTitle(), other.getTitle())
+ .append(getId(), other.getId())
+ .toComparison();
}
}
\ No newline at end of file
def save_widget
widget=Widget.find(params[:wid].to_i)
#TODO check owner of dashboard
- definition=java_facade.getWidget(widget.widget_key)
- errors_by_property_key={}
- definition.getWidgetProperties().each do |property_def|
- value=params[property_def.key()] || property_def.defaultValue()
- value='false' if value.empty? && property_def.type.name()==WidgetProperty::TYPE_BOOLEAN
-
- errors=WidgetProperty.validate_definition(property_def, value)
- if errors.empty?
- widget.set_property(property_def.key(), value, property_def.type.name())
- else
- widget.unset_property(property_def.key())
- errors_by_property_key[property_def.key()]=errors
+ Widget.transaction do
+ widget.properties.clear
+ widget.java_definition.getWidgetProperties().each do |java_property|
+ value=params[java_property.key()] || java_property.defaultValue()
+ if value && !value.empty?
+ prop = widget.properties.build(:kee => java_property.key, :text_value => value)
+ prop.save!
+ end
end
- end
-
- if errors_by_property_key.empty?
widget.configured=true
- widget.save
- widget.properties.each { |p| p.save }
+ widget.save!
render :update do |page|
page.redirect_to(url_for(:action => :configure, :did => widget.dashboard_id, :id => params[:id]))
end
- else
- widget.configured=false
- widget.save
- render :update do |page|
- page.replace_html "widget_props_#{widget.id}", :partial => 'dashboard/widget_properties', :locals => {:widget => widget, :definition => definition, :errors_by_property_key => errors_by_property_key}
- end
end
end
has_many :properties, :dependent => :delete_all, :class_name => 'WidgetProperty'
belongs_to :dashboards
- validates_presence_of :name
- validates_length_of :name, :within => 1..256
+ 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_presence_of :widget_key
+ validates_length_of :widget_key, :within => 1..256
def property(key)
- properties().each do |p|
+ self.properties().each do |p|
return p if (p.key==key)
end
nil
"block_#{id}"
end
- def property_value(key, default_value=nil)
+ def property_text_value(key)
prop=property(key)
- (prop ? prop.value : nil) || default_value
+ prop ? prop.text_value : nil
end
- def set_property(key, value, value_type)
+ def property_value(key)
prop=property(key)
- if prop
- prop.text_value=value
- prop.value_type=value_type
- else
- prop=self.properties.build(:kee => key, :text_value => value, :value_type => value_type)
- end
- properties_as_hash[key]=prop.typed_value
- end
-
- def unset_property(key)
- prop=property(key)
- self.properties.delete(prop) if prop
- end
-
- def delete_property(key)
- prop=property(key)
- if prop
- properties.delete(prop)
- end
+ prop ? prop.value : nil
end
def properties_as_hash
@properties_hash ||=
begin
hash={}
- properties.each do |prop|
- hash[prop.key]=prop.typed_value
+ java_definition.getWidgetProperties().each do |property_definition|
+ prop = property(property_definition.key)
+ hash[property_definition.key]=(prop ? prop.value : WidgetProperty.text_to_value(property_definition.defaultValue(), property_definition.type().name()))
end
hash
end
- @properties_hash
+ end
+
+ def java_definition
+ Java::OrgSonarServerUi::JRubyFacade.getInstance().getWidget(key)
+ end
+
+ def layout
+ java_definition.getWidgetLayout().name()
end
end
\ No newline at end of file
belongs_to :widget
- validates_length_of :kee, :within => 1..100
- validates_length_of :text_value, :maximum => 4000, :allow_blank => true, :allow_nil => true
+ validates_length_of :kee, :within => 1..100
+ validates_length_of :text_value, :maximum => 4000, :allow_blank => true, :allow_nil => true
def key
kee
end
- def value
- text_value
+ def text_value
+ read_attribute(:text_value) || default_text_value
end
- def typed_value
- case value_type
- when TYPE_INTEGER
- value.to_i
- when TYPE_FLOAT
- Float(value)
- when TYPE_BOOLEAN
- value=='true'
- when TYPE_METRIC
- Metric.by_key(value.to_s)
- else
- value
- end
+ def default_text_value
+ java_definition.defaultValue()
+ end
+
+ def type
+ @type ||=
+ begin
+ java_definition.type().name()
+ end
+ end
+
+ def java_definition
+ @java_definition ||=
+ begin
+ widget.java_definition.getWidgetProperty(key)
+ end
+ end
+
+ def value
+ WidgetProperty.text_to_value(text_value, type)
end
def to_hash_json
- {:key => key, :value => value.to_s}
+ {:key => key, :value => text_value}
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)}
+ xml.value { xml.cdata!(text_value) }
end
xml
end
- def self.validate_definition(definition, value)
- errors=[]
- if value.empty?
- errors<<"Missing value" unless definition.optional()
+ def self.text_to_value(text, type)
+ case type
+ when TYPE_INTEGER
+ text.to_i
+ when TYPE_FLOAT
+ Float(text)
+ when TYPE_BOOLEAN
+ text=='true'
+ when TYPE_METRIC
+ Metric.by_key(text)
+ else
+ text
+ end
+ end
+
+ protected
+ def validate
+ errors.add_to_base("Unknown property: #{key}") unless java_definition
+ errors.add_to_base("Unknown type for property #{key}") unless type
+ if text_value.empty?
+ errors.add_to_base("#{key} is empty") unless java_definition.optional()
else
- errors<<"Please type an integer (example: 123)" if definition.type.name()==TYPE_INTEGER && value.to_i.to_s!=value
- if definition.type.name()==TYPE_FLOAT
- begin
- Float(value)
- rescue
- errors<<"Please type a number (example: 123.45)"
- end
- end
- errors<<"Please check value" if definition.type.name()==TYPE_BOOLEAN && !(value=="true" || value=="false")
+ errors.add_to_base("#{key} is not an integer") if type==TYPE_INTEGER && !Api::Utils.is_integer?(text_value)
+ errors.add_to_base("#{key} is not a decimal number") if type==TYPE_FLOAT && !Api::Utils.is_number?(text_value)
+ errors.add_to_base("#{key} is not a boolean") if type==TYPE_BOOLEAN && !(text_value=="true" || text_value=="false")
end
- errors
end
+
end
\ No newline at end of file
- <%
+<%
begin
- widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
+ widget_body=render :inline => widget.java_definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
rescue => error
- logger.error(message('dashboard.cannot_render_widget_x', :params => [definition.getId(), error]))
- logger.error(error.backtrace.join("\n"))
- widget_body=""
+ logger.error(message('dashboard.cannot_render_widget_x', :params => [widget.java_definition.getId(), error]))
+ logger.error(error.backtrace.join("\n"))
+ widget_body=""
end
- %>
+%>
- <div class="handle" style="//overflow:hidden;//zoom:1;">
- <a class="block-remove" onclick="portal.deleteWidget(this);return false;"><%= message('delete') -%></a>
- <% if definition.isEditable() %>
- <a class="block-view-toggle" onclick="portal.editWidget(<%= widget.id -%>);return false;"><%= message('edit') -%></a>
- <% end %>
- <%= h message('widget.' + definition.getId() + '.name', :default => definition.getTitle()) -%>
- </div>
+<div class="handle" style="//overflow:hidden;//zoom:1;">
+ <a class="block-remove" onclick="portal.deleteWidget(this);return false;"><%= message('delete') -%></a>
+ <% if widget.java_definition.isEditable() %>
+ <a class="block-view-toggle" onclick="portal.editWidget(<%= widget.id -%>);return false;"><%= message('edit') -%></a>
+ <% end %>
+ <%= h message('widget.' + widget.java_definition.getId() + '.name', :default => widget.java_definition.getTitle()) -%>
+</div>
- <div class="widget_props" id="widget_props_<%= widget.id -%>" style="<%= 'display:none' if widget.configured -%>">
- <%= render :partial => 'dashboard/widget_properties', :locals => {:widget => widget, :definition => definition} -%>
- </div>
+<div class="widget_props" id="widget_props_<%= widget.id -%>" style="<%= 'display:none' if widget.configured -%>">
+ <%= render :partial => 'dashboard/widget_properties', :locals => {:widget => widget} -%>
+</div>
- <div id="widget_<%= widget.id -%>" class="configure_widget <%= definition.getId() -%>" style="height:100%;<%= 'display:none;' if !widget.configured -%>">
- <!--[if lte IE 6]>
- <style type="text/css">
- #dashboard .block .content .transparent {
- display: none;
- }
- </style>
- <![endif]-->
- <div class="transparent"></div>
- <% if widget_body.include? '<' %>
- <%
- default_layout=(definition.getWidgetLayout().name()=='DEFAULT')
+<div id="widget_<%= widget.id -%>" class="configure_widget <%= widget.java_definition.getId() -%>" style="height:100%;<%= 'display:none;' if !widget.configured -%>">
+ <!--[if lte IE 6]>
+ <style type="text/css">
+ #dashboard .block .content .transparent {
+ display: none;
+ }
+ </style>
+ <![endif]-->
+ <div class="transparent"></div>
+ <% if widget_body.include? '<' %>
+ <%
+ default_layout=(widget.layout=='DEFAULT')
if default_layout
- %>
- <div class="widget">
- <% end %>
- <%= widget_body -%>
- <% if default_layout %><div class="clear"> </div></div><% end %>
- <% else %>
- <div class="widget"><p><%= message('no_data') -%></p></div>
- <% end %>
- <div style="clear: both;"></div>
- </div>
+ %>
+ <div class="widget">
+ <% end %>
+ <%= widget_body -%>
+ <% if default_layout %>
+ <div class="clear"> </div></div>
+ <% end %>
+ <% else %>
+ <div class="widget"><p><%= message('no_data') -%></p></div>
+ <% end %>
+ <div style="clear: both;"></div>
+</div>
-<div class="<%= definition.getId() %>" style="height:100%;">
-<% if widget.configured %>
- <%
- begin
- widget_body=render :inline => definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
- rescue => error
- logger.error(message('dashboard.cannot_render_widget_x', :params => [definition.getId(), error]))
- logger.error(error.backtrace.join("\n"))
- widget_body=""
- end
+<div class="<%= widget.key %>" style="height:100%;">
+ <% if widget.configured %>
+ <%
+ begin
+ widget_body=render :inline => widget.java_definition.getTarget().getTemplate(), :locals => {:widget_properties => widget.properties_as_hash, :widget => widget, :dashboard_configuration => @dashboard_configuration}
+ rescue => error
+ logger.error(message('dashboard.cannot_render_widget_x', :params => [widget.key, error]))
+ logger.error(error.backtrace.join("\n"))
+ widget_body=""
+ end
- if widget_body.include?('<')
- %>
+ if widget_body.include?('<')
+ %>
+ <%
+ default_layout=(widget.java_definition.getWidgetLayout().name()=='DEFAULT')
+ if default_layout
+ %>
+ <div class="widget">
+ <% end %>
+ <%= widget_body -%>
+ <% if default_layout %>
+ <div class="clear"> </div></div>
+ <% end %>
<%
- default_layout=(definition.getWidgetLayout().name()=='DEFAULT')
- if default_layout
+ end
%>
- <div class="widget">
- <% end %>
- <%= widget_body -%>
- <% if default_layout %><div class="clear"> </div></div><% end %>
- <%
- end
- %>
-<% else %>
- <div class="widget">
- <p><a href="<%= url_for :action => :configure, :did => @dashboard.id, :id => @resource.id -%>"><%= message('dashboard.please_configure_the_widget_x', :params => definition.getTitle()) -%></a></p>
- </div>
-<% end %>
-<div style="clear: both;"></div>
+ <% else %>
+ <div class="widget">
+ <p>
+ <a href="<%= url_for :action => :configure, :did => @dashboard.id, :id => @resource.id -%>"><%= message('dashboard.please_configure_the_widget_x', :params => widget.java_definition.getTitle()) -%></a>
+ </p>
+ </div>
+ <% end %>
+ <div style="clear: both;"></div>
</div>
\ No newline at end of file
<td>
-<div class="widget_def" id="def_<%= definition.getId().tr('_', '') -%>">
-<p><b><%= h message('widget.' + definition.getId() + '.name', :default => definition.getTitle()) -%></b></p>
-<p><%= h message('widget.' + definition.getId() + '.description', :default => definition.getDescription()) -%></p>
-<%= form_tag :action => 'add_widget', :did => dashboard_id, :id => resource_id, :widget => definition.getId() %>
-<input type="submit" value="<%= message('dashboard.add_widget') -%>" >
-</form>
-</div>
+ <div class="widget_def" id="def_<%= definition.getId().tr('_', '') -%>">
+ <p><b><%= h message('widget.' + definition.getId() + '.name', :default => definition.getTitle()) -%></b></p>
+
+ <p><%= h message('widget.' + definition.getId() + '.description', :default => definition.getDescription()) -%></p>
+ <%= form_tag :action => 'add_widget', :did => dashboard_id, :id => resource_id, :widget => definition.getId() %>
+ <input type="submit" value="<%= message('dashboard.add_widget') -%>">
+ </form>
+ </div>
</td>
-<% form_remote_tag :url => {:action => 'save_widget', :wid => widget.id, :id => params[:id]}, :method => :post do -%>
- <% if defined?(errors_by_property_key) && errors_by_property_key %>
- <ul>
- <% errors_by_property_key.each_pair do |property_key, error| %>
- <li class="error"><%= property_key -%>: <%= h(error)-%></li>
- </ul>
- <% end %>
- <% end %>
- <table class="form width100">
- <tbody>
- <% definition.getWidgetProperties().each do |property_def|
- value=widget.property_value(property_def.key(), property_def.defaultValue())
- %>
- <tr>
- <td valign="top" class="thin nowrap"><b><%= property_def.key() -%></b><%= "*" unless property_def.optional()==true -%>: </td>
- <td id="row_<%= property_def.key() -%>">
- <%= property_value_field(property_def, value) -%>
- <span class="note">
+<% form_remote_tag :url => {:action => 'save_widget', :wid => widget.id, :id => params[:id]},
+ :method => :post,
+ :update => {:failure => "error#{widget.id}"},
+ :failure => "$('error#{widget.id}').show()" do -%>
+ <div id="error<%= widget.id -%>" class="error" style="display: none"></div>
+ <table class="form width100">
+ <tbody>
+ <% widget.java_definition.getWidgetProperties().each do |property_def| %>
+ <tr>
+ <td valign="top" class="thin nowrap"><b><%= property_def.key() -%></b><%= "*" unless property_def.optional()==true -%>:</td>
+ <td id="row_<%= property_def.key() -%>">
+ <%= property_value_field(property_def, widget.property_text_value(property_def.key())) -%>
+ <span class="note">
<%= message("widget." + widget.key + ".param." + property_def.key(), :default => property_def.description()) -%>
</span>
- </td>
- </tr>
- <% end %>
- <tr>
- <td colspan="2">
- <%= submit_tag message('save') %>
- <% if widget.configured %>
- <a href="#" onClick="portal.cancelEditWidget(<%= widget.id -%>);return false;"><%= message('cancel') -%></a>
- <% end %>
</td>
</tr>
- </tbody>
- </table>
- <%= hidden_field_tag "widgetid", "", :class => "widgetid" %>
+ <% end %>
+ <tr>
+ <td colspan="2">
+ <%= submit_tag message('save') %>
+ <% if widget.configured %>
+ <a href="#" onClick="portal.cancelEditWidget(<%= widget.id -%>);return false;"><%= message('cancel') -%></a>
+ <% end %>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <%= hidden_field_tag "widgetid", "", :class => "widgetid" %>
<% end -%>
\ No newline at end of file
<script type="text/javascript">
- <!--
- var options = {
- editorEnabled: true,
- portal: 'dashboard',
- column: 'dashboard-column',
- columnhandle: 'column-handle',
- block: 'block',
- handle: 'none',
- hoverclass: 'block-hover',
- dashboardstate: 'dashboardstate',
- toggle: 'block-toggle',
- blocklist: 'widget_defs',
- highlight_duration: 2,
- highlight_startcolor: '#cae3f2',
- highlight_endcolor: '#ffffff',
- saveurl: '<%= url_for :action => 'set_dashboard', :did => @dashboard.id, :id => @resource.id -%>'
- };
- var portal;
- function init_dashboard() {
- portal = new Portal(options);
- <% if params[:highlight] %>
- portal.highlightWidget(<%= params[:highlight] -%>);
- <% end %>
- }
- Event.observe(window, 'load', init_dashboard, false);
+ <!--
+ var options = {
+ editorEnabled:true,
+ portal:'dashboard',
+ column:'dashboard-column',
+ columnhandle:'column-handle',
+ block:'block',
+ handle:'none',
+ hoverclass:'block-hover',
+ dashboardstate:'dashboardstate',
+ toggle:'block-toggle',
+ blocklist:'widget_defs',
+ highlight_duration:2,
+ highlight_startcolor:'#cae3f2',
+ highlight_endcolor:'#ffffff',
+ saveurl:'<%= url_for :action => 'set_dashboard', :did => @dashboard.id, :id => @resource.id -%>'
+ };
+ var portal;
+ function init_dashboard() {
+ portal = new Portal(options);
+ <% if params[:highlight] %>
+ portal.highlightWidget(<%= params[:highlight] -%>);
+ <% end %>
+ }
+ Event.observe(window, 'load', init_dashboard, false);
+
+ function filterWidgets(category) {
+ new Ajax.Updater(
+ 'widget_defs',
+ '<%= url_for :controller => "dashboard", :action => "widget_definitions", :did => @dashboard.id, :id => @resource.id -%>&category=' + category,
+ {asynchronous:true, evalScripts:true});
+ $('filter-widgets-loading').show();
+ return false;
+ }
- function filterWidgets(category){
- new Ajax.Updater(
- 'widget_defs',
- '<%= url_for :controller => "dashboard", :action => "widget_definitions", :did => @dashboard.id, :id => @resource.id -%>&category=' + category,
- {asynchronous:true, evalScripts:true});
- $('filter-widgets-loading').show();
- return false;
- }
-
- //-->
+ //-->
</script>
<div id="dashboard">
<%= render :partial => 'dashboard/header', :locals => {:back => true} %>
<div id="widget_defs">
- <%= render :partial => 'dashboard/widget_definitions', :locals => {:dashboard_id => @dashboard.id, :resource_id => @resource.id, :filter_on_category => nil}-%>
+ <%= render :partial => 'dashboard/widget_definitions', :locals => {:dashboard_id => @dashboard.id, :resource_id => @resource.id, :filter_on_category => nil} -%>
</div>
<%
- columns=@dashboard.column_layout.split('-')
- for index in 1..columns.size()
+ columns=@dashboard.column_layout.split('-')
+ for index in 1..columns.size()
%>
<div class="dashboard-column-wrapper" style="width: <%= columns[index-1] -%>">
- <div class="dashboard-column" id="dashboard-column-<%= index -%>" style="margin: 0px <%= index<columns.size() ? "5px" : "0px" -%> 0px <%= index>1 ? "5px" : "0px" -%>;">
- <div class="column-handle" style="display: none"> </div>
+ <div class="dashboard-column" id="dashboard-column-<%= index -%>" style="margin: 0 <%= index<columns.size() ? "5px" : "0px" -%> 0 <%= index>1 ? "5px" : "0px" -%>;">
+ <div class="column-handle" style="display: none"></div>
- <%
- @dashboard.widgets.select{|widget| widget.column_index==index}.sort_by{|widget| widget.row_index}.each do |widget|
- widget_definition=@widget_definitions.find{|wd| wd.getId()==widget.widget_key }
- if widget_definition
- %>
- <div class="block" id="block_<%= widget.id -%>">
- <%= render :partial => 'dashboard/configure_widget', :locals => {:widget => widget, :definition => widget_definition} %>
- </div>
- <%
- end
- end
- %>
+ <%
+ @dashboard.widgets.select { |widget| widget.column_index==index }.sort_by { |widget| widget.row_index }.each do |widget|
+ %>
+ <div class="block" id="block_<%= widget.id -%>">
+ <%= render :partial => 'dashboard/configure_widget', :locals => {:widget => widget} %>
+ </div>
+ <%
+ end
+ %>
</div>
</div>
<% end %>
--- /dev/null
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+#
+
+#
+# Sonar 2.13
+#
+class DeleteValueTypeFromWidgetProperties < ActiveRecord::Migration
+
+ def self.up
+ remove_column('widget_properties', 'value_type')
+ WidgetProperty.reset_column_information()
+ end
+
+end
public void widgetShouldBeEditable() {
ViewProxy proxy = new ViewProxy<Widget>(new EditableWidget());
assertThat(proxy.isEditable(), is(true));
- assertThat(proxy.getWidgetProperties().length, is(2));
+ assertThat(proxy.getWidgetProperties().size(), is(2));
}
@Test
}
@WidgetProperties({
- @WidgetProperty(key="foo", optional = false),
- @WidgetProperty(key="bar", defaultValue = "30", type = WidgetPropertyType.INTEGER)
+ @WidgetProperty(key = "foo", optional = false),
+ @WidgetProperty(key = "bar", defaultValue = "30", type = WidgetPropertyType.INTEGER)
})
class EditableWidget implements Widget {
}
@WidgetProperties({
- @WidgetProperty(key="foo"),
- @WidgetProperty(key="bar")
+ @WidgetProperty(key = "foo"),
+ @WidgetProperty(key = "bar")
})
class WidgetWithOptionalProperties implements Widget {