]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3825 add columns
authorSimon Brandhof <simon.brandhof@gmail.com>
Wed, 28 Nov 2012 10:00:04 +0000 (11:00 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Wed, 28 Nov 2012 10:00:15 +0000 (11:00 +0100)
sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_list.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_treemap.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_treemap.rb [deleted file]
sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_treemap.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb

index 6e4d283ff16f570ee3c6f9590d0b154b6d463359..395b0f1690ff82c2379f1d2ba0f02511d1b022e8 100644 (file)
@@ -31,7 +31,8 @@ class MeasuresController < ApplicationController
     else
       @filter = MeasureFilter.new
     end
-    @filter.set_criteria_from_url_params(params)
+    @filter.criteria=(params)
+    @filter.enable_default_display
     @filter.execute(self, :user => current_user)
   end
 
@@ -42,6 +43,7 @@ class MeasuresController < ApplicationController
 
     @filter = find_filter(params[:id])
     @filter.load_criteria_from_data
+    @filter.enable_default_display
     @filter.execute(self, :user => current_user)
     render :action => 'search'
   end
@@ -52,7 +54,7 @@ class MeasuresController < ApplicationController
     else
       @filter = MeasureFilter.new
     end
-    @filter.set_criteria_from_url_params(params)
+    @filter.criteria=(params)
     @filter.convert_criteria_to_data
     render :partial => 'measures/save_form'
   end
index 7b3f3f6ab27f7d92e60dabe33dcbeec1c2e99943..a3a14416f96691bd70cdc3fa8e2021d9872a4d75 100644 (file)
@@ -26,10 +26,6 @@ module MeasuresHelper
     else
       html=h(column.name)
     end
-    #if column.variation
-    #  html="<img src='#{ApplicationController.root_context}/images/trend-up.png'></img> #{html}"
-    #end
-
     if filter.sort_key==column.key
       html << (filter.sort_asc? ? image_tag("asc12.png") : image_tag("desc12.png"))
     end
index 055f437eeec93d72d9c24ac94c1da3bbb5f84520..ac5fdf6db2f71b119ed692c42acf7068a19ce129 100644 (file)
@@ -34,10 +34,12 @@ class MeasureFilter < ActiveRecord::Base
       snapshot.resource
     end
 
+    # For internal use
     def add_measure(measure)
       @measures_by_metric[measure.metric] = measure
     end
 
+    # For internal use
     def add_link(link)
       @links ||= []
       @links << link
@@ -52,129 +54,9 @@ class MeasureFilter < ActiveRecord::Base
     end
   end
 
-  # Column to be displayed
-  class Column
-    attr_reader :key, :metric
-
-    def initialize(key)
-      @key = key
-      metric_key = @key.split(':')[1]
-      @metric = Metric.by_key(metric_key) if metric_key
-    end
-
-    def name
-      if @metric
-        Api::Utils.message("metric.#{@metric.key}.name", :default => @metric.short_name)
-      else
-        Api::Utils.message("measure_filter.col.#{@key}", :default => @key)
-      end
-    end
-
-    def align
-      @align ||=
-        begin
-          # by default is table cells are left-aligned
-          (@key=='name' || @key=='short_name' || @key=='description') ? '' : 'right'
-        end
-    end
-
-    def sort?
-      !links?
-    end
-
-    def links?
-      @key == 'links'
-    end
-  end
-
-  class Display
-    attr_reader :metric_ids
-
-    def initialize(filter)
-    end
-
-    def load_links?
-      false
-    end
-  end
-
-  class ListDisplay < Display
-    attr_reader :columns
-
-    KEY = :list
-
-    def initialize(filter)
-      filter.set_criteria_default_value('columns', ['metric:alert_status', 'name', 'date', 'metric:ncloc', 'metric:violations', 'links'])
-      filter.set_criteria_default_value('sort', 'name')
-      filter.set_criteria_default_value('asc', 'true')
-      filter.set_criteria_default_value('pageSize', '30')
-      filter.pagination.per_page = [filter.criteria['pageSize'].to_i, 200].min
-      filter.pagination.page = (filter.criteria['page'] || 1).to_i
-
-      @columns = filter.criteria['columns'].map { |column_key| Column.new(column_key) }
-      @metric_ids = @columns.map { |column| column.metric.id if column.metric }.compact.uniq
-    end
-
-    def load_links?
-      @columns.index { |column| column.links? }
-    end
-
-  end
-
-  class TreemapDisplay < Display
-    attr_reader :columns
-
-    KEY = :treemap
-
-    def initialize(filter)
-      filter.set_criteria_default_value('columns', ['metric:ncloc', 'metric:violations_density'])
-      @columns = filter.criteria['columns'].map { |column_key| Column.new(column_key) }
-      @metric_ids = @columns.map { |column| column.metric.id if column.metric }.compact.uniq
-    end
-
-    def size_metric
-      @size_metric ||= Metric.by_key('ncloc')
-    end
-
-    def color_metric
-      @color_metric ||= Metric.by_key('violations_density')
-    end
-  end
-
-  class CloudDisplay < Display
-    attr_reader :columns
-
-    KEY = :cloud
-
-    def initialize(filter)
-      filter.set_criteria_default_value('sort', 'name')
-      filter.set_criteria_default_value('asc', 'true')
-      @metric_ids = [size_metric.id, color_metric.id]
-    end
-
-    def size_metric
-      @size_metric ||= Metric.by_key('function_complexity')
-    end
-
-    def color_metric
-      @color_metric ||= Metric.by_key('violations_density')
-    end
-  end
-
-  DISPLAYS = [ListDisplay, TreemapDisplay, CloudDisplay]
-
-  SUPPORTED_CRITERIA_KEYS=Set.new([:qualifiers, :scopes, :onFavourites, :base, :onBaseComponents, :languages, :fromDate, :toDate, :beforeDays, :afterDays,
-                                   :keyRegexp, :nameRegexp,
-                                   :sort, :asc, :columns, :display, :pageSize, :page])
   CRITERIA_SEPARATOR = '|'
   CRITERIA_KEY_VALUE_SEPARATOR = ','
 
-  # Configuration available after call to execute()
-  attr_reader :pagination, :security_exclusions, :columns
-
-  # Results : sorted array of Result
-  attr_reader :base_result, :results
-
   belongs_to :user
   has_many :measure_filter_favourites, :dependent => :delete_all
 
@@ -182,9 +64,7 @@ class MeasureFilter < ActiveRecord::Base
   validates_length_of :name, :maximum => 100, :message => Api::Utils.message('measure_filter.name_too_long')
   validates_length_of :description, :allow_nil => true, :maximum => 4000
 
-  def criteria
-    @criteria ||= {}
-  end
+  attr_reader :pagination, :security_exclusions, :base_result, :results, :display
 
   def sort_key
     criteria['sort']
@@ -194,19 +74,38 @@ class MeasureFilter < ActiveRecord::Base
     criteria['asc']=='true'
   end
 
-# API for plugins
-  def self.register_display(display_class)
-    DISPLAYS<<display_class
+  # array of the metrics to use when loading measures
+  def metrics
+    @metrics ||= []
   end
 
-  def self.supported_criteria?(key)
-    SUPPORTED_CRITERIA_KEYS.include?(key.to_sym)
+  def metrics=(array)
+    @metrics = array
   end
 
-  def set_criteria_from_url_params(params)
+  def require_links=(flag)
+    @require_links=flag
+  end
+
+  # boolean flag that indicates if project links should be loaded
+  def require_links?
+    @require_links
+  end
+
+  def criteria(key=nil)
+    @criteria ||= {}
+    if key
+      @criteria[key.to_s]
+    else
+      @criteria
+    end
+  end
+
+  def criteria=(hash)
+    @display = nil
     @criteria = {}
-    params.each_pair do |k, v|
-      if MeasureFilter.supported_criteria?(k) && !v.empty? && v!=['']
+    hash.each_pair do |k, v|
+      if k && v && !v.empty? && v!=['']
         @criteria[k.to_s]=v
       end
     end
@@ -238,30 +137,19 @@ class MeasureFilter < ActiveRecord::Base
     self.data = string_data.join(CRITERIA_SEPARATOR)
   end
 
-  def display
-    @display ||=
-      begin
-        display_class = nil
-        key = criteria['display']
-        if key.present?
-          display_class = DISPLAYS.find { |d| d::KEY==key.to_sym }
-        end
-        display_class ||= DISPLAYS.first
-        display_class.new(self)
-      end
+  def enable_default_display
+    set_criteria_default_value('display', 'list')
   end
 
-
   # ==== Options
   # :user : the authenticated user
   def execute(controller, options={})
     init_results
-
+    init_display(options)
     user = options[:user]
     rows=Api::Utils.java_facade.executeMeasureFilter2(criteria, (user ? user.id : nil))
     snapshot_ids = filter_authorized_snapshot_ids(rows, controller)
     load_results(snapshot_ids)
-
     self
   end
 
@@ -289,9 +177,14 @@ class MeasureFilter < ActiveRecord::Base
     @pagination = Api::Pagination.new
     @security_exclusions = nil
     @results = nil
+    @base_result = nil
     self
   end
 
+  def init_display(options)
+    @display = MeasureFilterDisplay.create(self, options)
+  end
+
   def filter_authorized_snapshot_ids(rows, controller)
     project_ids = rows.map { |row| row.getResourceRootId() }.compact.uniq
     authorized_project_ids = controller.select_authorized(:user, project_ids)
@@ -303,6 +196,8 @@ class MeasureFilter < ActiveRecord::Base
 
   def load_results(snapshot_ids)
     @results = []
+    metric_ids = metrics.map(&:id)
+
     if !snapshot_ids.empty?
       results_by_snapshot_id = {}
       snapshots = Snapshot.find(:all, :include => ['project'], :conditions => ['id in (?)', snapshot_ids])
@@ -316,17 +211,17 @@ class MeasureFilter < ActiveRecord::Base
         @results << results_by_snapshot_id[sid]
       end
 
-      if display.metric_ids && !display.metric_ids.empty?
+      unless metric_ids.empty?
         measures = ProjectMeasure.find(:all, :conditions =>
-          ['rule_priority is null and rule_id is null and characteristic_id is null and person_id is null and snapshot_id in (?) and metric_id in (?)', snapshot_ids, display.metric_ids]
+          ['rule_priority is null and rule_id is null and characteristic_id is null and person_id is null and snapshot_id in (?) and metric_id in (?)', snapshot_ids, metric_ids]
         )
         measures.each do |measure|
           result = results_by_snapshot_id[measure.snapshot_id]
-          result.add_measure measure
+          result.add_measure(measure)
         end
       end
 
-      if display.load_links?
+      if require_links?
         project_ids = []
         results_by_project_id = {}
         snapshots.each do |snapshot|
@@ -343,9 +238,9 @@ class MeasureFilter < ActiveRecord::Base
       base_snapshot = Snapshot.find(:first, :include => 'project', :conditions => ['projects.kee=? and islast=?', criteria['base'], true])
       if base_snapshot
         @base_result = Result.new(base_snapshot)
-        if display.metric_ids && !display.metric_ids.empty?
+        unless metric_ids.empty?
           base_measures = ProjectMeasure.find(:all, :conditions =>
-            ['rule_priority is null and rule_id is null and characteristic_id is null and person_id is null and snapshot_id=? and metric_id in (?)', base_snapshot.id, display.metric_ids]
+            ['rule_priority is null and rule_id is null and characteristic_id is null and person_id is null and snapshot_id=? and metric_id in (?)', base_snapshot.id, metric_ids]
           )
           base_measures.each do |base_measure|
             @base_result.add_measure(base_measure)
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display.rb
new file mode 100644 (file)
index 0000000..55ae3aa
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+class MeasureFilterDisplay
+
+  DISPLAY_CLASSES = [MeasureFilterDisplayList, MeasureFilterDisplayTreemap]
+
+  def self.create(filter, options)
+    key = filter.criteria('display')
+    display_class=DISPLAY_CLASSES.find{|display_class| display_class::KEY==key.to_sym} if key
+    display_class.new(filter, options) if display_class
+  end
+
+  def self.keys
+    DISPLAY_CLASSES.map{|display_class| display_class::KEY}
+  end
+
+  def key
+    self.class::KEY
+  end
+
+  attr_reader :filter, :options
+
+  def initialize(filter, options)
+    @filter = filter
+    @options = options
+  end
+
+  def url_params
+    {}
+  end
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_list.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_list.rb
new file mode 100644 (file)
index 0000000..9d7fb97
--- /dev/null
@@ -0,0 +1,86 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+require 'set'
+class MeasureFilterDisplayList < MeasureFilterDisplay
+  KEY = :list
+
+  class Column
+    attr_reader :key, :metric
+
+    def initialize(key)
+      @key = key
+      metric_key = @key.split(':')[1]
+      @metric = Metric.by_key(metric_key) if metric_key
+    end
+
+    def name
+      if @metric
+        Api::Utils.message("metric.#{@metric.key}.name", :default => @metric.short_name)
+      else
+        Api::Utils.message("measure_filter.col.#{@key}", :default => @key)
+      end
+    end
+
+    def align
+      @align ||=
+          begin
+            # by default is table cells are left-aligned
+            (@key=='name' || @key=='short_name' || @key=='description') ? '' : 'right'
+          end
+    end
+
+    def sort?
+      !links?
+    end
+
+    def links?
+      @key == 'links'
+    end
+  end
+
+  attr_reader :columns
+
+  def initialize(filter, options)
+    super(filter, options)
+
+    # default values
+    filter.set_criteria_default_value('cols', ['metric:alert_status', 'name', 'date', 'metric:ncloc', 'metric:violations', 'links'])
+    filter.set_criteria_default_value('sort', 'name')
+    filter.set_criteria_default_value('asc', 'true')
+    filter.set_criteria_default_value('pageSize', '30')
+    filter.pagination.per_page = [filter.criteria['pageSize'].to_i, 200].min
+    filter.pagination.page = (filter.criteria['page'] || 1).to_i
+
+    @columns = []
+    metrics = []
+    filter.criteria('cols').each do |column_key|
+      column = Column.new(column_key)
+      @columns << column
+      metrics << column.metric if column.metric
+      filter.require_links=true if column.links?
+    end
+    filter.metrics=(metrics)
+  end
+
+  PROPERTY_KEYS = Set.new(['cols', 'sort', 'asc', 'pageSize'])
+  def url_params
+    @filter.criteria.delete_if { |k,v| !PROPERTY_KEYS.include?(k)}
+  end
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_treemap.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_display_treemap.rb
new file mode 100644 (file)
index 0000000..4640c33
--- /dev/null
@@ -0,0 +1,140 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+class MeasureFilterDisplayTreemap < MeasureFilterDisplay
+  include ActionView::Helpers::UrlHelper
+
+  KEY = :treemap
+
+  attr_reader :height, :id, :size, :size_metric, :color_metric
+
+  def initialize(filter, options)
+    super(filter, options)
+
+    @size_metric = Metric.by_key(@filter.criteria('tmSize')||'ncloc')
+    @color_metric = Metric.by_key(@filter.criteria('tmColor')||'violations_density')
+    @html_id = options[:html_id]
+    @filter.metrics=([@size_metric, @color_metric].compact)
+    @height = (@filter.criteria('tmHeight')||'600').to_i
+    @id_count = 0
+  end
+
+  def html
+    if filter.results
+      root = Treemap::Node.new(:id => -1, :label => '')
+      build_tree(root)
+
+      output = Sonar::HtmlOutput.new do |o|
+        # width in percents
+        o.width = 100
+        o.height = @height
+        o.full_html = false
+        o.details_at_depth = 1
+      end
+      html = output.to_html(root)
+      html + "<script>treemapById(#{@html_id}).onLoaded(#{@filter.results.size});</script>"
+    end
+  end
+
+  def empty?
+    @filter.results.nil? || @filter.results.empty?
+  end
+
+  private
+
+  def build_tree(node)
+    if @filter.results
+      @filter.results.each do |result|
+        size_measure=result.measure(@size_metric)
+        if size_measure
+          color_measure=(@color_metric ? result.measure(@color_metric) : nil)
+          resource = result.snapshot.resource
+          child = Treemap::Node.new(:id => "#{@html_id}-#{@id_count += 1}",
+                                    :size => size_value(size_measure),
+                                    :label => resource.name(false),
+                                    :title => escape_javascript(resource.name(true)),
+                                    :tooltip => tooltip(resource, size_measure, color_measure),
+                                    :color => html_color(color_measure),
+                                    :rid => resource.id,
+                                    :leaf => resource.source_code?)
+          node.add_child(child)
+        end
+      end
+    end
+  end
+
+  def tooltip(resource, size_measure, color_measure)
+    html=CGI::escapeHTML(resource.name(true))
+    html += " - #{CGI::escapeHTML(@size_metric.short_name)}: #{CGI::escapeHTML(size_measure.formatted_value)}"
+    if color_measure
+      html += " - #{CGI::escapeHTML(@color_metric.short_name)}: #{CGI::escapeHTML(color_measure.formatted_value)}"
+    end
+    html
+  end
+
+  def size_value(measure)
+    if measure.value
+      measure.value.to_f.abs||0.0
+    else
+      0.0
+    end
+  end
+
+  def html_color(measure)
+    MeasureColor.color(measure).html
+  end
+
+end
+
+class Sonar::HtmlOutput < Treemap::HtmlOutput
+
+  def draw_node(node)
+    return "" if node.bounds.nil?
+
+    html = ''
+    html += "<div style=\""
+    html += "overflow:hidden;position:absolute;"
+    html += "left:#{node.bounds.x1}%; top:#{node.bounds.y1}px;"
+    html += "width:#{node.bounds.width}%;height: #{node.bounds.height}px;"
+    html += "background-color:#FFF;\">"
+    html += "<div rid='#{node.rid}' id=\"tm-node-#{node.id}\" style='margin: 1px;background-color: #{node.color}; height: #{node.bounds.height-4}px;
+border: 1px solid #{node.color};' alt=\"#{node.tooltip}\" title=\"#{node.tooltip}\""
+    if node.leaf
+      html += "l=1 "
+    end
+    html += ' >'
+    html += draw_node_body(node)
+
+    if (!node.children.nil? && node.children.size > 0)
+      node.children.each do |c|
+        html += draw_node(c)
+      end
+    end
+    html + '</div></div>'
+  end
+
+  def draw_label(node)
+    if node.leaf
+      "<a onclick=\"window.open(this.href,'resource','height=800,width=900,scrollbars=1,resizable=1');return false;\" " +
+        "href=\"#{ApplicationController.root_context}/resource/index/#{node.rid}\">#{node_label(node)}</a>"
+    else
+      "<a href='#{ApplicationController.root_context}/dashboard/index/#{node.rid}'>#{node_label(node)}</a>"
+    end
+  end
+end
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_treemap.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_treemap.rb
deleted file mode 100644 (file)
index f2ea09a..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#
-# Sonar, entreprise quality control tool.
-# Copyright (C) 2008-2012 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# Sonar is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# Sonar is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with Sonar; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
-#
-class MeasureFilterTreemap
-  include ActionView::Helpers::UrlHelper
-
-  attr_reader :filter, :height, :id, :size, :size_metric, :color_metric
-
-  def initialize(filter, height, id)
-    @filter = filter
-    @height = height
-    @id = id
-    @size = 0
-    @size_metric = filter.display.size_metric
-    @color_metric = filter.display.color_metric
-  end
-
-  def html
-    root = Treemap::Node.new(:id => -1, :label => '')
-    build_tree(root)
-
-    output = Sonar::HtmlOutput.new do |o|
-      # width in percents
-      o.width = 100
-      o.height = @height
-      o.full_html = false
-      o.details_at_depth = 1
-    end
-    html = output.to_html(root)
-    html + "<script>treemapById(#{@id}).onLoaded(#{@size});</script>"
-  end
-
-  def empty?
-    @size==0
-  end
-
-  private
-
-  def build_tree(node)
-    if @filter.results
-      @filter.results.each do |result|
-        size_measure=result.measure(@size_metric)
-        if size_measure
-          color_measure=(@color_metric ? result.measure(@color_metric) : nil)
-          resource = result.snapshot.resource
-          child = Treemap::Node.new(:id => "#{@id}-#{@size += 1}",
-                                    :size => size_value(size_measure),
-                                    :label => resource.name(false),
-                                    :title => escape_javascript(resource.name(true)),
-                                    :tooltip => tooltip(resource, size_measure, color_measure),
-                                    :color => html_color(color_measure),
-                                    :rid => resource.id,
-                                    :leaf => resource.source_code?)
-          node.add_child(child)
-        end
-      end
-    end
-  end
-
-  def tooltip(resource, size_measure, color_measure)
-    html=CGI::escapeHTML(resource.name(true))
-    html += " - #{CGI::escapeHTML(@size_metric.short_name)}: #{CGI::escapeHTML(size_measure.formatted_value)}"
-    if color_measure
-      html += " - #{CGI::escapeHTML(@color_metric.short_name)}: #{CGI::escapeHTML(color_measure.formatted_value)}"
-    end
-    html
-  end
-
-  def size_value(measure)
-    if measure.value
-      measure.value.to_f.abs||0.0
-    else
-      0.0
-    end
-  end
-
-  def html_color(measure)
-    MeasureColor.color(measure).html
-  end
-
-end
-
-class Sonar::HtmlOutput < Treemap::HtmlOutput
-
-  def draw_node(node)
-    return "" if node.bounds.nil?
-
-    html = ''
-    html += "<div style=\""
-    html += "overflow:hidden;position:absolute;"
-    html += "left:#{node.bounds.x1}%; top:#{node.bounds.y1}px;"
-    html += "width:#{node.bounds.width}%;height: #{node.bounds.height}px;"
-    html += "background-color:#FFF;\">"
-    html += "<div rid='#{node.rid}' id=\"tm-node-#{node.id}\" style='margin: 1px;background-color: #{node.color}; height: #{node.bounds.height-4}px;
-border: 1px solid #{node.color};' alt=\"#{node.tooltip}\" title=\"#{node.tooltip}\""
-    if node.leaf
-      html += "l=1 "
-    end
-    html += ' >'
-    html += draw_node_body(node)
-
-    if (!node.children.nil? && node.children.size > 0)
-      node.children.each do |c|
-        html += draw_node(c)
-      end
-    end
-    html + '</div></div>'
-  end
-
-  def draw_label(node)
-    if node.leaf
-      "<a onclick=\"window.open(this.href,'resource','height=800,width=900,scrollbars=1,resizable=1');return false;\" " +
-        "href=\"#{ApplicationController.root_context}/resource/index/#{node.rid}\">#{node_label(node)}</a>"
-    else
-      "<a href='#{ApplicationController.root_context}/dashboard/index/#{node.rid}'>#{node_label(node)}</a>"
-    end
-  end
-end
\ No newline at end of file
index 900431e003b24eb995b051faf5480684db0f21d4..f690dc8a23e2bf4889c52c7d37777ed74ed7b850 100644 (file)
@@ -2,7 +2,55 @@
    display_favourites = logged_in?
    colspan = @filter.display.columns.size
    colspan += 1 if display_favourites
+   if edit_mode
+     content_for :script do
 %>
+    <script>
+      var cols = [<%= @filter.display.columns.map{|c| "'#{c.key}'"}.join(',')-%>];
+      function leftCol(id) {
+
+      }
+      function rightCol(id) {
+
+      }
+      function deleteCol(id) {
+
+      }
+    </script>
+  <%
+     end
+     end
+  %>
+
+<% if edit_mode %>
+  <table class="data width100 admin">
+    <tr>
+      <td>
+        <%= metric_select_tag 'metric', Metric.all.reject { |m| m.data? }, :html_id => 'select-metric', :allow_empty => true -%>
+        <button id="add-metric" disabled>Add</button>
+      </td>
+      <td class="right">
+        <a href="#" class="button" id="exit-edit">Done</a>
+      </td>
+    </tr>
+  </table>
+  <script>
+    $j("#select-metric").on("change", function (e) {
+      $j("#add-metric").removeAttr('disabled');
+    });
+    $j("#add-metric").on("click", function (e) {
+      var metric = $j("#select-metric option:selected").val();
+      cols.push('metric:' + metric);
+      window.location = window.location.href.replace(/&?cols\[\]=([^&]$|[^&]*)/g, '') + '&' + $j.map(cols, function (a) {
+        return 'cols[]=' + a;
+      }).join('&');
+    });
+    $j("#exit-edit").on("click", function (e) {
+      window.location = window.location.href.replace(/&?edit=([^&]$|[^&]*)/g, '');
+    });
+  </script>
+<% end %>
+
 <table class="data">
   <thead>
   <tr>
   </thead>
   <tbody>
 
+  <% if edit_mode %>
+    <tr class="admin">
+      <% if display_favourites %>
+        <th></th>
+      <% end %>
+      <% @filter.display.columns.each_with_index do |column, index| %>
+        <td class="<%= column.align -%>">
+          <a href="#" onclick="leftCol(<%= index -%>)" title="<%= message('move_left') -%>"><%= image_tag("controls/resultset_previous.png") -%></a>
+          <a href="#" onclick="deleteCol(<%= index -%>)" title="<%= message('delete_column') -%>"><%= image_tag("bin_closed.png") -%></a>
+          <a href="#" onclick="rightCol(<%= index -%>)" title="<%= message('move_right') -%>"><%= image_tag("controls/resultset_next.png") -%></a>
+        </td>
+      <% end %>
+  <% end %>
+
   <% if @filter.base_result %>
     <tr class="highlight">
       <% if display_favourites %>
index 0f4f89108a4c9cc07b2f8a8049f0ac51ff2377b6..9f9fce68896cd6400dea4511e6c1932ae90cd831 100644 (file)
@@ -1,4 +1,3 @@
-<% treemap = MeasureFilterTreemap.new(@filter, 600, @filter.id.to_s) %>
-<div class="treemap" style="width: 100%; height:<%= treemap.height %>px;">
-  <%= treemap.html() -%>
+<div class="treemap" style="width: 100%; height:<%= @filter.display.height %>px;">
+  <%= @filter.display.html -%>
 </div>
\ No newline at end of file
index 5e9fe5f1be1e69a6785f88b10abb199c5d80e2e8..7d8da93a2eb3472a42e0f3a3c2c89d47e537d58e 100644 (file)
     }
   </style>
 <% end %>
