active_scaffold 3.4.43 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/{LICENSE → LICENSE.md} +1 -1
  4. data/README.md +27 -19
  5. data/app/assets/javascripts/active_scaffold.js.erb +1 -1
  6. data/app/assets/javascripts/jquery/active_scaffold.js +95 -43
  7. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +30 -6
  8. data/app/assets/javascripts/prototype/tiny_mce_bridge.js +11 -1
  9. data/app/assets/stylesheets/active_scaffold_colors.scss +2 -2
  10. data/app/assets/stylesheets/active_scaffold_layout.css +36 -28
  11. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -3
  12. data/app/views/active_scaffold_overrides/_field_search.html.erb +8 -7
  13. data/app/views/active_scaffold_overrides/_form_association.html.erb +9 -9
  14. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +6 -6
  15. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +52 -50
  16. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  17. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_human_conditions.html.erb +3 -1
  19. data/app/views/active_scaffold_overrides/_list_calculations.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_list_column_headings.html.erb +2 -0
  21. data/app/views/active_scaffold_overrides/_list_messages.html.erb +5 -3
  22. data/app/views/active_scaffold_overrides/_list_record.html.erb +3 -1
  23. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +9 -9
  24. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -1
  25. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +18 -10
  26. data/app/views/active_scaffold_overrides/_render_field.js.erb +3 -3
  27. data/app/views/active_scaffold_overrides/_search.html.erb +7 -6
  28. data/app/views/active_scaffold_overrides/_show_actions.html.erb +14 -0
  29. data/app/views/active_scaffold_overrides/_show_association.html.erb +1 -1
  30. data/app/views/active_scaffold_overrides/_update_actions.html.erb +6 -2
  31. data/app/views/active_scaffold_overrides/_update_column.js.erb +1 -1
  32. data/app/views/active_scaffold_overrides/_update_form.html.erb +1 -1
  33. data/app/views/active_scaffold_overrides/destroy.js.erb +2 -3
  34. data/app/views/active_scaffold_overrides/edit_associated.js.erb +4 -3
  35. data/app/views/active_scaffold_overrides/on_action_update.js.erb +5 -3
  36. data/app/views/active_scaffold_overrides/on_create.js.erb +4 -4
  37. data/app/views/active_scaffold_overrides/on_update.js.erb +6 -6
  38. data/app/views/active_scaffold_overrides/show.html.erb +6 -0
  39. data/app/views/active_scaffold_overrides/update.html.erb +1 -1
  40. data/app/views/active_scaffold_overrides/update_column.js.erb +1 -1
  41. data/config/brakeman.ignore +26 -0
  42. data/config/brakeman.yml +3 -0
  43. data/config/i18n-tasks.yml +121 -0
  44. data/config/locales/de.yml +81 -70
  45. data/config/locales/en.yml +83 -74
  46. data/config/locales/es.yml +82 -73
  47. data/config/locales/fr.yml +86 -75
  48. data/config/locales/hu.yml +81 -70
  49. data/config/locales/ja.yml +71 -60
  50. data/config/locales/ru.yml +85 -74
  51. data/lib/active_scaffold.rb +3 -0
  52. data/lib/active_scaffold/actions/common_search.rb +11 -7
  53. data/lib/active_scaffold/actions/core.rb +119 -47
  54. data/lib/active_scaffold/actions/create.rb +1 -1
  55. data/lib/active_scaffold/actions/delete.rb +11 -8
  56. data/lib/active_scaffold/actions/field_search.rb +104 -6
  57. data/lib/active_scaffold/actions/list.rb +25 -21
  58. data/lib/active_scaffold/actions/mark.rb +12 -4
  59. data/lib/active_scaffold/actions/nested.rb +26 -26
  60. data/lib/active_scaffold/actions/search.rb +2 -2
  61. data/lib/active_scaffold/actions/show.rb +4 -5
  62. data/lib/active_scaffold/actions/subform.rb +9 -7
  63. data/lib/active_scaffold/actions/update.rb +20 -13
  64. data/lib/active_scaffold/active_record_permissions.rb +24 -5
  65. data/lib/active_scaffold/attribute_params.rb +68 -49
  66. data/lib/active_scaffold/bridges.rb +1 -1
  67. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +15 -19
  68. data/lib/active_scaffold/bridges/bitfields.rb +1 -1
  69. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +10 -14
  70. data/lib/active_scaffold/bridges/calendar_date_select.rb +0 -7
  71. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +19 -22
  72. data/lib/active_scaffold/bridges/cancan.rb +4 -3
  73. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +11 -21
  74. data/lib/active_scaffold/bridges/carrierwave.rb +2 -1
  75. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -6
  76. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +6 -39
  77. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  78. data/lib/active_scaffold/bridges/chosen.rb +4 -1
  79. data/lib/active_scaffold/bridges/chosen/helpers.rb +3 -2
  80. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -2
  81. data/lib/active_scaffold/bridges/date_picker.rb +3 -0
  82. data/lib/active_scaffold/bridges/date_picker/ext.rb +43 -38
  83. data/lib/active_scaffold/bridges/date_picker/helper.rb +24 -23
  84. data/lib/active_scaffold/bridges/dragonfly.rb +1 -1
  85. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +3 -7
  86. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -25
  87. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -2
  88. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +6 -8
  89. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +1 -1
  90. data/lib/active_scaffold/bridges/file_column/form_ui.rb +0 -2
  91. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -1
  92. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +1 -1
  93. data/lib/active_scaffold/bridges/paper_trail/actions.rb +1 -1
  94. data/lib/active_scaffold/bridges/paper_trail/helper.rb +1 -2
  95. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +3 -7
  96. data/lib/active_scaffold/bridges/paperclip.rb +1 -1
  97. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -28
  98. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  99. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +3 -7
  100. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  101. data/lib/active_scaffold/bridges/record_select/helpers.rb +14 -18
  102. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +4 -8
  103. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -20
  104. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +7 -22
  105. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +14 -14
  106. data/lib/active_scaffold/config/base.rb +9 -6
  107. data/lib/active_scaffold/config/core.rb +30 -21
  108. data/lib/active_scaffold/config/create.rb +2 -1
  109. data/lib/active_scaffold/config/delete.rb +2 -2
  110. data/lib/active_scaffold/config/field_search.rb +9 -3
  111. data/lib/active_scaffold/config/form.rb +4 -4
  112. data/lib/active_scaffold/config/list.rb +27 -23
  113. data/lib/active_scaffold/config/nested.rb +4 -4
  114. data/lib/active_scaffold/config/search.rb +6 -6
  115. data/lib/active_scaffold/config/show.rb +11 -1
  116. data/lib/active_scaffold/config/subform.rb +1 -1
  117. data/lib/active_scaffold/config/update.rb +4 -2
  118. data/lib/active_scaffold/constraints.rb +39 -36
  119. data/lib/active_scaffold/core.rb +36 -15
  120. data/lib/active_scaffold/data_structures/action_columns.rb +14 -9
  121. data/lib/active_scaffold/data_structures/action_link.rb +4 -5
  122. data/lib/active_scaffold/data_structures/action_links.rb +5 -4
  123. data/lib/active_scaffold/data_structures/actions.rb +2 -2
  124. data/lib/active_scaffold/data_structures/association.rb +8 -0
  125. data/lib/active_scaffold/data_structures/association/abstract.rb +147 -0
  126. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +42 -0
  127. data/lib/active_scaffold/data_structures/association/active_record.rb +94 -0
  128. data/lib/active_scaffold/data_structures/association/mongoid.rb +45 -0
  129. data/lib/active_scaffold/data_structures/bridge.rb +3 -6
  130. data/lib/active_scaffold/data_structures/column.rb +100 -82
  131. data/lib/active_scaffold/data_structures/columns.rb +21 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +22 -37
  133. data/lib/active_scaffold/data_structures/set.rb +4 -4
  134. data/lib/active_scaffold/data_structures/sorting.rb +29 -15
  135. data/lib/active_scaffold/engine.rb +3 -1
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +10 -5
  137. data/lib/active_scaffold/extensions/action_view_rendering.rb +65 -59
  138. data/lib/active_scaffold/extensions/left_outer_joins.rb +48 -53
  139. data/lib/active_scaffold/extensions/localize.rb +3 -4
  140. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +7 -11
  141. data/lib/active_scaffold/extensions/paginator_extensions.rb +20 -18
  142. data/lib/active_scaffold/extensions/routing_mapper.rb +104 -40
  143. data/lib/active_scaffold/extensions/to_label.rb +1 -1
  144. data/lib/active_scaffold/extensions/unsaved_associated.rb +4 -13
  145. data/lib/active_scaffold/extensions/unsaved_record.rb +12 -1
  146. data/lib/active_scaffold/finder.rb +200 -134
  147. data/lib/active_scaffold/helpers/action_link_helpers.rb +398 -0
  148. data/lib/active_scaffold/helpers/association_helpers.rb +12 -30
  149. data/lib/active_scaffold/helpers/controller_helpers.rb +74 -24
  150. data/lib/active_scaffold/helpers/form_column_helpers.rb +205 -112
  151. data/lib/active_scaffold/helpers/human_condition_helpers.rb +21 -11
  152. data/lib/active_scaffold/helpers/id_helpers.rb +1 -1
  153. data/lib/active_scaffold/helpers/list_column_helpers.rb +117 -39
  154. data/lib/active_scaffold/helpers/pagination_helpers.rb +11 -14
  155. data/lib/active_scaffold/helpers/search_column_helpers.rb +69 -32
  156. data/lib/active_scaffold/helpers/show_column_helpers.rb +9 -3
  157. data/lib/active_scaffold/helpers/view_helpers.rb +41 -426
  158. data/lib/active_scaffold/orm_checks.rb +109 -0
  159. data/lib/active_scaffold/paginator.rb +1 -1
  160. data/lib/active_scaffold/responds_to_parent.rb +12 -10
  161. data/lib/active_scaffold/tableless.rb +81 -43
  162. data/lib/active_scaffold/version.rb +2 -2
  163. data/lib/generators/active_scaffold/controller_generator.rb +49 -0
  164. data/lib/generators/active_scaffold/install_generator.rb +45 -0
  165. data/lib/generators/active_scaffold/resource_generator.rb +56 -0
  166. data/lib/generators/{active_scaffold_controller/templates → templates}/controller.rb +0 -0
  167. data/lib/generators/{active_scaffold_controller/templates → templates}/helper.rb +0 -0
  168. data/shoulda_macros/macros.rb +3 -3
  169. data/test/active_scaffold_config_mock.rb +33 -0
  170. data/test/bridges/bridge_test.rb +9 -9
  171. data/test/bridges/date_picker_test.rb +3 -1
  172. data/test/bridges/paper_trail_test.rb +2 -3
  173. data/test/bridges/paperclip_test.rb +21 -10
  174. data/test/bridges/tiny_mce_test.rb +20 -21
  175. data/test/class_with_finder.rb +42 -0
  176. data/test/company.rb +6 -4
  177. data/test/config/core_test.rb +1 -1
  178. data/test/config/create_test.rb +1 -1
  179. data/test/config/list_test.rb +3 -3
  180. data/test/config/update_test.rb +3 -3
  181. data/test/data_structures/action_columns_test.rb +3 -3
  182. data/test/data_structures/association_column_test.rb +5 -5
  183. data/test/data_structures/column_test.rb +14 -14
  184. data/test/data_structures/columns_test.rb +2 -2
  185. data/test/data_structures/set_test.rb +2 -2
  186. data/test/data_structures/sorting_test.rb +6 -4
  187. data/test/extensions/active_record_test.rb +1 -1
  188. data/test/extensions/routing_mapper_test.rb +64 -13
  189. data/test/helpers/form_column_helpers_test.rb +6 -6
  190. data/test/helpers/list_column_helpers_test.rb +9 -5
  191. data/test/helpers/pagination_helpers_test.rb +1 -0
  192. data/test/misc/active_record_permissions_test.rb +18 -1
  193. data/test/misc/attribute_params_test.rb +26 -17
  194. data/test/misc/calculation_test.rb +8 -31
  195. data/test/misc/configurable_test.rb +3 -2
  196. data/test/misc/constraints_test.rb +33 -22
  197. data/test/misc/convert_numbers_format_test.rb +28 -10
  198. data/test/misc/finder_test.rb +6 -29
  199. data/test/misc/parse_datetime_test.rb +160 -0
  200. data/test/misc/render_test.rb +1 -1
  201. data/test/misc/tableless_test.rb +24 -0
  202. data/test/mock_app/app/models/building.rb +2 -1
  203. data/test/mock_app/config.ru +1 -1
  204. data/test/mock_app/config/environments/test.rb +1 -1
  205. data/test/mock_app/config/routes.rb +11 -3
  206. data/test/model_stub.rb +11 -6
  207. data/test/run_all.rb +1 -1
  208. data/test/test_helper.rb +19 -4
  209. metadata +42 -23
  210. data/lib/active_scaffold/data_structures/error_message.rb +0 -22
  211. data/lib/active_scaffold/extensions/reverse_associations.rb +0 -119
  212. data/lib/generators/active_scaffold/USAGE +0 -29
  213. data/lib/generators/active_scaffold/active_scaffold_generator.rb +0 -21
  214. data/lib/generators/active_scaffold_controller/USAGE +0 -19
  215. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +0 -29
  216. data/test/data_structures/error_message_test.rb +0 -25
