]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1643 add widget properties
authorsimonbrandhof <simon.brandhof@gmail.com>
Fri, 29 Oct 2010 15:11:39 +0000 (15:11 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Fri, 29 Oct 2010 15:11:39 +0000 (15:11 +0000)
12 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/widget_properties_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb
sonar-server/src/main/webapp/WEB-INF/app/models/widget.rb
sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_configure_widget.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/index.html.erb
sonar-server/src/main/webapp/WEB-INF/db/migrate/151_create_dashboards.rb

index 7ec6bf86f8d5365f8790b22a8c3be4b17d7542c3..87e3743b8dded7e16fd5ddfffd5d228ffbf9c879 100644 (file)
@@ -24,27 +24,17 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;\r
 import java.lang.annotation.Target;\r
 \r
-/**\r
- * Created by IntelliJ IDEA.\r
- * User: dreik\r
- * Date: 09.08.2010\r
- * Time: 10:33:35\r
- */\r
 @Retention(RetentionPolicy.RUNTIME)\r
 @Target(ElementType.TYPE)\r
 public @interface WidgetProperty {\r
 \r
   String key();\r
 \r
-  String defaultValue() default "";\r
+  String type() default "STRING";\r
 \r
-  String name();\r
+  String defaultValue() default "";\r
 \r
   String description() default "";\r
 \r
-  String type() default "STRING";\r
-\r
-  String parameter() default "";\r
-\r
   boolean optional() default true;\r
 }\r
index 3fc136ecbc3d75d5978a701f1a347c2d10b71d2c..3811e1c6b832d01ef531b84617bdffc9f4e9d6d0 100644 (file)
@@ -21,7 +21,7 @@ class DashboardController < ApplicationController
 
   SECTION=Navigation::SECTION_RESOURCE
 
-  verify :method => :post, :only => [:set_layout, :add_widget, :set_dashboard], :redirect_to => {:action => :index}
+  verify :method => :post, :only => [:set_layout, :add_widget, :set_dashboard, :save_widget], :redirect_to => {:action => :index}
   before_filter :login_required, :except => [:index]
 
   def index
@@ -71,7 +71,7 @@ class DashboardController < ApplicationController
         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.row_index=order+1
           widget.save!
           all_ids<<widget.id
         end
@@ -92,14 +92,49 @@ class DashboardController < ApplicationController
         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)
+                                           :row_index => dashboard.column_size(dashboard.number_of_columns) + 1,
+                                           :configured => !definition.isEditable())
         widget_id=new_widget.id
       end
     end
     redirect_to :action => 'configure', :id => dashboard.id, :resource => params[:resource], :highlight => widget_id 
   end
 
+
+  def save_widget
+    widget=Widget.find(params[:id].to_i)
+    #TODO check owner of dashboard
+    definition=java_facade.getWidget(widget.widget_key)
+    errors_by_property_key={}
+    definition.getProperties().each do |property_def|
+      value=params[property_def.key()] || property_def.defaultValue()
+      value='false' if value.empty? && property_def.type()==WidgetProperty::TYPE_BOOLEAN
+
+      errors=WidgetProperty.validate_definition(property_def, value)
+      if errors.empty?
+        widget.set_property(property_def.key(), value)
+      else
+        widget.unset_property(property_def.key())
+        errors_by_property_key[property_def.key()]=errors
+      end
+    end
+
+    if errors_by_property_key.empty?
+      widget.configured=true
+      widget.save
+      widget.properties.each {|p| p.save}
+      render :update do |page|
+        page.redirect_to(url_for(:action => :configure, :id => widget.dashboard_id, :resource => params[:resource]))
+      end
+    else
+      widget.configured=false
+      widget.save
+      render :update do |page|
+        page.alert('errors ' + errors_by_property_key.inspect)
+      end
+    end
+  end
+
   private
 
   def load_dashboard
@@ -156,4 +191,5 @@ class DashboardController < ApplicationController
   def load_widget_definitions()
     @widget_definitions = java_facade.getWidgets()
   end
+
 end
\ No newline at end of file
index cfbdb64e9c657b9f11f4846feae13637d667345c..be8ccad5ad41ff774b79f8423832167859697832 100644 (file)
@@ -30,7 +30,7 @@ module DashboardHelper
 
   def active_widgets_ids_formatted(column)
     active_widget_ids=[]
