# 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
attr_reader :page_parameter_name
attr_reader :after_row_handler
attr_reader :before_row_handler
attr_reader :blank_slate_handler
attr_reader :last_row_handler
attr_reader :grid
@@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 config #:nodoc:
@view.config
end
def controller #:nodoc:
@view.controller
end
def add_column(vc) #:nodoc:
@columns_table[vc.fully_qualified_attribute_name] = vc if vc.attribute
@columns << vc
end
def [](k) #:nodoc:
@columns_table[k]
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.name}
end
def column_labels(filter = nil) #:nodoc:
filter_columns(filter).collect(&:name)
end
def each_column(filter = nil) #:nodoc:
filter_columns(filter).each{|col| yield col}
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).each{|col| yield col}
end
alias_method :each, :each_column
include Enumerable
def csv_export_icon #:nodoc:
content_tag(:div, '',
:title => NlMessage['csv_export_tooltip'],
:class => 'clickable export-to-csv-button'
)
end
def pagination_panel(number_of_columns, hide_csv_button) #:nodoc:
panel = yield
render_csv_button = @grid.export_to_csv_enabled && ! hide_csv_button
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 +html+. Also note that if the method returns a hash with a :class or 'class'
# element, it will not overwrite the class defined in +html+, 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 and :model.
def column(opts = {}, &block)
options = {
:allow_multiple_selection => Defaults::ALLOW_MULTIPLE_SELECTION,
:ordering => true,
:attribute => nil,
:auto_reload => Defaults::AUTO_RELOAD,
:boolean_filter_false_label => NlMessage['boolean_filter_false_label'],
:boolean_filter_true_label => NlMessage['boolean_filter_true_label'],
:class => nil,
:name => '',
:custom_filter => nil,
:detach_with_id => nil,
:filter_all_label => Defaults::CUSTOM_FILTER_ALL_LABEL,
:helper_style => Defaults::HELPER_STYLE,
:in_csv => true,
:in_html => true,
:model => nil,
:negation => Defaults::NEGATION_IN_STRING_FILTERS,
:filter => true,
:table_alias => nil,
:html => {}
}
opts.assert_valid_keys(options.keys)
options.merge!(opts)
unless options[:model].nil?
options[:model] = options[:model].constantize if options[:model].is_a? String
raise WiceGridArgumentError.new("Option :model can be either a class or a string instance") unless options[:model].is_a? Class
end
if options[:attribute].nil? && options[:model]
raise WiceGridArgumentError.new("Option :model is only used together with :attribute")
end
if options[:attribute] && options[:attribute].index('.')
raise WiceGridArgumentError.new("Invalid attribute name #{options[:attribute]}. An attribute name must not contain a table name!")
end
if options[:class]
Wice::WgHash.add_or_append_class_value!(options[:html], options[:class])
options.delete(:class)
end
if block.nil?
if ! options[:attribute].blank?
block = lambda{|obj| obj.send(options[:attribute])}
else
raise WiceGridArgumentError.new(
"Missing column block without attribute defined. You can only omit the block if attribute is present.")
end
end
klass = ViewColumn
if options[:attribute] &&
col_type_and_table_name = @grid.declare_column(options[:attribute], options[:model],
options[:custom_filter], options[:table_alias])
db_column, table_name, 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 Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], Symbol)
lambda{ @grid.distinct_values_for_column_in_resultset(options[:custom_filter]) }
elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], String) || WgEnumerable.all_items_are_of_class(options[:custom_filter], Numeric)
options[:custom_filter].map{|i| [i,i]}
elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], 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 = ViewColumn.get_column_processor(:custom)
else
klass = ViewColumn.get_column_processor(col_type)
end # custom_filter
end # attribute
vc = klass.new(block, options, @grid, table_name, main_table, custom_filter, @view)
vc.negation = options[:negation] if vc.respond_to? :negation=
vc.filter_all_label = options[:filter_all_label] if vc.kind_of?(ViewColumn.get_column_processor(:custom))
if vc.kind_of?(ViewColumn.get_column_processor(:boolean))
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
# Can be used to add HTML code (calculation results, for example) after all rows.
# Nothing is added if the block return +false+ or +nil+.
def last_row(&block)
@last_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 = Wice::WgHash.deep_clone controller.params
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(: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] = false
base_link_with_pp_info = controller.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.url_for(new_params).gsub(/\?+$/,'')]
end
def link_for_export(controller, format, extra_parameters = {}) #:nodoc:
new_params = Wice::WgHash.deep_clone controller.params
new_params.merge!(extra_parameters)
new_params[@grid.name] = {} unless new_params[@grid.name]
new_params[@grid.name][:export] = format
new_params[:only_path] = false
controller.url_for(new_params)
end
def column_link(column, direction, params, extra_parameters = {}) #:nodoc:
column_attribute_name = if column.attribute.index('.') or column.main_table
column.attribute
else
column.table_alias_or_table_name + '.' + column.attribute
end
query_params = {@grid.name => {
@@order_parameter_name => column_attribute_name,
@@order_direction_parameter_name => direction
}}
cleaned_params = Wice::WgHash.deep_clone params
cleaned_params.merge!(extra_parameters)
cleaned_params.delete(:controller)
cleaned_params.delete(:action)
query_params = Wice::WgHash.rec_merge(cleaned_params, query_params)
'?' + query_params.to_query
end
protected
def filter_columns(method_name = nil) #:nodoc:
method_name ? @columns.select(&method_name) : @columns
end
end
end
| | |