active_scaffold 3.3.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/README.md +5 -3
  4. data/app/assets/images/active_scaffold/refresh.png +0 -0
  5. data/app/assets/javascripts/jquery/active_scaffold.js +182 -91
  6. data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +14 -16
  7. data/app/assets/javascripts/jquery/draggable_lists.js +33 -26
  8. data/app/assets/javascripts/jquery/jquery.editinplace.js +3 -3
  9. data/app/assets/javascripts/prototype/active_scaffold.js +61 -19
  10. data/app/assets/stylesheets/active_scaffold_colors.css.scss +4 -0
  11. data/app/assets/stylesheets/active_scaffold_images.css.scss +3 -0
  12. data/app/assets/stylesheets/active_scaffold_layout.css +23 -2
  13. data/app/views/active_scaffold_overrides/_add_existing_form.html.erb +1 -3
  14. data/app/views/active_scaffold_overrides/_base_form.html.erb +7 -5
  15. data/app/views/active_scaffold_overrides/_field_search.html.erb +1 -2
  16. data/app/views/active_scaffold_overrides/_form.html.erb +6 -4
  17. data/app/views/active_scaffold_overrides/_form_association.html.erb +4 -3
  18. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +5 -5
  19. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +8 -6
  20. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +3 -2
  21. data/app/views/active_scaffold_overrides/_list.html.erb +8 -6
  22. data/app/views/active_scaffold_overrides/_list_column_headings.html.erb +1 -4
  23. data/app/views/active_scaffold_overrides/_list_pagination.html.erb +4 -4
  24. data/app/views/active_scaffold_overrides/_list_pagination_links.html.erb +1 -1
  25. data/app/views/active_scaffold_overrides/_list_record.html.erb +3 -3
  26. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +8 -1
  27. data/app/views/active_scaffold_overrides/_search.html.erb +7 -13
  28. data/app/views/active_scaffold_overrides/_show_columns.html.erb +1 -1
  29. data/app/views/active_scaffold_overrides/on_create.js.erb +4 -4
  30. data/app/views/active_scaffold_overrides/render_field_inplace.html.erb +1 -1
  31. data/app/views/active_scaffold_overrides/row.js.erb +1 -1
  32. data/config/locales/de.yml +106 -95
  33. data/config/locales/en.yml +108 -97
  34. data/config/locales/es.yml +109 -98
  35. data/config/locales/fr.yml +108 -97
  36. data/config/locales/hu.yml +109 -98
  37. data/config/locales/ja.yml +100 -89
  38. data/config/locales/ru.yml +115 -104
  39. data/lib/active_scaffold.rb +18 -294
  40. data/lib/active_scaffold/actions/common_search.rb +50 -17
  41. data/lib/active_scaffold/actions/core.rb +93 -22
  42. data/lib/active_scaffold/actions/create.rb +15 -6
  43. data/lib/active_scaffold/actions/field_search.rb +68 -60
  44. data/lib/active_scaffold/actions/list.rb +49 -28
  45. data/lib/active_scaffold/actions/nested.rb +14 -6
  46. data/lib/active_scaffold/actions/search.rb +36 -35
  47. data/lib/active_scaffold/actions/show.rb +9 -4
  48. data/lib/active_scaffold/actions/subform.rb +1 -1
  49. data/lib/active_scaffold/actions/update.rb +22 -7
  50. data/lib/active_scaffold/active_record_permissions.rb +125 -118
  51. data/lib/active_scaffold/attribute_params.rb +84 -66
  52. data/lib/active_scaffold/bridges.rb +3 -3
  53. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +10 -5
  54. data/lib/active_scaffold/bridges/cancan.rb +2 -1
  55. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +13 -2
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +11 -6
  57. data/lib/active_scaffold/bridges/chosen/helpers.rb +2 -2
  58. data/lib/active_scaffold/bridges/country_helper/country_helper_bridge.rb +45 -29
  59. data/lib/active_scaffold/bridges/date_picker/ext.rb +11 -6
  60. data/lib/active_scaffold/bridges/date_picker/helper.rb +5 -1
  61. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +10 -5
  62. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +6 -1
  63. data/lib/active_scaffold/bridges/file_column/form_ui.rb +12 -11
  64. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +14 -6
  65. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  66. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -12
  67. data/lib/active_scaffold/bridges/shared/date_bridge.rb +7 -8
  68. data/lib/active_scaffold/bridges/tiny_mce.rb +5 -3
  69. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +4 -5
  70. data/lib/active_scaffold/config/base.rb +4 -0
  71. data/lib/active_scaffold/config/core.rb +12 -5
  72. data/lib/active_scaffold/config/delete.rb +0 -2
  73. data/lib/active_scaffold/config/field_search.rb +1 -4
  74. data/lib/active_scaffold/config/form.rb +0 -2
  75. data/lib/active_scaffold/config/list.rb +31 -1
  76. data/lib/active_scaffold/config/search.rb +0 -3
  77. data/lib/active_scaffold/config/show.rb +0 -6
  78. data/lib/active_scaffold/config/subform.rb +1 -0
  79. data/lib/active_scaffold/configurable.rb +2 -2
  80. data/lib/active_scaffold/constraints.rb +11 -14
  81. data/lib/active_scaffold/core.rb +277 -0
  82. data/lib/active_scaffold/data_structures/action_columns.rb +18 -2
  83. data/lib/active_scaffold/data_structures/action_link.rb +25 -6
  84. data/lib/active_scaffold/data_structures/action_links.rb +9 -4
  85. data/lib/active_scaffold/data_structures/actions.rb +1 -1
  86. data/lib/active_scaffold/data_structures/column.rb +6 -6
  87. data/lib/active_scaffold/data_structures/columns.rb +2 -2
  88. data/lib/active_scaffold/data_structures/nested_info.rb +5 -1
  89. data/lib/active_scaffold/data_structures/sorting.rb +15 -5
  90. data/lib/active_scaffold/delayed_setup.rb +30 -0
  91. data/lib/active_scaffold/engine.rb +25 -0
  92. data/lib/active_scaffold/extensions/action_view_rendering.rb +1 -1
  93. data/lib/active_scaffold/extensions/left_outer_joins.rb +61 -21
  94. data/lib/active_scaffold/extensions/localize.rb +1 -1
  95. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +13 -8
  96. data/lib/active_scaffold/extensions/paginator_extensions.rb +5 -1
  97. data/lib/active_scaffold/extensions/reverse_associations.rb +1 -0
  98. data/lib/active_scaffold/extensions/routing_mapper.rb +1 -1
  99. data/lib/active_scaffold/extensions/unsaved_record.rb +4 -6
  100. data/lib/active_scaffold/finder.rb +79 -27
  101. data/lib/active_scaffold/helpers/association_helpers.rb +48 -18
  102. data/lib/active_scaffold/helpers/controller_helpers.rb +19 -10
  103. data/lib/active_scaffold/helpers/form_column_helpers.rb +185 -87
  104. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -1
  105. data/lib/active_scaffold/helpers/id_helpers.rb +14 -8
  106. data/lib/active_scaffold/helpers/list_column_helpers.rb +65 -56
  107. data/lib/active_scaffold/helpers/pagination_helpers.rb +5 -1
  108. data/lib/active_scaffold/helpers/search_column_helpers.rb +21 -18
  109. data/lib/active_scaffold/helpers/view_helpers.rb +102 -64
  110. data/lib/active_scaffold/responds_to_parent.rb +39 -64
  111. data/lib/active_scaffold/tableless.rb +129 -10
  112. data/lib/active_scaffold/version.rb +2 -2
  113. data/test/bridges/bridge_test.rb +1 -1
  114. data/test/bridges/date_picker_test.rb +2 -2
  115. data/test/bridges/paperclip_test.rb +10 -8
  116. data/test/bridges/tiny_mce_test.rb +2 -2
  117. data/test/company.rb +22 -10
  118. data/test/config/base_test.rb +1 -1
  119. data/test/config/core_test.rb +8 -6
  120. data/test/config/create_test.rb +6 -6
  121. data/test/config/delete_test.rb +4 -4
  122. data/test/config/field_search_test.rb +6 -6
  123. data/test/config/list_test.rb +7 -7
  124. data/test/config/nested_test.rb +8 -7
  125. data/test/config/search_test.rb +7 -7
  126. data/test/config/show_test.rb +5 -5
  127. data/test/config/subform_test.rb +1 -1
  128. data/test/config/update_test.rb +5 -4
  129. data/test/data_structures/action_columns_test.rb +15 -16
  130. data/test/data_structures/action_link_test.rb +10 -10
  131. data/test/data_structures/action_links_test.rb +6 -6
  132. data/test/data_structures/actions_test.rb +4 -4
  133. data/test/data_structures/association_column_test.rb +4 -4
  134. data/test/data_structures/column_test.rb +9 -9
  135. data/test/data_structures/columns_test.rb +7 -7
  136. data/test/data_structures/error_message_test.rb +2 -4
  137. data/test/data_structures/set_test.rb +13 -13
  138. data/test/data_structures/sorting_test.rb +8 -8
  139. data/test/data_structures/standard_column_test.rb +2 -2
  140. data/test/data_structures/validation_reflection_test.rb +8 -8
  141. data/test/data_structures/virtual_column_test.rb +5 -5
  142. data/test/extensions/active_record_test.rb +1 -1
  143. data/test/helpers/form_column_helpers_test.rb +5 -5
  144. data/test/helpers/list_column_helpers_test.rb +2 -1
  145. data/test/helpers/pagination_helpers_test.rb +1 -1
  146. data/test/misc/active_record_permissions_test.rb +23 -4
  147. data/test/misc/attribute_params_test.rb +304 -136
  148. data/test/misc/calculation_test.rb +55 -0
  149. data/test/misc/configurable_test.rb +22 -21
  150. data/test/misc/constraints_test.rb +10 -7
  151. data/test/misc/convert_numbers_format_test.rb +149 -0
  152. data/test/misc/finder_test.rb +17 -13
  153. data/test/misc/lang_test.rb +1 -1
  154. data/test/misc/tableless_test.rb +18 -0
  155. data/test/mock_app/app/controllers/addresses_controller.rb +4 -0
  156. data/test/mock_app/app/controllers/buildings_controller.rb +4 -0
  157. data/test/mock_app/app/controllers/cars_controller.rb +4 -0
  158. data/test/mock_app/app/controllers/contacts_controller.rb +4 -0
  159. data/test/mock_app/app/controllers/floors_controller.rb +6 -0
  160. data/test/mock_app/app/controllers/people_controller.rb +4 -0
  161. data/test/mock_app/app/models/address.rb +3 -0
  162. data/test/mock_app/app/models/building.rb +8 -0
  163. data/test/mock_app/app/models/car.rb +3 -0
  164. data/test/mock_app/app/models/contact.rb +3 -0
  165. data/test/mock_app/app/models/file_model.rb +19 -0
  166. data/test/mock_app/app/models/floor.rb +8 -0
  167. data/test/mock_app/app/models/person.rb +11 -0
  168. data/test/mock_app/config/application.rb +2 -0
  169. data/test/mock_app/config/environments/test.rb +1 -1
  170. data/test/mock_app/config/initializers/secret_token.rb +5 -1
  171. data/test/mock_app/config/routes.rb +1 -1
  172. data/test/mock_app/db/schema.rb +51 -0
  173. data/test/model_stub.rb +3 -3
  174. data/test/test_helper.rb +15 -12
  175. metadata +51 -50
  176. data/lib/active_scaffold/extensions/array.rb +0 -7
  177. data/lib/active_scaffold/extensions/cache_association.rb +0 -16
  178. data/lib/active_scaffold/extensions/usa_state.rb +0 -46
  179. data/lib/active_scaffold_env.rb +0 -13
  180. data/test/extensions/array_test.rb +0 -12
  181. data/test/mock_app/public/blank.html +0 -33
  182. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +0 -2
  183. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  184. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  185. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  186. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  187. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  188. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  189. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  190. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  191. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +0 -2
  192. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +0 -532
  193. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +0 -867
  194. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +0 -117
  195. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +0 -370
  196. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +0 -2
  197. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +0 -35
  198. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +0 -848
