From e41624ef15df64ad062f5b035c7ef308c1ff5cdc Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 8 Feb 2012 18:39:10 +0100 Subject: [PATCH] SONAR-3246 Provide "zoom in/zoom out" feature in the Filter Treemap component * Major refactoring of javascript code for treemap navigation. * Extract the execution of filters in a dedicated Ruby component : Filters.execute(filter) --- .../plugins/core/widgets/treemap.html.erb | 5 +- .../app/controllers/components_controller.rb | 25 +-- .../app/controllers/filters_controller.rb | 38 ++--- .../app/controllers/treemap_controller.rb | 28 ++-- .../WEB-INF/app/helpers/filters_helper.rb | 139 ---------------- .../main/webapp/WEB-INF/app/models/filter.rb | 30 ++-- .../WEB-INF/app/models/filter_context.rb | 3 +- .../main/webapp/WEB-INF/app/models/filters.rb | 147 +++++++++++++++++ .../WEB-INF/app/models/sonar/treemap.rb | 18 +-- .../views/components/_treemap_gradient.rhtml | 21 --- .../components/_treemap_set_default.html.erb | 17 +- .../components/_treemap_settings.html.erb | 25 +-- .../app/views/components/index.html.erb | 134 ++++++++-------- .../app/views/dashboard/_widget.html.erb | 2 +- .../views/filters/_customize_treemap.html.erb | 1 - .../WEB-INF/app/views/filters/_tabs.html.erb | 2 +- .../app/views/filters/_treemap.html.erb | 30 ++-- .../WEB-INF/app/views/filters/manage.html.erb | 2 +- .../app/views/filters/treemap.html.erb | 10 +- .../views/treemap/_treemap_container.html.erb | 13 +- .../WEB-INF/lib/authenticated_system.rb | 1 - .../webapp/WEB-INF/lib/need_authorization.rb | 1 - .../main/webapp/WEB-INF/lib/treemap/node.rb | 1 - .../main/webapp/javascripts/application.js | 151 ++++++++++++------ 24 files changed, 437 insertions(+), 407 deletions(-) create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/models/filters.rb delete mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_gradient.rhtml diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/treemap.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/treemap.html.erb index 93b2527aebf..ac08223ba83 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/treemap.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/treemap.html.erb @@ -2,6 +2,7 @@ :treemap_id => widget.id, :size_metric => widget_properties['sizeMetric'], :color_metric => widget_properties['colorMetric'], - :heightInPercents => widget_properties['heightInPercents'], - :resource_id => @resource.id + :height_in_percents => widget_properties['heightInPercents'], + :context_type => 'resource', + :context_id => @resource.id } -%> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/components_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/components_controller.rb index e232f838941..30d5cab8b47 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/components_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/components_controller.rb @@ -49,34 +49,11 @@ class ComponentsController < ApplicationController if @components_configuration.treemap_enabled? && @snapshots.size>1 @treemap = Sonar::Treemap.new(1, default_treemap_size_metric, TREEMAP_SIZE, TREEMAP_SIZE, { :color_metric => default_treemap_color_metric, - :root_snapshot => @snapshot, - :browsable => false + :root_snapshot => @snapshot }) end end - def treemap - snapshot=Snapshot.find(params[:sid]) - not_found("Snapshot not found") unless snapshot - access_denied unless has_role?(:user, snapshot) - - size_metric = (params[:size_metric] ? Metric.by_key(params[:size_metric]) : default_treemap_size_metric) - color_metric = (params[:color_metric] ? Metric.by_key(params[:color_metric]) : default_treemap_color_metric) - - @treemap = Sonar::Treemap.new(1, size_metric, TREEMAP_SIZE, TREEMAP_SIZE, { - :color_metric => color_metric, - :root_snapshot => snapshot, - :browsable => false - }) - - render(:update) do |page| - page.replace_html 'treemap', @treemap.generate_html - page.replace_html 'treemap_gradient', :partial => 'treemap/gradient', :locals => {:metric => @treemap.color_metric} - page.replace_html 'treemap_set_default', :partial => 'components/treemap_set_default', - :locals => {:controller => 'components', :size_metric => params[:size_metric], :color_metric => params[:color_metric], :rid => snapshot.project_id} - end - end - def update_default_treemap_metrics Property.set(TREEMAP_SIZE_METRIC_PROPERTY, params[:size_metric]) Property.set(TREEMAP_COLOR_METRIC_PROPERTY, params[:color_metric]) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/filters_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/filters_controller.rb index 4e667e275a2..8f235349921 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/filters_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/filters_controller.rb @@ -82,17 +82,15 @@ class FiltersController < ApplicationController def edit @filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(@filter) + access_denied unless @filter.authorized_to_edit?(self) - options=params - options[:user]=current_user - @filter_context=execute_filter(FilterContext.new(@filter, options)) + @filter_context=Filters.execute(@filter, self, params) render :action => 'new' end def update @filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(@filter) + access_denied unless @filter.authorized_to_edit?(self) load_filter_from_params(@filter, params) @@ -210,11 +208,12 @@ class FiltersController < ApplicationController column=FilterColumn.find(params[:id]) filter=column.filter - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) if column.deletable? column.destroy redirect_to :action => 'edit', :id => filter.id + redirect_to :action => 'edit', :id => filter.id else flash[:error]='Unknown column' redirect_to :action => 'manage' @@ -223,7 +222,7 @@ class FiltersController < ApplicationController def add_column filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.clean_columns_order() # clean the columns which are badly ordered (see SONAR-1902) fields=params[:column].split(',') @@ -240,7 +239,7 @@ class FiltersController < ApplicationController column=FilterColumn.find(params[:id]) filter=column.filter - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.clean_columns_order() # clean the columns which are badly ordered (see SONAR-1902) target_column=filter.column_by_id(params[:id].to_i) @@ -258,7 +257,7 @@ class FiltersController < ApplicationController column=FilterColumn.find(params[:id]) filter=column.filter - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.clean_columns_order() # clean the columns which are badly ordered (see SONAR-1902) target_column=filter.column_by_id(params[:id].to_i) @@ -276,7 +275,7 @@ class FiltersController < ApplicationController column=FilterColumn.find(params[:id]) filter=column.filter - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.columns.each do |col| if col==column @@ -297,7 +296,7 @@ class FiltersController < ApplicationController #--------------------------------------------------------------------- def set_view filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.default_view=params[:view] filter.save @@ -306,7 +305,7 @@ class FiltersController < ApplicationController def set_columns filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) filter.columns.clear params[:columns].each do |colstring| @@ -319,7 +318,7 @@ class FiltersController < ApplicationController def set_page_size filter=::Filter.find(params[:id]) - access_denied unless editable_filter?(filter) + access_denied unless filter.authorized_to_edit?(self) size=[::Filter::MAX_PAGE_SIZE, params[:size].to_i].min size=[::Filter::MIN_PAGE_SIZE, size].max @@ -364,7 +363,7 @@ class FiltersController < ApplicationController #--------------------------------------------------------------------- def treemap @filter=::Filter.find(params[:id]) - access_denied unless viewable_filter?(@filter) + access_denied unless @filter.authorized_to_execute?(self) @size_metric=Metric.by_key(params[:size_metric]) @color_metric=Metric.by_key(params[:color_metric]) @@ -373,9 +372,7 @@ class FiltersController < ApplicationController @filter.sorted_column=FilterColumn.new('family' => 'metric', :kee => @size_metric.key, :sort_direction => (@size_metric.direction>=0 ? 'ASC' : 'DESC')) - options=params - options[:user]=current_user - @filter_context=execute_filter(FilterContext.new(@filter, options)) + @filter_context=Filters.execute(@filter, self, params) @width=(params[:width]||'800').to_i @height=(params[:height]||'500').to_i @@ -383,8 +380,7 @@ class FiltersController < ApplicationController @treemap = Sonar::Treemap.new(@filter.id, @size_metric, @width, @height, { :color_metric => @color_metric, :period_index => @filter_context.period_index, - :measures_by_snapshot => @filter_context.measures_by_snapshot, - :browsable => false + :measures_by_snapshot => @filter_context.measures_by_snapshot }) @@ -468,9 +464,7 @@ class FiltersController < ApplicationController if @active @filter=@active.filter unless @filter.ajax_loading? - options=params - options[:user]=current_user - @filter_context=execute_filter(FilterContext.new(@filter, options)) + @filter_context=Filters.execute(@filter, self, params) load_masterproject() if @filter.projects_homepage? end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/treemap_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/treemap_controller.rb index 1d124172737..1406b0e2a8d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/treemap_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/treemap_controller.rb @@ -39,21 +39,31 @@ class TreemapController < ApplicationController color_metric=(params[:color_metric].present? ? Metric.by_key(params[:color_metric]) : nil) - resource = Project.by_key(params[:resource]) - bad_request('Unknown resource: ' + params[:resource]) unless resource - bad_request('Data not available') unless resource.last_snapshot - access_denied unless has_role?(:user, resource) + if params[:resource] + resource = Project.by_key(params[:resource]) + bad_request('Unknown resource: ' + params[:resource]) unless resource + bad_request('Data not available') unless resource.last_snapshot + access_denied unless has_role?(:user, resource) + elsif params[:filter] + filter=::Filter.find(params[:filter]) + bad_request('Unknown filter: ' + params[:filter]) unless filter + access_denied unless filter.authorized_to_execute?(self) + filter.sorted_column=FilterColumn.new('family' => 'metric', :kee => size_metric.key, :sort_direction => (size_metric.direction>=0 ? 'ASC' : 'DESC')) + filter_context=Filters.execute(filter, self, params) + else + bad_request('Missing parameter: resource or filter') + end treemap = Sonar::Treemap.new(html_id, size_metric, width.to_i, height.to_i, { :color_metric => color_metric, - :root_snapshot => resource.last_snapshot, - :period_index => params[:period_index].to_i, - :browsable => true + :root_snapshot => (resource ? resource.last_snapshot : nil), + :measures_by_snapshot => (filter_context ? filter_context.measures_by_snapshot : nil), + :period_index => params[:period_index].to_i }) render :update do |page| - page.replace_html "tm-#{html_id}", :partial => 'treemap', :object => treemap - page.replace_html "tm-gradient-#{html_id}", :partial => 'gradient', :locals => {:metric => color_metric} + page.replace_html "tm-#{html_id}", :partial => 'treemap', :object => treemap + page.replace_html "tm-gradient-#{html_id}", :partial => 'gradient', :locals => {:metric => color_metric} page.hide "tm-loading-#{html_id}" end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb index 50f25552a9d..d932ec1131b 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb @@ -19,107 +19,6 @@ # module FiltersHelper - def execute_filter(filter_context) - filter=filter_context.filter - java_filter=Java::OrgSonarServerFilters::Filter.new - - #----- FILTER ON RESOURCES - if filter.resource_id - snapshot=Snapshot.find(:first, :conditions => {:project_id => filter.resource_id, :islast => true}) - if snapshot - java_filter.setPath(snapshot.root_snapshot_id, snapshot.id, snapshot.path, (snapshot.view? || snapshot.subview?)) - else - java_filter.setPath(-1, -1, '', false) - end - end - - if filter.favourites - java_filter.setFavouriteIds((filter_context.user ? filter_context.user.favourite_ids : []).to_java(:Integer)) - end - - date_criterion=filter.criterion('date') - if date_criterion - java_filter.setDateCriterion(date_criterion.operator, date_criterion.value.to_i) - end - - key_criterion=filter.criterion('key') - if key_criterion - java_filter.setKeyRegexp(key_criterion.text_value) - end - - name_criterion=filter.criterion('name') - if name_criterion - java_filter.setNameRegexp(name_criterion.text_value) - end - - qualifier_criterion=filter.criterion('qualifier') - if qualifier_criterion - java_filter.setQualifiers(qualifier_criterion.text_values.to_java(:String)) - else - java_filter.setQualifiers([].to_java(:String)) - end - - language_criterion=filter.criterion('language') - if language_criterion - java_filter.setLanguages(language_criterion.text_values.to_java :String) - end - - - #----- FILTER ON MEASURES - filter.measure_criteria.each do |c| - java_filter.createMeasureCriterionOnValue(c.metric.id, c.operator, c.value, c.variation) - end - - - #----- SORTED COLUMN - if filter_context.sorted_column_id - filter.sorted_column=filter_context.sorted_column_id - end - if filter.sorted_column.on_name? - java_filter.setSortedByName() - - elsif filter.sorted_column.on_date? - java_filter.setSortedByDate() - - elsif filter.sorted_column.on_version? - java_filter.setSortedByVersion() - - elsif filter.sorted_column.on_language? - java_filter.setSortedByLanguage() - - elsif filter.sorted_column.on_metric? && filter.sorted_column.metric - metric=filter.sorted_column.metric - java_filter.setSortedMetricId(metric.id, metric.numeric?, filter.sorted_column.variation) - - end - - - #----- SORTING DIRECTION - if filter_context.ascending_sort.nil? - java_filter.setAscendingSort(filter.sorted_column.ascending?) - else - filter.sorted_column.ascending=filter_context.ascending_sort - java_filter.setAscendingSort(filter.sorted_column.ascending?) - end - - - if filter_context.ascending_sort - filter.sorted_column.ascending=filter_context.ascending_sort - end - java_filter.setAscendingSort(filter.sorted_column.ascending?) - - - #----- VARIATION - java_filter.setPeriodIndex(filter_context.period_index) - - #----- EXECUTION - java_result=java_facade.execute_filter(java_filter) - snapshot_ids=extract_snapshot_ids(java_result.getRows()) - - has_security_exclusions=(snapshot_ids.size < java_result.size()) - filter_context.process_results(snapshot_ids, has_security_exclusions) - end - def column_title(column, filter) if column.sortable? html=link_to h(column.display_name), url_for(:overwrite_params => {:asc => (!(column.ascending?)).to_s, :sort => column.id}) @@ -153,22 +52,6 @@ module FiltersHelper [size_metric, color_metric] end - def viewable_filter?(filter) - if logged_in? - filter.shared || (filter.user==current_user) - else - filter.shared - end - end - - def editable_filter?(filter) - if logged_in? - (filter.user && filter.user==current_user) || (!filter.user && is_admin?) - else - false - end - end - def period_names p1=Property.value('sonar.timemachine.period1', nil, 'previous_analysis') p2=Property.value('sonar.timemachine.period2', nil, '5') @@ -192,26 +75,4 @@ module FiltersHelper nil end end - - def extract_snapshot_ids(sql_rows) - sids=[] - project_ids=sql_rows.map{|r| r[2] ? to_integer(r[2]) : to_integer(r[1])}.compact.uniq - authorized_pids=select_authorized(:user, project_ids) - sql_rows.each do |row| - pid=(row[2] ? to_integer(row[2]) : to_integer(row[1])) - if authorized_pids.include?(pid) - sids< 1..100 validates_uniqueness_of :name, :scope => :user_id, :if => Proc.new { |filter| filter.user_id } - validates_inclusion_of :default_view, :in => ['list','treemap'], :allow_nil => true + validates_inclusion_of :default_view, :in => ['list', 'treemap'], :allow_nil => true def criterion(family, key=nil) criteria.each do |criterion| @@ -47,7 +47,7 @@ class Filter < ActiveRecord::Base def measure_criteria @measure_criteria ||= begin - criteria.select{|c| c.on_metric? && c.metric} + criteria.select { |c| c.on_metric? && c.metric } end end @@ -69,19 +69,19 @@ class Filter < ActiveRecord::Base end def measure_columns - columns.select{|col| col.metric} + columns.select { |col| col.metric } end def sorted_column @sorted_column ||= begin - columns.to_a.find{|c| c.sort_direction} || column('name') + columns.to_a.find { |c| c.sort_direction } || column('name') end end def sorted_column=(col_or_id) if col_or_id.is_a?(Fixnum) - @sorted_column=columns.to_a.find{|c| c.id==col_or_id} + @sorted_column=columns.to_a.find { |c| c.id==col_or_id } else @sorted_column=col_or_id end @@ -104,7 +104,7 @@ class Filter < ActiveRecord::Base TREEMAP_PAGE_SIZE else read_attribute(:page_size) || DEFAULT_PAGE_SIZE - end + end end def ajax_loading? @@ -133,14 +133,14 @@ class Filter < ActiveRecord::Base def period? period_index && period_index>0 end - + def column_by_id(col_id) columns.each do |col| return col if col.id==col_id end nil end - + def clean_columns_order columns.each_with_index do |col, index| col.order_index=index+1 @@ -149,6 +149,18 @@ class Filter < ActiveRecord::Base reload end + def authorized_to_execute?(authenticated_system) + shared || (user==authenticated_system.current_user) + end + + def authorized_to_edit?(authenticated_system) + if authenticated_system.logged_in? + (user && user==authenticated_system.current_user) || (!user && authenticated_system.is_admin?) + else + false + end + end + protected def before_validation @@ -158,7 +170,7 @@ class Filter < ActiveRecord::Base end # one column must be sorted - sorted_col=self.columns.to_a.find{|c| c.sort_direction} + sorted_col=self.columns.to_a.find { |c| c.sort_direction } unless sorted_col column('name').sort_direction='ASC' end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb index 4fe3bf196d6..b272ec46372 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 # class FilterContext - attr_accessor :filter, :page_size, :page_id, :security_exclusions, :period_index, :user, :sorted_column_id, :ascending_sort + attr_accessor :filter, :page_size, :page_id, :security_exclusions, :period_index, :sorted_column_id, :ascending_sort def initialize(filter, options={}) @filter = filter @@ -26,7 +26,6 @@ @page_id=(options[:page_id] ? options[:page_id].to_i : 1) @sorted_column_id=(options[:sort].blank? ? nil : options[:sort].to_i) @ascending_sort=(options[:asc].blank? ? nil : options[:asc]=='true') - @user=options[:user] @period_index = (options[:period] ? options[:period].to_i : @filter.period_index ) @metric_ids=(options[:metric_ids] || @filter.columns.map{|col| col.metric ? col.metric.id : nil}.compact.uniq) end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/filters.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/filters.rb new file mode 100644 index 00000000000..75312ecb27c --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/filters.rb @@ -0,0 +1,147 @@ +# +# 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 Filters + + def self.execute(filter, authenticated_system, options={}) + filter_context = FilterContext.new(filter, options) + java_filter=Java::OrgSonarServerFilters::Filter.new + + #----- FILTER ON RESOURCES + if filter.resource_id + snapshot=Snapshot.find(:first, :conditions => {:project_id => filter.resource_id, :islast => true}) + if snapshot + java_filter.setPath(snapshot.root_snapshot_id, snapshot.id, snapshot.path, (snapshot.view? || snapshot.subview?)) + else + java_filter.setPath(-1, -1, '', false) + end + end + + if filter.favourites + java_filter.setFavouriteIds((authenticated_system.current_user.favourite_ids||[]).to_java(:Integer)) + end + + date_criterion=filter.criterion('date') + if date_criterion + java_filter.setDateCriterion(date_criterion.operator, date_criterion.value.to_i) + end + + key_criterion=filter.criterion('key') + if key_criterion + java_filter.setKeyRegexp(key_criterion.text_value) + end + + name_criterion=filter.criterion('name') + if name_criterion + java_filter.setNameRegexp(name_criterion.text_value) + end + + qualifier_criterion=filter.criterion('qualifier') + if qualifier_criterion + java_filter.setQualifiers(qualifier_criterion.text_values.to_java(:String)) + else + java_filter.setQualifiers([].to_java(:String)) + end + + language_criterion=filter.criterion('language') + if language_criterion + java_filter.setLanguages(language_criterion.text_values.to_java :String) + end + + + #----- FILTER ON MEASURES + filter.measure_criteria.each do |c| + java_filter.createMeasureCriterionOnValue(c.metric.id, c.operator, c.value, c.variation) + end + + + #----- SORTED COLUMN + if filter_context.sorted_column_id + filter.sorted_column=filter_context.sorted_column_id + end + if filter.sorted_column.on_name? + java_filter.setSortedByName() + + elsif filter.sorted_column.on_date? + java_filter.setSortedByDate() + + elsif filter.sorted_column.on_version? + java_filter.setSortedByVersion() + + elsif filter.sorted_column.on_language? + java_filter.setSortedByLanguage() + + elsif filter.sorted_column.on_metric? && filter.sorted_column.metric + metric=filter.sorted_column.metric + java_filter.setSortedMetricId(metric.id, metric.numeric?, filter.sorted_column.variation) + + end + + + #----- SORTING DIRECTION + if filter_context.ascending_sort.nil? + java_filter.setAscendingSort(filter.sorted_column.ascending?) + else + filter.sorted_column.ascending=filter_context.ascending_sort + java_filter.setAscendingSort(filter.sorted_column.ascending?) + end + + + if filter_context.ascending_sort + filter.sorted_column.ascending=filter_context.ascending_sort + end + java_filter.setAscendingSort(filter.sorted_column.ascending?) + + + #----- VARIATION + java_filter.setPeriodIndex(filter_context.period_index) + + #----- EXECUTION + java_result=Java::OrgSonarServerUi::JRubyFacade.getInstance().execute_filter(java_filter) + snapshot_ids=extract_snapshot_ids(java_result.getRows(), authenticated_system) + + has_security_exclusions=(snapshot_ids.size < java_result.size()) + filter_context.process_results(snapshot_ids, has_security_exclusions) + filter_context + end + + private + + def self.extract_snapshot_ids(sql_rows, authenticated_system) + sids=[] + project_ids=sql_rows.map { |r| r[2] ? to_integer(r[2]) : to_integer(r[1]) }.compact.uniq + authorized_pids=authenticated_system.select_authorized(:user, project_ids) + sql_rows.each do |row| + pid=(row[2] ? to_integer(row[2]) : to_integer(row[1])) + if authorized_pids.include?(pid) + sids<0 @period_index = options[:period_index] end @@ -62,8 +61,7 @@ class Sonar::Treemap o.details_at_depth = 1 end html = output.to_html(root) - html += "" - html + html + "" end def empty? @@ -110,7 +108,7 @@ class Sonar::Treemap :tooltip => tooltip(resource, size_measure, color_measure), :color => html_color(color_measure), :rid => resource.copy_resource_id || resource.id, - :browsable => @browsable && resource.display_dashboard?) + :browsable => resource.display_dashboard?) node.add_child(child) end end @@ -174,9 +172,11 @@ border: 1px solid #{node.color};' " end def draw_label(node) - label= "" - label += node_label(node) - label += "" - label + if node.browsable + "#{node_label(node)}" + else + "#{node_label(node)}" + end end end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_gradient.rhtml b/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_gradient.rhtml deleted file mode 100644 index 7fbfab8f0ca..00000000000 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_gradient.rhtml +++ /dev/null @@ -1,21 +0,0 @@ -<% -if color_metric && color_metric.worst_value && color_metric.best_value - min=0 - max=0 - if color_metric.worst_value - <%= min -%><%= color_metric.suffix -%> <%= max - -%><%= color_metric.suffix -%> -<% -end -%> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_set_default.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_set_default.html.erb index 9b86f8d421f..fd8e959e3cf 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_set_default.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_set_default.html.erb @@ -1,4 +1,13 @@ -<% form_tag( - {:controller => controller, :action => "update_default_treemap_metrics", :size_metric => @treemap.size_metric.key, :color_metric => @treemap.color_metric.key, :rid => rid}, - :id => 'form_set_default') do %> -<% end %> +
+ + + +
+ \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_settings.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_settings.html.erb index a60e814320c..5d3a0be68ea 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_settings.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/components/_treemap_settings.html.erb @@ -2,44 +2,31 @@ <%= render :partial => 'components/treemap_set_default', :locals => {:controller => 'components', :size_metric => @treemap.size_metric.key, :color_metric => @treemap.color_metric.key, :rid => @project.id } %> -<% -url_params = {:action => action} -if defined?(@snapshot) && @snapshot - url_params[:sid]= @snapshot.id -end -remote_form_for :treemap, :url => url_params, :html => { :id => 'tm_form', :method => 'get' }, - :loading => "$('tm_form').disable();$('tm_loading').show();", - :complete => "$('tm_form').enable();$('tm_loading').hide();" do |form | %> -<%= submit_tag(value = message('update_verb'), :id => 'submit_treemap', :style => 'display:none;') %> <% if configuring? && has_role?(:admin) %> - + <% end %>
<%= message('size') -%>
<%= select_tag 'size_metric', options_grouped_by_domain(Sonar::Treemap.size_metrics, @treemap.size_metric.key), - :id => 'select_size_metric', :class => 'small',:onchange => "$('submit_treemap').click();" %> + :id => 'select_size_metric', :class => 'small',:onchange => "return treemapById(1).changeSizeMetric(this.value);" %>
<%= message('color') -%>   - - <%= render :partial => 'components/treemap_gradient', :locals => {:color_metric => @treemap.color_metric} %> + + <%= render :partial => 'treemap/gradient', :locals => {:metric => @treemap.color_metric} %> - <%= image_tag('loading.gif', {:id => "tm_loading", :style => 'display: none;'}) %> + <%= image_tag('loading.gif', {:id => "tm-loading-1", :style => 'display: none;'}) %>
<%= select_tag 'color_metric', options_grouped_by_domain(Sonar::Treemap.color_metrics, @treemap.color_metric.key), - :id => 'select_color_metric', :class => 'small', :onchange => "$('submit_treemap').click();" %> + :id => 'select_color_metric', :class => 'small', :onchange => "return treemapById(1).changeColorMetric(this.value);" %>
<%= button_to message('set_as_default'), "#", :id => 'set_default_treemap', :onclick => "$('form_set_default').submit()" %><%= button_to message('set_as_default'), "#", :id => 'set_default_treemap', :onclick => "submitDefaultForm();return false;" %>
-<% end %> - - - diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/components/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/components/index.html.erb index be9fef2c6f3..7bba5487427 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/components/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/components/index.html.erb @@ -1,75 +1,83 @@ <% if is_admin? %> -
- +
<% end %> <% if has_role?(:admin) && configuring? %> - <%= render :partial => 'list_edit_mode_controls', :locals => {:configured_columns => @columns, :components_configuration => @components_configuration}%> + <%= render :partial => 'list_edit_mode_controls', :locals => {:configured_columns => @columns, :components_configuration => @components_configuration} %> <% end %> <% if @snapshots.empty? && @project.nil? %> -

