# encoding: UTF-8
module Wice
class GridRenderer
include ActionView::Helpers::TagHelper
include ActionView::Helpers::CaptureHelper
include ActionView::Helpers::TextHelper
include ActionView::Helpers::AssetTagHelper
include ActionView::Helpers::JavaScriptHelper
def config
ActionController::Base.config
end
def controller
@grid.controller
end
attr_reader :page_parameter_name
attr_reader :after_row_handler
attr_reader :before_row_handler
attr_reader :blank_slate_handler
attr_reader :grid
attr_accessor :erb_mode
attr_accessor :reset_button_present, :submit_button_present, :show_hide_button_present, :csv_export_icon_present
@@order_parameter_name = "order"
@@order_direction_parameter_name = "order_direction"
@@page_parameter_name = "page"
def initialize(grid, view) #:nodoc:
@grid = grid
@grid.renderer = self
@columns = []
@columns_table = {}
@action_column_present = false
@view = view
end
def add_column(vc) #:nodoc:
@columns_table[vc.attribute_name] = vc if vc.attribute_name
@columns << vc
end
def [](k) #:nodoc:
@columns_table[k]
end
def output_csv?
return grid.output_csv?
end
def number_of_columns(filter = nil) #:nodoc:
filter_columns(filter).size
end
def each_column_label(filter = nil) #:nodoc:
filter_columns(filter).each{|col| yield col.column_name}
end
def column_labels(filter = nil) #:nodoc:
filter_columns(filter).collect(&:column_name)
end
def each_column(filter = nil) #:nodoc:
filter_columns(filter).each{|col| yield col}
end
def contains_a_text_input? #:nodoc:
filter_columns(:in_html).detect(&:contains_a_text_input)
end
def each_column_aware_of_one_last_one(filter = nil) #:nodoc:
cols = filter_columns(filter)
cols[0..-2].each{|col| yield col, false}
yield cols.last, true
end
def last_column_for_html #:nodoc:
filter_columns(:in_html).last
end
def select_for(filter) #:nodoc:
filter_columns(filter).select{|col| yield col}
end
def find_one_for(filter) #:nodoc:
filter_columns(filter).find{|col| yield col}
end
def each_column_with_attribute #:nodoc:
@columns.select(&:attribute_name).each{|col| yield col}
end
alias_method :each, :each_column
include Enumerable
def csv_export_icon #:nodoc:
tooltip = WiceGridNlMessageProvider.get_message(:CSV_EXPORT_TOOLTIP)
@csv_export_icon_present = true
image_tag(Defaults::CSV_EXPORT_ICON,
:title => tooltip,
:class => 'clickable export_to_csv_button',
:alt => tooltip
)
end
def pagination_panel(no_rightmost_column, hide_csv_button) #:nodoc:
panel = yield
render_csv_button = @grid.export_to_csv_enabled && ! hide_csv_button
number_of_columns = self.number_of_columns(:in_html)
number_of_columns -= 1 if no_rightmost_column
if panel.nil?
if render_csv_button
"
# tag of the current cell.
#
# In case of an array output, please note that if you need to define HTML attributes for all 's in a
# column, use +td_html_attrs+. Also note that if the method returns a hash with a :class or 'class'
# element, it will not overwrite the class defined in +td_html_attrs+, or classes added by the grid itself
# (+active_filter+ and +sorted+), instead they will be all concatenated:
#
#
# It is up to the developer to make sure that what in rendered in column cells
# corresponds to sorting and filtering specified by parameters :attribute_name and :model_class.
def column(opts = {}, &block)
options = {
:allow_multiple_selection => Defaults::ALLOW_MULTIPLE_SELECTION,
:allow_ordering => true,
:attribute_name => nil,
:auto_reload => Defaults::AUTO_RELOAD,
:boolean_filter_false_label => WiceGridNlMessageProvider.get_message(:BOOLEAN_FILTER_FALSE_LABEL),
:boolean_filter_true_label => WiceGridNlMessageProvider.get_message(:BOOLEAN_FILTER_TRUE_LABEL),
:class => nil,
:column_name => '',
:custom_filter => nil,
:custom_order => nil,
:detach_with_id => nil,
:filter_all_label => Defaults::CUSTOM_FILTER_ALL_LABEL,
:helper_style => Defaults::HELPER_STYLE,
:icon => nil,
:th_class => nil,
:subtitle => nil,
:in_csv => true,
:in_html => true,
:model_class => nil,
:negation_in_filter => Defaults::NEGATION_IN_STRING_FILTERS,
:no_filter => false,
:table_alias => nil,
:td_html_attrs => {}
}
opts.assert_valid_keys(options.keys)
options.merge!(opts)
unless options[:model_class].nil?
options[:model_class] = options[:model_class].constantize if options[:model_class].is_a? String
raise WiceGridArgumentError.new("Option :model_class can be either a class or a string instance") unless options[:model_class].is_a? Class
end
if options[:attribute_name].nil? && options[:model_class]
raise WiceGridArgumentError.new("Option :model_class is only used together with :attribute_name")
end
if options[:attribute_name] && options[:attribute_name].index('.')
raise WiceGridArgumentError.new("Invalid attribute name #{options[:attribute_name]}. An attribute name must not contain a table name!")
end
if options[:class]
options[:td_html_attrs].add_or_append_class_value!(options[:class])
options.delete(:class)
end
if block.nil?
if ! options[:attribute_name].blank?
block = lambda{|obj| obj.send(options[:attribute_name])}
else
raise WiceGridArgumentError.new(
"Missing column block without attribute_name defined. You can only omit the block if attribute_name is present.")
end
end
klass = ViewColumn
if options[:attribute_name] &&
col_type_and_table_name = @grid.declare_column(options[:attribute_name], options[:custom_filter])
db_column, table_name, is_main_table = col_type_and_table_name
col_type = db_column.type
if options[:custom_filter]
custom_filter = if options[:custom_filter] == :auto
lambda{ @grid.distinct_values_for_column(db_column) } # Thank God Ruby has higher order functions!!!
elsif options[:custom_filter].class == Symbol
lambda{ @grid.distinct_values_for_column_in_resultset([options[:custom_filter]])}
elsif options[:custom_filter].class == Hash
options[:custom_filter].keys
options[:custom_filter].to_a
elsif options[:custom_filter].class == Array
if options[:custom_filter].empty?
[]
elsif options[:custom_filter].all_items_are_of_class(Symbol)
lambda{ @grid.distinct_values_for_column_in_resultset(options[:custom_filter]) }
elsif options[:custom_filter].all_items_are_of_class(String) || options[:custom_filter].all_items_are_of_class(Numeric)
options[:custom_filter].map{|i| [i,i]}
elsif options[:custom_filter].all_items_are_of_class(Array)
options[:custom_filter]
else
raise WiceGridArgumentError.new(
':custom_filter can equal :auto, an array of string and/or numbers (direct values for the dropdown), ' +
'a homogeneous array of symbols (a sequence of methods to send to AR objects in the result set to ' +
'retrieve unique values for the dropdown), a Symbol (a shortcut for a one member array of symbols), ' +
'a hash where keys are labels and values are values for the dropdown option, or an array of two-item arrays, ' +
'each of which contains the label (first element) and the value (second element) for a dropdown option'
)
end
end
klass = ViewColumnCustomDropdown
else
klass = ViewColumn.handled_type[col_type] || ViewColumn
end # custom_filter
end # attribute_name
vc = klass.new(block, options, @grid, table_name, is_main_table, custom_filter, @view)
vc.negation = options[:negation_in_filter] if vc.respond_to? :negation=
vc.filter_all_label = options[:filter_all_label] if vc.kind_of?(ViewColumnCustomDropdown)
if vc.kind_of?(ViewColumnBoolean)
vc.boolean_filter_true_label = options[:boolean_filter_true_label]
vc.boolean_filter_false_label = options[:boolean_filter_false_label]
end
add_column(vc)
end
# Optional method inside the +grid+ block, to which every ActiveRecord instance is injected, just like +column+.
# Unlike +column+, it returns a hash which will be used as HTML attributes for the row with the given ActiveRecord instance.
#
# Note that if the method returns a hash with a :class or 'class' element, it will not overwrite
# classes +even+ and +odd+, instead they will be concatenated:
def row_attributes(&block)
@row_attributes_handler = block
end
# Can be used to add HTML code (another row, for example) right after each grid row.
# Nothing is added if the block return +false+ or +nil+.
def after_row(&block)
@after_row_handler = block
end
# Can be used to add HTML code (another row, for example) right before each grid row.
# Nothing is added if the block return +false+ or +nil+.
def before_row(&block)
@before_row_handler = block
end
# The output of the block submitted to +blank_slate+ is rendered instead of the whole grid if no filters are active
# and there are no records to render.
# In addition to the block style two other variants are accepted:
# * g.blank_slate "some text to be rendered"
# * g.blank_slate :partial => "partial_name"
def blank_slate(opts = nil, &block)
if (opts.is_a?(Hash) && opts.has_key?(:partial) && block.nil?) || (opts.is_a?(String) && block.nil?)
@blank_slate_handler = opts
elsif opts.nil? && block
@blank_slate_handler = block
else
raise WiceGridArgumentError.new("blank_slate accepts only a string, a block, or :template => 'path_to_template' ")
end
end
def get_row_attributes(ar_object) #:nodoc:
if @row_attributes_handler
row_attributes = @row_attributes_handler.call(ar_object)
row_attributes = {} if row_attributes.blank?
unless row_attributes.is_a?(Hash)
raise WiceGridArgumentError.new("row_attributes block must return a hash containing HTML attributes. The returned value is #{row_attributes.inspect}")
end
row_attributes
else
{}
end
end
def no_filter_needed? #:nodoc:
not @columns.inject(false){|a,b| a || b.filter_shown? }
end
def no_filter_needed_in_main_table? #:nodoc:
not @columns.inject(false){|a,b| a || b.filter_shown_in_main_table? }
end
def base_link_for_filter(controller, extra_parameters = {}) #:nodoc:
new_params = controller.params.deep_clone_yl
new_params.merge!(extra_parameters)
if new_params[@grid.name]
new_params[@grid.name].delete(:page) # we reset paging here
new_params[@grid.name].delete(:per_page) # we reset paging here
new_params[@grid.name].delete(:f) # no filter for the base url
new_params[@grid.name].delete(:foc) # nullify the focus
new_params[@grid.name].delete(:q) # and no request for the saved query
end
new_params[:only_path] = true
base_link_with_pp_info = controller.send( :url_for, new_params).gsub(/\?+$/,'')
if new_params[@grid.name]
new_params[@grid.name].delete(:pp) # and reset back to pagination if show all mode is on
end
[base_link_with_pp_info, controller.send(:url_for, new_params).gsub(/\?+$/,'')]
end
def link_for_export(controller, format, extra_parameters = {}) #:nodoc:
new_params = controller.params.deep_clone_yl
new_params.merge!(extra_parameters)
new_params[@grid.name] = {} unless new_params[@grid.name]
new_params[@grid.name][:export] = format
new_params[:only_path] = true
controller.send(:url_for, new_params)
end
def more_link(controller, extra_parameters)
new_params = controller.params.deep_clone_yl
new_params.merge!(extra_parameters)
new_params[@grid.name] = {} unless new_params[@grid.name]
new_params[@grid.name][:per_page] = @grid.status[:per_page].to_i + (@grid.options[:per_page] || Defaults::PER_PAGE)
new_params[:only_path] = true
controller.send(:url_for, new_params)
end
def column_link(column, direction, params, extra_parameters = {}) #:nodoc:
column_attribute_name = column.attribute_name
query_params = {@grid.name => {
@@order_parameter_name => column_attribute_name,
@@order_direction_parameter_name => direction
}}
cleaned_params = params.deep_clone_yl
cleaned_params.merge!(extra_parameters)
cleaned_params.delete(:controller)
cleaned_params.delete(:action)
query_params = cleaned_params.rec_merge(query_params)
'?' + query_params.to_query
end
def contains_auto_reloading_inputs #:nodoc:
contains_auto_reloading_elements(:has_auto_reloading_input?)
end
def contains_auto_reloading_inputs_with_negation_checkboxes #:nodoc:
contains_auto_reloading_elements(:auto_reloading_input_with_negation_checkbox?)
end
def contains_auto_reloading_selects #:nodoc:
contains_auto_reloading_elements(:has_auto_reloading_select?)
end
def contains_auto_reloading_calendars #:nodoc:
contains_auto_reloading_elements(:has_auto_reloading_calendar?)
end
protected
def contains_auto_reloading_elements(what) #:nodoc:
filter_columns(:in_html).detect{|column| column.filter_shown? && column.send(what)}
end
def filter_columns(method_name = nil) #:nodoc:
method_name ? @columns.select(&method_name) : @columns
end
end
end
| | |