@@ -15,9 +15,17 @@ module ActiveScaffold
15
15
  end
16
16
  end
17
17
 
18
+ def active_scaffold_grouped_by_label
19
+ text, = active_scaffold_config.field_search.group_options.find do |text, value|
20
+ (value || text).to_s == field_search_params['active_scaffold_group']
21
+ end
22
+ active_scaffold_translated_option(active_scaffold_group_column, text).first if text
23
+ end
24
+
18
25
  def format_human_condition(column, opt, from = nil, to = nil)
19
26
  attribute = column.active_record_class.human_attribute_name(column.name)
20
- opt ||= from && to ? :between : (from ? :'>=' : :'<=')
27
+ opt ||= :between if from && to
28
+ opt ||= from ? '>=' : '<='
21
29
  "#{attribute} #{as_(opt).downcase} #{from} #{to}"
22
30
  end
23
31
 
@@ -26,8 +34,8 @@ module ActiveScaffold
26
34
  to = "- #{format_number_value(controller.class.condition_value_for_numeric(column, value['to']), column.options)}" if value['opt'] == 'BETWEEN'
27
35
  format_human_condition column, value['opt'].downcase, from, to
28
36
  end
29
- alias_method :active_scaffold_human_condition_decimal, :active_scaffold_human_condition_integer
30
- alias_method :active_scaffold_human_condition_float, :active_scaffold_human_condition_integer
37
+ alias active_scaffold_human_condition_decimal active_scaffold_human_condition_integer
38
+ alias active_scaffold_human_condition_float active_scaffold_human_condition_integer
31
39
 
