active_scaffold 3.2.20 → 3.3.0.rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. data/CHANGELOG +19 -13
  2. data/README +66 -0
  3. data/app/assets/javascripts/jquery/active_scaffold.js +156 -113
  4. data/app/assets/javascripts/jquery/active_scaffold_chosen.js +11 -0
  5. data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +0 -1
  6. data/app/assets/javascripts/jquery/jquery.editinplace.js +132 -128
  7. data/app/assets/javascripts/prototype/active_scaffold.js +68 -25
  8. data/{frontends/default/views/_horizontal_subform_footer.html.erb → app/assets/javascripts/prototype/active_scaffold_chosen.js} +0 -0
  9. data/app/assets/stylesheets/active_scaffold_colors.css.scss +8 -1
  10. data/app/assets/stylesheets/active_scaffold_layout.css +14 -8
  11. data/{frontends/default/views → app/views/active_scaffold_overrides}/_add_existing_form.html.erb +0 -0
  12. data/{frontends/default/views → app/views/active_scaffold_overrides}/_base_form.html.erb +0 -0
  13. data/{frontends/default/views → app/views/active_scaffold_overrides}/_create_form.html.erb +0 -0
  14. data/{frontends/default/views → app/views/active_scaffold_overrides}/_create_form_on_list.html.erb +0 -0
  15. data/{frontends/default/views → app/views/active_scaffold_overrides}/_field_search.html.erb +0 -0
  16. data/{frontends/default/views → app/views/active_scaffold_overrides}/_form.html.erb +0 -0
  17. data/{frontends/default/views → app/views/active_scaffold_overrides}/_form_association.html.erb +8 -3
  18. data/{frontends/default/views → app/views/active_scaffold_overrides}/_form_association_footer.html.erb +5 -4
  19. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +85 -0
  20. data/app/views/active_scaffold_overrides/_form_attribute.html.erb +23 -0
  21. data/{frontends/default/views → app/views/active_scaffold_overrides}/_form_hidden_attribute.html.erb +0 -0
  22. data/{frontends/default/views → app/views/active_scaffold_overrides}/_form_messages.html.erb +0 -0
  23. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +12 -0
  24. data/app/views/active_scaffold_overrides/_horizontal_subform_footer.html.erb +0 -0
  25. data/{frontends/default/views → app/views/active_scaffold_overrides}/_horizontal_subform_header.html.erb +3 -2
  26. data/{frontends/default/views → app/views/active_scaffold_overrides}/_human_conditions.html.erb +0 -0
  27. data/app/views/active_scaffold_overrides/_list.html.erb +35 -0
  28. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_calculations.html.erb +0 -0
  29. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_column_headings.html.erb +0 -0
  30. data/app/views/active_scaffold_overrides/_list_header.html.erb +8 -0
  31. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_inline_adapter.html.erb +0 -0
  32. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_messages.html.erb +4 -4
  33. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_pagination.html.erb +0 -0
  34. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_pagination_links.html.erb +0 -0
  35. data/app/views/active_scaffold_overrides/_list_record.html.erb +30 -0
  36. data/{frontends/default/views → app/views/active_scaffold_overrides}/_list_with_header.html.erb +0 -0
  37. data/{frontends/default/views → app/views/active_scaffold_overrides}/_messages.html.erb +0 -0
  38. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +1 -0
  39. data/{frontends/default/views → app/views/active_scaffold_overrides}/_render_field.js.erb +9 -1
  40. data/{frontends/default/views → app/views/active_scaffold_overrides}/_row.html.erb +0 -0
  41. data/{frontends/default/views → app/views/active_scaffold_overrides}/_search.html.erb +0 -0
  42. data/{frontends/default/views → app/views/active_scaffold_overrides}/_search_attribute.html.erb +0 -0
  43. data/{frontends/default/views → app/views/active_scaffold_overrides}/_show.html.erb +0 -0
  44. data/{frontends/default/views → app/views/active_scaffold_overrides}/_show_columns.html.erb +0 -0
  45. data/app/views/active_scaffold_overrides/_update_actions.html.erb +9 -0
  46. data/{frontends/default/views → app/views/active_scaffold_overrides}/_update_calculations.js.erb +0 -0
  47. data/app/views/active_scaffold_overrides/_update_column.js.erb +16 -0
  48. data/{frontends/default/views → app/views/active_scaffold_overrides}/_update_form.html.erb +0 -0
  49. data/{frontends/default/views → app/views/active_scaffold_overrides}/_update_messages.js.erb +0 -0
  50. data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +8 -0
  51. data/{frontends/default/views → app/views/active_scaffold_overrides}/action_confirmation.html.erb +0 -0
  52. data/{frontends/default/views → app/views/active_scaffold_overrides}/add_existing.js.erb +0 -0
  53. data/{frontends/default/views → app/views/active_scaffold_overrides}/add_existing_form.html.erb +0 -0
  54. data/{frontends/default/views → app/views/active_scaffold_overrides}/create.html.erb +0 -0
  55. data/{frontends/default/views → app/views/active_scaffold_overrides}/delete.html.erb +0 -0
  56. data/{frontends/default/views → app/views/active_scaffold_overrides}/destroy.js.erb +0 -0
  57. data/{frontends/default/views → app/views/active_scaffold_overrides}/edit_associated.js.erb +1 -1
  58. data/{frontends/default/views → app/views/active_scaffold_overrides}/field_search.html.erb +0 -0
  59. data/{frontends/default/views → app/views/active_scaffold_overrides}/form_messages.js.erb +0 -0
  60. data/{frontends/default/views → app/views/active_scaffold_overrides}/list.html.erb +0 -0
  61. data/{frontends/default/views → app/views/active_scaffold_overrides}/on_action_update.js.erb +3 -3
  62. data/{frontends/default/views → app/views/active_scaffold_overrides}/on_create.js.erb +1 -1
  63. data/{frontends/default/views → app/views/active_scaffold_overrides}/on_mark.js.erb +0 -0
  64. data/{frontends/default/views → app/views/active_scaffold_overrides}/on_update.js.erb +4 -4
  65. data/{frontends/default/views → app/views/active_scaffold_overrides}/render_field.js.erb +0 -0
  66. data/{frontends/default/views → app/views/active_scaffold_overrides}/row.js.erb +1 -1
  67. data/{frontends/default/views → app/views/active_scaffold_overrides}/search.html.erb +0 -0
  68. data/{frontends/default/views → app/views/active_scaffold_overrides}/show.html.erb +0 -0
  69. data/{frontends/default/views → app/views/active_scaffold_overrides}/update.html.erb +1 -1
  70. data/app/views/active_scaffold_overrides/update_column.js.erb +26 -0
  71. data/{frontends/default/views → app/views/active_scaffold_overrides}/update_row.js.erb +0 -0
  72. data/config/locales/de.yml +1 -0
  73. data/config/locales/en.yml +1 -0
  74. data/config/locales/es.yml +1 -0
  75. data/config/locales/fr.yml +1 -0
  76. data/config/locales/hu.yml +1 -0
  77. data/config/locales/ja.yml +1 -0
  78. data/config/locales/ru.yml +1 -0
  79. data/lib/active_scaffold.rb +14 -26
  80. data/lib/active_scaffold/actions/core.rb +14 -11
  81. data/lib/active_scaffold/actions/create.rb +3 -3
  82. data/lib/active_scaffold/actions/delete.rb +3 -0
  83. data/lib/active_scaffold/actions/list.rb +9 -6
  84. data/lib/active_scaffold/actions/mark.rb +1 -1
  85. data/lib/active_scaffold/actions/nested.rb +8 -6
  86. data/lib/active_scaffold/actions/show.rb +6 -1
  87. data/lib/active_scaffold/actions/update.rb +39 -19
  88. data/lib/active_scaffold/active_record_permissions.rb +29 -12
  89. data/lib/active_scaffold/attribute_params.rb +14 -7
  90. data/lib/active_scaffold/bridges/calendar_date_select.rb +1 -1
  91. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +1 -2
  92. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  93. data/lib/active_scaffold/bridges/chosen.rb +14 -0
  94. data/lib/active_scaffold/bridges/chosen/helpers.rb +48 -0
  95. data/lib/active_scaffold/bridges/date_picker/helper.rb +7 -6
  96. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +1 -1
  97. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  98. data/lib/active_scaffold/bridges/file_column.rb +1 -1
  99. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
  100. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +2 -2
  101. data/lib/active_scaffold/bridges/file_column/list_ui.rb +4 -4
  102. data/lib/active_scaffold/bridges/paperclip.rb +1 -1
  103. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +1 -1
  104. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  105. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +1 -1
  106. data/lib/active_scaffold/bridges/shared/date_bridge.rb +1 -1
  107. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +1 -1
  108. data/lib/active_scaffold/config/core.rb +9 -2
  109. data/lib/active_scaffold/config/list.rb +9 -13
  110. data/lib/active_scaffold/config/nested.rb +11 -2
  111. data/lib/active_scaffold/data_structures/action_columns.rb +19 -5
  112. data/lib/active_scaffold/data_structures/action_link.rb +9 -2
  113. data/lib/active_scaffold/data_structures/action_links.rb +5 -36
  114. data/lib/active_scaffold/data_structures/column.rb +21 -21
  115. data/lib/active_scaffold/data_structures/nested_info.rb +31 -44
  116. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +1 -0
  117. data/lib/active_scaffold/extensions/action_view_rendering.rb +30 -36
  118. data/lib/active_scaffold/extensions/reverse_associations.rb +10 -6
  119. data/lib/active_scaffold/extensions/routing_mapper.rb +6 -5
  120. data/lib/active_scaffold/extensions/unsaved_associated.rb +1 -1
  121. data/lib/active_scaffold/finder.rb +18 -10
  122. data/lib/active_scaffold/helpers/association_helpers.rb +21 -2
  123. data/lib/active_scaffold/helpers/controller_helpers.rb +14 -16
  124. data/lib/active_scaffold/helpers/form_column_helpers.rb +161 -21
  125. data/lib/active_scaffold/helpers/id_helpers.rb +7 -7
  126. data/lib/active_scaffold/helpers/list_column_helpers.rb +42 -92
  127. data/lib/active_scaffold/helpers/search_column_helpers.rb +10 -3
  128. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -9
  129. data/lib/active_scaffold/helpers/view_helpers.rb +278 -78
  130. data/lib/active_scaffold/version.rb +2 -2
  131. data/lib/generators/active_scaffold_controller/templates/controller.rb +1 -1
  132. data/test/bridges/paperclip_test.rb +2 -2
  133. data/vendor/assets/javascripts/jquery-ui-timepicker-addon.js +1882 -1276
  134. metadata +79 -80
  135. data/README.md +0 -67
  136. data/frontends/default/views/_action_group.html.erb +0 -24
  137. data/frontends/default/views/_form_attribute.html.erb +0 -23
  138. data/frontends/default/views/_horizontal_subform.html.erb +0 -22
  139. data/frontends/default/views/_horizontal_subform_record.html.erb +0 -43
  140. data/frontends/default/views/_list.html.erb +0 -18
  141. data/frontends/default/views/_list_actions.html.erb +0 -15
  142. data/frontends/default/views/_list_header.html.erb +0 -10
  143. data/frontends/default/views/_list_record.html.erb +0 -13
  144. data/frontends/default/views/_list_record_columns.html.erb +0 -8
  145. data/frontends/default/views/_refresh_list.js.erb +0 -1
  146. data/frontends/default/views/_update_actions.html.erb +0 -9
  147. data/frontends/default/views/_vertical_subform.html.erb +0 -12
  148. data/frontends/default/views/_vertical_subform_record.html.erb +0 -43
  149. data/frontends/default/views/refresh_list.js.erb +0 -2
  150. data/frontends/default/views/update_column.js.erb +0 -15
  151. data/lib/active_scaffold/extensions/active_record_offset.rb +0 -12
  152. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +0 -7
