#-- # Copyright (C) 2007 Dimitrij Denissenko # Please read LICENSE document for more information. #++ module RecordList class Paginator attr_reader :columns, :order, :page, :per_page, :use_ajax, :skip Order = Struct.new(:column_name, :direction) PerPage = Struct.new(:options, :selected) def initialize(controller, collection_id, columns, options = {}) @controller = controller @collection_id = collection_id @columns = columns @session_store = options.delete(:session_store) || [] @use_ajax = options.delete(:use_ajax) @skip = options.delete(:skip) || {} @order = skip.include?(:sorting) ? options[:order] : determine_order(options, @controller.params) @per_page = skip.include?(:per_page) ? options[:per_page] : determine_per_page(options, @controller.params) @page = determine_page(options, @controller.params) end def sortable_column_names self.columns.collect{|c| c.sortable? ? c.name : nil}.compact end def column_map @column_map ||= self.columns.index_by(&:name) end protected def determine_order(options, params) order = nil if params[:order] && params[:order].match(%r{[\w]+\.[0|1]}) col, dir = params[:order].split('.') if self.sortable_column_names.include?(col) order = Order.new(col, dir.to_i) end end if order.blank? && @session_store.include?(:order) order = retrieve_from_session(:order) end if order.blank? && options[:order].is_a?(Array) && sortable_column_names.include?(options[:order].first.to_s) order = Order.new(options[:order][0].to_s, options[:order][1] == 1 ? 1 : 0) end if order.blank? && !sortable_column_names.empty? order = Order.new(sortable_column_names.first, 0) end if !order.blank? && @session_store.include?(:order) store_in_session(:order, order) end order end def determine_per_page(options, params) per_page = nil if options[:per_page].is_a?(Array) per_page = PerPage.new(options[:per_page].sort, options[:per_page].first) if params[:per_page] && per_page.options.include?(params[:per_page].to_i) per_page.selected = params[:per_page].to_i elsif @session_store.include?(:per_page) && stored = retrieve_from_session(:per_page) per_page.selected = stored end if @session_store.include?(:per_page) store_in_session(:per_page, per_page.selected) end end per_page end def determine_page(options, params) page = nil if params[:page] page = params[:page] elsif @session_store.include?(:page) page = retrieve_from_session(:page) end if page && @session_store.include?(:page) store_in_session(:page, page) end page end def session_id Inflector.singularize(@collection_id.to_s).to_sym end def retrieve_from_session(attr) @controller.session[:record_list][self.session_id][attr] rescue nil end def store_in_session(attr, value) @controller.session[:record_list] ||= {} @controller.session[:record_list][self.session_id] ||= {} @controller.session[:record_list][self.session_id][attr] = value end end module ControllerExtension def rl_paginate(collection_id, columns, options={}) rl = RecordList::Paginator.new(self, collection_id, columns, options) if !rl.skip.include?(:per_page) && rl.per_page options[:per_page] = rl.per_page.selected end unless rl.skip.include?(:sorting) options[:order] = nil if rl.order && column = rl.column_map[rl.order.column_name] options[:order] = column.order_clause(rl.order.direction) end end params[:page] = rl.page pages, records = paginate(collection_id, options) pages.class.send(:attr_accessor, :rl) pages.rl = rl [pages, records] end def rl_define_columns manager = ColumnMananger.new yield manager manager.columns end end module HelperExtension def rl_header(rl_paginator) cols = rl_paginator.rl.columns.collect do |col| content = [_(col.caption)] tag_opts = {} if col.sortable? tag_opts[:class] = 'sortable' url_opts = {:order => col.name + '.0'} if (rl_paginator.rl.order && rl_paginator.rl.order.column_name == col.name) tag_opts[:class] << ' active' img_path, alt = case rl_paginator.rl.order.direction when 1 [ 'record_list/sort_desc.png', _('Sort descending') ] else [ 'record_list/sort_asc.png', _('Sort ascending') ] end content << image_tag(img_path, :alt => alt, :title => alt) url_opts = {:order => "#{col.name}.#{rl_paginator.rl.order.direction == 1 ? '0' : '1'}"} end tag_opts[:onclick] = rl_onclick_value(rl_paginator, url_opts) end content_tag('th', content.join(' '), tag_opts) end content_tag('tr', cols.join("\n"), :class => 'header') end def rl_navigation(rl_paginator) links = [] if rl_paginator.page_count > 1 links << rl_pagination_prev_link(rl_paginator) links << rl_pagination_links(rl_paginator) links << rl_pagination_next_link(rl_paginator) end td = content_tag('td', links.join(' | '), :colspan => rl_paginator.rl.columns.size) content_tag('tr', td, :class => 'navigation') end def rl_pagination_prev_link(rl_paginator) prev_link = _('Previous page') if page = rl_paginator.current.previous prev_link = rl_link_to(rl_paginator, prev_link, :page => page) end prev_link end def rl_pagination_next_link(rl_paginator) next_link = _('Next page') if page = rl_paginator.current.next next_link = rl_link_to(rl_paginator, next_link, :page => page) end next_link end def rl_pagination_links(rl_paginator) pagination_links_each(rl_paginator, {}) do |n| rl_link_to(rl_paginator, n.to_s, :page => n) end end def rl_footer(rl_paginator) cspan1 = rl_paginator.rl.columns.size / 2 cspan2 = rl_paginator.rl.columns.size - cspan1 td1 = content_tag('td', rl_load_indicator(rl_paginator), :colspan => cspan1, :class => 'indicator') td2 = content_tag('td', rl_per_page_selector(rl_paginator), :colspan => cspan2, :class => 'per-page') content_tag('tr', td1 + td2, :class => 'footer') end def rl_per_page_selector(rl_paginator) return '' unless rl_paginator.rl.per_page _('Display per page') + ': ' + rl_paginator.rl.per_page.options.collect do |num| if num == rl_paginator.rl.per_page.selected num.to_s else rl_link_to(rl_paginator, num.to_s, :per_page => num) end end.join(' | ') end def rl_load_indicator(rl_paginator, html_id = 'loadIndicator') return '' unless rl_paginator.rl.use_ajax image_tag('indicator.gif', :id => html_id, :style => 'display:none;') end private def rl_onclick_value(rl_paginator, options = {}) if rl_paginator.rl.use_ajax remote_function(rl_ajax_options(options)) else "window.location.href='#{url_for(rl_link_options(options))}';" end end def rl_link_to(rl_paginator, caption, options = {}) if rl_paginator.rl.use_ajax link_to_remote(caption.to_s, rl_ajax_options(options)) else link_to(caption.to_s, rl_link_options(options)) end end def rl_link_options(options = {}) {:overwrite_params => options} end def rl_ajax_options(overwrite_params = {}) options = {} options[:url] = rl_link_options(overwrite_params) options[:loading] = "Element.show('loadIndicator')" options[:complete] = "Element.hide('loadIndicator')" options end end private class ColumnMananger attr_reader :columns def push(name, options = {}) @columns ||= [] @columns << Column.new(name, options) end end class Column attr_reader :name, :caption, :order, :column_name def initialize(name, options = {}) @name = name.to_s @caption = options[:caption] || @name.humanize @sortable = options[:sortable] == false ? false : true @order = options[:order] @column_name = options[:column_name] || @name end def sortable? @sortable end def order_clause(direction) suffix = direction == 1 ? 'DESC' : 'ASC' prefix = self.order ? self.order : self.column_name "#{prefix} #{suffix}" end end end ActionController::Base.send(:include, RecordList::ControllerExtension) ActionView::Base.send(:include, RecordList::HelperExtension)