32
40
  def active_scaffold_human_condition_string(column, value)
33
41
  opt = ActiveScaffold::Finder::STRING_COMPARATORS.key(value['opt']) || value['opt']
@@ -43,16 +51,16 @@ module ActiveScaffold
43
51
  to = "- #{I18n.l to}" if to
44
52
  format_human_condition column, value['opt'], from, to
45
53
  end
46
- alias_method :active_scaffold_human_condition_time, :active_scaffold_human_condition_date
47
- alias_method :active_scaffold_human_condition_datetime, :active_scaffold_human_condition_date
48
- alias_method :active_scaffold_human_condition_timestamp, :active_scaffold_human_condition_date
54
+ alias active_scaffold_human_condition_time active_scaffold_human_condition_date
55
+ alias active_scaffold_human_condition_datetime active_scaffold_human_condition_date
56
+ alias active_scaffold_human_condition_timestamp active_scaffold_human_condition_date
49
57
 
50
58
  def active_scaffold_human_condition_boolean(column, value)
51
59
  attribute = column.active_record_class.human_attribute_name(column.name)
52
60
  label = as_(ActiveScaffold::Core.column_type_cast(value, column.column) ? :true : :false)
53
61
  as_(:boolean, :scope => :human_conditions, :column => attribute, :value => label)
54
62
  end
55
- alias_method :active_scaffold_human_condition_checkbox, :active_scaffold_human_condition_boolean
63
+ alias active_scaffold_human_condition_checkbox active_scaffold_human_condition_boolean
56
64
 
57
65
  def active_scaffold_human_condition_null(column, value)
58
66
  format_human_condition column, value.to_sym
@@ -66,21 +74,23 @@ module ActiveScaffold
66
74
  associated = column.association.klass.where(:id => associated.map(&:to_i)).map(&method)
67
75
  elsif column.options[:options]
68
76
  associated = associated.collect do |value|
69
- text, val = column.options[:options].find { |text, val| (val.nil? ? text : val).to_s == value.to_s }
77
+ text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
70
78
  value = active_scaffold_translated_option(column, text, val).first if text
