PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/app/models/caboose/page_bar_generator.rb

https://github.com/billyswifty/caboose-cms
Ruby | 454 lines | 355 code | 43 blank | 56 comment | 107 complexity | 72e4e1e265ee2df70845599c4c2a5a4f MD5 | raw file
  1. # Note:
  2. # For includes to work with namespaces other than the root namespace, the
  3. # full namespaced class_name has to be set in the model on the association
  4. # being included. For example:
  5. #
  6. # class Animals::Dog
  7. # has_many :friends, :class_name => 'Animals::Dog'
  8. # end
  9. #
  10. module Caboose
  11. class PageBarGenerator
  12. #
  13. # Parameters:
  14. # params: array of key/value pairs that must include the following:
  15. # base_url: url without querystring onto which the parameters are added.
  16. # itemCount: Total number of items.
  17. #
  18. # In addition, the following parameters are not required but may be
  19. # included in the array:
  20. # itemsPerPage: Number of items you want to show per page. Defaults to 10 if not present.
  21. # page: Current page number. Defaults to 0 if not present.
  22. #
  23. attr_accessor :original_params, :params, :options, :custom_url_vars, :post_get
  24. #def initialize(post_get, params = nil, options = nil, &custom_url_vars = nil)
  25. def initialize(post_get, params = nil, options = nil)
  26. params = {} if params.nil?
  27. options = {} if options.nil?
  28. # Note: a few keys are required:
  29. # base_url, page, itemCount, itemsPerPage
  30. @post_get = post_get
  31. @original_params = {}
  32. @params = {}
  33. @options = {
  34. 'model' => '',
  35. 'sort' => '',
  36. 'desc' => 0,
  37. 'base_url' => '',
  38. 'page' => 1,
  39. 'item_count' => 0,
  40. 'items_per_page' => 10,
  41. 'abbreviations' => {},
  42. 'skip' => [], # params to skip when printing the page bar
  43. 'additional_where' => [],
  44. 'includes' => nil # Hash of association includes
  45. # {
  46. # search_field_1 => [association_name, join_table, column_name],
  47. # search_field_2 => [association_name, join_table, column_name]
  48. # }
  49. }
  50. params.each do |key, val|
  51. @original_params[key] = val
  52. @params[key] = val
  53. end
  54. options.each { |key, val| @options[key] = val }
  55. #@params.each { |key, val|
  56. # k = @options['abbreviations'].include?(key) ? @options['abbreviations'][key] : nil
  57. # @params[key] = post_get[key].nil? ? (k && !post_get[k].nil? ? post_get[k] : val) : post_get[key]
  58. #}
  59. new_params = {}
  60. keys_to_delete = []
  61. @params.each { |key, val|
  62. next if !@options['abbreviations'].has_key?(key)
  63. long_key = @options['abbreviations'][key]
  64. new_params[long_key] = post_get[key] ? post_get[key] : val
  65. keys_to_delete << key
  66. }
  67. keys_to_delete.each { |k| @params.delete(k) }
  68. new_params.each { |k,v| @params[k] = v }
  69. @original_params.each { |k,v| @original_params[k] = post_get[k] ? post_get[k] : v }
  70. @params.each { |k,v| @params[k] = post_get[k] ? post_get[k] : v }
  71. @options.each { |k,v| @options[k] = ok(post_get[k]) ? post_get[k] : v }
  72. #@custom_url_vars = custom_url_vars if !custom_url_vars.nil?
  73. @use_url_params = @options['use_url_params'].nil? ? Caboose.use_url_params : @options['use_url_params']
  74. fix_desc
  75. set_item_count
  76. end
  77. def set_item_count
  78. m = model_with_includes.where(self.where)
  79. n = self.near
  80. m = m.near(n[0], n[1]) if n
  81. @options['item_count'] = m.count
  82. end
  83. def model_with_includes
  84. m = @options['model'].constantize
  85. return m if @options['includes'].nil?
  86. associations = []
  87. # See if any fields that we know have includes have values
  88. @params.each do |k,v|
  89. next if v.nil? || (v.kind_of?(String) && k.length == 0)
  90. k.split('_concat_').each do |k2|
  91. next if !@options['includes'].has_key?(k2)
  92. associations << @options['includes'][k2][0]
  93. end
  94. end
  95. # See if any fields in the sort option are listed in a table_name.column_name format
  96. if @options['sort']
  97. @options['sort'].split(',').each do |col|
  98. tbl_col = col.split('.')
  99. associations << association_for_table_name(tbl_col[0]) if tbl_col && tbl_col.count > 1
  100. end
  101. end
  102. associations.uniq.each { |assoc| m = m.includes(assoc) }
  103. return m
  104. end
  105. def association_for_table_name(table_name)
  106. @options['includes'].each do |field, arr|
  107. return arr[0] if table_name_of_association(arr[0]) == table_name
  108. end
  109. return false
  110. end
  111. def table_name_of_association(assoc)
  112. return @options['model'].constantize.reflect_on_association(assoc.to_sym).class_name.constantize.table_name
  113. end
  114. def fix_desc
  115. @options['desc'] = 0 and return if @options['desc'].nil?
  116. return if @options['desc'] == 1
  117. return if @options['desc'] == 0
  118. @options['desc'] = 1 and return if @options['desc'] == 'true' || @options['desc'].is_a?(TrueClass)
  119. @options['desc'] = 0 and return if @options['desc'] == 'false' || @options['desc'].is_a?(FalseClass)
  120. @options['desc'] = @options['desc'].to_i
  121. end
  122. def ok(val)
  123. return false if val.nil?
  124. return true if val.is_a? Array
  125. return true if val.is_a? Hash
  126. return true if val.is_a? Integer
  127. return true if val.is_a? Fixnum
  128. return true if val.is_a? Float
  129. return true if val.is_a? Bignum
  130. return true if val.is_a? TrueClass
  131. return true if val.is_a? FalseClass
  132. return false if val == ""
  133. return true
  134. end
  135. def items
  136. assoc = model_with_includes.where(self.where)
  137. n = self.near
  138. assoc = assoc.near(n[0], n[1]) if n
  139. if @options['items_per_page'] != -1
  140. assoc = assoc.limit(self.limit).offset(self.offset)
  141. end
  142. return assoc.reorder(self.reorder).all
  143. end
  144. def all_items
  145. m = model_with_includes.where(self.where)
  146. n = self.near
  147. m = m.near(n[0], n[1]) if n
  148. return m.all
  149. end
  150. def item_values(attribute)
  151. arr = []
  152. m = model_with_includes.where(self.where)
  153. n = self.near
  154. m = m.near(n[0], n[1]) if n
  155. m.all.each do |m2|
  156. arr << m2[attribute]
  157. end
  158. return arr.uniq
  159. end
  160. def generate(summary = true)
  161. # Check for necessary parameter values
  162. return false if !ok(@options['base_url']) # Error: base_url is required for the page bar generator to work.
  163. return false if !ok(@options['item_count']) # Error: itemCount is required for the page bar generator to work.
  164. # Set default parameter values if not present
  165. @options['items_per_page'] = 10 if @options["items_per_page"].nil?
  166. @options['page'] = 1 if @options["page"].nil?
  167. page = @options["page"].to_i
  168. # Max links to show (must be odd)
  169. total_links = 5
  170. prev_page = page - 1
  171. next_page = page + 1
  172. total_pages = (@options['item_count'].to_f / @options['items_per_page'].to_f).ceil
  173. if (total_pages < total_links)
  174. start = 1
  175. stop = total_pages
  176. else
  177. start = page - (total_links/2).floor
  178. start = 1 if start < 1
  179. stop = start + total_links - 1
  180. if (stop > total_pages)
  181. stop = total_pages
  182. start = stop - total_links
  183. start = 1 if start < 1
  184. end
  185. end
  186. base_url = url_with_vars
  187. base_url << (@use_url_params ? "/" : (base_url.include?("?") ? "&" : "?"))
  188. keyval_delim = @use_url_params ? "/" : "="
  189. var_delim = @use_url_params ? "/" : "&"
  190. str = ''
  191. str << "<p>Results: showing page #{page} of #{total_pages}</p>\n" if summary
  192. if (total_pages > 1)
  193. str << "<div class='page_links'>\n"
  194. if (page > 1)
  195. str << "<a href='#{base_url}page#{keyval_delim}#{prev_page}'>Previous</a>"
  196. end
  197. for i in start..stop
  198. if (page != i)
  199. str << "<a href='#{base_url}page#{keyval_delim}#{i}'>#{i}</a>"
  200. else
  201. str << "<span class='current_page'>#{i}</span>"
  202. end
  203. end
  204. if (page < total_pages)
  205. str << "<a href='#{base_url}page#{keyval_delim}#{next_page}'>Next</a>"
  206. end
  207. str << "</div>\n"
  208. end
  209. return str
  210. end
  211. def url_with_vars()
  212. if !@custom_url_vars.nil?
  213. return @custom_url_vars.call @options['base_url'], @params
  214. end
  215. vars = []
  216. @original_params.each do |k,v|
  217. next if @options['skip'].include?(k)
  218. k = @options['abbreviations'].include?(k) ? @options['abbreviations'][k] : k
  219. if v.kind_of?(Array)
  220. v.each do |v2|
  221. if @use_url_params
  222. vars.push("#{k}/#{v2}") if !v2.nil?
  223. else
  224. vars.push("#{k}[]=#{v2}") if !v2.nil?
  225. end
  226. end
  227. else
  228. next if v.nil? || (v.kind_of?(String) && v.length == 0)
  229. if @use_url_params
  230. vars.push("#{k}/#{v}")
  231. else
  232. vars.push("#{k}=#{v}")
  233. end
  234. end
  235. end
  236. if @use_url_params
  237. vars.push("sort/#{@options['sort']}")
  238. vars.push("desc/#{@options['desc']}")
  239. #vars.push("page/#{@options['page']}")
  240. else
  241. vars.push("sort=#{@options['sort']}")
  242. vars.push("desc=#{@options['desc']}")
  243. #vars.push("page=#{@options['page']}")
  244. end
  245. return "#{@options['base_url']}" if vars.length == 0
  246. if @use_url_params
  247. vars = URI.escape(vars.join('/'))
  248. return "#{@options['base_url']}/#{vars}"
  249. end
  250. vars = URI.escape(vars.join('&'))
  251. return "#{@options['base_url']}?#{vars}"
  252. end
  253. def sortable_table_headings(cols)
  254. base_url = url_with_vars
  255. base_url << (base_url.include?("?") ? "&" : "?")
  256. str = ''
  257. # key = sort field, value = text to display
  258. cols.each do |sort, text|
  259. arrow = @options['sort'] == sort ? (@options['desc'] == 1 ? ' &uarr;' : ' &darr;') : ''
  260. #link = @options['base_url'] + "?#{vars}&sort=#{sort}&desc=" + (@options['desc'] == 1 ? "0" : "1")
  261. link = "#{base_url}sort=#{sort}&desc=" + (@options['desc'] == 1 ? "0" : "1")
  262. str += "<th><a href='#{link}'>#{text}#{arrow}</a></th>\n"
  263. end
  264. return str
  265. end
  266. def sortable_links(cols)
  267. base_url = url_with_vars
  268. base_url << (base_url.include?("?") ? "&" : "?")
  269. h = {}
  270. # key = sort field, value = text to display
  271. cols.each do |sort, text|
  272. arrow = @options['sort'] == sort ? (@options['desc'] == 1 ? ' &uarr;' : ' &darr;') : ''
  273. link = "#{base_url}sort=#{sort}&desc=" + (@options['desc'] == 1 ? "0" : "1")
  274. h[sort] = [link, "#{text}#{arrow}"]
  275. end
  276. return h
  277. end
  278. def where
  279. sql = []
  280. values = []
  281. table = @options['model'].constantize.table_name
  282. @params.each do |k,v|
  283. next if v.nil? || (v.kind_of?(String) && v.length == 0)
  284. next if k.ends_with?('_near')
  285. col = nil
  286. if @options['includes'] && @options['includes'].include?(k)
  287. arr = @options['includes'][k]
  288. col = "#{table_name_of_association(arr[0])}.#{arr[1]}"
  289. end
  290. if k.include?('_concat_')
  291. #arr = k.split('_concat_')
  292. #col1 = arr[0]
  293. #col2 = arr[1]
  294. #
  295. #col2 = col2[0..-5] if col2.ends_with?('_gte')
  296. #col2 = col2[0..-4] if col2.ends_with?('_gt')
  297. #col2 = col2[0..-5] if col2.ends_with?('_lte')
  298. #col2 = col2[0..-4] if col2.ends_with?('_lt')
  299. #col2 = col2[0..-4] if col2.ends_with?('_bw')
  300. #col2 = col2[0..-4] if col2.ends_with?('_ew')
  301. #col2 = col2[0..-6] if col2.ends_with?('_like')
  302. #
  303. #col = "concat(#{col1},' ', #{col2})"
  304. arr = k.split('_concat_')
  305. arr[arr.count-1] = arr[arr.count-1][0..-5] if k.ends_with?('_gte')
  306. arr[arr.count-1] = arr[arr.count-1][0..-4] if k.ends_with?('_gt')
  307. arr[arr.count-1] = arr[arr.count-1][0..-5] if k.ends_with?('_lte')
  308. arr[arr.count-1] = arr[arr.count-1][0..-4] if k.ends_with?('_lt')
  309. arr[arr.count-1] = arr[arr.count-1][0..-4] if k.ends_with?('_bw')
  310. arr[arr.count-1] = arr[arr.count-1][0..-4] if k.ends_with?('_ew')
  311. arr[arr.count-1] = arr[arr.count-1][0..-6] if k.ends_with?('_like')
  312. arr[arr.count-1] = arr[arr.count-1][0..-6] if k.ends_with?('_null')
  313. arr2 = []
  314. arr.each do |col|
  315. if @options['includes'] && @options['includes'].include?(col)
  316. arr3 = @options['includes'][col]
  317. arr2 << "#{table_name_of_association(arr3[0])}.#{arr3[1]}"
  318. else
  319. arr2 << col
  320. end
  321. end
  322. col = "concat(#{arr2.join(",' ',")})"
  323. end
  324. sql2 = ""
  325. if k.ends_with?('_gte')
  326. col = "#{table}.#{k[0..-5]}" if col.nil?
  327. sql2 = "#{col} >= ?"
  328. elsif k.ends_with?('_gt')
  329. col = "#{table}.#{k[0..-4]}" if col.nil?
  330. sql2 = "#{col} > ?"
  331. elsif k.ends_with?('_lte')
  332. col = "#{table}.#{k[0..-5]}" if col.nil?
  333. sql2 = "#{col} <= ?"
  334. elsif k.ends_with?('_lt')
  335. col = "#{table}.#{k[0..-4]}" if col.nil?
  336. sql2 = "#{col} < ?"
  337. elsif k.ends_with?('_bw')
  338. col = "#{table}.#{k[0..-4]}" if col.nil?
  339. sql2 = "upper(#{col}) like ?"
  340. v = v.kind_of?(Array) ? v.collect{ |v2| "#{v2}%".upcase } : "#{v}%".upcase
  341. elsif k.ends_with?('_ew')
  342. col = "#{table}.#{k[0..-4]}" if col.nil?
  343. sql2 = "upper(#{col}) like ?"
  344. v = v.kind_of?(Array) ? v.collect{ |v2| "%#{v2}".upcase } : "%#{v}".upcase
  345. elsif k.ends_with?('_like')
  346. col = "#{table}.#{k[0..-6]}" if col.nil?
  347. sql2 = "upper(#{col}) like ?"
  348. v = v.kind_of?(Array) ? v.collect{ |v2| "%#{v2}%".upcase } : "%#{v}%".upcase
  349. elsif k.ends_with?('_null')
  350. col = "#{table}.#{k[0..-6]}" if col.nil?
  351. sql2 = "#{col} #{v == true ? 'is' : 'is not'} null"
  352. #v = v == true ? 'is' : 'is not'
  353. else
  354. col = "#{table}.#{k}" if col.nil?
  355. sql2 = "#{col} = ?"
  356. end
  357. if v.kind_of?(Array)
  358. sql2 = "(" + v.collect{ |v2| "#{sql2}" }.join(" or ") + ")"
  359. v.each { |v2| values << v2 }
  360. elsif !k.ends_with?('_null')
  361. values << v
  362. end
  363. sql << sql2
  364. end
  365. @options['additional_where'].each { |x| sql << x }
  366. sql_str = sql.join(' and ')
  367. sql = [sql_str]
  368. values.each { |v| sql << v }
  369. return sql
  370. end
  371. def near
  372. @params.each do |k,v|
  373. next if !k.ends_with?('_near')
  374. arr = k.split('_')
  375. if arr.length == 3
  376. location = @post_get[arr[0]]
  377. radius = @post_get[arr[1]]
  378. v = [location, radius] if location && radius && location.strip.length > 0 && radius.strip.length > 0
  379. end
  380. next if v.nil? || !v.is_a?(Array) || v.count != 2
  381. return v
  382. end
  383. return nil
  384. end
  385. def limit
  386. return @options['items_per_page'].to_i
  387. end
  388. def offset
  389. return (@options['page'].to_i - 1) * @options['items_per_page'].to_i
  390. end
  391. def reorder
  392. str = "id"
  393. if (!@options['sort'].nil? && @options['sort'].length > 0)
  394. str = "#{@options['sort']}"
  395. end
  396. str << " desc" if @options['desc'] == 1
  397. return str
  398. end
  399. def to_json
  400. {
  401. :base_url => @options['base_url'],
  402. :sort => @options['sort'],
  403. :desc => @options['desc'],
  404. :item_count => @options['item_count'],
  405. :items_per_page => @options['items_per_page'],
  406. :page => @options['page'],
  407. :use_url_params => @use_url_params
  408. }
  409. end
  410. end
  411. end