aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2012-11-21 13:37:45 +0100
committerSimon Brandhof <simon.brandhof@gmail.com>2012-11-21 13:49:22 +0100
commit2f2699e356f1a653148d552c56a730c2af0025fe (patch)
treeee9f98c7a4fb4c6da204fe91718ce526fa9e91a6
parentaaa127f45f9e6d499ee9b32b400fdfb046e0a0d7 (diff)
downloadsonarqube-2f2699e356f1a653148d552c56a730c2af0025fe.tar.gz
sonarqube-2f2699e356f1a653148d552c56a730c2af0025fe.zip
SONAR-3825 new page for measure filters
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java97
-rw-r--r--sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java18
-rw-r--r--sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java11
-rw-r--r--sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java6
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java14
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb12
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb207
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb41
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_treemap.html.erb0
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb72
11 files changed, 447 insertions, 33 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
index e3e8e475c6b..c58cd5f6dc5 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
@@ -403,7 +403,7 @@ filters.display_form.as=Display as
filters.display_form.table=Table
filters.display_form.treemap=Treemap
filters.build_date=Build date
-filters.col.date=Build date
+filters.col.date=Date
filters.col.language=Language
filters.col.name=Name
filters.col.links=Links
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 9b9c124aa13..42a4f3e0f0a 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
@@ -20,20 +20,26 @@
package org.sonar.core.measure;
import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
import org.sonar.api.measures.Metric;
+import org.sonar.api.utils.DateUtils;
import javax.annotation.Nullable;
-import java.util.Collections;
+
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
+import java.util.Map;
public class MeasureFilter {
+ private static final String[] EMPTY = {};
+
// conditions on resources
private String baseResourceKey;
private boolean onBaseResourceChildren = false; // only if getBaseResourceKey is set
- private List<String> resourceScopes = Lists.newArrayList();
- private List<String> resourceQualifiers = Lists.newArrayList();
- private List<String> resourceLanguages = Lists.newArrayList();
+ private String[] resourceScopes = EMPTY;
+ private String[] resourceQualifiers = EMPTY;
+ private String[] resourceLanguages = EMPTY;
private String resourceKeyRegexp;
private String resourceName;
private Date fromDate = null, toDate = null;
@@ -63,28 +69,33 @@ public class MeasureFilter {
return onBaseResourceChildren;
}
- public MeasureFilter setResourceScopes(@Nullable List<String> l) {
- this.resourceScopes = (l != null ? l : Collections.<String>emptyList());
+ public MeasureFilter setResourceScopes(String... l) {
+ this.resourceScopes = (l != null ? l : EMPTY);
return this;
}
- public MeasureFilter setResourceQualifiers(List<String> l) {
- this.resourceQualifiers = (l != null ? l : Collections.<String>emptyList());
+ public MeasureFilter setResourceQualifiers(String... l) {
+ this.resourceQualifiers = l;
return this;
}
- public MeasureFilter setResourceQualifiers(String... l) {
- this.resourceQualifiers = Lists.newArrayList(l);
+ public MeasureFilter setResourceLanguages(String... l) {
+ this.resourceLanguages = (l != null ? l : EMPTY);
return this;
}
- public MeasureFilter setResourceLanguages(List<String> l) {
- this.resourceLanguages = (l != null ? l : Collections.<String>emptyList());
+ public MeasureFilter setResourceScopes(@Nullable List<String> l) {
+ this.resourceScopes = (l != null ? l.toArray(new String[l.size()]) : EMPTY);
return this;
}
- public MeasureFilter setResourceLanguages(String... l) {
- this.resourceLanguages = Lists.newArrayList(l);
+ public MeasureFilter setResourceQualifiers(@Nullable List<String> l) {
+ this.resourceQualifiers = (l != null ? l.toArray(new String[l.size()]) : EMPTY);
+ return this;
+ }
+
+ public MeasureFilter setResourceLanguages(@Nullable List<String> l) {
+ this.resourceLanguages = (l != null ? l.toArray(new String[l.size()]) : EMPTY);
return this;
}
@@ -159,15 +170,15 @@ public class MeasureFilter {
return toDate;
}
- public List<String> getResourceScopes() {
+ public String[] getResourceScopes() {
return resourceScopes;
}
- public List<String> getResourceQualifiers() {
+ public String[] getResourceQualifiers() {
return resourceQualifiers;
}
- public List<String> getResourceLanguages() {
+ public String[] getResourceLanguages() {
return resourceLanguages;
}
@@ -179,4 +190,56 @@ public class MeasureFilter {
return sort;
}
+ public static MeasureFilter create(Map<String, String> properties) {
+ MeasureFilter filter = new MeasureFilter();
+ filter.setBaseResourceKey(properties.get("base"));
+ filter.setResourceScopes(toArray(properties.get("scopes")));
+ filter.setResourceQualifiers(toArray(properties.get("qualifiers")));
+ filter.setResourceLanguages(toArray(properties.get("languages")));
+ if (properties.containsKey("onBaseChildren")) {
+ filter.setOnBaseResourceChildren(Boolean.valueOf(properties.get("onBaseChildren")));
+ }
+ filter.setResourceName(properties.get("nameRegexp"));
+ filter.setResourceKeyRegexp(properties.get("keyRegexp"));
+ if (properties.containsKey("fromDate")) {
+ filter.setFromDate(toDate(properties.get("fromDate")));
+ } else if (properties.containsKey("afterDays")) {
+ filter.setFromDate(toDays(properties.get("afterDays")));
+ }
+ if (properties.containsKey("toDate")) {
+ filter.setToDate(toDate(properties.get("toDate")));
+ } else if (properties.containsKey("beforeDays")) {
+ filter.setToDate(toDays(properties.get("beforeDays")));
+ }
+
+ if (properties.containsKey("favourites")) {
+ filter.setUserFavourites(Boolean.valueOf(properties.get("favourites")));
+ }
+ return filter;
+ }
+
+ private static String[] toArray(@Nullable String s) {
+ if (s == null) {
+ return EMPTY;
+ }
+ return StringUtils.split(s, ",");
+ }
+
+ private static Date toDate(@Nullable String date) {
+ if (date != null) {
+ return DateUtils.parseDate(date);
+ }
+ return null;
+ }
+
+ private static Date toDays(@Nullable String s) {
+ if (s != null) {
+ int days = Integer.valueOf(s);
+ Date date = org.apache.commons.lang.time.DateUtils.truncate(new Date(), Calendar.DATE);
+ date = org.apache.commons.lang.time.DateUtils.addDays(date, -days);
+ return date;
+ }
+ return null;
+ }
+
}
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 253c2875d6f..fcebe106ea9 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
@@ -27,7 +27,9 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.ServerComponent;
import javax.annotation.Nullable;
+
import java.util.List;
+import java.util.Map;
public class MeasureFilterEngine implements ServerComponent {
private static final Logger FILTER_LOG = LoggerFactory.getLogger("org.sonar.MEASURE_FILTER");
@@ -44,6 +46,22 @@ public class MeasureFilterEngine implements ServerComponent {
return execute(filterJson, userId, FILTER_LOG);
}
+ public List<MeasureFilterRow> execute2(Map<String, String> filterMap, @Nullable Long userId) throws ParseException {
+ Logger logger = FILTER_LOG;
+ MeasureFilterContext context = new MeasureFilterContext();
+ context.setJson(filterMap.toString());
+ context.setUserId(userId);
+ try {
+ long start = System.currentTimeMillis();
+ MeasureFilter filter = MeasureFilter.create(filterMap);
+ List<MeasureFilterRow> rows = executor.execute(filter, context);
+ log(context, rows, (System.currentTimeMillis() - start), logger);
+ return rows;
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to execute filter: " + context, e);
+ }
+ }
+
@VisibleForTesting
List<MeasureFilterRow> execute(String filterJson, @Nullable Long userId, Logger logger) {
MeasureFilterContext context = new MeasureFilterContext();
diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java
index c95717e2c2a..bb41a9c77ed 100644
--- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java
+++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java
@@ -37,7 +37,6 @@ import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.util.Collection;
import java.util.List;
class MeasureFilterSql {
@@ -150,15 +149,15 @@ class MeasureFilterSql {
if (context.getBaseSnapshot() == null) {
sql.append(" AND p.copy_resource_id IS NULL ");
}
- if (!filter.getResourceQualifiers().isEmpty()) {
+ if (filter.getResourceQualifiers().length > 0) {
sql.append(" AND s.qualifier IN ");
appendInStatement(filter.getResourceQualifiers(), sql);
}
- if (!filter.getResourceScopes().isEmpty()) {
+ if (filter.getResourceScopes().length > 0) {
sql.append(" AND s.scope IN ");
appendInStatement(filter.getResourceScopes(), sql);
}
- if (!filter.getResourceLanguages().isEmpty()) {
+ if (filter.getResourceLanguages().length > 0) {
sql.append(" AND p.language IN ");
appendInStatement(filter.getResourceLanguages(), sql);
}
@@ -217,7 +216,7 @@ class MeasureFilterSql {
sql.append(" AND s.project_id IN (SELECT rindex.resource_id FROM resource_index rindex WHERE rindex.kee like '");
sql.append(StringEscapeUtils.escapeSql(StringUtils.lowerCase(filter.getResourceName())));
sql.append("%'");
- if (!filter.getResourceQualifiers().isEmpty()) {
+ if (filter.getResourceQualifiers().length > 0) {
sql.append(" AND rindex.qualifier IN ");
appendInStatement(filter.getResourceQualifiers(), sql);
}
@@ -259,7 +258,7 @@ class MeasureFilterSql {
}
- private static void appendInStatement(Collection<String> values, StringBuilder to) {
+ private static void appendInStatement(String[] values, StringBuilder to) {
to.append(" ('");
to.append(StringUtils.join(values, "','"));
to.append("') ");
diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java
index 3dedb2d09a1..ce7ed550464 100644
--- a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java
@@ -62,9 +62,9 @@ public class MeasureFilterDecoderTest {
assertThat(filter.getBaseResourceKey()).isEqualTo("org.struts");
assertThat(filter.isOnBaseResourceChildren()).isTrue();
- assertThat(filter.getResourceScopes()).containsExactly("PRJ");
- assertThat(filter.getResourceQualifiers()).containsExactly("TRK", "CLA");
- assertThat(filter.getResourceLanguages()).containsExactly("java", "php");
+ assertThat(filter.getResourceScopes()).containsOnly("PRJ");
+ assertThat(filter.getResourceQualifiers()).containsOnly("TRK", "CLA");
+ assertThat(filter.getResourceLanguages()).containsOnly("java", "php");
assertThat(filter.getResourceName()).isEqualTo("Struts");
assertThat(filter.getResourceKeyRegexp()).isEqualTo("*foo*");
assertThat(filter.getFromDate().getYear()).isEqualTo(2012 - 1900);
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
index 8fb4fc40ae0..cbedd251521 100644
--- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
+++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
@@ -102,6 +102,10 @@ public final class JRubyFacade {
return get(MeasureFilterEngine.class).execute(json, userId);
}
+ public List<MeasureFilterRow> executeMeasureFilter2(Map<String,String> map, @Nullable Long userId) throws ParseException {
+ return get(MeasureFilterEngine.class).execute2(map, userId);
+ }
+
public Collection<ResourceType> getResourceTypesForFilter() {
return get(ResourceTypes.class).getAll(ResourceTypes.AVAILABLE_FOR_FILTERS);
}
@@ -314,7 +318,7 @@ public final class JRubyFacade {
public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) {
getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId],
- RulePriority.values()[newSeverityId], userName);
+ RulePriority.values()[newSeverityId], userName);
}
public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) {
@@ -510,10 +514,10 @@ public final class JRubyFacade {
// notifier is null when creating the administrator in the migration script 011.
if (notifier != null) {
notifier.onNewUser(NewUserHandler.Context.builder()
- .setLogin(fields.get("login"))
- .setName(fields.get("name"))
- .setEmail(fields.get("email"))
- .build());
+ .setLogin(fields.get("login"))
+ .setName(fields.get("name"))
+ .setEmail(fields.get("email"))
+ .build());
}
}
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 0b6662b207f..25a0248df1c 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
@@ -19,9 +19,19 @@
#
class MeasuresController < ApplicationController
+ SECTION=Navigation::SECTION_HOME
+
def index
-
+ render :action => 'search'
end
+ def search
+ options = {
+ :user => current_user,
+ :page => (params[:page] || 1)
+ }
+ @filter = MeasureFilter.new(params)
+ @filter.execute(self, options)
+ 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
new file mode 100644
index 00000000000..225f233c164
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb
@@ -0,0 +1,207 @@
+#
+# 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 {library}; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+#
+class MeasureFilter
+ # Row in the table of results
+ class Data
+ attr_reader :snapshot, :measures_by_metric, :links
+
+ def initialize(snapshot)
+ @snapshot = snapshot
+ @measures_by_metric = {}
+ @links = nil
+ end
+
+ def add_measure(measure)
+ @measures_by_metric[measure.metric] = measure
+ end
+
+ def add_link(link)
+ @links ||= []
+ @links << link
+ end
+
+ def measure(metric)
+ @measures_by_metric[metric]
+ 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?
+ @key == 'name'
+ end
+
+ def links?
+ @key == 'links'
+ end
+ end
+
+ class Display
+ def init_filter(filter)
+
+ end
+ end
+
+ class ListDisplay < Display
+ def key
+ 'list'
+ end
+
+ def init_filter(filter)
+
+ end
+ end
+
+ class TreemapDisplay < Display
+ def key
+ 'treemap'
+ end
+ end
+
+ DEFAULT_OPTIONS = {:page => 1, :page_size => 50}
+ DEFAULT_COLUMNS = [Column.new('name'), Column.new('date'), Column.new('metric:ncloc'), Column.new('metric:violations')]
+ DISPLAYS = [ListDisplay.new, TreemapDisplay.new]
+
+ # Simple hash {string key => fixnum or boolean or string}
+ attr_accessor :criteria
+
+ # Configuration available after call to execute()
+ attr_reader :pagination, :security_exclusions, :columns, :display
+
+ # Results : sorted array of Data
+ attr_reader :data
+
+ def initialize(criteria={})
+ @criteria = criteria
+ end
+
+ # ==== Options
+ # 'page' : page id starting with 1. Used on table display.
+ # 'page_size' : number of results per page.
+ # 'user' : the authenticated user
+ # 'period' : index of the period between 1 and 5
+ #
+ def execute(controller, options={})
+ return reset_results if @criteria.empty?
+ init_columns
+ init_display(options)
+
+ opts = DEFAULT_OPTIONS.merge(options)
+ user = opts[:user]
+
+ rows=Api::Utils.java_facade.executeMeasureFilter2(@criteria, (user ? user.id : nil))
+ snapshot_ids = filter_authorized_snapshot_ids(rows, controller)
+ snapshot_ids = paginate_snapshot_ids(snapshot_ids, opts)
+ init_data(snapshot_ids)
+
+ self
+ end
+
+ private
+
+ def init_columns
+ fields = @criteria['columns']
+ if fields.present?
+ @columns = fields.split(',').map { |field| Column.new(field) }
+ else
+ @columns = DEFAULT_COLUMNS.clone
+ end
+ end
+
+ def init_display(options)
+ key = @criteria['display']
+ if key.present?
+ @display = DISPLAYS.find { |display| display.key==key }
+ end
+ @display ||= DISPLAYS.first
+ end
+
+ def reset_results
+ @pagination = nil
+ @security_exclusions = nil
+ @data = nil
+ self
+ 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)
+ snapshot_ids = rows.map { |row| row.getSnapshotId() if authorized_project_ids.include?(row.getResourceRootId()) }.compact
+ @security_exclusions = (snapshot_ids.size<rows.size)
+ snapshot_ids
+ end
+
+ def paginate_snapshot_ids(snapshot_ids, options)
+ @pagination = Api::Pagination.new({
+ :per_page => options[:page_size],
+ :page => options[:page],
+ :count => snapshot_ids.size})
+ from = (@pagination.page - 1) * @pagination.per_page
+ to = (@pagination.page * @pagination.per_page) - 1
+ to = snapshot_ids.size - 1 if to >= snapshot_ids.size
+ snapshot_ids[from..to]
+ end
+
+ def init_data(snapshot_ids)
+ @data = []
+ if !snapshot_ids.empty?
+ data_by_snapshot_id = {}
+ snapshots = Snapshot.find(:all, :include => ['project'], :conditions => ['id in (?)', snapshot_ids])
+ snapshots.each do |snapshot|
+ data = Data.new(snapshot)
+ data_by_snapshot_id[snapshot.id] = data
+ @data << data
+ end
+
+ metric_ids = @columns.map { |column| column.metric }.compact.uniq.map { |metric| metric.id }
+ 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, metric_ids]
+ )
+ measures.each do |measure|
+ data = data_by_snapshot_id[measure.snapshot_id]
+ data.add_measure measure
+ end
+ end
+
+ if @columns.index { |column| column.links? }
+ project_ids = []
+ data_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]
+ 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)
+ end
+ end
+ end
+ end
+
+end \ No newline at end of file
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
new file mode 100644
index 00000000000..fd24b72fa60
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb
@@ -0,0 +1,41 @@
+<table class="data">
+ <thead>
+ <tr>
+ <% @filter.columns.each do |column| %>
+ <% if column.metric %>
+ <th class="right"><%= Api::Utils.message("metric.#{column.metric.key}.name", :default => column.metric.short_name) -%></th>
+ <% elsif column.key=='name' %>
+ <th class="left"><%= Api::Utils.message("filters.col.name") -%></th>
+ <% else %>
+ <th class="right"><%= Api::Utils.message("filters.col.#{column.key}", :default => column.key) -%></th>
+ <% end %>
+ <% end %>
+ </tr>
+ </thead>
+ <tbody>
+ <% @filter.data.each do |data| %>
+ <tr class="<%= cycle 'even', 'odd' -%>">
+ <% @filter.columns.each do |column| %>
+ <% if column.metric %>
+ <td class="right">
+ <%= format_measure(data.measure(column.metric)) -%>
+ </td>
+ <% elsif column.key=='name' %>
+ <td class="left">
+ <%= qualifier_icon(data.snapshot) %> <%= link_to_resource(data.snapshot.resource, h(data.snapshot.resource.name(true)), {:title => data.snapshot.resource.key}) -%>
+ </td>
+ <% elsif column.key=='date' %>
+ <td class="right">
+ <%= human_short_date(data.snapshot.created_at) -%>
+ </td>
+ <% elsif column.key=='key' %>
+ <td class="left"><span class="small"><%= data.snapshot.resource.kee -%></span></td>
+ <% else %>
+ <td></td>
+ <% end %>
+ <% end %>
+ </tr>
+ <% end %>
+ </tbody>
+ <%= render :partial => 'utils/tfoot_pagination', :locals => {:pagination => @filter.pagination, :colspan => @filter.columns.size} %>
+</table> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_treemap.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_treemap.html.erb
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_treemap.html.erb
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
new file mode 100644
index 00000000000..fd1fc74d54c
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
@@ -0,0 +1,72 @@
+<% content_for :style do %>
+ <style type="text/css">
+ #filter-form {
+ float: left;
+ width: 240px;
+ }
+
+ #filter-result {
+ padding-left: 250px;
+ }
+ </style>
+<% end %>
+
+<% url_options = params.reject { |k, v| v.empty? || k=='search' }.merge({:controller => 'measures', :action => 'search'}) %>
+
+<div id="measure-filter">
+ <div id="filter-form">
+ <form method="POST" action="<%= ApplicationController.root_context -%>/measures/search">
+ <table>
+ <tbody>
+ <tr>
+ <td>Base:</td>
+ <td><input type="text" name="base"></td>
+ </tr>
+ <tr>
+ <td>Qualifiers:</td>
+ <td>
+ <select name="qualifiers">
+ <option value="">Any</option>
+ <option value="TRK">Project</option>
+ <option value="TRK,BRC">Project</option>
+ <option value="DIR,PAC">Directory/Package</option>
+ <option value="FIL,CLA">File</option>
+ <option value="UTS">Unit Test File</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Language:</td>
+ <td><input type="text" name="language"></td>
+ </tr>
+ <tr>
+ <td>Name:</td>
+ <td><input type="text" name="nameRegexp"></td>
+ </tr>
+ <tr>
+ <td>Key:</td>
+ <td><input type="text" name="keyRegexp"></td>
+ </tr>
+ <tr>
+ <td><input type="submit" name="search" value="Search"></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+ </div>
+ <% if @filter %>
+ <div id="filter-result">
+ <% MeasureFilter::DISPLAYS.each do |display| %>
+ <%= link_to display.key, url_options.merge(:display => display.key) -%>
+ <% end %>
+
+ <%= render :partial => "measures/display_#{@filter.display.key}.html.erb" -%>
+ <p>
+ <% permalink = url_for(url_options) %>
+ <a href="<%= permalink -%>">Permalink</a>: <input type="text" value="<%= permalink -%>" size="100">
+ </p>
+ </div>
+ <% end %>
+
+</div> \ No newline at end of file
/option> Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
summaryrefslogtreecommitdiffstats
path: root/COPYING
blob: dba13ed2ddf783ee8118c6a581dbf75305f816a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661