From: Simon Brandhof Date: Sat, 24 Nov 2012 16:17:54 +0000 (+0100) Subject: SONAR-3825 manage my filters X-Git-Tag: 3.4~280 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ea13e4fddb1a70c36d0c6b641825cb8289aba50b;p=sonarqube.git SONAR-3825 manage my filters --- diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java index be5b8966ad5..5c4d247a6d6 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java @@ -190,6 +190,6 @@ public class MeasureFilter { @Override public String toString() { - return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE); + return ReflectionToStringBuilder.toString(this); } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java index 80c3627e881..a161e7e9d45 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java @@ -54,11 +54,11 @@ public class MeasureFilterEngine implements ServerComponent { public List execute2(Map filterMap, @Nullable Long userId) throws ParseException { Logger logger = FILTER_LOG; MeasureFilterContext context = new MeasureFilterContext(); - context.setJson(Joiner.on("|").withKeyValueSeparator("=").join(filterMap)); context.setUserId(userId); try { long start = System.currentTimeMillis(); MeasureFilter filter = factory.create(filterMap); + context.setJson(filter.toString()); List rows = executor.execute(filter, context); log(context, rows, (System.currentTimeMillis() - start), logger); return rows; diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java index 8908226fc30..761f8d5c121 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java @@ -26,6 +26,7 @@ import org.sonar.api.utils.DateUtils; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -41,33 +42,33 @@ public class MeasureFilterFactory implements ServerComponent { public MeasureFilter create(Map properties) { MeasureFilter filter = new MeasureFilter(); - filter.setBaseResourceKey((String)properties.get("base")); - filter.setResourceScopes((List)properties.get("scopes")); - filter.setResourceQualifiers((List)(properties.get("qualifiers"))); - filter.setResourceLanguages((List)(properties.get("languages"))); - if (properties.containsKey("onBaseChildren")) { - filter.setOnBaseResourceChildren(Boolean.valueOf((String)properties.get("onBaseChildren"))); + filter.setBaseResourceKey((String) properties.get("base")); + filter.setResourceScopes(toList(properties.get("scopes"))); + filter.setResourceQualifiers(toList(properties.get("qualifiers"))); + filter.setResourceLanguages(toList(properties.get("languages"))); + if (properties.containsKey("onBaseComponents")) { + filter.setOnBaseResourceChildren(Boolean.valueOf((String) properties.get("onBaseComponents"))); } - filter.setResourceName((String)properties.get("nameRegexp")); - filter.setResourceKeyRegexp((String)properties.get("keyRegexp")); + filter.setResourceName((String) properties.get("nameRegexp")); + filter.setResourceKeyRegexp((String) properties.get("keyRegexp")); if (properties.containsKey("fromDate")) { - filter.setFromDate(toDate((String)properties.get("fromDate"))); + filter.setFromDate(toDate((String) properties.get("fromDate"))); } else if (properties.containsKey("afterDays")) { - filter.setFromDate(toDays((String)properties.get("afterDays"))); + filter.setFromDate(toDays((String) properties.get("afterDays"))); } if (properties.containsKey("toDate")) { - filter.setToDate(toDate((String)properties.get("toDate"))); + filter.setToDate(toDate((String) properties.get("toDate"))); } else if (properties.containsKey("beforeDays")) { - filter.setToDate(toDays((String)properties.get("beforeDays"))); + filter.setToDate(toDays((String) properties.get("beforeDays"))); } - if (properties.containsKey("favourites")) { - filter.setUserFavourites(Boolean.valueOf((String)properties.get("favourites"))); + if (properties.containsKey("onFavourites")) { + filter.setUserFavourites(Boolean.valueOf((String) properties.get("onFavourites"))); } if (properties.containsKey("asc")) { - filter.setSortAsc(Boolean.valueOf((String)properties.get("asc"))); + filter.setSortAsc(Boolean.valueOf((String) properties.get("asc"))); } - String s = (String)properties.get("sort"); + String s = (String) properties.get("sort"); if (s != null) { if (StringUtils.startsWith(s, "metric:")) { filter.setSortOnMetric(metricFinder.findByKey(StringUtils.substringAfter(s, "metric:"))); @@ -81,6 +82,18 @@ public class MeasureFilterFactory implements ServerComponent { return filter; } + private List toList(@Nullable Object obj) { + List result = null; + if (obj != null) { + if (obj instanceof String) { + result = Arrays.asList((String)obj); + } else { + result = (List)obj; + } + } + return result; + } + private static Date toDate(@Nullable String date) { if (date != null) { return DateUtils.parseDate(date); diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java index 3eae1162992..b29e11b549f 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java @@ -23,7 +23,7 @@ import org.sonar.api.measures.Metric; class MeasureFilterSort { public static enum Field { - KEY, NAME, VERSION, LANGUAGE, DATE, METRIC + KEY, NAME, VERSION, LANGUAGE, DATE, METRIC, SHORT_NAME, DESCRIPTION } private Field field = Field.NAME; @@ -85,6 +85,12 @@ class MeasureFilterSort { case NAME: column = "p.long_name"; break; + case SHORT_NAME: + column = "p.name"; + break; + case DESCRIPTION: + column = "p.description"; + break; case VERSION: column = "s.version"; break; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index e67a2a9ef63..98e7e586b04 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -517,7 +517,7 @@ CREATE TABLE "SEMAPHORES" ( CREATE TABLE "MEASURE_FILTERS" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "NAME" VARCHAR(100) NOT NULL, - "SHARED" BOOLEAN NOT NULL, + "SHARED" BOOLEAN NOT NULL DEFAULT FALSE, "USER_ID" INTEGER, "DESCRIPTION" VARCHAR(4000), "DATA" CLOB(2147483647), diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb index 25a0248df1c..ab33a006d21 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb @@ -22,16 +22,108 @@ class MeasuresController < ApplicationController SECTION=Navigation::SECTION_HOME def index + @filter = MeasureFilter.new render :action => 'search' end def search - options = { - :user => current_user, - :page => (params[:page] || 1) - } - @filter = MeasureFilter.new(params) - @filter.execute(self, options) + if params[:id] + @filter = MeasureFilter.find(params[:id]) + else + @filter = MeasureFilter.new + end + @filter.set_criteria_from_url_params(params) + @filter.execute(self, :user => current_user) end + # Load existing filter + def filter + require_parameters :id + + @filter = find_filter(params[:id]) + @filter.load_criteria_from_data + @filter.execute(self, :user => current_user) + render :action => 'search' + end + + def save_form + if params[:id].present? + @filter = find_filter(params[:id]) + else + @filter = MeasureFilter.new + end + @filter.set_criteria_from_url_params(params) + @filter.convert_criteria_to_data + render :partial => 'measures/save_form' + end + + def save + verify_post_request + access_denied unless logged_in? + + if params[:id].present? + @filter = find_filter(params[:id]) + else + @filter = MeasureFilter.new + @filter.user_id=current_user.id + end + @filter.name=params[:name] + @filter.description=params[:description] + @filter.shared=(params[:shared]=='true') + @filter.data=URI.unescape(params[:data]) + if @filter.save + render :text => @filter.id.to_s, :status => 200 + else + render :partial => 'measures/save_form', :status => 400 + end + end + + # GET /measures/manage + def manage + access_denied unless logged_in? + end + + def edit_form + require_parameters :id + @filter = find_filter(params[:id]) + render :partial => 'measures/edit_form' + end + + def edit + verify_post_request + access_denied unless logged_in? + require_parameters :id + + @filter = MeasureFilter.find(params[:id]) + access_denied unless owner?(@filter) + @filter.name=params[:name] + @filter.description=params[:description] + @filter.shared=(params[:shared]=='true') + if @filter.save + render :text => @filter.id.to_s, :status => 200 + else + render :partial => 'measures/edit_form', :status => 400 + end + end + + def delete + verify_post_request + access_denied unless logged_in? + require_parameters :id + + @filter = find_filter(params[:id]) + @filter.destroy + redirect_to :action => 'manage' + end + + private + def find_filter(id) + filter = MeasureFilter.find(id) + access_denied unless filter.shared || owner?(filter) + filter + end + + def owner?(filter) + current_user && filter.user_id==current_user.id + end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb index eb887f649f5..a4c30ef5227 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb @@ -285,14 +285,14 @@ module ApplicationHelper period_index=nil if period_index && period_index<=0 if resource.display_dashboard? if options[:dashboard] - link_to(name || resource.name, {:overwrite_params => {:controller => 'dashboard', :action => 'index', :id => resource.id, :period => period_index, - :tab => options[:tab], :rule => options[:rule]}}, :title => options[:title]) + link_to(name || resource.name, params.merge({:controller => 'dashboard', :action => 'index', :id => resource.id, :period => period_index, + :tab => options[:tab], :rule => options[:rule]}), :title => options[:title]) elsif options[:filter] - link_to(name || resource.name, {:overwrite_params => {:controller => 'dashboard', :action => 'index', :did => nil, :id => resource.id, :period => period_index, - :tab => options[:tab], :rule => options[:rule]}}, :title => options[:title]) + link_to(name || resource.name, params.merge({:controller => 'dashboard', :action => 'index', :did => nil, :id => resource.id, :period => period_index, + :tab => options[:tab], :rule => options[:rule]}), :title => options[:title]) else # stay on the same page (for example components) - link_to(name || resource.name, {:overwrite_params => {:id => resource.id, :period => period_index, :tab => options[:tab], :rule => options[:rule]}}, :title => options[:title]) + link_to(name || resource.name, params.merge({:id => resource.id, :period => period_index, :tab => options[:tab], :rule => options[:rule]}), :title => options[:title]) end else if options[:line] diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb index 6d4beff64a9..77f3593f123 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb @@ -19,11 +19,12 @@ # module MeasuresHelper - def list_column_title(filter, column, url_params) + def list_column_html(filter, column) + if column.sort? - html = link_to(h(column.display_name), url_params.merge({:controller => 'measures', :action => 'search', :asc => (!filter.sort_asc?).to_s, :sort => column.key})) + html = link_to(h(column.name), filter.url_params.merge({:controller => 'measures', :action => 'search', :asc => (!filter.sort_asc?).to_s, :sort => column.key})) else - html=h(column.display_name) + html=h(column.name) end #if column.variation # html=" #{html}" @@ -35,4 +36,29 @@ module MeasuresHelper "#{html}" end + def list_cell_html(column, result) + if column.metric + format_measure(result.measure(column.metric)) + elsif column.key=='name' + "#{qualifier_icon(result.snapshot)} #{link_to(result.snapshot.resource.name(true), {:controller => 'dashboard', :id => result.snapshot.resource_id}, :title => result.snapshot.resource.key)}" + elsif column.key=='short_name' + "#{qualifier_icon(result.snapshot)} #{link_to(result.snapshot.resource.name(false), {:controller => 'dashboard', :id => result.snapshot.resource_id}, :title => result.snapshot.resource.key)}" + elsif column.key=='date' + human_short_date(result.snapshot.created_at) + elsif column.key=='key' + "#{result.snapshot.resource.kee}" + elsif column.key=='description' + h result.snapshot.resource.description + elsif column.key=='version' + h result.snapshot.version + elsif column.key=='language' + h result.snapshot.resource.language + elsif column.key=='links' && result.links + html = '' + result.links.select { |link| link.href.start_with?('http') }.each do |link| + html += link_to(image_tag(link.icon, :alt => link.name), link.href, :class => 'nolink', :popup => true) unless link.custom? + end + html + end + end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb index fd71d2aa752..3e187330e35 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb @@ -17,9 +17,11 @@ # License along with Sonar; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 # -class MeasureFilter +require 'set' +class MeasureFilter < ActiveRecord::Base + # Row in the table of results - class Data + class Result attr_reader :snapshot, :measures_by_metric, :links def initialize(snapshot) @@ -52,7 +54,7 @@ class MeasureFilter @metric = Metric.by_key(metric_key) if metric_key end - def display_name + def name if @metric Api::Utils.message("metric.#{@metric.key}.name", :default => @metric.short_name) else @@ -61,7 +63,11 @@ class MeasureFilter end def align - (@key=='name') ? 'left' : 'right' + @align ||= + begin + # by default is table cells are left-aligned + (@key=='name' || @key=='short_name' || @key=='description') ? '' : 'right' + end end def sort? @@ -74,108 +80,168 @@ class MeasureFilter end class Display - def prepare_filter(filter, options) + attr_reader :metric_ids + + def initialize(filter) + end + + def load_links? + false end end class ListDisplay < Display - def key - 'list' + attr_reader :columns + + KEY = :list + + def initialize(filter) + filter.set_criteria_default_value('columns', ['name', 'short_name', 'description', 'links', 'date', 'language', 'version', 'alert', 'metric:ncloc', 'metric:violations']) + 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 prepare_filter(filter, options) - filter.set_criteria_default_value(:columns, 'name,date,metric:ncloc,metric:violations') - filter.set_criteria_default_value(:sort, 'name') - filter.set_criteria_default_value(:asc, true) - filter.set_criteria_default_value(:listPageSize, 30) - filter.pagination.per_page = [filter.criteria[:listPageSize].to_i, 200].min - filter.pagination.page = (options[:page] || 1).to_i + def load_links? + @columns.index { |column| column.links? } end end class TreemapDisplay < Display - def key - 'treemap' - end + KEY = :treemap end - DISPLAYS = [ListDisplay.new, TreemapDisplay.new] + DISPLAYS = [ListDisplay, TreemapDisplay] - def self.register_display(display) - DISPLAYS << display - end + 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 = ',' - # Simple hash {string key => fixnum or boolean or string} - attr_accessor :criteria +# Configuration available after call to execute() + attr_reader :pagination, :security_exclusions, :columns - # Configuration available after call to execute() - attr_reader :pagination, :security_exclusions, :columns, :display +# Results : sorted array of Result + attr_reader :results - # Results : sorted array of Data - attr_reader :data + belongs_to :user + validates_presence_of :name, :message => Api::Utils.message('measure_filter.missing_name') + 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 initialize(criteria={}) - @criteria = criteria - @pagination = Api::Pagination.new + def criteria + @criteria ||= {} end def sort_key - @criteria[:sort] + criteria['sort'] end def sort_asc? - @criteria[:asc]=='true' + criteria['asc']=='true' + end + +# API for plugins + def self.register_display(display_class) + DISPLAYS< self.id}) end - def init_filter(options) - @display.prepare_filter(self, options) - @columns = @criteria[:columns].split(',').map { |col_key| Column.new(col_key) } - end + private - def reset_results + def init_results @pagination = Api::Pagination.new @security_exclusions = nil - @data = nil + @results = nil self end @@ -185,48 +251,63 @@ class MeasureFilter snapshot_ids = rows.map { |row| row.getSnapshotId() if authorized_project_ids.include?(row.getResourceRootId()) }.compact @security_exclusions = (snapshot_ids.size ['project'], :conditions => ['id in (?)', snapshot_ids]) snapshots.each do |snapshot| - data = Data.new(snapshot) - data_by_snapshot_id[snapshot.id] = data + result = Result.new(snapshot) + results_by_snapshot_id[snapshot.id] = result end - # @data must be in the same order than the snapshot ids + # @results must be in the same order than the snapshot ids snapshot_ids.each do |sid| - @data << data_by_snapshot_id[sid] + @results << results_by_snapshot_id[sid] end - metric_ids = @columns.map { |column| column.metric }.compact.uniq.map { |metric| metric.id } - unless metric_ids.empty? + if display.metric_ids && !display.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, 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, display.metric_ids] ) measures.each do |measure| - data = data_by_snapshot_id[measure.snapshot_id] - data.add_measure measure + result = results_by_snapshot_id[measure.snapshot_id] + result.add_measure measure end end - if @columns.index { |column| column.links? } + if display.load_links? project_ids = [] - data_by_project_id = {} + results_by_project_id = {} snapshots.each do |snapshot| project_ids << snapshot.project_id - data_by_project_id[snapshot.project_id] = data_by_snapshot_id[snapshot.id] + results_by_project_id[snapshot.project_id] = results_by_snapshot_id[snapshot.id] end links = ProjectLink.find(:all, :conditions => {:project_id => project_ids}, :order => 'link_type') links.each do |link| - data_by_project_id[link.project_id].add_link(link) + results_by_project_id[link.project_id].add_link(link) end end end end + def validate + # validate uniqueness of name + if id + # update existing filter + count = MeasureFilter.count('id', :conditions => ['name=? and user_id=? and id<>?', name, user_id, id]) + else + # new filter + count = MeasureFilter.count('id', :conditions => ['name=? and user_id=?', name, user_id]) + end + errors.add_to_base('Name already exists') if count>0 + + if shared + count = MeasureFilter.count('id', :conditions => ['name=? and shared=? and user_id!=?', name, true, user_id]) + errors.add_to_base('Other users already shared filters with the same name') if count>0 + end + end end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb index 08618b572d3..ea4fdeb8030 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/user.rb @@ -30,6 +30,7 @@ class User < ActiveRecord::Base has_many :filters, :dependent => :destroy has_many :active_dashboards, :dependent => :destroy, :order => 'order_index' has_many :dashboards, :dependent => :destroy + has_many :measure_filters, :class_name => 'MeasureFilter', :dependent => :destroy include Authentication include Authentication::ByPassword diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb index a6b58412fc6..2d734b4b0d7 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/widget_property.rb @@ -72,7 +72,7 @@ class WidgetProperty < ActiveRecord::Base protected def validate errors.add_to_base("Unknown property: #{key}") unless java_definition - PropertyType::validate(key, type, java_definition.optional(), text_value, errors); + PropertyType::validate(key, type, java_definition.optional(), text_value, errors) end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb index 15648dbf9e8..ee642f3c0c9 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb @@ -1,43 +1,33 @@ <% display_favourites = logged_in? - colspan = @filter.columns.size + colspan = @filter.display.columns.size colspan += 1 if display_favourites %> - <% if display_favourites %><% end %> - <% @filter.columns.each do |column| %> - <%= list_column_title(@filter, column, url_params) -%> + <% if display_favourites %> + + <% end %> + <% @filter.display.columns.each do |column| %> + <%= list_column_html(@filter, column) -%> <% end %> - <% @filter.data.each do |data| %> + <% @filter.results.each do |result| %> - <% if display_favourites %><% end %> - <% @filter.columns.each do |column| %> - <% if column.metric %> - - <% elsif column.key=='name' %> - - <% elsif column.key=='date' %> - - <% elsif column.key=='key' %> - - <% else %> - - <% end %> + <% if display_favourites %> + + <% end %> + <% @filter.display.columns.each do |column| %> + <% end %> <% end %> - <% if @filter.data.empty? %> + <% if @filter.results.empty? %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_edit_form.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_edit_form.html.erb new file mode 100644 index 00000000000..1708e9552c5 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_edit_form.html.erb @@ -0,0 +1,33 @@ + + +
+
+

Edit Filter

+
+
+ <% @filter.errors.each do |attr, msg| %> +

<%= h msg -%>

+ <% end %> +
+ + +
+
+ + +
+
+ + /> +
+
+ +
+ + \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_save_form.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_save_form.html.erb new file mode 100644 index 00000000000..0390f579c0d --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_save_form.html.erb @@ -0,0 +1,37 @@ + + + +
+
+

Save Filter

+
+ +
+ <% @filter.errors.each do |attr, msg| %> +

<%= h msg -%>

+ <% end %> +
+ + +
+
+ + +
+
+ + /> +
+
+ +
+ + \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/manage.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/manage.html.erb new file mode 100644 index 00000000000..33f46b05630 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/manage.html.erb @@ -0,0 +1,47 @@ +
+
<%= link_to_favourite(data.snapshot.resource) -%> - <%= format_measure(data.measure(column.metric)) -%> - - <%= qualifier_icon(data.snapshot) %> <%= link_to_resource(data.snapshot.resource, h(data.snapshot.resource.name(true)), {:title => data.snapshot.resource.key}) -%> - - <%= human_short_date(data.snapshot.created_at) -%> - <%= data.snapshot.resource.kee -%><%= link_to_favourite(result.snapshot.resource) -%> + <%= list_cell_html(column, result) -%> +
<%= message 'no_data' -%>
+ + + + + + + + + <% if current_user.measure_filters.empty? %> + + + + <% else %> + <% current_user.measure_filters.each do |filter| %> + + + + + + <% end %> + <% end %> + +
<%= message('name') -%><%= message('sharing') -%><%= message('operations') -%>
<%= message('filters.no_filters') -%>
+ <%= link_to h(filter.name), :action => 'filter', :id => filter.id -%> + <% if filter.description %> +
<%= h filter.description -%>
+ <% end %> +
+ <% if filter.shared %> + Shared with all users + <% else %> + Private + <% end %> + + <%= message('edit') -%> +   + <%= link_to_action message('delete'), "#{ApplicationController.root_context}/measures/delete/#{filter.id}", + :class => 'link-action link-red', + :id => "delete_#{filter.name.parameterize}", + :confirm_button => message('delete'), + :confirm_title => 'measure_filters.delete_confirm_title', + :confirm_msg => 'measure_filters.are_you_sure_want_delete_filter_x', + :confirm_msg_params => [filter.name] -%> +
+ diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb index 09750fc6be3..4905680ff25 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb @@ -20,40 +20,37 @@ <% end %> -<% url_params = params.reject { |k, v| v.nil? || v=='' || k.starts_with?('_') }.merge({:controller => 'measures', :action => 'search'}) %> -
-
+ + <% if @filter.id %> + + <% end %> @@ -69,7 +66,7 @@ @@ -97,31 +94,54 @@
Base:
- +
- Direct children:
- <%= check_box_tag 'onBaseChildren', 'true', params['onBaseChildren']=='true' -%> + On components:
+ <%= check_box_tag 'onBaseComponents', 'true', @filter.criteria['onBaseComponents'] -%>
Qualifiers:
- <%= select_tag 'qualifiers[]', options_for_select([['Any', ''], ['Project', 'TRK'], ['Sub-project', 'BRC'], ['Directory/Package', 'DIR']], params['qualifiers']||''), :size => 5, :multiple => true -%> + <%= select_tag 'qualifiers[]', options_for_select([['Any', ''], ['Project', 'TRK'], ['Sub-project', 'BRC'], ['Directory/Package', 'PAC'], ['File', 'CLA']], @filter.criteria['qualifiers']||''), :size => 5, :multiple => true -%>
Language:
- + <% languages = [['Any', '']].concat(Api::Utils.languages.map { |lang| [lang.name, lang.key] }) %> + <%= select_tag 'languages[]', options_for_select(languages, @filter.criteria['languages']||''), :size => 5, :multiple => true -%>
Favourites only:
- <%= check_box_tag 'favourites', 'true', params['favourites']=='true' %> + <%= check_box_tag 'onFavourites', 'true', params['onFavourites']=='true' %>
- + Reset
+ +
+ + <% if logged_in? %> + + <% end %>
- <% if @filter %> + + <% if @filter.results %>
+ <% if @filter.name %> +

<%= h @filter.name -%>

+ <% if @filter.description %> +

<%= h @filter.description -%>

+ <% end %> + <% end %> + Display as: - <% MeasureFilter::DISPLAYS.each do |display| %> - <%= link_to_if display.key!=@filter.display.key, display.key, url_params.merge(:display => display.key) -%> + <% 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 => nil) -%> <% end %> - <%= render :partial => "measures/display_#{@filter.display.key}", :locals => {:url_params => url_params} -%> -

- <% permalink = url_for(url_params) %> - Permalink: -

+ <% if logged_in? && (@filter.user_id==nil || @filter.user_id==current_user.id) %> + <%= message('save') -%> + <% end %> + + <%= render :partial => "measures/display_#{@filter.display.class::KEY}" -%> +
+ <% if @filter.security_exclusions %>

<%= message('results_not_display_due_to_security') -%>

<% end %>
<% end %> +

\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/356_create_measure_filters.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/356_create_measure_filters.rb index 4a50f1103aa..30f5210435a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/356_create_measure_filters.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/356_create_measure_filters.rb @@ -26,7 +26,7 @@ class CreateMeasureFilters < ActiveRecord::Migration create_table 'measure_filters' do |t| t.column 'name', :string, :null => false, :limit => 100 t.column 'user_id', :integer, :null => true - t.column 'shared', :boolean, :null => true + t.column 'shared', :boolean, :default => false, :null => false t.column 'description', :string, :null => true, :limit => 4000 t.column 'data', :text, :null => true t.timestamps diff --git a/sonar-server/src/main/webapp/javascripts/application.js b/sonar-server/src/main/webapp/javascripts/application.js index adb76a2821a..579609ebdcd 100644 --- a/sonar-server/src/main/webapp/javascripts/application.js +++ b/sonar-server/src/main/webapp/javascripts/application.js @@ -287,10 +287,10 @@ Treemap.prototype.onLoaded = function (componentsSize) { }); $dialog.dialog("open"); }).error(function () { - alert("Server error. Please contact your administrator."); - }).complete(function() { - $dialog.removeClass('ui-widget-overlay'); - }); + alert("Server error. Please contact your administrator."); + }).complete(function () { + $dialog.removeClass('ui-widget-overlay'); + }); $link.click(function () { $dialog.dialog('open'); @@ -301,22 +301,22 @@ Treemap.prototype.onLoaded = function (componentsSize) { }); }); }, - modalForm:function () { + modalForm:function (ajax_options) { return this.each(function () { var obj = $j(this); obj.submit(function (event) { $j('input[type=submit]', this).attr('disabled', 'disabled'); - $j.ajax({ + $j.ajax($j.extend({ type:'POST', url:obj.attr('action'), data:obj.serialize(), success:function (data) { - location.reload(); + window.location.reload(); }, error:function (xhr, textStatus, errorThrown) { $j("#modal").html(xhr.responseText); } - }); + }, ajax_options)); return false; }); });