-
+<% content_for :script do %>
+  <script>
+    function submitSearch() {
+      // remove empty parameters from URL
+      var form = $j("#filter-form");
+      form.find(':input[value=""]').attr('name', '');
+      form.submit();
+      return false;
+    }
+  </script>
+<% end %>
 <div id="measure-filters">
-  <div id="filter-form" class="page-split-left">
-    <form method="GET" action="<%= ApplicationController.root_context -%>/measures/search">
+  <div class="page-split-left">
+    <form id="filter-form" method="GET" action="<%= ApplicationController.root_context -%>/measures/search">
       <% if @filter.id %>
         <input type="hidden" name="id" value="<%= @filter.id -%>">
       <% end %>
+      <%
+         if @filter.display
+           @filter.display.url_params.each_pair do |k, v|
+             if v.is_a?(String)
+      %>
+            <%= hidden_field_tag k, v -%>
+          <% else
+            v.each do |string_val|
+          %>
+              <%= hidden_field_tag "#{k}[]", string_val -%>
+          <% end
+             end
+             end
+             end
+          %>
       <table class="width100">
         <tbody>
         <tr>
         <tr>
           <td>
             <br/>
-            <input type="submit" name="search" value="Search">
+            <input type="button" name="search" value="Search" onclick="submitSearch()">
             <a href="<%= ApplicationController.root_context -%>/measures">New search</a>
           </td>
         </tr>
     <% end %>
   </div>
 