71
79
  value
72
80
  end
73
81
  end
74
82
  as_(:association, :scope => :human_conditions, :column => attribute, :value => associated.join(', '))
75
83
  end
76
- alias_method :active_scaffold_human_condition_multi_select, :active_scaffold_human_condition_select
77
- alias_method :active_scaffold_human_condition_record_select, :active_scaffold_human_condition_select
84
+ alias active_scaffold_human_condition_multi_select active_scaffold_human_condition_select
85
+ alias active_scaffold_human_condition_record_select active_scaffold_human_condition_select
86
+ alias active_scaffold_human_condition_chosen active_scaffold_human_condition_select
87
+ alias active_scaffold_human_condition_multi_chosen active_scaffold_human_condition_select
78
88
 
79
89
  # the naming convention for overriding form fields with helpers
80
90
  def override_human_condition_column(column)
81
91
  override_helper column, 'human_condition_column'
82
92
  end
83
- alias_method :override_human_condition_column?, :override_human_condition_column
93
+ alias override_human_condition_column? override_human_condition_column
84
94
 
85
95
  def override_human_condition?(search_ui)
86
96
  respond_to?(override_human_condition(search_ui))
@@ -130,7 +130,7 @@ module ActiveScaffold
130
130
 
131
131
  # whitelists id-safe characters
132
132
  def clean_id(val)
133
- val.gsub /[^-_0-9a-zA-Z]/, '-'
133
+ val.gsub(/[^-_0-9a-zA-Z]/, '-')
134
134
  end
135
135
  end
136
136
  end
@@ -1,11 +1,15 @@
1
- # coding: utf-8
2
1
  module ActiveScaffold
3
2
  module Helpers
4
3
  # Helpers that assist with the rendering of a List Column
5
4
  module ListColumnHelpers
6
5
  def get_column_value(record, column)
7
- method = get_column_method(record, column)
8
- value = send(method, record, column)
6
+ record = record.send(column.delegated_association.name) if column.delegated_association
7
+ if record
8
+ method = get_column_method(record, column)
9
+ value = send(method, record, column)
10
+ else
11
+ value = nil
12
+ end
9
13
  value = '&nbsp;'.html_safe if value.nil? || value.blank? # fix for IE 6
10
14
  return value
11
15
  rescue StandardError => e
@@ -38,7 +42,9 @@ module ActiveScaffold
38
42
  if column.link && !skip_action_link?(column.link, record)
39
43
  link = column.link
40
44
  associated = record.send(column.association.name) if column.association
41
- render_action_link(link, record, :link => text, :authorized => link.action.nil? || column_link_authorized?(link, column, record, associated))
45
+ authorized = link.action.nil?
46
+ authorized, reason = column_link_authorized?(link, column, record, associated) unless authorized
47
+ render_action_link(link, record, :link => text, :authorized => authorized, :not_authorized_reason => reason)
42
48
  elsif inplace_edit?(record, column)
43
49
  active_scaffold_inplace_edit(record, column, :formatted_column => text)
44
50
  elsif active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.wrap_tag
@@ -84,7 +90,7 @@ module ActiveScaffold
84
90
  options.delete(:disabled) if inplace_edit?(record, column)
85
91
  check_box(:record, column.name, options)
86
92
  end
87
-
93
+
88
94
  def active_scaffold_column_percentage(record, column)
89
95
  options = column.options[:slider] || {}
90
96
  options = options.merge(min: record.send(options[:min_method])) if options[:min_method]
@@ -93,10 +99,31 @@ module ActiveScaffold
93
99
  as_slider options.merge(value: value || record.send(column.name))
94
100
  end
95
101
 
102
+ def active_scaffold_column_month(record, column)
103
+ l record.send(column.name), format: :year_month
104
+ end
105
+
106
+ def active_scaffold_column_week(record, column)
107
+ l record.send(column.name), format: :week
108
+ end
109
+
110
+ def tel_to(text)
111
+ groups = text.to_s.scan(/(?:^\+)?\d+/)
112
+ extension = groups.pop if text.to_s =~ /\s*[^\d\s]+\s*\d+$/
113
+ link_to text, "tel:#{[groups.join('-'), extension].compact.join(',')}"
114
+ end
115
+
116
+ def active_scaffold_column_telephone(record, column)
117
+ phone = record.send column.name
118
+ return if phone.blank?
119
+ phone = number_to_phone(phone) unless column.options[:format] == false
120
+ tel_to phone
121
+ end
122
+
96
123
  def column_override(column)
97
124
  override_helper column, 'column'
98
125
  end
99
- alias_method :column_override?, :column_override
126
+ alias column_override? column_override
100
127
 
101
128
  # the naming convention for overriding column types with helpers
102
129
  def override_column_ui(list_ui)
@@ -105,7 +132,7 @@ module ActiveScaffold
105
132
  method = "active_scaffold_column_#{list_ui}"
106
133
  @_column_ui_overrides[list_ui] = (method if respond_to? method)
107
134
  end
108
- alias_method :override_column_ui?, :override_column_ui
135
+ alias override_column_ui? override_column_ui
109
136
 
110
137
  ##
111
138
  ## Formatting
@@ -113,19 +140,23 @@ module ActiveScaffold
113
140
  def format_column_value(record, column, value = nil)
114
141
  value ||= record.send(column.name) unless record.nil?
115
142
  if column.association.nil?
116
- if [:select, :radio].include?(column.form_ui) && column.options[:options]
117
- text, val = column.options[:options].find { |text, val| (val.nil? ? text : val).to_s == value.to_s }
143
+ if %i[select radio].include?(column.form_ui) && column.options[:options]
144
+ text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
118
145
  value = active_scaffold_translated_option(column, text, val).first if text