@@ -2,18 +2,37 @@ module ActiveScaffold
2
2
  module Helpers
3
3
  module AssociationHelpers
4
4
  # Provides a way to honor the :conditions on an association while searching the association's klass
5
- def association_options_find(association, conditions = nil)
6
- relation = association.klass.where(conditions).where(association.options[:conditions])
5
+ def association_options_find(association, conditions = nil, klass = nil)
6
+ if klass.nil? && association.options[:polymorphic]
7
+ class_name = @record.send(association.foreign_type)
8
+ if class_name.present?
9
+ klass = class_name.constantize
10
+ else
11
+ return []
12
+ end
13
+ else
14
+ klass ||= association.klass
15
+ end
16
+
17
+ conditions = options_for_association_conditions(association) if conditions.nil?
18
+ relation = klass.where(conditions).where(association.options[:conditions])
7
19
  relation = relation.includes(association.options[:include]) if association.options[:include]
20
+ relation = yield(relation) if block_given?
8
21
  relation.all
9
22
  end
10
23
 
24
+ # Provides a way to honor the :conditions on an association while searching the association's klass
25
+ def sorted_association_options_find(association, conditions = nil)
26
+ association_options_find(association, conditions).sort_by(&:to_label)
27
+ end
28
+
11
29
  def association_options_count(association, conditions = nil)