<%= message('components.no_projects_have_been_analysed') -%>No projects have been analysed.

-

<%= message('components.explanation_launch_sonar_to_have_results') -%>

+

<%= message('components.no_projects_have_been_analysed') -%>No projects have been analysed.

+

<%= message('components.explanation_launch_sonar_to_have_results') -%>

<% else %> - - - + + <% end %> + +
- - <%= render :partial => 'list_table_header', :locals => {:configured_columns => @columns} if !configuring? || ( !is_admin? && configuring? ) %> - <%= render :partial => 'list_table_header_edit_mode', :locals => {:configured_columns => @columns} if configuring? && is_admin? %> - - <% if @snapshots.empty? %> - - <% else - @snapshots.each do |snapshot| %> - <% project = snapshot.project %> - - <% alert_status_measure=search_measure(@measures_by_snapshot[snapshot], Metric::ALERT_STATUS) - alert_status_x=(alert_status_measure ? alert_status_measure.data : '') - %> - - - - <% end %> - -
No components
<%= html_measure(alert_status_measure, nil, true, nil, nil, nil) %> - <% if logged_in? %><%= link_to_favourite(project) -%><% end %> - <%= link_to_resource(project, image_tag('zoom.png')) %> - - <%= qualifier_icon(snapshot) %> - <% if snapshot.project.display_dashboard? %> - "><%= snapshot.project.name -%> - <% else %> - <%= snapshot.project.name %> + + + - <% if @treemap %> - - + <% end %> + <% end %> + +
+ + <%= render :partial => 'list_table_header', :locals => {:configured_columns => @columns} if !configuring? || (!is_admin? && configuring?) %> + <%= render :partial => 'list_table_header_edit_mode', :locals => {:configured_columns => @columns} if configuring? && is_admin? %> + + <% if @snapshots.empty? %> + + + + <% else + @snapshots.each do |snapshot| %> + <% project = snapshot.project %> + + <% alert_status_measure=search_measure(@measures_by_snapshot[snapshot], Metric::ALERT_STATUS) + alert_status_x=(alert_status_measure ? alert_status_measure.data : '') + %> + + + + <% @columns.each do |column| %> + <%= get_column_content(column, snapshot, @measures_by_snapshot) -%> <% end %> - - <% @columns.each do |column| %> - <%= get_column_content(column, snapshot, @measures_by_snapshot) -%> - <% end %> - - <% end %> - <% end %> - -
No components
<%= html_measure(alert_status_measure, nil, true, nil, nil, nil) %> + <% if logged_in? %><%= link_to_favourite(project) -%> + <% end %> + <%= link_to_resource(project, image_tag('zoom.png')) %> + + <%= qualifier_icon(snapshot) %> + <% if snapshot.project.display_dashboard? %> + "><%= snapshot.project.name -%> + <% else %> + <%= snapshot.project.name %> + <% end %> +
- -

 

