module RedmineExtensions class EasyQueryPresenter < BasePresenter # --- GETTERS --- attr_accessor :loading_group, :page_module, :row_limit, :export_formats def initialize(*attrs) super @export_formats = ActiveSupport::OrderedHash.new @export_formats[:csv] = {} @export_formats[:pdf] = {} @export_formats[:xlsx] = {} end def entities(options={}) @entities ||= @options[:entities] || h.instance_variable_get(:@entities) || model.entities(options) end def entity_count(options={}) @entity_count ||= h.instance_variable_get(:@entity_count) || model.entity_count(options) end def entity_pages h.instance_variable_get(:@entity_pages) end def available_outputs outputs.available_outputs end def outputs @outputs ||= Outputs.new(self) end def display_save_button true end # ----- RENDERING HELPERS ---- def default_name h.l(self.class.name.underscore, :scope => [:easy_query, :name]) end def name model.new_record? ? default_name : model.name end def show_free_search? options.key?(:show_free_search) ? options[:show_free_search] : options[:page_module].nil? && model.searchable_columns.any? end def render_exports? outputs.table? end def display_columns_select?(action='edit') true end def display_sort_options?(action='edit') true end def display_group_by_select?(action='edit') true end def display_settings?(action) true end def has_default_filter? model.filters == model.default_filter end def render_zoom_links? false end def to_model self end def model_name EasyQuery.model_name end def to_partial_path 'easy_queries/easy_query' end def block_name options[:block_name] || ( page_module ? page_module.page_zone_module.module_name : nil ) end def modul_uniq_id options[:modul_uniq_id] || '' end def render_zoom_links return unless render_zoom_links? # TODO: it should give a presenter itself to the partial and there decide what and how to render if self.page_module h.render(:partial => 'easy_queries/zoom_links', :locals => {:query => self, :base_url => {}, :block_name => self.page_module.page_zone_module.module_name}) else h.render(:partial => 'easy_queries/zoom_links', :locals => {:query => self}) end end def prepare_table_render #prepared for a period settings before render end def entity_list(entities=self.entities) if model.entity.class.respond_to?(:each_with_easy_level) model.entity.class.each_with_easy_level(entities) do |entity, level| yield entity, level end else entities.each do |entity| yield entity, nil end end end def entity_css_classes(entity, options={}) entity.css_classes if entity.respond_to?(:css_classes) end # Returns a additional fast-icons buttons # - entity - instance of ... # - query - easy_query # - options - :no_link => true - no html links will be rendered # def additional_beginning_buttons(entity, options={}) return ''.html_safe if model.nil? || entity.nil? easy_query_additional_buttons_method = "#{model.class.name.underscore}_additional_beginning_buttons".to_sym additional_buttons = '' if h.respond_to?(easy_query_additional_buttons_method) additional_buttons = h.send(easy_query_additional_buttons_method, entity, options) end return additional_buttons.html_safe end def additional_ending_buttons(entity, options={}) return ''.html_safe if model.nil? || entity.nil? easy_query_additional_buttons_method = "#{model.class.name.underscore}_additional_ending_buttons".to_sym additional_buttons = '' if h.respond_to?(easy_query_additional_buttons_method) additional_buttons = h.send(easy_query_additional_buttons_method, entity, options) end return additional_buttons.html_safe end def column_header(column, options={}) if !options[:disable_sort] && column.sortable if page_module h.easy_page_module_sort_header_tag(page_module, model, column.name.to_s, {:class => column.css_classes, :caption => column.caption, :default_order => column.default_order}) else h.sort_header_tag(column.name.to_s, {:class => column.css_classes, :caption => column.caption, :default_order => column.default_order}) end else h.content_tag(:th, column.caption, {:class => column.css_classes}) end end # Get values from Proc, select only valid filters, sort and cache list of filters. Return array of arrays def filters_for_select @filters_for_select ||= model.available_filters.select do |name, filter| filter.valid? end.sort { |a, b| a[1] <=> b[1] } return @filters_for_select end def operators_for_select(filter_type) EasyQueryFilter.operators_by_filter_type[filter_type].collect { |o| [l(EasyQueryFilter.operators[o]), o] } end def other_formats_links(options={}) if options[:no_container] yield RedmineExtensions::Export::EasyOtherFormatsBuilder.new(h) else h.concat('
'.html_safe) yield RedmineExtensions::Export::EasyOtherFormatsBuilder.new(h) h.concat('
'.html_safe) end end def available_columns_for_select h.options_for_select (model.available_columns - model.columns).reject(&:frozen?).collect {|column| [column.caption(true), column.name]} end def selected_columns_for_select h.options_for_select (model.columns & model.available_columns).reject(&:frozen?).collect {|column| [column.caption(true), column.name]} end #------- DATA FOR RESULTS ------- # Returns count of entities on the list action # returns groups_count if query is grouped and entity_count otherwise def entity_count_for_list(options={}) if model.grouped? return model.groups_count(options) else return model.entity_count(options) end end def entities_for_html(options={}) options[:limit] ||= row_limit return model.entities_for_group(loading_group, options) if loading_group if model.grouped? return model.groups(options) else return model.entities(options) end end alias_method :prepare_html_result, :entities_for_html def entities_for_export(options={}) if model.grouped? return model.groups(options.merge(:include_entities => true)) else return {nil => {:entities => model.entities(options), :sums => model.send(:summarize_entities, entities)}} end end alias_method :prepare_export_result, :entities_for_export def filters_active? model.filters.any? end #------ MIDDLE LAYER ------ # Returns count of entities on the list action # returns groups_count if query is grouped and entity_count otherwise def entity_count_for_list(options={}) if self.grouped? model.groups_count(options) else model.entity_count(options) end end def path(params={}) if self.new_record? entity_easy_query_path(self.to_params.merge(params)) else entity_easy_query_path({:query_id => model}.merge(params)) end end def entity_easy_query_path(options = {}) options = options.dup h.polymorphic_path([(options.delete(:project) || self.project), self.entity], options) end # ----- SERIALIZATION HELPERS ------ def from_params(params) return if params.nil? if params['set_filter'] == '1' model.filters = {} model.group_by = '' else model.filters = model.default_filter model.group_by = model.default_group_by end if params['fields'] && params['fields'].is_a?(Array) params['values'] ||= {} params['fields'].each do |field| model.add_filter(field, params['operators'][field], params['values'][field]) end else model.available_filters.keys.each do |field| model.add_short_filter(field, params[field]) if params[field] end end model.group_by = params['group_by'] if params['group_by'].present? model.show_sum_row = params['show_sum_row'].try(:to_boolean) if params['show_sum_row'].present? model.load_groups_opened = params['load_groups_opened'].try(:to_boolean) if params['load_groups_opened'].present? self.outputs_from_params(params) if params['easy_query'] && params['easy_query']['columns_to_export'] == 'all' model.column_names = available_columns.collect { |col| col.name.to_s } elsif params['column_names'] && params['column_names'].is_a?(Array) if params['column_names'].first && params['column_names'].first.include?(',') model.column_names = params['column_names'].first.split(',') else model.column_names = params['column_names'] end end if params['settings'] && params['settings'].is_a?(Hash) if model.settings.is_a?(Hash) model.settings.merge!(params['settings']) else model.settings = params['settings'].dup end end self.set_additional_params(params) model.sort_criteria = params['sort_criteria'] if params['sort_criteria'] @sort_helper = SortHelper::SortCriteria.new if params['sort'].present? @sort_helper.available_criteria = sortable_columns @sort_helper.from_param(params['sort']) @sort_helper.criteria = model.sort_criteria_init if @sort_helper.empty? model.sort_criteria = @sort_helper.to_a end if params['easy_query_q'] model.use_free_search = true model.free_search_question = params['easy_query_q'] model.free_search_question.strip! # extract tokens from the question # eg. hello "bye bye" => ["hello", "bye bye"] model.free_search_tokens = model.free_search_question.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect { |m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '') } # tokens must be at least 2 characters long model.free_search_tokens = model.free_search_tokens.uniq.select { |w| w.length > 1 } model.free_search_tokens.slice! 5..-1 if model.free_search_tokens.size > 5 end end def outputs_from_params(params) if params['outputs'].is_a?(Array) model.outputs = params['outputs'] & available_outputs.collect(&:to_s) else model.outputs = available_outputs.select do |output| if params[output.to_s].present? true elsif !params['output'].blank? params['output'] == output end end end model.outputs = ['table'] if model.outputs.empty? end def set_additional_params(params) end def to_params easy_query_params = {:set_filter => '1', :type => model.class.name, :fields => [], :operators => {}, :values => {}} model.filters.each do |f, o| easy_query_params[:fields] << f easy_query_params[:operators][f] = o[:operator] easy_query_params[:values][f] = o[:values] end easy_query_params[:group_by] = model.group_by easy_query_params[:column_names] = (model.column_names || []).collect(&:to_s) easy_query_params[:load_groups_opened] = model.load_groups_opened ? '1' : '0' easy_query_params[:show_sum_row] = model.show_sum_row ? '1' : '0' easy_query_params[:show_avatars] = model.show_avatars ? '1' : '0' easy_query_params end # ----- OUTPUTS HELPER CLASS ---- class Outputs include Enumerable def initialize(presenter) @presenter = presenter @query = presenter.model @query.outputs = ['table'] unless @query.outputs.any? @outputs = @query.outputs.map{|o| RedmineExtensions::QueryOutput.output_klass_for(o).new(presenter) } end def each(&block) @outputs.each{|o| yield(o) } end def available_outputs RedmineExtensions::QueryOutput.available_outputs_for( @query ) end def available_output_instances @available_outputs ||= RedmineExtensions::QueryOutput.available_output_klasses_for( @query ).map{|klass| klass.new(@presenter) } end def output_enabled?(output) @query.outputs.include?(output.to_s) end def render_edit_selects(style=:check_box, options={}) available_output_instances.map{|o| o.render_edit_box(style, options) }.join('').html_safe end def render_edit @outputs.map{ |output| output.render_edit }.join('').html_safe end def method_missing(name, *args) if name.to_s.ends_with?('?') output_enabled?(name.to_s[0..-2]) else super end end end end end