12
30
  association.klass.where(conditions).where(association.options[:conditions]).count
13
31
  end
14
32
 
15
33
  # returns options for the given association as a collection of [id, label] pairs intended for the +options_for_select+ helper.
16
34
  def options_for_association(association, include_all = false)
35
+ ActiveSupport::Deprecation.warn "options_for_association should not be used, use association_options_find directly"
17
36
  available_records = association_options_find(association, include_all ? nil : options_for_association_conditions(association))
18
37
  available_records ||= []
19
38
  available_records.sort{|a,b| a.to_label <=> b.to_label}.collect { |model| [ model.to_label, model.id ] }
@@ -2,24 +2,20 @@ module ActiveScaffold
2
2
  module Helpers
3
3
  module ControllerHelpers
4
4
  def self.included(controller)
5
- controller.class_eval { helper_method :params_for, :params_conditions, :main_path_to_return, :render_parent?, :render_parent_options, :render_parent_action, :nested_singular_association?, :build_associated}
5
+ controller.class_eval { helper_method :params_for, :conditions_from_params, :main_path_to_return, :render_parent?, :render_parent_options, :render_parent_action, :nested_singular_association?, :build_associated}
6
6
  end
7
7
 
8
8
  include ActiveScaffold::Helpers::IdHelpers
9
-
10
- def params_conditions
11
- conditions_from_params.keys
12
- end
13
9
 
14
10
  def params_for(options = {})
15
11
  # :adapter and :position are one-use rendering arguments. they should not propagate.
16
12
  # :sort, :sort_direction, and :page are arguments that stored in the session. they need not propagate.
17
13
  # and wow. no we don't want to propagate :record.
18
14
  # :commit is a special rails variable for form buttons
19
- blacklist = [:adapter, :position, :sort, :sort_direction, :page, :record, :commit, :_method, :authenticity_token, :iframe]
15
+ blacklist = [:adapter, :position, :sort, :sort_direction, :page, :record, :commit, :_method, :authenticity_token, :iframe, :associated_id, :dont_close]
20
16
  unless @params_for
21
17
  @params_for = {}
22
- params.select { |key, value| blacklist.exclude? key.to_sym if key }.each {|key, value| @params_for[key.to_sym] = value.duplicable? ? value.clone : value}
18
+ params.except(*blacklist).each {|key, value| @params_for[key.to_sym] = value.duplicable? ? value.clone : value}
23
19
  @params_for[:controller] = '/' + @params_for[:controller].to_s unless @params_for[:controller].to_s.first(1) == '/' # for namespaced controllers
24
20
  @params_for.delete(:id) if @params_for[:id].nil?
25
21
  end
@@ -31,23 +27,21 @@ module ActiveScaffold
31
27
  if params[:return_to]
32
28
  params[:return_to]
33
29
  else
30
+ exclude_parameters = [:utf8, :associated_id]
34
31
  parameters = {}
35
- if params[:parent_controller]
36
- parameters[:controller] = params[:parent_controller]
37
- #parameters[:eid] = params[:parent_controller] # not neeeded anymore?
32
+ if params[:parent_scaffold] && nested? && nested.singular_association?
33
+ parameters[:controller] = params[:parent_scaffold]
34
+ exclude_parameters.concat [nested.param_name, :association, :parent_scaffold]
35
+ #parameters[:eid] = params[:parent_scaffold] # not neeeded anymore?
38
36
  end
39
37
  parameters.merge! nested.to_params if nested?
40
38
  if params[:parent_sti]
41
39
  parameters[:controller] = params[:parent_sti]
42
40
  #parameters[:eid] = nil # not neeeded anymore?
43
41
  end
44
- parameters[:parent_column] = nil
45
- parameters[:parent_id] = nil
46
42
  parameters[:action] = "index"
47
43
  parameters[:id] = nil
48
- parameters[:associated_id] = nil
49
- parameters[:utf8] = nil
50
- params_for(parameters)
44
+ params_for(parameters).except(*exclude_parameters)
51
45
  end