119
146
  end
120
- if value.is_a? Numeric
147
+ if grouped_search? && column == search_group_column && search_group_function
148
+ format_grouped_search_column(value, column.options)
149
+ elsif value.is_a? Numeric
121
150
  format_number_value(value, column.options)
122
151
  else
123
152
  format_value(value, column.options)
124
153
  end
125
154
  else
126
- if column.plural_association?
155
+ if column.association.collection?
127
156
  associated_size = value.size if column.associated_number? # get count before cache association
128
- cache_association(record.association(column.name), column, associated_size) unless value.loaded?
157
+ if column.association.respond_to_target? && !value.loaded?
158
+ cache_association(record.association(column.name), column, associated_size)
159
+ end
129
160
  end
130
161
  format_association_value(value, column, associated_size)
131
162
  end
@@ -134,38 +165,55 @@ module ActiveScaffold
134
165
  def format_number_value(value, options = {})
135
166
  if value
136
167
  value = case options[:format]
137
- when :size
138
- number_to_human_size(value, options[:i18n_options] || {})
139
- when :percentage
140
- number_to_percentage(value, options[:i18n_options] || {})
141
- when :currency
142
- number_to_currency(value, options[:i18n_options] || {})
143
- when :i18n_number
144
- send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
145
- else
146
- value
168
+ when :size
169
+ number_to_human_size(value, options[:i18n_options] || {})
170
+ when :percentage
171
+ number_to_percentage(value, options[:i18n_options] || {})
172
+ when :currency
173
+ number_to_currency(value, options[:i18n_options] || {})
174
+ when :i18n_number
175
+ send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
176
+ else
177
+ value
147
178
  end
148
179
  end
149
180
  clean_column_value(value)
150
181
  end
151
182
 
183
+ def format_grouped_search_column(value, options = {})
184
+ case search_group_function
185
+ when 'year_month'
186
+ year, month = value.to_s.scan(/(\d*)(\d{2})/)[0]
187
+ I18n.l(Date.new(year.to_i, month.to_i, 1), format: options[:group_format] || search_group_function.to_sym)
188
+ when 'year_quarter'
189
+ year, quarter = value.to_s.scan(/(\d*)(\d)/)[0]
190
+ I18n.t(options[:group_format] || search_group_function, scope: 'date.formats', year: year, quarter: quarter)
191
+ when 'quarter'
192
+ I18n.t(options[:group_format] || search_group_function, scope: 'date.formats', num: value)
193
+ when 'month'
194
+ I18n.l(Date.new(Time.zone.today.year, value, 1), format: options[:group_format] || search_group_function.to_sym)
195
+ else value
196
+ end
197
+ end
198
+
152
199
  def format_collection_association_value(value, column, label_method, size)
153
200
  if column.associated_limit.nil?
154
201
  firsts = value.collect(&label_method)
155
- elsif column.associated_limit == 0
202
+ safe_join firsts, active_scaffold_config.list.association_join_text
203
+ elsif column.associated_limit.zero?
156
204
  size if column.associated_number?
157
205
  else
158
- firsts = value.first(column.associated_limit)
159
- firsts.collect!(&label_method)
206
+ firsts = value.loaded? ? value[0, column.associated_limit] : value.limit(column.associated_limit)
207
+ firsts = firsts.map(&label_method)
160
208
  firsts << '…' if value.size > column.associated_limit
161
- text = firsts.join(h(active_scaffold_config.list.association_join_text)).html_safe
209
+ text = safe_join firsts, active_scaffold_config.list.association_join_text
162
210
  text << " (#{size})" if column.associated_number? && column.associated_limit && value.size > column.associated_limit
163
211
  text
164
212
  end
165
213
  end
166
214
 
167
215
  def format_singular_association_value(value, column, label_method)
168
- if column.polymorphic_association?
216
+ if column.association.polymorphic?
169
217
  "#{value.class.model_name.human}: #{value.send(label_method)}"
170
218
  else
171
219
  value.send(label_method)
@@ -215,7 +263,14 @@ module ActiveScaffold
215
263
 
216
264
  def inplace_edit?(record, column)
217
265
  return unless column.inplace_edit
218
- editable = controller.send(:update_authorized?, record) if controller.respond_to?(:update_authorized?, true)
266
+ if controller.respond_to?(:update_authorized?, true)
267
+ if controller.method(:update_authorized?).parameters.size == 2
268
+ return Array(controller.send(:update_authorized?, record, column.name))[0]
269
+ else
270
+ ActiveSupport::Deprecation.warn 'add column = nil parameter to update_authorized? on your controller'
271
+ editable = Array(controller.send(:update_authorized?, record))[0]
272
+ end
273
+ end
219
274
  editable || record.authorized_for?(:crud_type => :update, :column => column.name)
220
275
  end
221
276
 
@@ -239,17 +294,14 @@ module ActiveScaffold
239
294
 
240
295
  def inplace_edit_control(column)
241
296
  return unless inplace_edit?(active_scaffold_config.model, column) && inplace_edit_cloning?(column)
242
- old_record, @record = @record, active_scaffold_config.model.new # TODO: remove when relying on @record is removed
243
297
  column = column.clone
244
298
  column.options = column.options.clone
245
299
  column.form_ui = :select if column.association && column.form_ui.nil?
246
- options = active_scaffold_input_options(column).merge(:object => active_scaffold_config.model.new)
300
+ options = active_scaffold_input_options(column).merge(:object => column.active_record_class.new)
247
301
  options[:class] = "#{options[:class]} inplace_field"
248
302
  options[:"data-id"] = options[:id]
249
303
  options[:id] = nil