-
  -
- <%= @treemap.generate_html() %> -
- <%= render :partial => 'components/treemap_settings', :locals => {:action => 'treemap'} %> +
+ +

 

+ <% if @treemap %> +
  + + +
+ <%= @treemap.generate_html() %> +
+ <%= render :partial => 'components/treemap_settings', :locals => {:action => 'treemap'} %> +
<% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb index 49a370f57b1..54e1bcc5b91 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget.html.erb @@ -1,4 +1,4 @@ -
+
<% if widget.configured %> <% begin diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_customize_treemap.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_customize_treemap.html.erb index 0b40e245251..2afa78f3d41 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_customize_treemap.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_customize_treemap.html.erb @@ -16,7 +16,6 @@ <%= select_tag 'columns[]', options_grouped_by_domain(Sonar::Treemap.color_metrics, color_metric.key), :id => 'color_metric' %> - <%= render :partial => 'components/treemap_gradient', :locals => {:color_metric => color_metric} %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_tabs.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_tabs.html.erb index d6570269035..09fd2a9013d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_tabs.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_tabs.html.erb @@ -2,7 +2,7 @@
  • <%= message('filters.add_filter') -%>
  • - <% if @filter && @filter.id && editable_filter?(@filter) %> + <% if @filter && @filter.id && @filter.authorized_to_edit?(self) %>
  • <%= message('filters.edit_filter') -%>
  • <% end %>
  • <%= link_to message('filters.manage_filters'), {:action => 'manage'} -%>
  • diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_treemap.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_treemap.html.erb index 93d0455651d..e4e23281cde 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_treemap.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/_treemap.html.erb @@ -1,26 +1,16 @@ <% if @filter.period? %> -<%= message('filters.treemap_not_supported_for_period_selection') -%> + <%= message('filters.treemap_not_supported_for_period_selection') -%> <% else %> <% - metrics=treemap_metrics(@filter) - size_metric=metrics[0] - color_metric=metrics[1] + metrics=treemap_metrics(@filter) %> -
    - <%= image_tag 'loading.gif' %> -
    -
    - + <%= render :partial => 'treemap/treemap_container', :locals => { + :treemap_id => @filter.id, + :size_metric => metrics[0], + :color_metric => metrics[1], + :height_in_percents => 50.0, + :context_type => 'filter', + :context_id => @filter.id + } -%> <% end %> \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/manage.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/manage.html.erb index 2c8b206dff1..8d7486cb191 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/manage.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/manage.html.erb @@ -38,7 +38,7 @@ <% end %> - <% if editable_filter?(active.filter) %> + <% if active.filter.authorized_to_edit?(self) %> <%= link_to message('edit'), {:action => 'edit', :id => active.filter_id}, :id => "edit-#{u active.name}" %> | <%= link_to message('delete'), {:action => 'deactivate', :id => active.filter_id}, :method => :post, :confirm => message('filters.do_you_want_to_delete'), :id => "delete-#{u active.name}" %> <% else %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/treemap.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/treemap.html.erb index eabcc80f151..9f0e3858940 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/filters/treemap.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/filters/treemap.html.erb @@ -20,6 +20,10 @@
    <% end %> -
    - <%= render :partial => 'treemap/treemap' -%> -
    \ No newline at end of file +
    + <%= render :partial => 'treemap/treemap', :locals => {:treemap => @treemap} -%> +
    +
    +
    <%= message('treemap.click_help') -%>
    +
    /
    +
    diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/treemap/_treemap_container.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/treemap/_treemap_container.html.erb index 008c38df8fb..e313e7e4a61 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/treemap/_treemap_container.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/treemap/_treemap_container.html.erb @@ -5,20 +5,20 @@ <%= message('size') -%>
    <%= select_tag "size", options_grouped_by_domain(Sonar::Treemap.size_metrics, (size_metric ? size_metric.key : nil), :include_empty => true), - :id => "tm-size-#{treemap_id}", :class => 'small spacer-right', :onchange => "refreshTm(#{treemap_id}, null)" %> + :id => "tm-size-#{treemap_id}", :class => 'small spacer-right', :onchange => "return treemapById(#{treemap_id}).changeSizeMetric(this.value)" %> <%= message('color') -%>
    <%= select_tag 'color', options_grouped_by_domain(Sonar::Treemap.color_metrics, (color_metric ? color_metric.key : nil), :include_empty => true), - :id => "tm-color-#{treemap_id}", :class => 'small', :onchange => "refreshTm(#{treemap_id}, null)" %> + :id => "tm-color-#{treemap_id}", :class => 'small', :onchange => "return treemapById(#{treemap_id}).changeColorMetric(this.value)" %> <%= image_tag 'loading.gif', :id => "tm-loading-#{treemap_id}", :style => 'vertical-align: top;display: none' -%> - +