52
46
  end
53
47
 
@@ -84,7 +78,11 @@ module ActiveScaffold
84
78
 
85
79
  def build_associated(column, record)
86
80
  if column.singular_association?
87
- record.send(:"build_#{column.name}")
81
+ if column.association.options[:through]
82
+ record.send(:"build_#{column.association.through_reflection.name}").send(:"build_#{column.name}")
83
+ else
84
+ record.send(:"build_#{column.name}")
85
+ end
88
86
  else
89
87
  record.send(column.name).build
90
88
  end
@@ -58,6 +58,24 @@ module ActiveScaffold
58
58
  raise e
59
59
  end
60
60
  end
61
+
62
+ def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false)
63
+ if add_class
64
+ col_class = []
65
+ col_class << 'required' if column.required?
66
+ col_class << column.css_class unless column.css_class.nil? || column.css_class.is_a?(Proc)
67
+ col_class << 'hidden' if column_renders_as(column) == :hidden
68
+ col_class << 'checkbox' if column.form_ui == :checkbox
69
+ col_class = col_class.join(' ')
70
+ end
71
+ unless readonly and not @record.new_record? or not @record.authorized_for?(:crud_type => crud_type, :column => column.name)
72
+ render :partial => form_partial_for_column(column), :locals => { :column => column, :scope => scope, :col_class => col_class }
73
+ else
74
+ options = active_scaffold_input_options(column, scope).except(:name)
75
+ options[:class] = "#{options[:class]} #{col_class}" if col_class
76
+ content_tag :span, get_column_value(@record, column), options
77
+ end
78
+ end
61
79
 
62
80
  # the standard active scaffold options used for textual inputs
63
81
  def active_scaffold_input_text_options(options = {})
@@ -70,6 +88,12 @@ module ActiveScaffold
70
88
  def active_scaffold_input_options(column, scope = nil, options = {})
71
89
  name = scope ? "record#{scope}[#{column.name}]" : "record[#{column.name}]"
72
90
 
91
+ # Add some HTML5 attributes for in-browser validation and better user experience
92
+ if column.required? && (!@disable_required_for_new || scope.nil? || @record.persisted?)
93
+ options[:required] = true
94
+ end
95
+ options[:placeholder] = column.placeholder if column.placeholder.present?
96
+
73
97
  # Fix for keeping unique IDs in subform
74
98
  id_control = "record_#{column.name}_#{[params[:eid], params[:id]].compact.join '_'}"
75
99
  id_control += scope_id(scope) if scope
@@ -81,16 +105,24 @@ module ActiveScaffold
81
105
  end
82
106
 
83
107
  def update_columns_options(column, scope, options)
84
- if column.update_columns
85
- form_action = params[:action] == 'edit' ? :update : :create
86
- url_params = {:action => 'render_field', :id => params[:id], :column => column.name}
108
+ form_action = if scope
109
+ subform_controller = controller.class.active_scaffold_controller_for(@record.class)
110
+ subform_controller.active_scaffold_config.subform
111
+ elsif [:new, :create, :edit, :update, :render_field].include? params[:action].to_sym
112
+ active_scaffold_config.send(@record.new_record? ? :create : :update)
113
+ end
114
+ if form_action && column.update_columns && (column.update_columns & form_action.columns.names).present?
115
+ url_params = {:action => 'render_field', :column => column.name, :id => nil}
116
+ url_params[:id] = @record.id if column.send_form_on_update_column
87
117
  url_params[:eid] = params[:eid] if params[:eid]
88
- url_params[:controller] = controller.class.active_scaffold_controller_for(@record.class).controller_path if scope
89
- url_params[:scope] = scope if scope
118
+ if scope
119
+ url_params[:controller] = subform_controller.controller_path
120
+ url_params[:scope] = scope
121
+ end
90
122
 
91
123
  options[:class] = "#{options[:class]} update_form".strip
92
124
  options['data-update_url'] = url_for(url_params)
93
- options['data-update_send_form'] = true if column.send_form_on_update_column
125
+ options['data-update_send_form'] = column.send_form_on_update_column
94
126
  options['data-update_send_form_selector'] = column.options[:send_form_selector] if column.options[:send_form_selector]
95
127
  end
96
128
  options
@@ -99,41 +131,57 @@ module ActiveScaffold
99
131
  ##
100
132
  ## Form input methods
101
133
  ##
134
+
135
+ def grouped_options_for_select(column, select_options, optgroup)
136
+ group_label = active_scaffold_config_for(column.association.klass).columns[optgroup].try(:association) ? :to_label : :to_s
137
+ select_options.group_by(&optgroup.to_sym).collect do |group, options|
138
+ [group.send(group_label), options.collect {|r| [r.to_label, r.id]}]
139
+ end
140
+ end
102
141
 
103
142
  def active_scaffold_translate_select_options(options)
104
- options[:include_blank] = as_(options[:include_blank]) if options[:include_blank].is_a? Symbol
105
- options[:prompt] = as_(options[:prompt]) if options[:prompt].is_a? Symbol
143
+ options[:include_blank] = as_(options[:include_blank].to_s) if options[:include_blank].is_a? Symbol
144
+ options[:prompt] = as_(options[:prompt].to_s) if options[:prompt].is_a? Symbol
106
145
  options
107
146
  end
108
147
 
109
148
  def active_scaffold_input_singular_association(column, html_options)
110
149
  associated = @record.send(column.association.name)
111
150
 
112
- select_options = options_for_association(column.association)
113
- select_options.unshift([ associated.to_label, associated.id ]) unless associated.nil? or select_options.find {|label, id| id == associated.id}
151
+ select_options = sorted_association_options_find(column.association)
152
+ select_options.unshift(associated) unless associated.nil? || select_options.include?(associated)
114
153
 
115
154
  method = column.name