250
- content_tag(:div, active_scaffold_input_for(column, nil, options), :style => 'display:none;', :class => inplace_edit_control_css_class).tap do
251
- @record = old_record # TODO: remove when relying on @record is removed
252
- end
304
+ content_tag(:div, active_scaffold_input_for(column, nil, options), :style => 'display:none;', :class => inplace_edit_control_css_class)
253
305
  end
254
306
 
255
307
  def inplace_edit_control_css_class
@@ -273,8 +325,8 @@ module ActiveScaffold
273
325
  elsif inplace_edit_cloning?(column)
274
326
  data[:ie_mode] = :clone
275
327
  elsif column.inplace_edit == :ajax
276
- url = url_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name)
277
- plural = column.plural_association? && !override_form_field?(column) && [:select, :record_select].include?(column.form_ui)
328
+ url = url_for(params_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name))
329
+ plural = column.association.try(:collection?) && !override_form_field?(column) && %i[select record_select].include?(column.form_ui)
278
330
  data[:ie_render_url] = url
279
331
  data[:ie_mode] = :ajax
280
332
  data[:ie_plural] = plural
@@ -282,6 +334,8 @@ module ActiveScaffold
282
334
  data
283
335
  end
284
336
 
337
+ # MARK
338
+
285
339
  def all_marked?
286
340
  if active_scaffold_config.mark.mark_all_mode == :page
287
341
  @page.items.detect { |record| !marked_records.include?(record.id) }.nil?
@@ -298,6 +352,8 @@ module ActiveScaffold
298
352
  content_tag(:span, check_box_tag("#{controller_id}_mark_heading_span_input", '1', all_marked?), tag_options)
299
353
  end
300
354
 
355
+ # COLUMN HEADINGS
356
+
301
357
  def column_heading_attributes(column, sorting, sort_direction)
302
358
  {:id => active_scaffold_column_header_id(column), :class => column_heading_class(column, sorting), :title => strip_tags(column.description).presence}
303
359
  end
@@ -322,11 +378,12 @@ module ActiveScaffold
322
378
  options = {:id => nil, :class => 'as_sort',
323
379
  'data-page-history' => controller_id,
324
380
  :remote => true, :method => :get}
325
- # :id needed because rails reuse it even if we delete from params (like do_refresh_list does)
326
- url_options = params_for(:action => :index, :page => 1, :id => params[:id],
327
- :sort => column.name, :sort_direction => sort_direction)
381
+ url_options = {action: :index, page: 1, sort: column.name, sort_direction: sort_direction}
382
+ # :id needed because rails reuse it even if it was deleted from params (like do_refresh_list does)
383
+ url_options[:id] = nil if @remove_id_from_list_links
384
+ url_options = params_for(url_options)
328
385
  unless active_scaffold_config.store_user_settings
329
- url_options.merge!(:search => search_params) if search_params.present?
386
+ url_options[:search] = search_params if respond_to?(:search_params) && search_params.present?
330
387
  end
331
388
  link_to column_heading_label(column), url_options, options
332
389
  else
@@ -337,6 +394,27 @@ module ActiveScaffold
337
394
  def column_heading_label(column)
338
395
  column.label
339
396
  end
397
+
398
+ # CALCULATIONS
399
+
400
+ def column_calculation(column)
401
+ if column.calculate.instance_of? Proc
402
+ column.calculate.call(@records)
403
+ else
404
+ calculate_query.calculate(column.calculate, column.name)
405
+ end
406
+ end
407
+
408
+ def render_column_calculation(column)
409
+ calculation = column_calculation(column)
410
+ override_formatter = "render_#{column.name}_#{column.calculate.is_a?(Proc) ? :calculate : column.calculate}"
411
+ calculation = send(override_formatter, calculation) if respond_to? override_formatter
412
+ format_column_calculation(column, calculation)
413
+ end
414
+
415
+ def format_column_calculation(column, calculation)
416
+ "#{"#{as_(column.calculate)}: " unless column.calculate.is_a? Proc}#{format_column_value nil, column, calculation}"
417
+ end
340
418
  end
341
419
  end
342
420
  end
@@ -6,14 +6,11 @@ module ActiveScaffold
6
6
  end
7
7
 
8
8
  def pagination_url_options(url_options = nil)
9
- # :id needed because rails reuse it even if we delete from params (like do_refresh_list does)
10
- url_options ||= params_for(:action => @pagination_action || :index, :id => params[:id])
11
- unless active_scaffold_config.store_user_settings
12
- url_options.merge!(:search => search_params) if search_params.present?
13
- if active_scaffold_config.list.user.user_sorting?
14
- column, direction = active_scaffold_config.list.user.sorting.first
15
- url_options.merge!(:sort => column.name, :sort_direction => direction)
16
- end
9
+ if url_options.nil?
10
+ url_options = {action: @pagination_action || :index}
11
+ # :id needed because rails reuse it even if it was deleted from params (like do_refresh_list does)
12
+ url_options[:id] = nil if @remove_id_from_list_links
13
+ url_options = params_for(url_options)
17
14
  end
18
15
  url_options
19
16
  end
@@ -51,11 +48,11 @@ module ActiveScaffold
51
48
  html << '..' if start_number > last_page + 1
52
49
 
53
50
  [start_number, last_page + 1].max.upto(end_number) do |num|
54
- if current_page.number == num
55
- html << content_tag(:span, num.to_s, :class => 'as_paginate current')
56
- else
57
- html << pagination_ajax_link(num, url_options, options)
58
- end
51
+ html << if current_page.number == num
52
+ content_tag(:span, num.to_s, :class => 'as_paginate current')
53
+ else
54
+ pagination_ajax_link(num, url_options, options)
55
+ end
59
56
  end
60
57
 
61
58
  if current_page.pager.infinite?
