# encoding: UTF-8 module Wice module Controller def self.included(base) #:nodoc: base.extend(ClassMethods) end module ClassMethods # Used to add query processing action methods into a controller. # Read section "Saving Queries How-To" in README for more details. def save_wice_grid_queries include Wice::SerializedQueriesControllerMixin end end protected attr_accessor :wice_grid_instances # Creates a grid object to be used in the view. This is the main model, and the underlying table is the default table - # if in other parameters a column name is mentioned without the name of the table, this table is implied. # Just like in an ordinary ActiveRecord find you can use :joins, :include, and :conditions. # # The first parameter is an ActiveRecord class name or an ActiveRecord::Relation instance. The generated ActiveRecord call will use it as the # receiver of the paginate method: klass.paginate(...) # # The second parameters is a hash of parameters: # * :joins - ActiveRecord :joins option. # * :include - ActiveRecord :include option. # * :conditions - ActiveRecord :conditions option. # * :per_page - Number of rows per one page. The default is 10. # * :page - The page to show when rendering the grid for the first time. The default is one, naturally. # * :order - Name of the column to sort by. Can be of a short form (just the name of the column) if this # is a column of the main table (the table of the main ActiveRecord model, the first parameter of initialize_grid), # or a fully qualified name with the name of the table. # * :order_direction - :asc for ascending or :desc for descending. The default is :asc. # * :name - name of the grid. Only needed if there is a second grid on a page. The name serves as the base name for # HTTP parametes, DOM IDs, etc. The shorter the name, the shorter the GET request is. The name can only contain alphanumeruc characters. # * :enable_export_to_csv - :csv_file_name - Name of the exported CSV file. If the parameter is missing, the name of the grid will be used instead. # * :csv_field_separator - field separator for CSV files. The default is defined in +CSV_FIELD_SEPARATOR+ in the config file. # * :custom_order - used for overriding the ORDER BY clause with custom sql code (for example, including a function). # The value of the parameter is a hash where keys are fully qualified names # of database columns, and values the required chunks of SQL to use in the ORDER BY clause, either as strings or Proc object # evaluating to string. See section 'Custom Ordering' in the README. # * :saved_query - id of the saved query or the query object itself to load initially. # Read section "Saving Queries How-To" in README for more details. # * :after - defined a name of a controller method which would be called by the grid after all user input has been processed, # with a single parameter which is a Proc object. Once called, the object returns a list of all records of the current selection # throughout all pages. See section "Integration With The Application" in the README. # * :total_entries - If not specified, will_paginate will run a select count # * :select - ActiveRecord :select option. Please do not forget that :select is ignored # when :include is present. It is unlikely you would need :select with WiceGrid, but if you do, # use it with care :) # * :group - ActiveRecord :group option. Use it if you are sure you know what you are doing :) # * :with_paginated_resultset - a callback executed from within the plugin to process records of the current page. # Can be a lambda object or a controller method name (symbol). The argument to the callback is the array of the records. # * :with_resultset - a callback executed from within the plugin to process all records browsable through # all pages with the current filters. Can be a lambda object or a controller method name (symbol). The argument to # the callback is a lambda object which returns the list of records when called. See the README for the explanation. # # Defaults for parameters :per_page, :order_direction, and :name # can be changed in lib/wice_grid_config.rb, this is convenient if you want to set a project wide setting # without having to repeat it for every grid instance. def initialize_grid(klass, opts = {}) wg = WiceGrid.new(klass, self, opts) self.wice_grid_instances = [] if self.wice_grid_instances.nil? self.wice_grid_instances << wg wg end # +export_grid_if_requested+ is a controller method which should be called at the end of each action containing grids with enabled # CSV export. # # CSV export will only work if each WiceGrid helper is placed in a partial of its own (requiring it from the master template # of course for the usual flow). # +export_grid_if_requested+ intercepts CSV export requests and evaluates the corresponding partial with the required grid helper. # By default for each grid +export_grid_if_requested+ will look for a partial # whose name follows the following pattern: # # _GRID_NAME_grid.html.erb # # For example, a grid named +orders+ is supposed to be found in a template called _orders_grid.html.erb, # Remember that the default name of grids is +grid+. # # This convention can be easily overridden by supplying a hash parameter to +export_grid_if_requested+ where each key is the name of # a grid, and the value is the name of the template (like it is specified for +render+, i.e. without '_' and extensions): # # export_grid_if_requested(:grid => 'orders', 'grid2' => 'invoices') # # If the request is not a CSV export request, the method does nothing and returns +false+, if it is a CSV export request, # the method returns +true+. # # If the action has no explicit +render+ call, it's OK to just place +export_grid_if_requested+ as the last line of the action. Otherwise, # to avoid double rendering, use the return value of the method to conditionally call your +render+ : # # export_grid_if_requested || render(:action => 'index') # # It's also possible to supply a block which will be called if no CSV export is requested: # # export_grid_if_requested do # render(:action => 'index') # end def export_grid_if_requested(opts = {}) grid = self.wice_grid_instances.detect(&:output_csv?) if grid template_name = opts[grid.name] || opts[grid.name.intern] template_name ||= grid.name + '_grid' temp_filename = render_to_string(:partial => template_name) temp_filename = temp_filename.strip filename = (grid.csv_file_name || grid.name ) + '.csv' grid.csv_tempfile.close send_file_rails2 temp_filename, :filename => filename, :type => 'text/csv' grid.csv_tempfile = nil true else yield if block_given? false end end # +wice_grid_custom_filter_params+ generates HTTP parameters understood by WiceGrid custom filters. # Combined with Rails route helpers it allows to generate links leading to # grids with pre-selected custom filters. # # Parameters: # * :grid_name - The name of the grid. Just like parameter :name of # initialize_grid, the parameter is optional, and when absent, the name # 'grid' is assumed # * :attribute and :model - should be the same as :attribute and # :model of the column declaration with the target custom filter. # * :value - the value of the column filter. def wice_grid_custom_filter_params(opts = {}) options = {:grid_name => 'grid', :attribute => nil, :model => nil, :value => nil} options.merge!(opts) [:attribute, :value].each do |key| raise ::Wice::WiceGridArgumentError.new("wice_grid_custom_filter_params: :#{key} is a mandatory argument") unless options[key] end attr_name = if options[:model] unless options[:model].nil? options[:model] = options[:model].constantize if options[:model].is_a? String raise Wice::WiceGridArgumentError.new("Option :model can be either a class or a string instance") unless options[:model].is_a? Class end options[:model].table_name + '.' + options[:attribute] else options[:attribute] end {"#{options[:grid_name]}[f][#{attr_name}][]" => options[:value]} end private def send_file_rails2(path, options = {}) #:doc: raise "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) options[:length] ||= File.size(path) options[:filename] ||= File.basename(path) unless options[:url_based_filename] send_file_headers_rails2! options @performed_render = false logger.info "Sending file #{path}" unless logger.nil? File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read } end DEFAULT_SEND_FILE_OPTIONS_RAILS2 = { :type => 'application/octet-stream'.freeze, :disposition => 'attachment'.freeze, :stream => true, :buffer_size => 4096, :x_sendfile => false }.freeze def send_file_headers_rails2!(options) #:doc: options.update(DEFAULT_SEND_FILE_OPTIONS_RAILS2.merge(options)) [:length, :type, :disposition].each do |arg| raise ArgumentError, ":#{arg} option required" if options[arg].nil? end disposition = options[:disposition].dup || 'attachment' disposition <<= %(; filename="#{options[:filename]}") if options[:filename] content_type = options[:type] content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers headers.merge!( 'Content-Length' => options[:length].to_s, 'Content-Type' => content_type, 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary' ) # Fix a problem with IE 6.0 on opening downloaded files: # If Cache-Control: no-cache is set (which Rails does by default), # IE removes the file it just downloaded from its cache immediately # after it displays the "open/save" dialog, which means that if you # hit "open" the file isn't there anymore when the application that # is called for handling the download is run, so let's workaround that headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' end end end