116
- #html_options[:name] += '[id]'
117
155
  options = {:selected => associated.try(:id), :include_blank => as_(:_select_)}
118
156
 
119
157
  html_options.update(column.options[:html_options] || {})
120
158
  options.update(column.options)
121
- html_options[:name] = "#{html_options[:name]}[]" if (html_options[:multiple] == true && !html_options[:name].to_s.ends_with?("[]"))
159
+ html_options[:name] = "#{html_options[:name]}[]" if html_options[:multiple] == true && !html_options[:name].to_s.ends_with?("[]")
122
160
  active_scaffold_translate_select_options(options)
123
- select(:record, method, select_options.uniq, options, html_options)
161
+
162
+ if optgroup = options.delete(:optgroup)
163
+ select(:record, method, grouped_options_for_select(column, select_options, optgroup), options, html_options)
164
+ else
165
+ collection_select(:record, method, select_options, :id, :to_label, options, html_options)
166
+ end
167
+ end
168
+
169
+ def active_scaffold_plural_association_options(column)
170
+ associated_options = @record.send(column.association.name)
171
+ [associated_options, associated_options | sorted_association_options_find(column.association)]
124
172
  end
125
173
 
126
174
  def active_scaffold_input_plural_association(column, options)
127
- associated_options = @record.send(column.association.name).collect {|r| [r.to_label, r.id]}
128
- select_options = associated_options | options_for_association(column.association)
175
+ associated_options, select_options = active_scaffold_plural_association_options(column)
129
176
  return content_tag(:span, as_(:no_options), :class => options[:class], :id => options[:id]) if select_options.empty?
130
177
 
131
- active_scaffold_checkbox_list(column, select_options, associated_options.collect {|a| a[1]}, options)
178
+ active_scaffold_checkbox_list(column, select_options.collect {|r| [r.to_label, r.id]}, associated_options.collect(&:id), options)
132
179
  end
133
180
 
134
181
  def active_scaffold_checkbox_list(column, select_options, associated_ids, options)
135
- html = content_tag :ul, :class => "#{options[:class]} checkbox-list", :id => options[:id] do
136
- content = hidden_field_tag("#{options[:name]}[]", '')
182
+ html = hidden_field_tag("#{options[:name]}[]", '')
183
+ html << content_tag(:ul, :class => "#{options[:class]} checkbox-list", :id => options[:id]) do
184
+ content = ''.html_safe
137
185
  select_options.each_with_index do |option, i|
138
186
  label, id = option
139
187
  this_id = "#{options[:id]}_#{i}_id"
@@ -152,10 +200,14 @@ module ActiveScaffold
152
200
  value = text if value.nil?
153
201
  [(text.is_a?(Symbol) ? column.active_record_class.human_attribute_name(text) : text), value]
154
202
  end
203
+
204
+ def active_scaffold_enum_options(column)
205
+ column.options[:options]
206
+ end
155
207
 
156
208
  def active_scaffold_input_enum(column, html_options)
157
209
  options = { :selected => @record.send(column.name) }
158
- options_for_select = column.options[:options].collect do |text, value|
210
+ options_for_select = active_scaffold_enum_options(column).collect do |text, value|
159
211
  active_scaffold_translated_option(column, text, value)
160
212
  end
161
213
  html_options.update(column.options[:html_options] || {})
@@ -200,6 +252,41 @@ module ActiveScaffold
200
252
  text_field :record, column.name, options.merge(column.options)
201
253
  end
202
254
 
255
+ # Some fields from HTML5 (primarily for using in-browser validation)
256
+ # Sadly, many of them lacks browser support
257
+
258
+ # A text box, that accepts only valid email address (in-browser validation)
259
+ def active_scaffold_input_email(column, options)
260
+ options = active_scaffold_input_text_options(options)
261
+ email_field :record, column.name, options.merge(column.options)
262
+ end
263
+
264
+ # A text box, that accepts only valid URI (in-browser validation)
265
+ def active_scaffold_input_url(column, options)
266
+ options = active_scaffold_input_text_options(options)
267
+ url_field :record, column.name, options.merge(column.options)
268
+ end
269
+
270
+ # A text box, that accepts only valid phone-number (in-browser validation)
271
+ def active_scaffold_input_telephone(column, options)
272
+ options = active_scaffold_input_text_options(options)
273
+ telephone_field :record, column.name, options.merge(column.options)
274
+ end
275
+
276
+ # A spinbox control for number values (in-browser validation)
277
+ def active_scaffold_input_number(column, options)
278
+ options = numerical_constraints_for_column(column, options)
279
+ options = active_scaffold_input_text_options(options)
280
+ number_field :record, column.name, options.merge(column.options)
281
+ end
282
+
283
+ # A slider control for number values (in-browser validation)
284
+ def active_scaffold_input_range(column, options)
285
+ options = numerical_constraints_for_column(column, options)
286
+ options = active_scaffold_input_text_options(options)
287
+ range_field :record, column.name, options.merge(column.options)
288
+ end
289
+
203
290
  #
204
291
  # Column.type-based inputs
205
292
  #
@@ -304,8 +391,9 @@ module ActiveScaffold
304
391
  options.merge!(active_scaffold_input_text_options)
305
392
  record_select_field(options[:name], @record, options)
306
393
  else
307
- select_options = options_for_select(options_for_association(nested.association)) #unless column.through_association?
308
- select_options ||= options_for_select(active_scaffold_config.model.all.collect {|c| [h(c.to_label), c.id]})
394
+ select_options = sorted_association_options_find(nested.association)
395
+ select_options ||= active_scaffold_config.model.all
396
+ select_options = options_from_collection_for_select(select_options, :id, :to_label)
309
397
  select_tag 'associated_id', ('<option value="">' + as_(:_select_) + '</option>' + select_options).html_safe unless select_options.empty?