@@ -29,23 +29,22 @@ module ActiveScaffold
29
29
  active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'from') <<
30
30
  content_tag(:span, (" - " + active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'to')).html_safe,
31
31
  :id => "#{options[:id]}_between", :class => "as_search_range_between", :style => current_search['opt'] == 'BETWEEN' ? nil : "display: none")
32
- content_tag("span", numeric_controls.html_safe, :id => "#{options[:id]}_numeric", :style => ActiveScaffold::Finder::NumericComparators.include?(current_search['opt']) ? nil : "display: none")
32
+ content_tag("span", numeric_controls.html_safe, :id => "#{options[:id]}_numeric", :class => "search-date-numeric", :style => ActiveScaffold::Finder::NumericComparators.include?(current_search['opt']) ? nil : "display: none")
33
33
  end
34
34
 
35
35
  def active_scaffold_search_date_bridge_trend_tag(column, options, current_search)
36
36
  active_scaffold_date_bridge_trend_tag(column, options,
37
- {:name_prefix => 'search',
38
- :number_value => current_search['number'],
37
+ {:number_value => current_search['number'],
39
38
  :unit_value => current_search["unit"],
40
39
  :show => (current_search['opt'] == 'PAST' || current_search['opt'] == 'FUTURE')})
41
40
  end
42
41
 
43
42
  def active_scaffold_date_bridge_trend_tag(column, options, trend_options)
44
- trend_controls = text_field_tag("#{trend_options[:name_prefix]}[#{column.name}][number]", trend_options[:number_value], :class => 'text-input', :size => 10, :autocomplete => 'off') << " " <<
45
- select_tag("#{trend_options[:name_prefix]}[#{column.name}][unit]",
43
+ trend_controls = text_field_tag("#{options[:name]}[number]", trend_options[:number_value], :class => 'text-input', :size => 10, :autocomplete => 'off') << " " <<
44
+ select_tag("#{options[:name]}[unit]",
46
45
  options_for_select(active_scaffold_search_date_bridge_trend_units(column), trend_options[:unit_value]),
47
46
  :class => 'text-input')
48
- content_tag("span", trend_controls.html_safe, :id => "#{options[:id]}_trend", :style => trend_options[:show] ? nil : "display: none")
47
+ content_tag("span", trend_controls.html_safe, :id => "#{options[:id]}_trend", :class => "search-date-trend", :style => trend_options[:show] ? nil : "display: none")
49
48
  end
50
49
 
51
50
  def active_scaffold_search_date_bridge_trend_units(column)
@@ -55,10 +54,10 @@ module ActiveScaffold
55
54
  end
56
55
 
57
56
  def active_scaffold_search_date_bridge_range_tag(column, options, current_search)
58
- range_controls = select_tag("search[#{column.name}][range]",
57
+ range_controls = select_tag("#{options[:name]}[range]",
59
58
  options_for_select( ActiveScaffold::Finder::DateRanges.collect{|range| [as_(range.downcase.to_sym), range]}, current_search["range"]),
60
59
  :class => 'text-input', :id => nil)
61
- content_tag("span", range_controls.html_safe, :id => "#{options[:id]}_range", :style => (current_search['opt'] == 'RANGE') ? nil : "display: none")
60
+ content_tag("span", range_controls.html_safe, :id => "#{options[:id]}_range", :class => "search-date-range", :style => (current_search['opt'] == 'RANGE') ? nil : "display: none")
62
61
  end
63
62
 
64
63
  def column_datetime?(column)
@@ -1,6 +1,7 @@
1
1
  class ActiveScaffold::Bridges::TinyMce < ActiveScaffold::DataStructures::Bridge
2
+ autoload :Helpers, 'active_scaffold/bridges/tiny_mce/helpers.rb'
2
3
  def self.install
3
- require File.join(File.dirname(__FILE__), "tiny_mce/helpers.rb")
4
+ ActionView::Base.class_eval { include ActiveScaffold::Bridges::TinyMce::Helpers }
4
5
  end
5
6
 
6
7
  def self.install?
@@ -8,9 +9,10 @@ class ActiveScaffold::Bridges::TinyMce < ActiveScaffold::DataStructures::Bridge
8
9
  end
9
10
 
10
11
  def self.javascripts
11
- if ActiveScaffold.js_framework == :jquery
12
+ case ActiveScaffold.js_framework
13
+ when :jquery
12
14
  ['tinymce-jquery', 'jquery/tiny_mce_bridge']
13
- else
15
+ when :prototype
14
16
  ['tinymce', 'prototype/tiny_mce_bridge']
15
17
  end
16
18
  end
@@ -26,9 +26,10 @@ class ActiveScaffold::Bridges::TinyMce
26
26
  end
27
27
 
28
28
  def onsubmit_with_tiny_mce
29
- if ActiveScaffold.js_framework == :jquery
30
- submit_js = 'tinyMCE.triggerSave();$(\'textarea.mceEditor\').each(function(index, elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, $(elem).attr(\'id\')); });'
31
- else
29
+ case ActiveScaffold.js_framework
30
+ when :jquery
31
+ submit_js = 'tinyMCE.triggerSave();jQuery(\'textarea.mceEditor\').each(function(index, elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, jQuery(elem).attr(\'id\')); });'
32
+ when :prototype
32
33
  submit_js = 'tinyMCE.triggerSave();this.select(\'textarea.mceEditor\').each(function(elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, elem.id); });'
33
34
  end
34
35
  [onsubmit_without_tiny_mce, submit_js].compact.join ';'
@@ -42,5 +43,3 @@ class ActiveScaffold::Bridges::TinyMce
42
43
  end
43
44
  end
44
45
  end
45
-
46
- ActionView::Base.class_eval { include ActiveScaffold::Bridges::TinyMce::Helpers }
@@ -6,7 +6,11 @@ module ActiveScaffold::Config
6
6
  def initialize(core_config)
7
7
  @core = core_config
8
8
  @action_group = self.class.action_group.clone if self.class.action_group
9
+
10
+ # start with the ActionLink defined globally
11
+ @link = self.class.link.clone if self.class.respond_to?(:link) && self.class.link
9
12
  end
13
+ attr_reader :core
10
14
 
11
15
  def self.inherited(subclass)
12
16
  class << subclass
@@ -1,8 +1,6 @@
1
1
  module ActiveScaffold::Config
2
- # to fix the ckeditor bridge problem
2
+ # to fix the ckeditor bridge problem inherit from full class name
3
3
  class Core < ActiveScaffold::Config::Base
4
- # code commented out (see above)
5
- #class Core < Base
6
4
  # global level configuration
7
5
  # --------------------------
8
6
 
@@ -33,6 +31,10 @@ module ActiveScaffold::Config
33
31
  cattr_accessor :cache_association_options
34
32
  @@cache_association_options = true
35
33
 
34
+ # enable setting ETag and LastModified on responses and using fresh_when/stale? to respond with 304 and avoid rendering views
35
+ cattr_accessor :conditional_get_support
36
+ @@conditional_get_support = false
37
+
36
38
  # enable saving user settings in session (per_page, limit, page, sort, search params)
37
39
  cattr_accessor :store_user_settings
38
40
  @@store_user_settings = true
@@ -55,7 +57,7 @@ module ActiveScaffold::Config
55
57
  # * current_user_method - what method on the controller returns the current user. default: :current_user
56
58
  # * default_permission - what the default permission is. default: true
57
59
  def self.security
58
- ActiveRecordPermissions
60
+ ActiveScaffold::ActiveRecordPermissions
59
61
  end
60
62
 
61
63
  # columns that should be ignored for every model. these should be metadata columns like change dates, versions, etc.
@@ -109,6 +111,9 @@ module ActiveScaffold::Config
109
111
  # enable caching of association options
110
112
  attr_accessor :cache_association_options
111
113
 
114
+ # enable setting ETag and LastModified on responses and using fresh_when/stale? to respond with 304 and avoid rendering views
115
+ attr_accessor :conditional_get_support
116
+
112
117
  # enable saving user settings in session (per_page, limit, page, sort, search params)
113
118
  attr_accessor :store_user_settings
114
119
 
@@ -153,8 +158,9 @@ module ActiveScaffold::Config
153
158
  @columns = ActiveScaffold::DataStructures::Columns.new(self.model, attribute_names + association_column_names)
154
159
 
155
160
  # and then, let's remove some columns from the inheritable set.
161
+ content_columns = Set.new(self.model.content_columns.map(&:name))
156
162
  @columns.exclude(*self.class.ignore_columns)
157
- @columns.exclude(*@columns.find_all { |c| c.column and (c.column.primary or c.column.name =~ /(_id|_count)$/) }.collect {|c| c.name})
163
+ @columns.exclude(*@columns.find_all { |c| c.column and content_columns.exclude?(c.column.name) }.collect {|c| c.name})
158
164
  @columns.exclude(*self.model.reflect_on_all_associations.collect{|a| :"#{a.name}_type" if a.options[:polymorphic]}.compact)
159
165
 
160
166
  # inherit the global frontend
@@ -162,6 +168,7 @@ module ActiveScaffold::Config
162
168
  @theme = self.class.theme
163
169
  @cache_action_link_urls = self.class.cache_action_link_urls
164
170
  @cache_association_options = self.class.cache_association_options
171
+ @conditional_get_support = self.class.conditional_get_support
165
172
  @store_user_settings = self.class.store_user_settings
166
173
  @sti_create_links = self.class.sti_create_links
167
174
 
@@ -4,8 +4,6 @@ module ActiveScaffold::Config
4
4
 
5
5
  def initialize(core_config)
6
6
  super
7
- # start with the ActionLink defined globally
8
- @link = self.class.link.clone
9
7
  @refresh_list = self.class.refresh_list
10
8
  end
11
9
 
@@ -6,9 +6,6 @@ module ActiveScaffold::Config
6
6
  super
7
7
  @text_search = self.class.text_search
8
8
  @human_conditions = self.class.human_conditions
9
-
10
- # start with the ActionLink defined globally
11
- @link = self.class.link.clone
12
9
  end
13
10
 
14
11
 
@@ -16,7 +13,7 @@ module ActiveScaffold::Config
16
13
  # --------------------------
17
14
  # the ActionLink for this action
18
15
  cattr_reader :link
19
- @@link = ActiveScaffold::DataStructures::ActionLink.new('show_search', :label => :search, :type => :collection, :security_method => :search_authorized?, :ignore_method => :search_ignore?)
16
+ @@link = ActiveScaffold::DataStructures::ActionLink.new('show_search', :label => :search, :type => :collection, :security_method => :search_authorized?, :ignore_method => :field_search_ignore?)
20
17
 
21
18
  # A flag for how the search should do full-text searching in the database:
22
19
  # * :full: LIKE %?%
@@ -2,8 +2,6 @@ module ActiveScaffold::Config
2
2
  class Form < Base
3
3
  def initialize(core_config)
4
4
  super
5
- # start with the ActionLink defined globally
6
- @link = self.class.link.clone unless self.class.link.nil?
7
5
  @show_unauthorized_columns = self.class.show_unauthorized_columns
8
6
  @refresh_list = self.class.refresh_list
9
7
  @persistent = self.class.persistent
@@ -18,6 +18,7 @@ module ActiveScaffold::Config
18
18
  @empty_field_text = self.class.empty_field_text
19
19
  @association_join_text = self.class.association_join_text
20
20
  @pagination = self.class.pagination
21
+ @auto_pagination = self.class.auto_pagination
21
22
  @show_search_reset = self.class.show_search_reset
22
23
  @reset_link = self.class.reset_link.clone
23
24
  @wrap_tag = self.class.wrap_tag
@@ -26,6 +27,7 @@ module ActiveScaffold::Config
26
27
  @messages_above_header = self.class.messages_above_header
27
28
  @auto_select_columns = self.class.auto_select_columns
28
29
  @refresh_with_header = self.class.refresh_with_header
30
+ @calculate_etag = self.class.calculate_etag
29
31
  end
30
32
 
31
33
  # global level configuration
@@ -65,6 +67,12 @@ module ActiveScaffold::Config
65
67
  cattr_accessor :pagination
66
68
  @@pagination = true
67
69
 
70
+ # Auto paginate, only can be used with pagination enabled
71
+ # * true: First page will be loaded on first request, next pages will be requested by AJAX until all items are loaded
72
+ # * false: Disable auto pagination
73
+ cattr_accessor :auto_pagination
74
+ @@auto_pagination = false
75
+
68
76
  # show a link to reset the search next to filtered message
69
77
  cattr_accessor :show_search_reset
70
78
  @@show_search_reset = true
@@ -90,6 +98,11 @@ module ActiveScaffold::Config
90
98
  cattr_accessor :auto_select_columns
91
99
  @@auto_select_columns = false
92
100
 
101
+ # Enable ETag calculation (when conditional_get_support is enabled), it requires to load records for page, when is disabled query can be avoided when page is cached in browser
102
+ # order clause will be used for ETag when calculate_etag is disabled, so query for records can be avoided
103
+ cattr_accessor :calculate_etag
104
+ @@calculate_etag = false
105
+
93
106
  # instance-level configuration
94
107
  # ----------------------------
95
108
 
@@ -119,6 +132,11 @@ module ActiveScaffold::Config
119
132
  # * false: Disable pagination
120
133
  attr_accessor :pagination
121
134
 
135
+ # Auto paginate, only can be used with pagination enabled
136
+ # * true: First page will be loaded on first request, next pages will be requested by AJAX until all items are loaded
137
+ # * false: Disable auto pagination
138
+ attr_accessor :auto_pagination
139
+
122
140
  # what string to use when a field is empty
123
141
  attr_accessor :empty_field_text
124
142
 
@@ -169,6 +187,14 @@ module ActiveScaffold::Config
169
187
  end
170
188
 
171
189
  def search_partial
190
+ if @always_show_search == true
191
+ auto_search_partial
192
+ else
193
+ @always_show_search.to_s if @core.actions.include? @always_show_search
194
+ end
195
+ end
196
+
197
+ def auto_search_partial
172
198
  return "search" if @core.actions.include?(:search)
173
199
  return "field_search" if @core.actions.include?(:field_search)
174
200
  end
@@ -198,6 +224,10 @@ module ActiveScaffold::Config
198
224
  # Enable auto select columns on list, so only columns needed for list columns are selected
199
225
  attr_accessor :auto_select_columns
200
226
 
227
+ # Enable ETag calculation (when conditional_get_support is enabled), it requires to load records for page, when is disabled query can be avoided when page is cached in browser
228
+ # order clause will be used for ETag when calculate_etag is disabled, so query for records can be avoided
229
+ attr_accessor :calculate_etag
230
+
201
231
  class UserSettings < UserSettings
202
232
  def initialize(conf, storage, params)
203
233
  super(conf, storage, params, :list)
@@ -216,7 +246,7 @@ module ActiveScaffold::Config
216
246
  end
217
247
 
218
248
  def page
219
- self['page'] = @params['page'] if @params.has_key? 'page'
249
+ self['page'] = @params['page'] || 1 if @params.has_key?('page') || @conf.auto_pagination
220
250
  self['page'] || 1
221
251
  end
222
252
 
@@ -7,9 +7,6 @@ module ActiveScaffold::Config
7
7
  @text_search = self.class.text_search
8
8
  @live = self.class.live?
9
9
  @split_terms = self.class.split_terms
10
-
11
- # start with the ActionLink defined globally
12
- @link = self.class.link.clone
13
10
  end
14
11
 
15
12
 
@@ -2,12 +2,6 @@ module ActiveScaffold::Config
2
2
  class Show < Base
3
3
  self.crud_type = :read
4
4
 
5
- def initialize(core_config)
6
- super
7
- # start with the ActionLink defined globally
8
- @link = self.class.link.clone
9
- end
10
-
11
5
  # global level configuration
12
6
  # --------------------------
13
7
  cattr_accessor :link
@@ -22,6 +22,7 @@ module ActiveScaffold::Config
22
22
  unless @columns
23
23
  if @core.actions.include? :update
24
24
  @columns = @core.update.columns.clone
25
+ @columns.action = self
25
26
  else
26
27
  self.columns = @core.columns._inheritable
27
28
  end
@@ -7,7 +7,7 @@ module ActiveScaffold
7
7
  module Configurable
8
8
  def configure(&configuration_block)
9
9
  return unless configuration_block
10
- @configuration_binding = configuration_block.binding
10
+ @configuration_binding = eval("self", configuration_block.binding)
11
11
  ret = instance_exec self, &configuration_block
12
12
  @configuration_binding = nil
13
13
  return ret
@@ -21,7 +21,7 @@ module ActiveScaffold
21
21
  if @configuration_binding.nil?
22
22
  raise $!
23
23
  else
24
- eval("self", @configuration_binding).send(name, *args)
24
+ @configuration_binding.send(name, *args)
25
25
  end
26
26
  end
27
27
  end
@@ -55,7 +55,7 @@ module ActiveScaffold
55
55
  field = far_association.klass.primary_key
56
56
  table = far_association.table_name
57
57
 
58
- active_scaffold_includes.concat([{k => far_association.name}]) # e.g. {:den => :park}
58
+ active_scaffold_references.concat([{k => far_association.name}]) # e.g. {:den => :park}
59
59
  hash_conditions.merge!("#{table}.#{field}" => v.values.first)
60
60
 
61
61
  # association column constraint
@@ -63,13 +63,17 @@ module ActiveScaffold
63
63
  if column.association.macro == :has_and_belongs_to_many
64
64
  active_scaffold_habtm_joins.concat column.includes
65
65
  elsif !column.association.options[:polymorphic]
66
- active_scaffold_includes.concat column.includes
66
+ if column.association.macro == :belongs_to
67
+ active_scaffold_preload.concat column.includes
68
+ else
69
+ active_scaffold_references.concat column.includes
70
+ end
67
71
  end
68
72
  hash_conditions.merge!(condition_from_association_constraint(column.association, v))
69
73
 
70
74
  # regular column constraints
71
75
  elsif column.searchable? && params[column.name] != v
72
- active_scaffold_includes.concat column.includes if column.includes.present?
76
+ active_scaffold_references.concat column.references if column.includes.present?
73
77
  conditions << [column.search_sql.collect { |search_sql| "#{search_sql} = ?" }.join(' OR '), *([v] * column.search_sql.size)]
74
78
  end
75
79
  # unknown-to-activescaffold-but-real-database-column constraint
@@ -90,23 +94,16 @@ module ActiveScaffold
90
94
  # we have to use the other model's primary_key.
91
95
  #
92
96
  # please see the relevant tests for concrete examples.
93
- field = if [:has_one, :has_many].include?(association.macro)
97
+
98
+ field = if [:has_one, :has_many, :has_and_belongs_to_many].include?(association.macro)
94
99
  association.klass.primary_key
95
- elsif [:has_and_belongs_to_many].include?(association.macro)
96
- association.association_foreign_key
97
100
  else
98
101
  association.options[:foreign_key] || association.name.to_s.foreign_key
99
102
  end
100
103
 
101
104
  table = case association.macro
102
- when :has_and_belongs_to_many
103
- association.options[:join_table]
104
-
105
- when :belongs_to
106
- active_scaffold_config.model.table_name
107
-
108
- else
109
- association.table_name
105
+ when :belongs_to then active_scaffold_config.model.table_name
106
+ else association.table_name
110
107
  end
111
108
 
112
109
  if association.options[:primary_key]
@@ -0,0 +1,277 @@
1
+ module ActiveScaffold
2
+ module Core
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.module_eval do
6
+ # TODO: these should be in actions/core
7
+ before_filter :handle_user_settings
8
+ before_filter :check_input_device
9
+ end
10
+
11
+ base.helper_method :touch_device?
12
+ base.helper_method :hover_via_click?
13
+ base.helper_method :active_scaffold_constraints
14
+ end
15
+
16
+ def active_scaffold_config
17
+ self.class.active_scaffold_config
18
+ end
19
+
20
+ def active_scaffold_config_for(klass)
21
+ self.class.active_scaffold_config_for(klass)
22
+ end
23
+
24
+ def active_scaffold_session_storage_key(id = nil)
25
+ id ||= params[:eid] || "#{params[:controller]}#{"_#{nested_parent_id}" if nested?}"
26
+ "as:#{id}"
27
+ end
28
+
29
+ def active_scaffold_session_storage(id = nil)
30
+ session_index = active_scaffold_session_storage_key(id)
31
+ session[session_index] ||= {}
32
+ session[session_index]
33
+ end
34
+
35
+ def clear_storage
36
+ session_index = active_scaffold_session_storage_key
37
+ session.delete(session_index) unless session[session_index].present?
38
+ end
39
+
40
+ # at some point we need to pass the session and params into config. we'll just take care of that before any particular action occurs by passing those hashes off to the UserSettings class of each action.
41
+ def handle_user_settings
42
+ if self.class.uses_active_scaffold?
43
+ active_scaffold_config.actions.each do |action_name|
44
+ conf_instance = active_scaffold_config.send(action_name) rescue next
45
+ next if conf_instance.class::UserSettings == ActiveScaffold::Config::Base::UserSettings # if it hasn't been extended, skip it
46
+ storage = active_scaffold_config.store_user_settings ? active_scaffold_session_storage : {}
47
+ conf_instance.user = conf_instance.class::UserSettings.new(conf_instance, storage, params)
48
+ end
49
+ end
50
+ end
51
+
52
+ def check_input_device
53
+ if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/(iPhone|iPod|iPad)/i]
54
+ session[:input_device_type] = 'TOUCH'
55
+ session[:hover_supported] = false
56
+ else
57
+ session[:input_device_type] = 'MOUSE'
58
+ session[:hover_supported] = true
59
+ end if session[:input_device_type].nil?
60
+ end
61
+
62
+ def touch_device?
63
+ session[:input_device_type] == 'TOUCH'
64
+ end
65
+
66
+ def hover_via_click?
67
+ session[:hover_supported] == false
68
+ end
69
+
70
+ module ClassMethods
71
+ def active_scaffold(model_id = nil, &block)
72
+ extend Prefixes
73
+ # initialize bridges here
74
+ ActiveScaffold::Bridges.run_all
75
+
76
+ # converts Foo::BarController to 'bar' and FooBarsController to 'foo_bar' and AddressController to 'address'
77
+ model_id = self.to_s.split('::').last.sub(/Controller$/, '').pluralize.singularize.underscore unless model_id
78
+
79
+ # run the configuration
80
+ @active_scaffold_config = ActiveScaffold::Config::Core.new(model_id)
81
+ @active_scaffold_config_block = block
82
+ self.links_for_associations
83
+
84
+ self.active_scaffold_superclasses_blocks.each {|superblock| self.active_scaffold_config.configure &superblock}
85
+ self.active_scaffold_config.sti_children = nil # reset sti_children if set in parent block
86
+ self.active_scaffold_config.configure &block if block_given?
87
+ self.active_scaffold_config._configure_sti unless self.active_scaffold_config.sti_children.nil?
88
+ self.active_scaffold_config._load_action_columns
89
+
90
+ # defines the attribute read methods on the model, so record.send() doesn't find protected/private methods instead
91
+ klass = self.active_scaffold_config.model
92
+ # Rails 4.0.4 has removed attribute_methods_generated,
93
+ # and made define_attribute_methods threadsave to call multiple times.
94
+ # Check for that here.
95
+ if ((Rails::VERSION::MAJOR == 4) && !klass.respond_to?(:attribute_methods_generated)) ||
96
+ !klass.attribute_methods_generated?
97
+ klass.define_attribute_methods
98
+ end
99
+ # include the rest of the code into the controller: the action core and the included actions
100
+ module_eval do
101
+ include ActiveScaffold::Finder
102
+ include ActiveScaffold::Constraints
103
+ include ActiveScaffold::AttributeParams
104
+ include ActiveScaffold::Actions::Core
105
+ active_scaffold_config.actions.each do |mod|
106
+ name = mod.to_s.camelize
107
+ include "ActiveScaffold::Actions::#{name}".constantize
108
+
109
+ # sneak the action links from the actions into the main set
110
+ mod_conf = active_scaffold_config.send(mod)
111
+ if mod_conf.respond_to?(:link) && (link = mod_conf.link)
112
+ if link.is_a? Array
113
+ link.each {|current| active_scaffold_config.action_links.add_to_group(current, active_scaffold_config.send(mod).action_group)}
114
+ elsif link.is_a? ActiveScaffold::DataStructures::ActionLink
115
+ active_scaffold_config.action_links.add_to_group(link, active_scaffold_config.send(mod).action_group)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ self._add_sti_create_links if self.active_scaffold_config.add_sti_create_links?
121
+ end
122
+
123
+ module Prefixes
124
+ def parent_prefixes
125
+ @parent_prefixes ||= super << 'active_scaffold_overrides'
126
+ end
127
+ end
128
+
129
+ # To be called after include action modules
130
+ def _add_sti_create_links
131
+ new_action_link = active_scaffold_config.action_links.collection['new']
132
+ unless new_action_link.nil? || active_scaffold_config.sti_children.empty?
133
+ active_scaffold_config.action_links.collection.delete('new')
134
+ active_scaffold_config.sti_children.each do |child|
135
+ new_sti_link = Marshal.load(Marshal.dump(new_action_link)) # deep clone
136
+ new_sti_link.label = as_(:create_model, :model => child.to_s.camelize.constantize.model_name.human)
137
+ new_sti_link.parameters = {:parent_sti => controller_path}
138
+ new_sti_link.controller = Proc.new { active_scaffold_controller_for(child.to_s.camelize.constantize).controller_path }
139
+ active_scaffold_config.action_links.collection.create.add(new_sti_link)
140
+ end
141
+ end
142
+ end
143
+
144
+ # Create the automatic column links. Note that this has to happen when configuration is *done*, because otherwise the Nested module could be disabled. Actually, it could still be disabled later, couldn't it?
145
+ def links_for_associations
146
+ return unless active_scaffold_config.actions.include? :list and active_scaffold_config.actions.include? :nested
147
+ active_scaffold_config.columns.each do |column|
148
+ next unless column.link.nil? and column.autolink?
149
+ #lazy load of action_link, cause it was really slowing down app in dev mode
150
+ #and might lead to trouble cause of cyclic constantization of controllers
151
+ #and might be unnecessary cause it is done before columns are configured
152
+ column.set_link(Proc.new {|col| link_for_association(col)})
153
+ end
154
+ end
155
+
156
+ def active_scaffold_controller_for_column(column, options = {})
157
+ begin
158
+ if column.polymorphic_association?
159
+ :polymorph
160
+ elsif options.include?(:controller)
161
+ "#{options[:controller].to_s.camelize}Controller".constantize
162
+ else
163
+ active_scaffold_controller_for(column.association.klass)
164
+ end
165
+ rescue ActiveScaffold::ControllerNotFound
166
+ nil
167
+ end
168
+ end
169
+
170
+ def link_for_association(column, options = {})
171
+ controller = active_scaffold_controller_for_column(column, options)
172
+
173
+ unless controller.nil?
174
+ options.reverse_merge! :position => :after, :type => :member, :controller => (controller == :polymorph ? controller : controller.controller_path), :column => column
175
+ options[:parameters] ||= {}
176
+ options[:parameters].reverse_merge! :association => column.association.name
177
+ if column.plural_association?
178
+ ActiveScaffold::DataStructures::ActionLink.new('index', options.merge(:refresh_on_close => true))
179
+ else
180
+ actions = controller.active_scaffold_config.actions unless controller == :polymorph
181
+ actions ||= [:create, :update, :show]
182
+ column.actions_for_association_links.delete :new unless actions.include? :create
183
+ column.actions_for_association_links.delete :edit unless actions.include? :update
184
+ column.actions_for_association_links.delete :show unless actions.include? :show
185
+ ActiveScaffold::DataStructures::ActionLink.new(nil, options.merge(:html_options => {:class => column.name}))
186
+ end
187
+ end
188
+ end
189
+
190
+ def link_for_association_as_scope(scope, options = {})
191
+ options.reverse_merge! :label => scope, :position => :after, :type => :member, :controller => controller_path
192
+ options[:parameters] ||= {}
193
+ options[:parameters].reverse_merge! :named_scope => scope
194
+ ActiveScaffold::DataStructures::ActionLink.new('index', options)
195
+ end
196
+
197
+ def add_active_scaffold_path(path)
198
+ as_path = File.join(ActiveScaffold::Config::Core.plugin_directory, 'app', 'views')
199
+ index = view_paths.find_index { |p| p.to_s == as_path }
200
+ if index
201
+ self.view_paths = view_paths[0..index-1] + Array(path) + view_paths[index..-1]
202
+ else
203
+ append_view_path path
204
+ end
205
+ end
206
+
207
+ def active_scaffold_config
208
+ if @active_scaffold_config.nil?
209
+ self.superclass.active_scaffold_config if self.superclass.respond_to? :active_scaffold_config
210
+ else
211
+ @active_scaffold_config
212
+ end
213
+ end
214
+
215
+ def active_scaffold_config_block
216
+ @active_scaffold_config_block
217
+ end
218
+
219
+ def active_scaffold_superclasses_blocks
220
+ blocks = []
221
+ klass = self.superclass
222
+ while klass.respond_to? :active_scaffold_superclasses_blocks
223
+ blocks << klass.active_scaffold_config_block
224
+ klass = klass.superclass
225
+ end
226
+ blocks.compact.reverse
227
+ end
228
+
229
+ def active_scaffold_config_for(klass)
230
+ begin
231
+ controller = active_scaffold_controller_for(klass)
232
+ rescue ActiveScaffold::ControllerNotFound
233
+ config = ActiveScaffold::Config::Core.new(klass)
234
+ config._load_action_columns
235
+ config
236
+ else
237
+ controller.active_scaffold_config
238
+ end
239
+ end
240
+
241
+ def active_scaffold_controller_for(klass)
242
+ ActiveScaffold::Core.active_scaffold_controller_for(klass, self.to_s.split('::')[0...-1].join('::') + '::')
243
+ end
244
+
245
+
246
+ def uses_active_scaffold?
247
+ !active_scaffold_config.nil?
248
+ end
249
+ end
250
+
251
+ # Tries to find a controller for the given ActiveRecord model.
252
+ # Searches in the namespace of the current controller for singular and plural versions of the conventional "#{model}Controller" syntax.
253
+ # You may override this method to customize the search routine.
254
+ def self.active_scaffold_controller_for(klass, controller_namespace = '::')
255
+ error_message = []
256
+ [controller_namespace, ''].each do |namespace|
257
+ ["#{klass.to_s.underscore.pluralize}", "#{klass.to_s.underscore.pluralize.singularize}"].each do |controller_name|
258
+ begin
259
+ controller = "#{namespace}#{controller_name.camelize}Controller".constantize
260
+ rescue NameError => error
261
+ # Only rescue NameError associated with the controller constant not existing - not other compile errors
262
+ if error.message["uninitialized constant #{controller}"]
263
+ error_message << "#{namespace}#{controller_name.camelize}Controller"
264
+ next
265
+ else
266
+ raise
267
+ end
268
+ end
269
+ raise ActiveScaffold::ControllerNotFound, "#{controller} missing ActiveScaffold", caller unless controller.uses_active_scaffold?
270
+ raise ActiveScaffold::ControllerNotFound, "ActiveScaffold on #{controller} is not for #{klass} model.", caller unless controller.active_scaffold_config.model.to_s == klass.to_s
271
+ return controller
272
+ end
273
+ end
274
+ raise ActiveScaffold::ControllerNotFound, "Could not find " + error_message.join(" or "), caller
275
+ end
276
+ end
277
+ end