@@ -68,7 +65,7 @@ module ActiveScaffold
68
65
  html << pagination_ajax_link(num, url_options, options)
69
66
  end
70
67
  end
71
- html.join(' ').html_safe
68
+ safe_join html, ' '
72
69
  end
73
70
  end
74
71
  end
@@ -7,8 +7,10 @@ module ActiveScaffold
7
7
  def active_scaffold_search_for(column, options = nil)
8
8
  options ||= active_scaffold_search_options(column)
9
9
  record = options[:object]
10
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
11
- record ||= @record # TODO: Remove when relying on @record is removed
10
+ if column.delegated_association
11
+ record = record.send(column.delegated_association.name) || column.active_record_class.new
12
+ options[:object] = record
13
+ end
12
14
 
13
15
  # first, check if the dev has created an override for this specific field for search
14
16
  if (method = override_search_field(column))
@@ -41,7 +43,7 @@ module ActiveScaffold
41
43
  # final ultimate fallback: use rails' generic input method
42
44
  else
43
45
  # for textual fields we pass different options
44
- text_types = [:text, :string, :integer, :float, :decimal]
46
+ text_types = %i[text string integer float decimal]
45
47
  options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
46
48
  text_field(:record, column.name, options.merge(column.options))
47
49
  end
@@ -64,7 +66,7 @@ module ActiveScaffold
64
66
  end
65
67
 
66
68
  def search_label_for(column, options)
67
- options[:id] unless [:range, :integer, :decimal, :float, :string, :date_picker, :datetime_picker, :calendar_date_select].include? column.search_ui
69
+ options[:id] unless %i[range integer decimal float string date_picker datetime_picker calendar_date_select].include? column.search_ui
68
70
  end
69
71
 
70
72
  ##
@@ -73,8 +75,6 @@ module ActiveScaffold
73
75
 
74
76
  def active_scaffold_search_multi_select(column, options)
75
77
  record = options.delete(:object)
76
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
77
- record ||= @record # TODO: Remove when relying on @record is removed
78
78
  associated = options.delete :value
79
79
  associated = [associated].compact unless associated.is_a? Array
80
80
 
@@ -96,12 +96,10 @@ module ActiveScaffold
96
96
 
97
97
  def active_scaffold_search_select(column, html_options, options = {})
98
98
  record = html_options.delete(:object)
99
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in html_options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
100
- record ||= @record # TODO: Remove when relying on @record is removed
101
99
  associated = html_options.delete :value
102
100
  if column.association
103
101
  associated = associated.is_a?(Array) ? associated.map(&:to_i) : associated.to_i unless associated.nil?
104
- method = column.association.macro == :belongs_to ? column.association.foreign_key : column.name
102
+ method = column.association.belongs_to? ? column.association.foreign_key : column.name
105
103
  select_options = sorted_association_options_find(column.association, false, record)
106
104
  else
107
105
  method = column.name
@@ -113,7 +111,7 @@ module ActiveScaffold
113
111
  options = options.merge(:selected => associated).merge column.options
114
112
  html_options.merge! column.options[:html_options] || {}
115
113
  if html_options[:multiple]
116
- html_options[:name] += '[]'
114
+ active_scaffold_select_name_with_multiple html_options
117
115
  else
118
116
  options[:include_blank] ||= as_(:_select_)
119
117
  active_scaffold_translate_select_options(options)
@@ -143,7 +141,27 @@ module ActiveScaffold
143
141
  select_tag(options[:name], options_for_select(select_options, ActiveScaffold::Core.column_type_cast(options[:value], column.column)), :id => options[:id])
144
142
  end
145
143
  # we can't use checkbox ui because it's not possible to decide whether search for this field or not
146
- alias_method :active_scaffold_search_checkbox, :active_scaffold_search_boolean
144
+ alias active_scaffold_search_checkbox active_scaffold_search_boolean
145
+
146
+ def active_scaffold_group_search_column(record, options)
147
+ select_tag 'search[active_scaffold_group]', options_for_select(active_scaffold_group_search_options, selected: field_search_params['active_scaffold_group'])
148
+ end
149
+
150
+ def active_scaffold_group_search_options
151
+ options = active_scaffold_config.field_search.group_options.collect do |text, value|
152
+ active_scaffold_translated_option(active_scaffold_group_column, text, value)
153
+ end
154
+ [[as_(:no_group), '']].concat options
155
+ end
156
+
157
+ def active_scaffold_group_column
158
+ return if active_scaffold_config.field_search.group_options.blank?
159
+ @_active_scaffold_group_column ||= begin
160
+ column = ActiveScaffold::DataStructures::Column.new(:active_scaffold_group, active_scaffold_config.model)
161
+ column.label = :group_by
162
+ column
163
+ end
164
+ end
147
165
 
148
166
  def active_scaffold_search_null(column, options)
149
167
  select_options = []
@@ -165,10 +183,11 @@ module ActiveScaffold
165
183
  def active_scaffold_search_range_comparator_options(column)
166
184
  select_options = ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
167
185
  if active_scaffold_search_range_string?(column)
168
- select_options.unshift *ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
186
+ comparators = ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
187
+ select_options.unshift(*comparators)
169
188
  end
170
189
  if include_null_comparators? column
171
- select_options += ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
190
+ select_options.concat ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
172
191
  end
173
192
  select_options
174
193
  end
@@ -176,13 +195,13 @@ module ActiveScaffold
176
195
  def include_null_comparators?(column)
177
196
  return column.options[:null_comparators] if column.options.key? :null_comparators
178
197
  if column.association
179
- column.association.macro != :belongs_to || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
198
+ !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
180
199
  else
181
200
  column.column.try(:null)
182
201
  end
183
202
  end
184
203
 