310
398
  end
311
399
  end
@@ -317,6 +405,58 @@ module ActiveScaffold
317
405
  active_scaffold_config.model.model_name.human
318
406
  end
319
407
  end
408
+
409
+ # Try to get numerical constraints from model's validators
410
+ def numerical_constraints_for_column(column, options)
411
+ if column.numerical_constraints.nil?
412
+ numerical_constraints = {}
413
+ validators = column.active_record_class.validators.select do |v|
414
+ v.is_a? ActiveModel::Validations::NumericalityValidator and v.attributes.include? column.name
415
+ end
416
+ equal_to = (v = validators.find{ |v| v.options[:equal_to] }) ? v.options[:equal_to] : nil
417
+
418
+ # If there is equal_to constraint - use it (unless otherwise specified by user)
419
+ if equal_to and not (options[:min] or options[:max])
420
+ numerical_constraints[:min] = numerical_constraints[:max] = equal_to
421
+ else # find minimum and maximum from validators
422
+ # we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
423
+ only_integer = column.column.type == :integer if column.column
424
+ only_integer ||= !!validators.find{ |v| v.options[:only_integer] }
425
+ margin = only_integer ? 1 : 0
426
+
427
+ # Minimum
428
+ unless options[:min]
429
+ min = validators.map{ |v| v.options[:greater_than_or_equal] }.compact.max
430
+ greater_than = validators.map{ |v| v.options[:greater_than] }.compact.max
431
+ numerical_constraints[:min] = [min, (greater_than+margin if greater_than)].compact.max
432
+ end
433
+
434
+ # Maximum
435
+ unless options[:max]
436
+ max = validators.map{ |v| v.options[:less_than_or_equal] }.compact.min
437
+ less_than = validators.map{ |v| v.options[:less_than] }.compact.min
438
+ numerical_constraints[:max] = [max, (less_than-margin if less_than)].compact.min
439
+ end
440
+
441
+ # Set step = 2 for column values restricted to be odd or even (but only if minimum is set)
442
+ unless options[:step]
443
+ only_odd_valid = validators.any?{ |v| v.options[:odd] }
444
+ only_even_valid = validators.any?{ |v| v.options[:even] } unless only_odd_valid
445
+ if !only_integer
446
+ numerical_constraints[:step] ||= "0.#{'0'*(column.column.scale-1)}1" if column.column && column.column.scale.to_i > 0
447
+ elsif options[:min] and options[:min].respond_to? :even? and (only_odd_valid or only_even_valid)
448
+ numerical_constraints[:step] = 2
449
+ numerical_constraints[:min] += 1 if only_odd_valid and not options[:min].odd?
450
+ numerical_constraints[:min] += 1 if only_even_valid and not options[:min].even?
451
+ end
452
+ numerical_constraints[:step] ||= 'any' unless only_integer
453
+ end
454
+ end
455
+
456
+ column.numerical_constraints = numerical_constraints
457
+ end
458
+ return column.numerical_constraints.merge(options)
459
+ end
320
460
  end
321
461
  end
322
462
  end
@@ -58,14 +58,14 @@ module ActiveScaffold
58
58
  def element_row_id(options = {})
59
59
  options[:action] ||= params[:action]
60
60
  options[:id] ||= params[:id]
61
- options[:id] ||= params[:parent_id]
61
+ options[:id] ||= nested.parent_id if nested?
62
62
  clean_id "#{options[:controller_id] || controller_id}-#{options[:action]}-#{options[:id]}-row"
63
63
  end
64
64
 
65
65
  def element_cell_id(options = {})
66
66
  options[:action] ||= params[:action]
67
67
  options[:id] ||= params[:id]
68
- options[:id] ||= params[:parent_id]
68
+ options[:id] ||= nested.parent_id if nested?
69
69
  options[:name] ||= params[:name]
70
70
  clean_id "#{controller_id}-#{options[:action]}-#{options[:id]}-#{options[:name]}-cell"
71
71
  end
@@ -73,7 +73,7 @@ module ActiveScaffold
73
73
  def element_form_id(options = {})
74
74
  options[:action] ||= params[:action]
75
75
  options[:id] ||= params[:id]
76
- options[:id] ||= params[:parent_id]
76
+ options[:id] ||= nested.parent_id if nested?
77
77
  clean_id "#{controller_id}-#{options[:action]}-#{options[:id]}-form"
78
78
  end
79
79
 
@@ -89,26 +89,26 @@ module ActiveScaffold
89
89
 
90
90
  def sub_section_id(options = {})
91
91
  options[:id] ||= params[:id]
92
- options[:id] ||= params[:parent_id]
92
+ options[:id] ||= nested.parent_id if nested?
93
93
  clean_id "#{controller_id}-#{options[:id]}-#{options[:sub_section]}-subsection"
94
94
  end
95
95
 
96
96
  def sub_form_id(options = {})
97
97
  options[:id] ||= params[:id]
98
- options[:id] ||= params[:parent_id]
98
+ options[:id] ||= nested.parent_id if nested?
99
99
  clean_id "#{controller_id}-#{options[:id]}-#{options[:association]}-subform"
100
100
  end
101
101
 
102
102
  def sub_form_list_id(options = {})
103
103
  options[:id] ||= params[:id]
104
- options[:id] ||= params[:parent_id]
104
+ options[:id] ||= nested.parent_id if nested?
105
105
  clean_id "#{controller_id}-#{options[:id]}-#{options[:association]}-subform-list"
106
106
  end
107
107
 
108
108
  def element_messages_id(options = {})
109
109
  options[:action] ||= params[:action]
110
110
  options[:id] ||= params[:id]
111
- options[:id] ||= params[:parent_id]
111
+ options[:id] ||= nested.parent_id if nested?
112
112
  clean_id "#{controller_id}-#{options[:action]}-#{options[:id]}-messages"
