active_scaffold 3.2.20 → 3.3.0.rc

Sign up to get free protection for your applications and to get access to all the features.
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