185
- def active_scaffold_search_range(column, options)
204
+ def active_scaffold_search_range(column, options, input_method = :text_field_tag, input_options = {})
186
205
  opt_value, from_value, to_value = field_search_params_range_values(column)
187
206
 
188
207
  select_options = active_scaffold_search_range_comparator_options(column)
@@ -199,34 +218,48 @@ module ActiveScaffold
199
218
  to_value = format_number_value(to_value, column.options) if to_value.is_a?(Numeric)
200
219
  html = select_tag("#{options[:name]}[opt]", options_for_select(select_options, opt_value),
201
220
  :id => "#{options[:id]}_opt", :class => 'as_search_range_option')
221
+ from_options = active_scaffold_input_text_options(input_options.merge(:id => options[:id], :size => text_field_size))
222
+ to_options = from_options.merge(:id => "#{options[:id]}_to")
202
223
  html << content_tag('span', :id => "#{options[:id]}_numeric", :style => ActiveScaffold::Finder::NULL_COMPARATORS.include?(opt_value) ? 'display: none' : nil) do
203
- text_field_tag("#{options[:name]}[from]", from_value, active_scaffold_input_text_options(:id => options[:id], :size => text_field_size)) <<
224
+ send(input_method, "#{options[:name]}[from]", from_value, input_options) <<
204
225
  content_tag(
205
- :span, (
206
- ' - ' + text_field_tag("#{options[:name]}[to]", to_value,
207
- active_scaffold_input_text_options(:id => "#{options[:id]}_to", :size => text_field_size))
208
- ).html_safe,
226
+ :span,
227
+ safe_join([' - ', send(input_method, "#{options[:name]}[to]", to_value, to_options)]),
209
228
  :id => "#{options[:id]}_between", :class => 'as_search_range_between', :style => (opt_value == 'BETWEEN') ? nil : 'display: none'
210
229
  )
211
230
  end
212
231
  content_tag :span, html, :class => 'search_range'
213
232
  end
214
- alias_method :active_scaffold_search_integer, :active_scaffold_search_range
215
- alias_method :active_scaffold_search_decimal, :active_scaffold_search_range
216
- alias_method :active_scaffold_search_float, :active_scaffold_search_range
217
- alias_method :active_scaffold_search_string, :active_scaffold_search_range
233
+ alias active_scaffold_search_string active_scaffold_search_range
234
+
235
+ def active_scaffold_search_integer(column, options)
236
+ active_scaffold_search_range(column, options, :number_field_tag, step: '1')
237
+ end
238
+
239
+ def active_scaffold_search_decimal(column, options)
240
+ active_scaffold_search_range(column, options, :number_field_tag, step: :any)
241
+ end
242
+ alias active_scaffold_search_float active_scaffold_search_decimal
218
243
 
219
244
  def field_search_datetime_value(value)
220
- DateTime.new(value[:year].to_i, value[:month].to_i, value[:day].to_i, value[:hour].to_i, value[:minute].to_i, value[:second].to_i) unless value.nil? || value[:year].blank?
245
+ Time.zone.local(value[:year].to_i, value[:month].to_i, value[:day].to_i, value[:hour].to_i, value[:minute].to_i, value[:second].to_i) unless value.nil? || value[:year].blank?
221
246
  end
222
247
 
223
248
  def active_scaffold_search_datetime(column, options)
224
249
  _, from_value, to_value = field_search_params_range_values(column)
225
250
  options = column.options.merge(options)
226
- helper = "select_#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
251
+ type = "#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
252
+ use_select = options.delete(:use_select)
253
+ helper = use_select ? "select_#{type}" : "#{type}#{'_local' if type == 'datetime'}_field"
254
+ if use_select
255
+ default_from_options = {include_blank: true, prefix: "#{options[:name]}[from]"}
256
+ default_to_options = {include_blank: true, prefix: "#{options[:name]}[to]"}
257
+ end
227
258
 
228
- send(helper, field_search_datetime_value(from_value), {:include_blank => true, :prefix => "#{options[:name]}[from]"}.merge(options)) <<
229
- ' - '.html_safe << send(helper, field_search_datetime_value(to_value), {:include_blank => true, :prefix => "#{options[:name]}[to]"}.merge(options))
259
+ safe_join [
260
+ send(helper, field_search_datetime_value(from_value), options.reverse_merge(default_from_options || {})),
261
+ send(helper, field_search_datetime_value(to_value), options.reverse_merge(default_to_options || {}))
262
+ ], ' - '
230
263
  end
231
264
 
232
265
  def active_scaffold_search_date(column, options)
@@ -236,7 +269,7 @@ module ActiveScaffold
236
269
  def active_scaffold_search_time(column, options)
237
270
  active_scaffold_search_datetime(column, options.merge!(:discard_date => true))
238
271
  end
239
- alias_method :active_scaffold_search_timestamp, :active_scaffold_search_datetime
272
+ alias active_scaffold_search_timestamp active_scaffold_search_datetime
240
273
 
241
274
  ##
242
275
  ## Search column override signatures
@@ -267,16 +300,20 @@ module ActiveScaffold
267
300
  visibles << column
268
301
  end
269
302
  end
303
+ if active_scaffold_group_column
304
+ columns = grouped_search? || search_config.optional_columns.empty? ? visibles : hiddens
305
+ columns << active_scaffold_group_column
306
+ end
270
307
  [visibles, hiddens]
271
308
  end
272
309
 
273
310
  def searched_by?(column)
274
- value = field_search_params[column.name]
311
+ value = field_search_params[column.name.to_s]
275
312
  case value
276
313
  when Hash
277
- !value['from'].blank?
314
+ value['from'].present?
278
315
  when String
279
- !value.blank?
316
+ value.present?
280
317
  else
281
318
  false
282
319
  end