113
113
  end
114
114
 
@@ -5,32 +5,37 @@ module ActiveScaffold
5
5
  module ListColumnHelpers
6
6
  def get_column_value(record, column)
7
7
  begin
8
- # check for an override helper
9
- value = if (method = column_override(column))
8
+ method = get_column_method(record, column)
9
+ value = send(method, record, column)
10
+ value = '&nbsp;'.html_safe if value.nil? or value.blank? # fix for IE 6
11
+ return value
12
+ rescue Exception => e
13
+ logger.error "#{Time.now.to_s} #{e.inspect} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
14
+ raise e
15
+ end
16
+ end
17
+
18
+
19
+ def get_column_method(record, column)
20
+ # check for an override helper
21
+ method = column.list_method
22
+ unless method
23
+ method = if (method = column_override(column))
10
24
  # we only pass the record as the argument. we previously also passed the formatted_value,
11
25
  # but mike perham pointed out that prohibited the usage of overrides to improve on the
12
26
  # performance of our default formatting. see issue #138.
13
- if method(method).arity == 1
14
- ActiveSupport::Deprecation.warn("Add column argument to field override, signature is unified with list_ui")
15
- send(method, record)
16
- else
17
- send(method, record, column)
18
- end
27
+ method
19
28
  # second, check if the dev has specified a valid list_ui for this column
20
29
  elsif column.list_ui and (method = override_column_ui(column.list_ui))
21
- send(method, column, record)
30
+ method
22
31
  elsif column.column and (method = override_column_ui(column.column.type))
23
- send(method, column, record)
32
+ method
24
33
  else
25
- format_column_value(record, column)
34
+ :format_column_value
26
35
  end
27
-
28
- value = '&nbsp;'.html_safe if value.nil? or (value.respond_to?(:empty?) and value.empty?) # fix for IE 6
29
- return value
30
- rescue Exception => e
31
- logger.error Time.now.to_s + "#{e.inspect} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
32
- raise e
36
+ column.list_method = method
33
37
  end
38
+ method
34
39
  end
35
40
 
36
41
  # TODO: move empty_field_text and &nbsp; logic in here?
@@ -39,21 +44,7 @@ module ActiveScaffold
39
44
  if column.link
40
45
  link = column.link
41
46
  associated = record.send(column.association.name) if column.association
42
- url_options = params_for(:action => nil, :id => record.id)
43
-
44
- # setup automatic link
45
- if column.autolink? && column.singular_association? # link to inline form
46
- link = action_link_to_inline_form(column, record, associated, text)
47
- return text if link.nil?
48
- else
49
- url_options[:link] = text
50
- end
51
-
52
- if column_link_authorized?(link, column, record, associated)
53
- render_action_link(link, url_options, record)
54
- else
55
- "<a class='disabled'>#{text}</a>".html_safe
56
- end
47
+ render_action_link(link, record, :link => text, :authorized => link.action.nil? || column_link_authorized?(link, column, record, associated))
57
48
  elsif inplace_edit?(record, column)
58
49
  active_scaffold_inplace_edit(record, column, {:formatted_column => text})
59
50
  elsif active_scaffold_config.list.wrap_tag
@@ -63,53 +54,6 @@ module ActiveScaffold
63
54
  end
64
55
  end
65
56
 
66
- # setup the action link to inline form
67
- def action_link_to_inline_form(column, record, associated, text)
68
- link = column.link.clone
69
- link.label = text
70
- if column.polymorphic_association?
71
- polymorphic_controller = controller_path_for_activerecord(record.send(column.association.name).class)
72
- return link if polymorphic_controller.nil?
73
- link.controller = polymorphic_controller
74
- end
75
- configure_column_link(link, associated, column.actions_for_association_links)
76
- end
77
-
78
- def configure_column_link(link, associated, actions)
79
- if column_empty?(associated) # if association is empty, we only can link to create form
80
- if actions.include?(:new)
81
- link.action = 'new'
82
- link.crud_type = :create
83
- link.label = as_(:create_new)
84
- end
85
- elsif actions.include?(:edit)
86
- link.action = 'edit'
87
- link.crud_type = :update
88
- elsif actions.include?(:show)
89
- link.action = 'show'
90
- link.crud_type = :read
91
- elsif actions.include?(:list)
92
- link.action = 'index'
93
- link.crud_type = :read
94
- end
95
- link if link.action.present?
96
- end
97
-
98
- def column_link_authorized?(link, column, record, associated)
99
- if column.association
100
- associated_for_authorized = if column.plural_association? || (associated.respond_to?(:blank?) && associated.blank?)
101
- column.association.klass
102
- else
103
- associated
104
- end
105
- authorized = associated_for_authorized.authorized_for?(:crud_type => link.crud_type)
106
- authorized = authorized and record.authorized_for?(:crud_type => :update, :column => column.name) if link.crud_type == :create
107
- authorized
108
- else
109
- record.authorized_for?(:crud_type => link.crud_type)
110
- end
111
- end
112
-
113
57
  # There are two basic ways to clean a column's value: h() and sanitize(). The latter is useful
114
58
  # when the column contains *valid* html data, and you want to just disable any scripting. People
115
59
  # can always use field overrides to clean data one way or the other, but having this override
@@ -124,16 +68,20 @@ module ActiveScaffold
124
68
  ##
125
69
  ## Overrides
126
70
  ##
127
- def active_scaffold_column_text(column, record)
71
+ def active_scaffold_column_text(record, column)
128
72
  clean_column_value(truncate(record.send(column.name), :length => column.options[:truncate] || 50))
129
73
  end
130
74
 
