Ticket grouping (#2679).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2696 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -58,16 +58,27 @@ class IssuesController < ApplicationController
|
||||
end
|
||||
@issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
|
||||
@issue_pages = Paginator.new self, @issue_count, limit, params['page']
|
||||
@issues = Issue.find :all, :order => sort_clause,
|
||||
@issues = Issue.find :all, :order => [@query.group_by_sort_order, sort_clause].compact.join(','),
|
||||
:include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
|
||||
:conditions => @query.statement,
|
||||
:limit => limit,
|
||||
:offset => @issue_pages.current.offset
|
||||
respond_to do |format|
|
||||
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
|
||||
format.html {
|
||||
if @query.grouped?
|
||||
# Retrieve the issue count by group
|
||||
@issue_count_by_group = begin
|
||||
Issue.count(:group => @query.group_by, :include => [:status, :project], :conditions => @query.statement)
|
||||
# Rails will raise an (unexpected) error if there's only a nil group value
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
{nil => @issue_count}
|
||||
end
|
||||
end
|
||||
render :template => 'issues/index.rhtml', :layout => !request.xhr?
|
||||
}
|
||||
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
|
||||
format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
|
||||
format.pdf { send_data(issues_to_pdf(@issues, @project), :type => 'application/pdf', :filename => 'export.pdf') }
|
||||
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
|
||||
end
|
||||
else
|
||||
# Send html if the query is not valid
|
||||
@@ -483,10 +494,11 @@ private
|
||||
@query.add_short_filter(field, params[field]) if params[field]
|
||||
end
|
||||
end
|
||||
session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
|
||||
@query.group_by = params[:group_by]
|
||||
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by}
|
||||
else
|
||||
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
|
||||
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
|
||||
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by])
|
||||
@query.project = @project
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,6 +30,7 @@ class QueriesController < ApplicationController
|
||||
params[:fields].each do |field|
|
||||
@query.add_filter(field, params[:operators][field], params[:values][field])
|
||||
end if params[:fields]
|
||||
@query.group_by ||= params[:group_by]
|
||||
|
||||
if request.post? && params[:confirm] && @query.save
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
|
||||
+33
-9
@@ -16,12 +16,13 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class QueryColumn
|
||||
attr_accessor :name, :sortable, :default_order
|
||||
attr_accessor :name, :sortable, :groupable, :default_order
|
||||
include Redmine::I18n
|
||||
|
||||
def initialize(name, options={})
|
||||
self.name = name
|
||||
self.sortable = options[:sortable]
|
||||
self.groupable = options[:groupable] || false
|
||||
self.default_order = options[:default_order]
|
||||
end
|
||||
|
||||
@@ -98,20 +99,20 @@ class Query < ActiveRecord::Base
|
||||
cattr_reader :operators_by_filter_type
|
||||
|
||||
@@available_columns = [
|
||||
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name"),
|
||||
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position"),
|
||||
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position"),
|
||||
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
|
||||
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
|
||||
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
|
||||
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
|
||||
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc', :groupable => true),
|
||||
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
|
||||
QueryColumn.new(:author),
|
||||
QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname"]),
|
||||
QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true),
|
||||
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
|
||||
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"),
|
||||
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc'),
|
||||
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
|
||||
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
|
||||
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
|
||||
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
|
||||
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
|
||||
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio"),
|
||||
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
|
||||
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
|
||||
]
|
||||
cattr_reader :available_columns
|
||||
@@ -241,6 +242,11 @@ class Query < ActiveRecord::Base
|
||||
).collect {|cf| QueryCustomFieldColumn.new(cf) }
|
||||
end
|
||||
|
||||
# Returns an array of columns that can be used to group the results
|
||||
def groupable_columns
|
||||
available_columns.select {|c| c.groupable}
|
||||
end
|
||||
|
||||
def columns
|
||||
if has_default_columns?
|
||||
available_columns.select do |c|
|
||||
@@ -288,6 +294,24 @@ class Query < ActiveRecord::Base
|
||||
sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
|
||||
end
|
||||
|
||||
# Returns the SQL sort order that should be prepended for grouping
|
||||
def group_by_sort_order
|
||||
if grouped? && (column = group_by_column)
|
||||
column.sortable.is_a?(Array) ?
|
||||
column.sortable.collect {|s| "#{s} #{column.default_order}"}.join(',') :
|
||||
"#{column.sortable} #{column.default_order}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the query is a grouped query
|
||||
def grouped?
|
||||
!group_by.blank?
|
||||
end
|
||||
|
||||
def group_by_column
|
||||
groupable_columns.detect {|c| c.name.to_s == group_by}
|
||||
end
|
||||
|
||||
def project_statement
|
||||
project_clauses = []
|
||||
if project && !@project.descendants.active.empty?
|
||||
|
||||
@@ -9,8 +9,18 @@
|
||||
<%= column_header(column) %>
|
||||
<% end %>
|
||||
</tr></thead>
|
||||
<% group = false %>
|
||||
<tbody>
|
||||
<% issues.each do |issue| -%>
|
||||
<% if @query.grouped? && issue.send(@query.group_by) != group %>
|
||||
<% group = issue.send(@query.group_by) %>
|
||||
<% reset_cycle %>
|
||||
<tr class="group">
|
||||
<td colspan="<%= query.columns.size + 2 %>">
|
||||
<%= group.blank? ? 'None' : group %> <span class="count">(<%= @issue_count_by_group[group] %>)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %>">
|
||||
<td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
|
||||
<td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
|
||||
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
|
||||
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
|
||||
<div id="query_form_content">
|
||||
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
|
||||
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
||||
</fieldset>
|
||||
<p><%= l(:field_group_by) %>
|
||||
<%= select_tag('group_by', options_for_select([[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, @query.group_by)) %></p>
|
||||
</div>
|
||||
<p class="buttons">
|
||||
|
||||
<%= link_to_remote l(:button_apply),
|
||||
{ :url => { :set_filter => 1 },
|
||||
:update => "content",
|
||||
@@ -23,7 +29,6 @@
|
||||
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
|
||||
<% end %>
|
||||
</p>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="contextual">
|
||||
@@ -36,6 +41,7 @@
|
||||
<div id="query_form"></div>
|
||||
<% html_title @query.name %>
|
||||
<% end %>
|
||||
|
||||
<%= error_messages_for 'query' %>
|
||||
<% if @query.valid? %>
|
||||
<% if @issues.empty? %>
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
|
||||
<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
|
||||
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
|
||||
|
||||
<p><label for="query_group_by"><%= l(:field_group_by) %></label>
|
||||
<%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %></p>
|
||||
</div>
|
||||
|
||||
<fieldset><legend><%= l(:label_filter_plural) %></legend>
|
||||
|
||||
Reference in New Issue
Block a user