@@ -28,9 +28,6 @@
diff --git a/sonar-server/src/main/webapp/WEB-INF/lib/authenticated_system.rb b/sonar-server/src/main/webapp/WEB-INF/lib/authenticated_system.rb index 10015abc172..d05adeae8d1 100644 --- a/sonar-server/src/main/webapp/WEB-INF/lib/authenticated_system.rb +++ b/sonar-server/src/main/webapp/WEB-INF/lib/authenticated_system.rb @@ -1,5 +1,4 @@ module AuthenticatedSystem - protected # Returns true or false if the user is logged in. # Preloads @current_user with the user model if they're logged in. def logged_in? diff --git a/sonar-server/src/main/webapp/WEB-INF/lib/need_authorization.rb b/sonar-server/src/main/webapp/WEB-INF/lib/need_authorization.rb index 81c967d2581..cb6b9e77fce 100644 --- a/sonar-server/src/main/webapp/WEB-INF/lib/need_authorization.rb +++ b/sonar-server/src/main/webapp/WEB-INF/lib/need_authorization.rb @@ -150,7 +150,6 @@ module NeedAuthorization # These methods depend on the restful_authentication plugin. # module Helper - protected def has_role?(role, objects=nil) (current_user || Anonymous.user).has_role?(role, objects) diff --git a/sonar-server/src/main/webapp/WEB-INF/lib/treemap/node.rb b/sonar-server/src/main/webapp/WEB-INF/lib/treemap/node.rb index dbcc6e4e0fa..223288212d5 100644 --- a/sonar-server/src/main/webapp/WEB-INF/lib/treemap/node.rb +++ b/sonar-server/src/main/webapp/WEB-INF/lib/treemap/node.rb @@ -66,7 +66,6 @@ module Treemap @children = [] @rid = opts[:rid] @browsable = opts[:browsable] - if(@id.nil?) make_id end diff --git a/sonar-server/src/main/webapp/javascripts/application.js b/sonar-server/src/main/webapp/javascripts/application.js index 5cd9cea884b..9dcd5ecb55a 100644 --- a/sonar-server/src/main/webapp/javascripts/application.js +++ b/sonar-server/src/main/webapp/javascripts/application.js @@ -140,23 +140,90 @@ var SelectBox = { } }; -var treemapContexts = {}; -function enableTreemap(treemap_id, components_size) { - for (var i = 1; i <= components_size; i++) { - var elt = $('tm-node-' + treemap_id + '-' + i); - elt.oncontextmenu = function () { - return false - }; - elt.observe('mouseup', function (event) { - context = treemapContexts[treemap_id]; - onTmClick(treemap_id, event, context); - }); - } +var treemaps = {}; + +function treemapById(id) { + return treemaps[id]; } +var TreemapContext = function (type, id, label) { + this.type = type; + this.id = id; + this.label = label; +}; + +/** + * HTML elements : + * tm-#{id} : required treemap container + * tm-bc-#{id} : required breadcrumb + * tm-loading-#{id} : optional loading icon + */ +var Treemap = function (id, sizeMetric, colorMetric, heightInPercents) { + this.id = id; + this.sizeMetric = sizeMetric; + this.colorMetric = colorMetric; + this.heightInPercents = heightInPercents; + this.breadcrumb = []; + treemaps[id] = this; +}; +Treemap.prototype.initResource = function (resourceId) { + this.breadcrumb.push(new TreemapContext('resource', resourceId, '')); + return this; +}; +Treemap.prototype.initFilter = function (filterId) { + this.breadcrumb.push(new TreemapContext('filter', filterId, '')); + return this; +}; +Treemap.prototype.init = function (type, id) { + this.breadcrumb.push(new TreemapContext(type, id, '')); + return this; +}; +Treemap.prototype.changeSizeMetric = function (metric) { + this.sizeMetric = metric; + this.load(); + return false; +}; +Treemap.prototype.changeColorMetric = function (metric) { + this.colorMetric = metric; + this.load(); + return false; +}; +Treemap.prototype.currentContext = function () { + if (this.breadcrumb.length > 0) { + return this.breadcrumb[this.breadcrumb.length - 1]; + } + return null; +}; +Treemap.prototype.width = function () { + return $('tm-' + this.id).getWidth() - 10; +}; +Treemap.prototype.load = function () { + var context = this.currentContext(); + var width = this.width(); + var height = Math.round(width * Math.abs(this.heightInPercents / 100.0)); + var output = ''; + this.breadcrumb.each(function (ctx) { + output += ctx.label + ' / '; + }); + if ($('tm-bc-' + this.id)!=null) { + $('tm-bc-' + this.id).innerHTML = output;} + var loadingIcon = $('tm-loading-' + this.id); + if (loadingIcon != null) { + loadingIcon.show(); + } -function onTmClick(treemap_id, event, context) { + new Ajax.Request( + baseUrl + '/treemap/index?id=' + this.id + '&width=' + width + '&height=' + height + '&size_metric=' + this.sizeMetric + '&color_metric=' + this.colorMetric + '&' + context.type + '=' + context.id, + { + asynchronous:true, + evalScripts:true + }); +}; +Treemap.prototype.htmlNode = function (nodeId) { + return $('tm-node-' + this.id + '-' + nodeId); +}; +Treemap.prototype.handleClick = function (event) { if (Event.isLeftClick(event)) { var link = event.findElement('a'); if (link != null) { @@ -169,44 +236,36 @@ function onTmClick(treemap_id, event, context) { var browsable = elt.hasAttribute('b'); if (browsable) { var label = elt.innerText || elt.textContent; - context.push([rid, label]); - refreshTm(treemap_id, rid); - } else { - openResource(rid); + var context = new TreemapContext('resource', rid, label); + this.breadcrumb.push(context); + this.load(); } } else if (Event.isRightClick(event)) { - if (context.length > 1) { - context.pop(); - var rid = context[context.length - 1][0]; - refreshTm(treemap_id, rid); + if (this.breadcrumb.length > 1) { + this.breadcrumb.pop(); + this.load(); } } -} - -function refreshTm(treemap_id, resource_id) { - var size = $F('tm-size-' + treemap_id); - var color = $F('tm-color-' + treemap_id); - var width = $('tm-' + treemap_id).getWidth() - 10; - var height = Math.round(width * Math.abs(parseFloat($F('tm-h-' + treemap_id)) / 100.0)); - var rid = (resource_id != null ? resource_id : context[context.length - 1][0]); - - context = treemapContexts[treemap_id]; - var output = ''; - context.each(function (elt) { - output += elt[1] + ' / '; - }); - $('tm-bc-' + treemap_id).innerHTML = output; - $('tm-loading-' + treemap_id).show(); - - new Ajax.Request( - baseUrl + '/treemap/index?id=' + treemap_id + '&width=' + width + '&height=' + height + '&size_metric=' + size + '&color_metric=' + color + '&resource=' + rid, - {asynchronous:true, evalScripts:true}); - - return false; -} +}; +Treemap.prototype.onLoaded = function (componentsSize) { + for (var i = 1; i <= componentsSize; i++) { + var elt = this.htmlNode(i); + elt.oncontextmenu = function () { + return false + }; + elt.observe('mouseup', this.handleClick.bind(this)); + } +}; -function openResource(key) { - document.location = baseUrl + '/dashboard/index/' + key; +function openResource(key, options) { + if (typeof popup == "undefined") { + popup = false; + } + if (popup) { + window.open(this.href, 'resource', 'height=800,width=900,scrollbars=1,resizable=1'); + } else { + document.location = baseUrl + '/dashboard/index/' + key; + } return false; } \ No newline at end of file -- 2.39.5