131
- def active_scaffold_column_marked(column, record)
75
+ def active_scaffold_column_fulltext(record, column)
76
+ clean_column_value(record.send(column.name))
77
+ end
78
+
79
+ def active_scaffold_column_marked(record, column)
132
80
  options = {:id => nil, :object => record}
133
81
  content_tag(:span, check_box(:record, column.name, options), :class => 'in_place_editor_field', :data => {:ie_id => record.id.to_s})
134
82
  end
135
83
 
136
- def active_scaffold_column_checkbox(column, record)
84
+ def active_scaffold_column_checkbox(record, column)
137
85
  options = {:disabled => true, :id => nil, :object => record}
138
86
  options.delete(:disabled) if inplace_edit?(record, column)
139
87
  check_box(:record, column.name, options)
@@ -146,8 +94,10 @@ module ActiveScaffold
146
94
 
147
95
  # the naming convention for overriding column types with helpers
148
96
  def override_column_ui(list_ui)
97
+ @_column_ui_overrides ||= {}
98
+ return @_column_ui_overrides[list_ui] if @_column_ui_overrides.include? list_ui
149
99
  method = "active_scaffold_column_#{list_ui}"
150
- method if respond_to? method
100
+ @_column_ui_overrides[list_ui] = (method if respond_to? method)
151
101
  end
152
102
  alias_method :override_column_ui?, :override_column_ui
153
103
 
@@ -237,7 +187,7 @@ module ActiveScaffold
237
187
  if column.associated_limit.nil?
238
188
  Rails.logger.warn "ActiveScaffold: Enable eager loading for #{column.name} association to reduce SQL queries"
239
189
  elsif column.associated_limit > 0
240
- value.target = value.find(:all, :limit => column.associated_limit + 1, :select => column.select_associated_columns)
190
+ value.target = value.find(:all, :limit => column.associated_limit + 1, :select => column.select_columns)
241
191
  elsif @cache_associations
242
192
  value.target = size.to_i.zero? ? [] : [nil]
243
193
  end
@@ -251,8 +201,7 @@ module ActiveScaffold
251
201
  def inplace_edit?(record, column)
252
202
  if column.inplace_edit
253
203
  editable = controller.send(:update_authorized?, record) if controller.respond_to?(:update_authorized?)
254
- editable = record.authorized_for?(:crud_type => :update, :column => column.name) if editable.nil? || editable == true
255
- editable
204
+ editable ||= record.authorized_for?(:crud_type => :update, :column => column.name)
256
205
  end
257
206
  end
258
207
 
@@ -265,6 +214,7 @@ module ActiveScaffold
265
214
  id_options = {:id => record.id.to_s, :action => 'update_column', :name => column.name.to_s}
266
215
  tag_options = {:id => element_cell_id(id_options), :class => "in_place_editor_field",
267
216
  :title => as_(:click_to_edit), :data => {:ie_id => record.id.to_s}}
217
+ tag_options[:data][:ie_update] = column.inplace_edit if column.inplace_edit != true
268
218
 
269
219
  content_tag(:span, as_(:inplace_edit_handle), :class => 'handle') <<
270
220
  content_tag(:span, formatted_column, tag_options)
@@ -288,7 +238,7 @@ module ActiveScaffold
288
238
 
289
239
  def inplace_edit_data(column)
290
240
  data = {}
291
- data[:ie_url] = url_for({:controller => params_for[:controller], :action => "update_column", :column => column.name, :id => '__id__'})
241
+ data[:ie_url] = url_for(params_for(:action => "update_column", :column => column.name, :id => '__id__'))
292
242
  data[:ie_cancel_text] = column.options[:cancel_text] || as_(:cancel)
293
243
  data[:ie_loading_text] = column.options[:loading_text] || as_(:loading)
294
244
  data[:ie_save_text] = column.options[:save_text] || as_(:update)
@@ -302,7 +252,7 @@ module ActiveScaffold
302
252
  elsif inplace_edit_cloning?(column)
303
253
  data[:ie_mode] = :clone
304
254
  elsif column.inplace_edit == :ajax
305
- url = url_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :column => column.name, :update_column => column.name, :in_place_editing => true)
255
+ url = url_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name)
306
256
  plural = column.plural_association? && !override_form_field?(column) && [:select, :record_select].include?(column.form_ui)
307
257
  data[:ie_render_url] = url
308
258
  data[:ie_mode] = :ajax
@@ -315,7 +265,7 @@ module ActiveScaffold
315
265
  if active_scaffold_config.mark.mark_all_mode == :page
316
266
  all_marked = @page.items.detect { |record| !marked_records.include?(record.id) }.nil?
317
267
  else
318
- all_marked = (marked_records.length >= @page.pager.count)
268
+ all_marked = (marked_records.length >= @page.pager.count.to_i)
319
269
  end
320
270
  end
321
271
 
@@ -356,12 +306,12 @@ module ActiveScaffold
356
306
  end
357
307
  end
358
308
 
359
- def render_nested_view(action_links, url_options, record)
309
+ def render_nested_view(action_links, record)
360
310
  rendered = []
361
311
  action_links.member.each do |link|
362
312
  if link.nested_link? && link.column && @nested_auto_open[link.column.name] && @records.length <= @nested_auto_open[link.column.name] && controller.respond_to?(:render_component_into_view)
363
- link_url_options = {:adapter => '_list_inline_adapter', :format => :js}.merge(action_link_url_options(link, url_options, record, options = {:reuse_eid => true}))
364
- link_id = get_action_link_id(link_url_options, record, link.column)
313
+ link_url_options = {:adapter => '_list_inline_adapter', :format => :js}.merge(action_link_url_options(link, record))
314
+ link_id = get_action_link_id(link, record)
365
315
  rendered << (controller.send(:render_component_into_view, link_url_options) + javascript_tag("ActiveScaffold.ActionLink.get('#{link_id}').set_opened();"))
366
316
  end
367
317
  end