-    @dashboard.widgets.find(:all, :conditions => {:column_index => column}, :order => :order_index).each do |widget|
+    @dashboard.widgets.find(:all, :conditions => {:column_index => column}, :order => 'row_index ASC').each do |widget|
       widget_view=nil
       found_index=-1
       @widgets.each_with_index {|item, index|
index 543a2864f63acc5c3afe8093b36467e5f9acd4d8..9b353e4f638f37a94577b8b4f791b01970a5e566 100644 (file)
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
 #\r
 module WidgetPropertiesHelper\r
-  VALUE_TYPE_INT = 'INT'\r
-  VALUE_TYPE_BOOLEAN = 'BOOL'\r
-  VALUE_TYPE_FLOAT = 'FLOAT'\r
-  VALUE_TYPE_STRING = 'STRING'\r
-  VALUE_TYPE_REGEXP = 'REGEXP'\r
+  \r
    \r
   def valid_property_value?(type, value, parameter="")\r
-    if type==VALUE_TYPE_INT\r
+    if type==WidgetProperty::TYPE_INTEGER\r
       value.to_i.to_s == value\r
 \r
-    elsif type==VALUE_TYPE_FLOAT\r
+    elsif type==WidgetProperty::TYPE_FLOAT\r
       true if Float(value) rescue false\r
 \r
-    elsif type==VALUE_TYPE_BOOLEAN\r
+    elsif type==WidgetProperty::TYPE_BOOLEAN\r
       value=="1" || value=="0"\r
 \r
-    elsif type==VALUE_TYPE_STRING\r
+    elsif type==WidgetProperty::TYPE_STRING\r
       true\r
 \r
-    elsif type==VALUE_TYPE_REGEXP\r
-      value.to_s.match(parameter) == nil ? false : true\r
-\r
     else\r
       false\r
     end\r
   end\r
    \r
-  def property_value_field(type, fieldname, value, param_value="")\r
-    val= param_value ? param_value : value\r
-    \r
-    if type==VALUE_TYPE_INT\r
-      text_field_tag fieldname, val , :size => 10\r
+  def property_value_field(definition, value)\r
+    val=value || definition.defaultValue()\r
+    if definition.type()==WidgetProperty::TYPE_INTEGER\r
+      text_field_tag definition.key(), val, :size => 10\r
       \r
-    elsif type==VALUE_TYPE_FLOAT\r
-      text_field_tag fieldname, val, :size => 10\r
+    elsif definition.type()==WidgetProperty::TYPE_FLOAT\r
+      text_field_tag definition.key(), val, :size => 10\r
 \r
-    elsif type==VALUE_TYPE_BOOLEAN\r
-      opts="<option value=''>Select value</option>"\r
-      opts+="<option value='1'"+(val=="1" ? " selected" : "" )+">Yes</option>"\r
-      opts+="<option value='0'"+(val=="0" ? " selected" : "" )+">No</option>"\r
-      select_tag fieldname, opts\r
+    elsif definition.type()==WidgetProperty::TYPE_BOOLEAN\r
+      check_box_tag definition.key(), "true", val=='true'\r
 \r
-    elsif type==VALUE_TYPE_STRING\r
-      text_field_tag fieldname, val, :size => 10\r
+    elsif definition.type()==WidgetProperty::TYPE_STRING\r
+      text_field_tag definition.key(), val, :size => 10\r
 \r
-    elsif type==VALUE_TYPE_REGEXP\r
-      text_field_tag fieldname, val, :size => 10\r
     else\r
-      hidden_field_tag fieldname\r
+      hidden_field_tag definition.key()\r
     end\r
   end\r
     \r
index 11416159f189475015a8ea3e779fc65de39bbd98..8751e4a7c4c892568edcd719a34f7ed523fa875d 100644 (file)
@@ -20,7 +20,7 @@
 class Dashboard < ActiveRecord::Base\r
   belongs_to :user\r
 \r
-  has_many :widgets, :include => 'widget_properties', :dependent => :delete_all\r
+  has_many :widgets, :include => 'properties', :dependent => :delete_all\r
   has_many :active_dashboards, :dependent => :delete_all\r
 \r
   validates_length_of :name, :within => 1..256\r
@@ -49,8 +49,8 @@ class Dashboard < ActiveRecord::Base
   end\r
 \r
   def column_size(column_index)\r
-    last_widget=widgets.select{|w| w.column_index==column_index}.max{|x,y| x.order_index <=> y.order_index}\r
-    last_widget ? last_widget.order_index : 0\r
+    last_widget=widgets.select{|w| w.column_index==column_index}.max{|x,y| x.row_index <=> y.row_index}\r
+    last_widget ? last_widget.row_index : 0\r
   end\r
 \r
   def deep_copy()\r
@@ -59,9 +59,9 @@ class Dashboard < ActiveRecord::Base
     self.widgets.each do |child|\r
       new_widget = Widget.create(child.attributes)\r
 \r
-      child.widget_properties.each do |prop|\r
+      child.properties.each do |prop|\r
         widget_prop = WidgetProperty.create(prop.attributes)\r
-        new_widget.widget_properties << widget_prop\r
+        new_widget.properties << widget_prop\r
       end\r
 \r
       new_widget.save\r
index 62e04d842cc111a9540bf7bd07835cebe2ffa47e..7cba190ed7fb7267c7b996a7559650a9c84f27a3 100644 (file)
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
 #\r
 class Widget < ActiveRecord::Base\r
-  STATE_ACTIVE='A'\r
-  STATE_INACTIVE='I'\r
-  \r
-  has_many :widget_properties, :dependent => :delete_all\r
+  has_many :properties, :dependent => :delete_all, :class_name => 'WidgetProperty'\r
   belongs_to :dashboards\r
 \r
   validates_presence_of     :name\r
@@ -30,57 +27,52 @@ class Widget < ActiveRecord::Base
   validates_presence_of     :widget_key\r
   validates_length_of       :widget_key, :within => 1..256\r
 \r
-  validates_length_of       :description,  :maximum => 1000, :allow_blank => true, :allow_nil => true\r
-\r
-  def state\r
-    read_attribute(:state) || 'V'\r
-  end\r
-\r
   #---------------------------------------------------------------------\r
   # WIDGET PROPERTIES\r
   #---------------------------------------------------------------------\r
-  def properties\r
-    widget_properties\r
-  end\r
-  \r
-  def widget_property(key)\r
-    widget_properties().each do |p|\r
+  def property(key)\r
+    properties().each do |p|\r
       return p if (p.key==key)\r
     end\r
     nil\r
   end\r
 \r
-  def widget_property_value(key)\r
-    prop=widget_property(key)\r
-    prop ? prop.value : nil\r
+  def property_value(key, default_value=nil)\r
+    prop=property(key)\r
+    (prop ? prop.value : nil) || default_value\r
   end\r
 \r
-  def set_widget_property(options)\r
-    key=options[:kee]\r
-    prop=widget_property(key)\r
+  def set_property(key, value)\r
+    prop=property(key)\r
     if prop\r
-      prop.attributes=options\r
-      prop.widget_id=id\r
-      prop.save!\r
+      prop.text_value=value\r
     else\r
-      prop=WidgetProperty.new(options)\r
-      prop.widget_id=id\r
-      widget_properties<<prop\r
+      self.properties.build(:kee => key, :text_value => value)\r
     end\r
+    properties_as_hash[key]=value\r
   end\r
 \r
-  def delete_widget_property(key)\r
-    prop=widget_property(key)\r
+  def unset_property(key)\r
+    prop=property(key)\r
+    self.properties.delete(prop) if prop\r
+  end\r
+\r
+  def delete_property(key)\r
+    prop=property(key)\r
     if prop\r
-      widget_properties.delete(prop)\r
+      properties.delete(prop)\r
     end\r
   end\r
 \r
   def properties_as_hash\r
-    hash={}\r
-    widget_properties.each do |prop|\r
-      hash[prop.key]=prop.value\r
-    end\r
-    hash\r
+    @properties_hash ||=\r
+      begin\r
+        hash={}\r
+        properties.each do |prop|\r
+          hash[prop.key]=prop.value\r
+        end\r
+        hash\r
+      end\r
+    @properties_hash\r
   end\r
 end
\ No newline at end of file
index be6b9522aa54dffafffb9c16dae9f4f01f8ac348..3909618cde1f6efa2fa37750e587a5c9a8ab2808 100644 (file)
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
 #\r
 class WidgetProperty < ActiveRecord::Base\r
+  TYPE_INTEGER = 'INTEGER'\r
+  TYPE_BOOLEAN = 'BOOLEAN'\r
+  TYPE_FLOAT = 'FLOAT'\r
+  TYPE_STRING = 'STRING'\r
 \r
-  belongs_to :widgets\r
+  belongs_to :widget\r
 \r
-  validates_presence_of     :kee\r
   validates_length_of       :kee, :within => 1..100\r
-  validates_length_of       :description,  :maximum => 4000, :allow_blank => true, :allow_nil => true\r
   validates_length_of       :text_value,   :maximum => 4000, :allow_blank => true, :allow_nil => true\r
 \r
   def key\r
-      kee\r
-    end\r
+    kee\r
+  end\r
 \r
   def value\r
-      text_value\r
+    text_value\r
   end\r
 \r
   def to_hash_json\r
@@ -45,5 +47,22 @@ class WidgetProperty < ActiveRecord::Base
     end\r
     xml\r
   end\r
-           \r
+\r
+  def self.validate_definition(definition, value)\r
+    errors=[]\r
+    if value.empty?\r
+      errors<<"Missing value" unless definition.optional()\r
+    else\r
+      errors<<"Please type an integer (example: 123)" if definition.type()==TYPE_INTEGER && value.to_i.to_s!=value\r
+      if definition.type()==TYPE_FLOAT\r
+        begin\r
+          Float(value)\r
+        rescue\r
+          errors<<"Please type a number (example: 123.45)"\r
+        end\r
+      end\r
+      errors<<"Please check value" if definition.type()==TYPE_BOOLEAN && !(value=="true" || value=="false")\r
+    end\r
+    errors\r
+  end\r
 end
\ No newline at end of file
index 4712106bb1a8fa0febc728ac8150a234fbb340e4..ec677b689a8cefd042b77c28a5fad0eb207df9f0 100644 (file)
     <a class="block-view-toggle" onclick="portal.editWidget(<%= widget.id -%>);return false;">Edit</a>
   <% end %>
 </div>
-<% if defined?(validation_result) %>
-  <div class="error" style="<%= "display: none;" unless validation_result["errormsg"] %>clear:both;margin: 0;border-bottom:0;">
-    <span class="errormsg"><%= validation_result["errormsg"] if validation_result["errormsg"] %></span> &nbsp;&nbsp;[<a href="#" onclick="hide_block_info($(this).up('.block'));return false;">hide</a>]
-  </div>
-  <div class="notice" style="<%= "display: none;" unless validation_result["infomsg"] %>clear:both;margin: 0;border-bottom:0;">
-    <span class="infomsg"><%= validation_result["infomsg"] if validation_result["infomsg"] %></span> &nbsp;&nbsp;[<a href="#" onclick="hide_block_info($(this).up('.block'));return false;">hide</a>]
-  </div>
-<% end %>
 
 
-<div class="widget_props" id="widget_props_<%= widget.id -%>" style="display: none">
-  <% form_remote_tag :url => {:action => 'save_widget', :id => @dashboard.id}, :method => :post do -%>
-    <table class="form" align="center" style="border-collapse:separate;border-spacing:5px;">
+<div class="widget_props" id="widget_props_<%= widget.id -%>" style="<%= 'display:none' if widget.configured -%>">
+  <% form_remote_tag :url => {:action => 'save_widget', :id => widget.id, :resource => params[:resource]}, :method => :post do -%>
+    <table class="form">
       <tbody>
-      <% definition.getProperties().each do |property|
-          db_property_value=widget.widget_property_value(property.key())
-          entered_value=params[property.key()]
-          entered_value=property.defaultValue() if !entered_value || entered_value.empty?
+      <% definition.getProperties().each do |property_def|
+          value=widget.property_value(property_def.key(), property_def.defaultValue())
           editrow_class=""
           if defined?(validation_result)
-              editrow_class= validation_result[property.key()]=="valid" ? "valid-editrow" : "invalid-editrow"
+              editrow_class= validation_result[property_def.key()]=="valid" ? "valid-editrow" : "invalid-editrow"
           end
       %>
           <tr>
-            <td class="first"><%= property.name() -%>:<br>
-                <span style="font-size: 85%;font-weight: normal;">[<%= property.type()+" "+property.key() -%>]</span></td>
-            <td id="row_<%= property.key() -%>" class="editrow <%= editrow_class %>" style="vertical-align: middle;"><%= property_value_field(property.type(), property.key(), db_property_value, entered_value) %><%= "&nbsp;*" unless property.optional() %>
-              <br>
-              <span style="font-size: 85%;font-weight: normal;">[<%= property.description() -%>]</span></td>
+            <td valign="top" 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"><%= property_def.description() -%></span>
+            </td>
           </tr>
       <% end %>
+      <tr>
+        <td colspan="2">
+          <%= submit_tag 'Save' %>
+          <% if widget.configured %>
+            <a href="#" onClick="portal.cancelEditWidget(<%= widget.id -%>);return false;">Cancel</a>
+          <% end %>
+        </td>
+      </tr>
       </tbody>
     </table>
     <%= hidden_field_tag "widgetid", "", :class => "widgetid" %>
-    <div align="center"><%= submit_tag 'Save' %> <a href="#" onClick="portal.cancelEditWidget(<%= widget.id -%>);return false;">Cancel</a></div>
-
 <% end -%>
 </div>
 
 
-<div id="widget_<%= widget.id -%>" class="configure_widget <%= definition.getId() -%>" style="height:100%;">
+<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 {
index e3b9aeaf071e522633eee6ec569f403e77675109..8ec8175729469c6e861af2b0c81a042dbe980530 100644 (file)
@@ -1,18 +1,24 @@
 <div class="<%= definition.getId() %>" style="height:100%;">
-<%
-  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.configured %>
+  <%
+    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 %>
-<%
-  end
-%>
+    if widget_body.include?('<')
+  %>
+    <%= widget_body %>
+  <%
+    end
+  %>
+<% else %>
+  <div class="widget">
+  <p>Please configure the widget <b><%= definition.getTitle() -%></b>.</p>
+  </div>
+<% end %>
 <div style="clear: both;"></div>
 </div>
\ No newline at end of file
index 8ff64d6fb48ee1a024c43a996048967fd34b9420..7c0f54e612a1713294336f9b16754ee8ee8c0bbe 100644 (file)
@@ -11,7 +11,7 @@
         dashboardstate: 'dashboardstate',
         toggle: 'block-toggle',
         blocklist: 'widget_defs',
-        highlight_duration: 3,
+        highlight_duration: 2,
         highlight_startcolor: '#cae3f2',
         highlight_endcolor: '#ffffff',
         saveurl: '<%= url_for :action => 'set_dashboard', :id => @dashboard.id, :resource => @resource.id -%>'
@@ -52,7 +52,7 @@
       <div class="column-handle" style="display: none"> </div>
 
     <%
-      @dashboard.widgets.select{|widget| widget.column_index==index}.sort_by{|widget| widget.order_index}.each do |widget|
+      @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
     %>
index e7a6d569daa2642464ffe0d155ce39885b083056..8902604dea92f488d707631c7e8e3d4273b2f9ff 100644 (file)
@@ -8,7 +8,7 @@
     <div class="dashboard-column-wrapper" style="width: <%= (columns.size()>0) ? columns[index-1].to_i : 100 %>%; clear: right;">
     <div class="dashboard-column" id="dashboard-column-<%= index -%>" style="margin: 0px <%= index<columns.size() ? "5px" : "0px" %> 0px <%= index>1 ? "5px" : "0px" %>;">
     <%
-      @dashboard.widgets.select{|widget| widget.column_index==index}.sort_by{|widget| widget.order_index}.each do |widget|
+      @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
     %>
index 180e9c53db2f50617f006b3278abfa5632360e1c..6130b0814a67b233b3007d2fd727cc4ffa83e625 100755 (executable)
@@ -44,8 +44,8 @@ class CreateDashboards < ActiveRecord::Migration
       t.column :name, :string, :null => true, :limit => 256
       t.column :description, :string, :null => true, :limit => 1000
       t.column :column_index, :integer, :null => true
-      t.column :order_index, :integer, :null => true
-      t.column :state, :string, :null => true, :limit => 1
+      t.column :row_index, :integer, :null => true
+      t.column :configured, :boolean, :null => true
       t.timestamps
     end
     add_index :widgets, [:dashboard_id], :name => 'widgets_dashboards'
@@ -53,7 +53,6 @@ class CreateDashboards < ActiveRecord::Migration
 
     create_table :widget_properties do |t|
       t.column :widget_id, :integer, :null => false
-      t.column :description, :string, :null => true, :limit => 4000
       t.column :kee, :string, :null => true, :limit => 100
       t.column :text_value, :string, :null => true, :limit => 4000
     end
@@ -66,18 +65,18 @@ class CreateDashboards < ActiveRecord::Migration
 
   def self.create_dashboard
     dashboard=::Dashboard.new(:name => 'Dashboard', :shared => true, :description => 'Default dashboard', :column_layout => "50-50")
-    dashboard.widgets.build(:widget_key => 'static_analysis', :name => 'Static analysis', :column_index => 1, :order_index => 1, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'comments_duplications', :name => 'Comments duplications', :column_index => 1, :order_index => 2, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'extended_analysis', :name => 'Extended analysis', :column_index => 1, :order_index => 3, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'code_coverage', :name => 'Code coverage', :column_index => 1, :order_index => 4, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'events', :name => 'Events', :column_index => 1, :order_index => 5, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'description', :name => 'Description', :column_index => 1, :order_index => 6, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'rules', :name => 'Rules', :column_index => 2, :order_index => 1, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'alerts', :name => 'Alerts', :column_index => 2, :order_index => 2, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'custom_measures', :name => 'Custom measures', :column_index => 2, :order_index => 3, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'file-design', :name => 'File design', :column_index => 2, :order_index => 4, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'package-design', :name => 'Package design', :column_index => 2, :order_index => 5, :state => Widget::STATE_ACTIVE)
-    dashboard.widgets.build(:widget_key => 'ckjm', :name => 'CKJM', :column_index => 2, :order_index => 6, :state => Widget::STATE_ACTIVE)
+    dashboard.widgets.build(:widget_key => 'static_analysis', :name => 'Static analysis', :column_index => 1, :row_index => 1, :configured => true)
+    dashboard.widgets.build(:widget_key => 'comments_duplications', :name => 'Comments duplications', :column_index => 1, :row_index => 2, :configured => true)
+    dashboard.widgets.build(:widget_key => 'extended_analysis', :name => 'Extended analysis', :column_index => 1, :row_index => 3, :configured => true)
+    dashboard.widgets.build(:widget_key => 'code_coverage', :name => 'Code coverage', :column_index => 1, :row_index => 4, :configured => true)
+    dashboard.widgets.build(:widget_key => 'events', :name => 'Events', :column_index => 1, :row_index => 5, :configured => true)
+    dashboard.widgets.build(:widget_key => 'description', :name => 'Description', :column_index => 1, :row_index => 6, :configured => true)
+    dashboard.widgets.build(:widget_key => 'rules', :name => 'Rules', :column_index => 2, :row_index => 1, :configured => true)
+    dashboard.widgets.build(:widget_key => 'alerts', :name => 'Alerts', :column_index => 2, :row_index => 2, :configured => true)
+    dashboard.widgets.build(:widget_key => 'custom_measures', :name => 'Custom measures', :column_index => 2, :row_index => 3, :configured => true)
+    dashboard.widgets.build(:widget_key => 'file-design', :name => 'File design', :column_index => 2, :row_index => 4, :configured => true)
+    dashboard.widgets.build(:widget_key => 'package-design', :name => 'Package design', :column_index => 2, :row_index => 5, :configured => true)
+    dashboard.widgets.build(:widget_key => 'ckjm', :name => 'CKJM', :column_index => 2, :row_index => 6, :configured => true)
 
     dashboard.save
     dashboard