queries_helper.rb
357 lines
| 12.5 KiB
| text/x-ruby
|
RubyLexer
|
r8090 | # encoding: utf-8 | ||
# | ||||
|
r5159 | # Redmine - project management software | ||
|
r14856 | # Copyright (C) 2006-2016 Jean-Philippe Lang | ||
|
r771 | # | ||
# This program is free software; you can redistribute it and/or | ||||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; either version 2 | ||||
# of the License, or (at your option) any later version. | ||||
|
r6767 | # | ||
|
r771 | # This program 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 General Public License for more details. | ||||
|
r6767 | # | ||
|
r771 | # You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
|
r92 | |||
|
r771 | module QueriesHelper | ||
|
r12779 | include ApplicationHelper | ||
|
r9979 | def filters_options_for_select(query) | ||
|
r13280 | ungrouped = [] | ||
grouped = {} | ||||
query.available_filters.map do |field, field_options| | ||||
|
r15262 | if field_options[:type] == :relation | ||
|
r14972 | group = :label_relations | ||
|
r15262 | elsif field_options[:type] == :tree | ||
group = query.is_a?(IssueQuery) ? :label_relations : nil | ||||
|
r15809 | elsif field =~ /^cf_\d+\./ | ||
group = (field_options[:through] || field_options[:field]).try(:name) | ||||
|
r13280 | elsif field =~ /^(.+)\./ | ||
# association filters | ||||
|
r15808 | group = "field_#{$1}".to_sym | ||
|
r13280 | elsif %w(member_of_group assigned_to_role).include?(field) | ||
group = :field_assigned_to | ||||
elsif field_options[:type] == :date_past || field_options[:type] == :date | ||||
group = :label_date | ||||
end | ||||
if group | ||||
(grouped[group] ||= []) << [field_options[:name], field] | ||||
else | ||||
ungrouped << [field_options[:name], field] | ||||
end | ||||
end | ||||
# Don't group dates if there's only one (eg. time entries filters) | ||||
|
r15719 | if grouped[:label_date].try(:size) == 1 | ||
|
r13280 | ungrouped << grouped.delete(:label_date).first | ||
end | ||||
s = options_for_select([[]] + ungrouped) | ||||
if grouped.present? | ||||
|
r15809 | localized_grouped = grouped.map {|k,v| [k.is_a?(Symbol) ? l(k) : k.to_s, v]} | ||
|
r13280 | s << grouped_options_for_select(localized_grouped) | ||
|
r9979 | end | ||
|
r13280 | s | ||
|
r92 | end | ||
|
r6767 | |||
|
r11466 | def query_filters_hidden_tags(query) | ||
tags = ''.html_safe | ||||
query.filters.each do |field, options| | ||||
tags << hidden_field_tag("f[]", field, :id => nil) | ||||
tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil) | ||||
options[:values].each do |value| | ||||
tags << hidden_field_tag("v[#{field}][]", value, :id => nil) | ||||
end | ||||
end | ||||
tags | ||||
end | ||||
def query_columns_hidden_tags(query) | ||||
tags = ''.html_safe | ||||
query.columns.each do |column| | ||||
tags << hidden_field_tag("c[]", column.name, :id => nil) | ||||
end | ||||
tags | ||||
end | ||||
def query_hidden_tags(query) | ||||
query_filters_hidden_tags(query) + query_columns_hidden_tags(query) | ||||
end | ||||
|
r15267 | def group_by_column_select_tag(query) | ||
options = [[]] + query.groupable_columns.collect {|c| [c.caption, c.name.to_s]} | ||||
select_tag('group_by', options_for_select(options, @query.group_by)) | ||||
end | ||||
|
r10721 | def available_block_columns_tags(query) | ||
tags = ''.html_safe | ||||
query.available_block_columns.each do |column| | ||||
|
r12378 | tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline') | ||
|
r10721 | end | ||
tags | ||||
end | ||||
|
r14260 | def available_totalable_columns_tags(query) | ||
tags = ''.html_safe | ||||
query.available_totalable_columns.each do |column| | ||||
tags << content_tag('label', check_box_tag('t[]', column.name.to_s, query.totalable_columns.include?(column), :id => nil) + " #{column.caption}", :class => 'inline') | ||||
end | ||||
|
r15128 | tags << hidden_field_tag('t[]', '') | ||
|
r14260 | tags | ||
end | ||||
|
r11221 | def query_available_inline_columns_options(query) | ||
(query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]} | ||||
end | ||||
def query_selected_inline_columns_options(query) | ||||
(query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]} | ||||
end | ||||
|
r11222 | def render_query_columns_selection(query, options={}) | ||
tag_name = (options[:name] || 'c') + '[]' | ||||
render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name} | ||||
end | ||||
|
r15267 | def grouped_query_results(items, query, item_count_by_group, &block) | ||
previous_group, first = false, true | ||||
totals_by_group = query.totalable_columns.inject({}) do |h, column| | ||||
h[column] = query.total_by_group_for(column) | ||||
h | ||||
end | ||||
items.each do |item| | ||||
group_name = group_count = nil | ||||
if query.grouped? | ||||
group = query.group_by_column.value(item) | ||||
if first || group != previous_group | ||||
if group.blank? && group != false | ||||
group_name = "(#{l(:label_blank_value)})" | ||||
else | ||||
group_name = format_object(group) | ||||
end | ||||
group_name ||= "" | ||||
group_count = item_count_by_group ? item_count_by_group[group] : nil | ||||
group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe | ||||
end | ||||
end | ||||
yield item, group_name, group_count, group_totals | ||||
previous_group, first = group, false | ||||
end | ||||
end | ||||
|
r14260 | def render_query_totals(query) | ||
return unless query.totalable_columns.present? | ||||
totals = query.totalable_columns.map do |column| | ||||
|
r14283 | total_tag(column, query.total_for(column)) | ||
|
r14260 | end | ||
content_tag('p', totals.join(" ").html_safe, :class => "query-totals") | ||||
end | ||||
|
r14283 | def total_tag(column, value) | ||
label = content_tag('span', "#{column.caption}:") | ||||
|
r15583 | value = if [:hours, :spent_hours, :total_spent_hours, :estimated_hours].include? column.name | ||
format_hours(value) | ||||
else | ||||
format_object(value) | ||||
end | ||||
value = content_tag('span', value, :class => 'value') | ||||
|
r14283 | content_tag('span', label + " " + value, :class => "total-for-#{column.name.to_s.dasherize}") | ||
end | ||||
|
r771 | def column_header(column) | ||
|
r2169 | column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption, | ||
|
r6767 | :default_order => column.default_order) : | ||
|
r6207 | content_tag('th', h(column.caption)) | ||
|
r771 | end | ||
|
r6767 | |||
|
r15719 | def column_content(column, item) | ||
value = column.value_object(item) | ||||
|
r8601 | if value.is_a?(Array) | ||
|
r15719 | value.collect {|v| column_value(column, item, v)}.compact.join(', ').html_safe | ||
|
r8601 | else | ||
|
r15719 | column_value(column, item, value) | ||
|
r8601 | end | ||
end | ||||
|
r15719 | |||
def column_value(column, item, value) | ||||
|
r12087 | case column.name | ||
when :id | ||||
|
r15719 | link_to value, issue_path(item) | ||
|
r12087 | when :subject | ||
|
r15719 | link_to value, issue_path(item) | ||
|
r13174 | when :parent | ||
value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : '' | ||||
|
r12087 | when :description | ||
|
r15719 | item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : '' | ||
|
r12087 | when :done_ratio | ||
|
r14469 | progress_bar(value) | ||
|
r12087 | when :relations | ||
|
r10303 | content_tag('span', | ||
|
r15719 | value.to_s(item) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, | ||
:class => value.css_classes_for(item)) | ||||
|
r15583 | when :hours, :spent_hours, :total_spent_hours, :estimated_hours | ||
format_hours(value) | ||||
|
r2998 | else | ||
|
r12087 | format_object(value) | ||
|
r771 | end | ||
end | ||||
|
r3577 | |||
|
r15719 | def csv_content(column, item) | ||
value = column.value_object(item) | ||||
|
r11211 | if value.is_a?(Array) | ||
|
r15719 | value.collect {|v| csv_value(column, item, v)}.compact.join(', ') | ||
|
r11211 | else | ||
|
r15719 | csv_value(column, item, value) | ||
|
r11211 | end | ||
end | ||||
|
r13180 | def csv_value(column, object, value) | ||
|
r12779 | format_object(value, false) do |value| | ||
case value.class.name | ||||
when 'Float' | ||||
sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator)) | ||||
when 'IssueRelation' | ||||
|
r13183 | value.to_s(object) | ||
|
r13173 | when 'Issue' | ||
|
r13180 | if object.is_a?(TimeEntry) | ||
"#{value.tracker} ##{value.id}: #{value.subject}" | ||||
else | ||||
value.id | ||||
end | ||||
|
r12779 | else | ||
value | ||||
end | ||||
|
r11211 | end | ||
end | ||||
|
r11218 | def query_to_csv(items, query, options={}) | ||
|
r14292 | options ||= {} | ||
|
r11218 | columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns) | ||
query.available_block_columns.each do |column| | ||||
if options[column.name].present? | ||||
columns << column | ||||
end | ||||
end | ||||
|
r13920 | Redmine::Export::CSV.generate do |csv| | ||
|
r11218 | # csv header fields | ||
|
r13920 | csv << columns.map {|c| c.caption.to_s} | ||
|
r11218 | # csv lines | ||
items.each do |item| | ||||
|
r13920 | csv << columns.map {|c| csv_content(c, item)} | ||
|
r11218 | end | ||
end | ||||
end | ||||
|
r3577 | # Retrieve query from session or build a new query | ||
|
r15257 | def retrieve_query(klass=IssueQuery, use_session=true) | ||
session_key = klass.name.underscore.to_sym | ||||
if params[:query_id].present? | ||||
|
r3577 | cond = "project_id IS NULL" | ||
cond << " OR project_id = #{@project.id}" if @project | ||||
|
r15257 | @query = klass.where(cond).find(params[:query_id]) | ||
|
r6043 | raise ::Unauthorized unless @query.visible? | ||
|
r7536 | @query.project = @project | ||
|
r15257 | session[session_key] = {:id => @query.id, :project_id => @query.project_id} if use_session | ||
|
r3577 | sort_clear | ||
|
r15257 | elsif api_request? || params[:set_filter] || !use_session || session[session_key].nil? || session[session_key][:project_id] != (@project ? @project.id : nil) | ||
|
r7529 | # Give it a name, required to be valid | ||
|
r15257 | @query = klass.new(:name => "_", :project => @project) | ||
|
r10739 | @query.build_from_params(params) | ||
|
r15257 | session[session_key] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :totalable_names => @query.totalable_names} if use_session | ||
|
r3577 | else | ||
|
r7529 | # retrieve from session | ||
|
r11789 | @query = nil | ||
|
r15257 | @query = klass.find_by_id(session[session_key][:id]) if session[session_key][:id] | ||
@query ||= klass.new(:name => "_", :filters => session[session_key][:filters], :group_by => session[session_key][:group_by], :column_names => session[session_key][:column_names], :totalable_names => session[session_key][:totalable_names]) | ||||
|
r7993 | @query.project = @project | ||
|
r7529 | end | ||
end | ||||
|
r7536 | |||
|
r15468 | def retrieve_query_from_session(klass=IssueQuery) | ||
session_key = klass.name.underscore.to_sym | ||||
session_data = session[session_key] | ||||
if session_data | ||||
if session_data[:id] | ||||
@query = IssueQuery.find_by_id(session_data[:id]) | ||||
|
r8537 | return unless @query | ||
else | ||||
|
r15468 | @query = IssueQuery.new(:name => "_", :filters => session_data[:filters], :group_by => session_data[:group_by], :column_names => session_data[:column_names], :totalable_names => session_data[:totalable_names]) | ||
|
r8537 | end | ||
|
r15468 | if session_data.has_key?(:project_id) | ||
@query.project_id = session_data[:project_id] | ||||
|
r8368 | else | ||
@query.project = @project | ||||
end | ||||
@query | ||||
end | ||||
end | ||||
|
r14820 | |||
# Returns the query definition as hidden field tags | ||||
def query_as_hidden_field_tags(query) | ||||
tags = hidden_field_tag("set_filter", "1", :id => nil) | ||||
if query.filters.present? | ||||
query.filters.each do |field, filter| | ||||
tags << hidden_field_tag("f[]", field, :id => nil) | ||||
tags << hidden_field_tag("op[#{field}]", filter[:operator], :id => nil) | ||||
filter[:values].each do |value| | ||||
tags << hidden_field_tag("v[#{field}][]", value, :id => nil) | ||||
end | ||||
end | ||||
|
r15226 | else | ||
tags << hidden_field_tag("f[]", "", :id => nil) | ||||
|
r14820 | end | ||
if query.column_names.present? | ||||
query.column_names.each do |name| | ||||
tags << hidden_field_tag("c[]", name, :id => nil) | ||||
end | ||||
end | ||||
if query.totalable_names.present? | ||||
query.totalable_names.each do |name| | ||||
tags << hidden_field_tag("t[]", name, :id => nil) | ||||
end | ||||
end | ||||
if query.group_by.present? | ||||
tags << hidden_field_tag("group_by", query.group_by, :id => nil) | ||||
end | ||||
tags | ||||
end | ||||
|
r15258 | |||
|
r15260 | # Returns the queries that are rendered in the sidebar | ||
def sidebar_queries(klass, project) | ||||
klass.visible.global_or_on_project(@project).sorted.to_a | ||||
|
r15258 | end | ||
|
r15260 | # Renders a group of queries | ||
|
r15258 | def query_links(title, queries) | ||
return '' if queries.empty? | ||||
# links to #index on issues/show | ||||
url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : {} | ||||
content_tag('h3', title) + "\n" + | ||||
content_tag('ul', | ||||
queries.collect {|query| | ||||
css = 'query' | ||||
css << ' selected' if query == @query | ||||
content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css)) | ||||
}.join("\n").html_safe, | ||||
:class => 'queries' | ||||
) + "\n" | ||||
end | ||||
|
r15260 | # Renders the list of queries for the sidebar | ||
def render_sidebar_queries(klass, project) | ||||
queries = sidebar_queries(klass, project) | ||||
|
r15258 | out = ''.html_safe | ||
|
r15260 | out << query_links(l(:label_my_queries), queries.select(&:is_private?)) | ||
out << query_links(l(:label_query_plural), queries.reject(&:is_private?)) | ||||
|
r15258 | out | ||
end | ||||
|
r92 | end | ||