queries_helper.rb 9.6 KB
Newer Older
1 2
# encoding: utf-8
#
jplang's avatar
jplang committed
3
# Redmine - project management software
jplang's avatar
jplang committed
4
# Copyright (C) 2006-2016  Jean-Philippe Lang
5 6 7 8 9
#
# 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.
10
#
11 12 13 14
# 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.
15
#
16 17 18
# 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.
jplang's avatar
jplang committed
19

20
module QueriesHelper
21 22
  include ApplicationHelper

jplang's avatar
jplang committed
23
  def filters_options_for_select(query)
24 25 26
    ungrouped = []
    grouped = {}
    query.available_filters.map do |field, field_options|
27
      if [:tree, :relation].include?(field_options[:type]) 
28
        group = :label_relations
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
      elsif field =~ /^(.+)\./
        # association filters
        group = "field_#{$1}"
      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)
    if grouped[:label_date].try(:size) == 1 
      ungrouped << grouped.delete(:label_date).first
    end
    s = options_for_select([[]] + ungrouped)
    if grouped.present?
      localized_grouped = grouped.map {|k,v| [l(k), v]}
      s << grouped_options_for_select(localized_grouped)
jplang's avatar
jplang committed
51
    end
52
    s
jplang's avatar
jplang committed
53
  end
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
  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

79 80 81
  def available_block_columns_tags(query)
    tags = ''.html_safe
    query.available_block_columns.each do |column|
jplang's avatar
jplang committed
82
      tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline')
83 84 85 86
    end
    tags
  end

87 88 89 90 91
  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
92
    tags << hidden_field_tag('t[]', '')
93 94 95
    tags
  end

96 97 98 99 100 101 102 103
  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

104 105 106 107 108
  def render_query_columns_selection(query, options={})
    tag_name = (options[:name] || 'c') + '[]'
    render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
  end

109 110 111
  def render_query_totals(query)
    return unless query.totalable_columns.present?
    totals = query.totalable_columns.map do |column|
112
      total_tag(column, query.total_for(column))
113 114 115 116
    end
    content_tag('p', totals.join(" ").html_safe, :class => "query-totals")
  end

117 118 119 120 121 122
  def total_tag(column, value)
    label = content_tag('span', "#{column.caption}:")
    value = content_tag('span', format_object(value), :class => 'value')
    content_tag('span', label + " " + value, :class => "total-for-#{column.name.to_s.dasherize}")
  end

123
  def column_header(column)
124
    column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
125
                                                        :default_order => column.default_order) :
jplang's avatar
jplang committed
126
                      content_tag('th', h(column.caption))
127
  end
128

129
  def column_content(column, issue)
130
    value = column.value_object(issue)
131
    if value.is_a?(Array)
132
      value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
133 134 135 136 137 138
    else
      column_value(column, issue, value)
    end
  end
  
  def column_value(column, issue, value)
139 140 141 142 143
    case column.name
    when :id
      link_to value, issue_path(issue)
    when :subject
      link_to value, issue_path(issue)
144 145
    when :parent
      value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
146 147 148
    when :description
      issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
    when :done_ratio
149
      progress_bar(value)
150
    when :relations
151
      content_tag('span',
jplang's avatar
jplang committed
152
        value.to_s(issue) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe,
153
        :class => value.css_classes_for(issue))
154
    else
155
      format_object(value)
156 157
    end
  end
edavis10's avatar
edavis10 committed
158

159
  def csv_content(column, issue)
160
    value = column.value_object(issue)
161 162 163 164 165 166 167
    if value.is_a?(Array)
      value.collect {|v| csv_value(column, issue, v)}.compact.join(', ')
    else
      csv_value(column, issue, value)
    end
  end

168
  def csv_value(column, object, value)
169 170 171 172 173
    format_object(value, false) do |value|
      case value.class.name
      when 'Float'
        sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator))
      when 'IssueRelation'
jplang's avatar
jplang committed
174
        value.to_s(object)
175
      when 'Issue'
176 177 178 179 180
        if object.is_a?(TimeEntry)
          "#{value.tracker} ##{value.id}: #{value.subject}"
        else
          value.id
        end
181 182 183
      else
        value
      end
184 185 186
    end
  end

187
  def query_to_csv(items, query, options={})
188
    options ||= {}
189 190 191 192 193 194 195
    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

196
    Redmine::Export::CSV.generate do |csv|
197
      # csv header fields
198
      csv << columns.map {|c| c.caption.to_s}
199 200
      # csv lines
      items.each do |item|
201
        csv << columns.map {|c| csv_content(c, item)}
202 203 204 205
      end
    end
  end

edavis10's avatar
edavis10 committed
206 207 208 209 210
  # Retrieve query from session or build a new query
  def retrieve_query
    if !params[:query_id].blank?
      cond = "project_id IS NULL"
      cond << " OR project_id = #{@project.id}" if @project
211
      @query = IssueQuery.where(cond).find(params[:query_id])
212
      raise ::Unauthorized unless @query.visible?
213
      @query.project = @project
edavis10's avatar
edavis10 committed
214 215
      session[:query] = {:id => @query.id, :project_id => @query.project_id}
      sort_clear
216 217
    elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
      # Give it a name, required to be valid
218
      @query = IssueQuery.new(:name => "_")
219
      @query.project = @project
220
      @query.build_from_params(params)
221
      session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :totalable_names => @query.totalable_names}
edavis10's avatar
edavis10 committed
222
    else
223
      # retrieve from session
jplang's avatar
jplang committed
224
      @query = nil
225
      @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
226
      @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :totalable_names => session[:query][:totalable_names])
227
      @query.project = @project
228 229
    end
  end
230

231 232
  def retrieve_query_from_session
    if session[:query]
233
      if session[:query][:id]
234
        @query = IssueQuery.find_by_id(session[:query][:id])
235 236
        return unless @query
      else
237
        @query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :totalable_names => session[:query][:totalable_names])
238
      end
239 240 241 242 243 244 245 246
      if session[:query].has_key?(:project_id)
        @query.project_id = session[:query][:project_id]
      else
        @query.project = @project
      end
      @query
    end
  end
247 248 249 250 251 252 253 254 255 256 257 258 259

  # 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
260 261
    else
      tags << hidden_field_tag("f[]", "", :id => nil)
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    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
jplang's avatar
jplang committed
279
end