-  <% if @filter.results %>
+  <% if @filter.results && @filter.display %>
     <div id="filter-result" class="page-split-right width100">
       <% if @filter.name %>
         <p>
 
       <% end %>
 
-      Display as:
-      <% MeasureFilter::DISPLAYS.each do |display_class| %>
-        <%= link_to_if display_class::KEY!=@filter.display.class::KEY, display_class::KEY, params.merge(:action => 'search', :display => display_class::KEY, :id => @filter.id) -%>
-      <% end %>
-
-      <% if logged_in? && (@filter.user_id==nil || @filter.user_id==current_user.id) %>
-        <a id="save_as" href="<%= url_for params.merge({:action => 'save_form', :id => @filter.id}) -%>" class="link-action open-modal"><%= message('save') -%></a>
+      <%
+         edit_mode = (params[:edit]=='true')
+         unless edit_mode %>
+        Display as:
+        <% MeasureFilterDisplay.keys.each do |display_key| %>
+          <%= link_to_if display_key!=@filter.display.key, display_key, params.merge(:action => 'search', :display => display_key, :id => @filter.id) -%>
+        <% end %>
+        <a id="edit" href="<%= url_for params.merge({:edit => true, :id => @filter.id}) -%>" class="button"><%= message('configure') -%></a>
+        <% if logged_in? && (@filter.user_id==nil || @filter.user_id==current_user.id) %>
+          <a id="save_as" href="<%= url_for params.merge({:action => 'save_form', :id => @filter.id}) -%>" class="button open-modal"><%= message('save') -%></a>
+        <% end %>
       <% end %>
-
-      <%= render :partial => "measures/display_#{@filter.display.class::KEY}" -%>
+      <%= render :partial => "measures/display_#{@filter.display.class::KEY}", :locals => {:edit_mode => edit_mode} -%>
       <br>
 
       <% if @filter.security_exclusions %>