active_scaffold_san 3.0.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. data/.autotest +27 -0
  2. data/.document +5 -0
  3. data/CHANGELOG +179 -0
  4. data/Gemfile +13 -0
  5. data/Gemfile.lock +20 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README +69 -0
  8. data/Rakefile +53 -0
  9. data/active_scaffold_vho.gemspec +388 -0
  10. data/frontends/default/images/add.gif +0 -0
  11. data/frontends/default/images/arrow_down.gif +0 -0
  12. data/frontends/default/images/arrow_up.gif +0 -0
  13. data/frontends/default/images/close.gif +0 -0
  14. data/frontends/default/images/config.png +0 -0
  15. data/frontends/default/images/cross.png +0 -0
  16. data/frontends/default/images/gears.png +0 -0
  17. data/frontends/default/images/indicator-small.gif +0 -0
  18. data/frontends/default/images/indicator.gif +0 -0
  19. data/frontends/default/images/magnifier.png +0 -0
  20. data/frontends/default/javascripts/jquery/active_scaffold.js +1004 -0
  21. data/frontends/default/javascripts/jquery/jquery.editinplace.js +743 -0
  22. data/frontends/default/javascripts/prototype/active_scaffold.js +1003 -0
  23. data/frontends/default/javascripts/prototype/dhtml_history.js +867 -0
  24. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  25. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  26. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  27. data/frontends/default/stylesheets/stylesheet.css +973 -0
  28. data/frontends/default/views/_action_group.html.erb +20 -0
  29. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  30. data/frontends/default/views/_base_form.html.erb +51 -0
  31. data/frontends/default/views/_create_form.html.erb +8 -0
  32. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  33. data/frontends/default/views/_field_search.html.erb +32 -0
  34. data/frontends/default/views/_form.html.erb +24 -0
  35. data/frontends/default/views/_form_association.html.erb +19 -0
  36. data/frontends/default/views/_form_association_footer.html.erb +40 -0
  37. data/frontends/default/views/_form_attribute.html.erb +15 -0
  38. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  39. data/frontends/default/views/_form_messages.html.erb +5 -0
  40. data/frontends/default/views/_horizontal_subform.html.erb +29 -0
  41. data/frontends/default/views/_horizontal_subform_header.html.erb +10 -0
  42. data/frontends/default/views/_horizontal_subform_record.html.erb +37 -0
  43. data/frontends/default/views/_human_conditions.html.erb +1 -0
  44. data/frontends/default/views/_list.html.erb +18 -0
  45. data/frontends/default/views/_list_actions.html.erb +15 -0
  46. data/frontends/default/views/_list_calculations.html.erb +16 -0
  47. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  48. data/frontends/default/views/_list_header.html.erb +10 -0
  49. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  50. data/frontends/default/views/_list_messages.html.erb +32 -0
  51. data/frontends/default/views/_list_pagination.html.erb +11 -0
  52. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  53. data/frontends/default/views/_list_record.html.erb +14 -0
  54. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  55. data/frontends/default/views/_list_with_header.html.erb +32 -0
  56. data/frontends/default/views/_messages.html.erb +10 -0
  57. data/frontends/default/views/_render_field.js.rjs +10 -0
  58. data/frontends/default/views/_row.html.erb +12 -0
  59. data/frontends/default/views/_search.html.erb +34 -0
  60. data/frontends/default/views/_search_attribute.html.erb +10 -0
  61. data/frontends/default/views/_show.html.erb +8 -0
  62. data/frontends/default/views/_show_columns.html.erb +15 -0
  63. data/frontends/default/views/_update_actions.html.erb +9 -0
  64. data/frontends/default/views/_update_form.html.erb +6 -0
  65. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  66. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  67. data/frontends/default/views/action_confirmation.html.erb +13 -0
  68. data/frontends/default/views/add_existing.js.rjs +17 -0
  69. data/frontends/default/views/add_existing_form.html.erb +5 -0
  70. data/frontends/default/views/create.html.erb +5 -0
  71. data/frontends/default/views/delete.html.erb +13 -0
  72. data/frontends/default/views/destroy.js.rjs +23 -0
  73. data/frontends/default/views/edit_associated.js.rjs +11 -0
  74. data/frontends/default/views/field_search.html.erb +5 -0
  75. data/frontends/default/views/form_messages.js.rjs +1 -0
  76. data/frontends/default/views/list.html.erb +1 -0
  77. data/frontends/default/views/list.js.rjs +1 -0
  78. data/frontends/default/views/on_action_update.js.rjs +8 -0
  79. data/frontends/default/views/on_create.js.rjs +41 -0
  80. data/frontends/default/views/on_mark_all.js.rjs +4 -0
  81. data/frontends/default/views/on_update.js.rjs +28 -0
  82. data/frontends/default/views/search.html.erb +5 -0
  83. data/frontends/default/views/show.html.erb +5 -0
  84. data/frontends/default/views/update.html.erb +8 -0
  85. data/frontends/default/views/update_column.js.rjs +13 -0
  86. data/frontends/default/views/update_row.js.rjs +1 -0
  87. data/init.rb +8 -0
  88. data/lib/active_scaffold.rb +360 -0
  89. data/lib/active_scaffold/actions/common_search.rb +22 -0
  90. data/lib/active_scaffold/actions/core.rb +180 -0
  91. data/lib/active_scaffold/actions/create.rb +149 -0
  92. data/lib/active_scaffold/actions/delete.rb +75 -0
  93. data/lib/active_scaffold/actions/field_search.rb +82 -0
  94. data/lib/active_scaffold/actions/list.rb +184 -0
  95. data/lib/active_scaffold/actions/mark.rb +63 -0
  96. data/lib/active_scaffold/actions/nested.rb +250 -0
  97. data/lib/active_scaffold/actions/search.rb +47 -0
  98. data/lib/active_scaffold/actions/show.rb +61 -0
  99. data/lib/active_scaffold/actions/subform.rb +27 -0
  100. data/lib/active_scaffold/actions/update.rb +151 -0
  101. data/lib/active_scaffold/active_record_permissions.rb +134 -0
  102. data/lib/active_scaffold/attribute_params.rb +211 -0
  103. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  104. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +39 -0
  105. data/lib/active_scaffold/bridges/bridge.rb +59 -0
  106. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  107. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +83 -0
  108. data/lib/active_scaffold/bridges/cancan/bridge.rb +12 -0
  109. data/lib/active_scaffold/bridges/cancan/lib/cancan_bridge.rb +107 -0
  110. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +9 -0
  111. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +33 -0
  112. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +12 -0
  113. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +45 -0
  114. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  115. data/lib/active_scaffold/bridges/date_picker/bridge.rb +24 -0
  116. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +225 -0
  117. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  118. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  119. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  120. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  121. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  122. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  123. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  124. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  125. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  126. data/lib/active_scaffold/bridges/paperclip/bridge.rb +12 -0
  127. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  128. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  129. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  130. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  131. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  132. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  133. data/lib/active_scaffold/bridges/shared/date_bridge.rb +209 -0
  134. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  135. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +61 -0
  136. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  137. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  138. data/lib/active_scaffold/config/base.rb +62 -0
  139. data/lib/active_scaffold/config/core.rb +220 -0
  140. data/lib/active_scaffold/config/create.rb +51 -0
  141. data/lib/active_scaffold/config/delete.rb +34 -0
  142. data/lib/active_scaffold/config/field_search.rb +75 -0
  143. data/lib/active_scaffold/config/form.rb +47 -0
  144. data/lib/active_scaffold/config/list.rb +174 -0
  145. data/lib/active_scaffold/config/mark.rb +22 -0
  146. data/lib/active_scaffold/config/nested.rb +44 -0
  147. data/lib/active_scaffold/config/search.rb +69 -0
  148. data/lib/active_scaffold/config/show.rb +35 -0
  149. data/lib/active_scaffold/config/subform.rb +35 -0
  150. data/lib/active_scaffold/config/update.rb +46 -0
  151. data/lib/active_scaffold/configurable.rb +29 -0
  152. data/lib/active_scaffold/constraints.rb +184 -0
  153. data/lib/active_scaffold/data_structures/action_columns.rb +137 -0
  154. data/lib/active_scaffold/data_structures/action_link.rb +175 -0
  155. data/lib/active_scaffold/data_structures/action_links.rb +185 -0
  156. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  157. data/lib/active_scaffold/data_structures/column.rb +355 -0
  158. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  159. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  160. data/lib/active_scaffold/data_structures/nested_info.rb +123 -0
  161. data/lib/active_scaffold/data_structures/set.rb +62 -0
  162. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  163. data/lib/active_scaffold/extensions/action_controller_rendering.rb +20 -0
  164. data/lib/active_scaffold/extensions/action_view_rendering.rb +123 -0
  165. data/lib/active_scaffold/extensions/action_view_resolver.rb +7 -0
  166. data/lib/active_scaffold/extensions/active_association_reflection.rb +13 -0
  167. data/lib/active_scaffold/extensions/active_record_offset.rb +12 -0
  168. data/lib/active_scaffold/extensions/array.rb +7 -0
  169. data/lib/active_scaffold/extensions/localize.rb +10 -0
  170. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +12 -0
  171. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +7 -0
  172. data/lib/active_scaffold/extensions/paginator_extensions.rb +26 -0
  173. data/lib/active_scaffold/extensions/reverse_associations.rb +62 -0
  174. data/lib/active_scaffold/extensions/routing_mapper.rb +34 -0
  175. data/lib/active_scaffold/extensions/to_label.rb +8 -0
  176. data/lib/active_scaffold/extensions/unsaved_associated.rb +61 -0
  177. data/lib/active_scaffold/extensions/unsaved_record.rb +20 -0
  178. data/lib/active_scaffold/extensions/usa_state.rb +46 -0
  179. data/lib/active_scaffold/finder.rb +343 -0
  180. data/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  181. data/lib/active_scaffold/helpers/controller_helpers.rb +87 -0
  182. data/lib/active_scaffold/helpers/country_helpers.rb +352 -0
  183. data/lib/active_scaffold/helpers/form_column_helpers.rb +350 -0
  184. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  185. data/lib/active_scaffold/helpers/id_helpers.rb +127 -0
  186. data/lib/active_scaffold/helpers/list_column_helpers.rb +361 -0
  187. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  188. data/lib/active_scaffold/helpers/search_column_helpers.rb +249 -0
  189. data/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  190. data/lib/active_scaffold/helpers/view_helpers.rb +360 -0
  191. data/lib/active_scaffold/locale/de.rb +120 -0
  192. data/lib/active_scaffold/locale/en.rb +119 -0
  193. data/lib/active_scaffold/locale/es.yml +115 -0
  194. data/lib/active_scaffold/locale/fr.rb +122 -0
  195. data/lib/active_scaffold/locale/hu.yml +63 -0
  196. data/lib/active_scaffold/locale/ja.yml +64 -0
  197. data/lib/active_scaffold/locale/ru.yml +119 -0
  198. data/lib/active_scaffold/marked_model.rb +38 -0
  199. data/lib/active_scaffold/paginator.rb +136 -0
  200. data/lib/active_scaffold/responds_to_parent.rb +70 -0
  201. data/lib/active_scaffold/version.rb +9 -0
  202. data/lib/active_scaffold_assets.rb +45 -0
  203. data/lib/active_scaffold_env.rb +14 -0
  204. data/lib/active_scaffold_vho.rb +2 -0
  205. data/lib/generators/active_scaffold/USAGE +29 -0
  206. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  207. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  208. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +29 -0
  209. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  210. data/lib/generators/active_scaffold_controller/templates/helper.rb +2 -0
  211. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  212. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +61 -0
  213. data/public/blank.html +33 -0
  214. data/shoulda_macros/macros.rb +136 -0
  215. data/test/bridges/bridge_test.rb +47 -0
  216. data/test/config/base_test.rb +15 -0
  217. data/test/config/create_test.rb +55 -0
  218. data/test/config/list_test.rb +74 -0
  219. data/test/config/show_test.rb +43 -0
  220. data/test/config/update_test.rb +17 -0
  221. data/test/const_mocker.rb +36 -0
  222. data/test/data_structures/action_columns_test.rb +113 -0
  223. data/test/data_structures/action_link_test.rb +78 -0
  224. data/test/data_structures/action_links_test.rb +78 -0
  225. data/test/data_structures/actions_test.rb +25 -0
  226. data/test/data_structures/association_column_test.rb +42 -0
  227. data/test/data_structures/column_test.rb +185 -0
  228. data/test/data_structures/columns_test.rb +69 -0
  229. data/test/data_structures/error_message_test.rb +28 -0
  230. data/test/data_structures/set_test.rb +86 -0
  231. data/test/data_structures/sorting_test.rb +126 -0
  232. data/test/data_structures/standard_column_test.rb +24 -0
  233. data/test/data_structures/virtual_column_test.rb +23 -0
  234. data/test/extensions/active_record_test.rb +45 -0
  235. data/test/extensions/array_test.rb +12 -0
  236. data/test/helpers/form_column_helpers_test.rb +31 -0
  237. data/test/helpers/list_column_helpers_test.rb +31 -0
  238. data/test/helpers/pagination_helpers_test.rb +55 -0
  239. data/test/misc/active_record_permissions_test.rb +154 -0
  240. data/test/misc/attribute_params_test.rb +110 -0
  241. data/test/misc/configurable_test.rb +96 -0
  242. data/test/misc/constraints_test.rb +193 -0
  243. data/test/misc/finder_test.rb +93 -0
  244. data/test/misc/lang_test.rb +12 -0
  245. data/test/mock_app/.gitignore +2 -0
  246. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  247. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  248. data/test/mock_app/config/boot.rb +110 -0
  249. data/test/mock_app/config/database.yml +16 -0
  250. data/test/mock_app/config/environment.rb +43 -0
  251. data/test/mock_app/config/environments/development.rb +17 -0
  252. data/test/mock_app/config/environments/production.rb +28 -0
  253. data/test/mock_app/config/environments/test.rb +28 -0
  254. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  255. data/test/mock_app/config/initializers/inflections.rb +10 -0
  256. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  257. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  258. data/test/mock_app/config/initializers/session_store.rb +15 -0
  259. data/test/mock_app/config/locales/en.yml +5 -0
  260. data/test/mock_app/config/routes.rb +43 -0
  261. data/test/mock_app/db/test.sqlite3 +1 -0
  262. data/test/mock_app/public/blank.html +33 -0
  263. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  264. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  265. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  266. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  267. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  268. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  269. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  270. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  271. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  272. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  273. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  274. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  275. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  276. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  277. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  278. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  279. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +842 -0
  280. data/test/model_stub.rb +55 -0
  281. data/test/run_all.rb +8 -0
  282. data/test/test_helper.rb +39 -0
  283. data/uninstall.rb +13 -0
  284. metadata +497 -0
@@ -0,0 +1,47 @@
1
+ module ActiveScaffold::Actions
2
+ module Search
3
+ include ActiveScaffold::Actions::CommonSearch
4
+ def self.included(base)
5
+ base.before_filter :search_authorized_filter, :only => :show_search
6
+ base.before_filter :store_search_params_into_session, :only => [:index]
7
+ base.before_filter :do_search, :only => [:index]
8
+ base.helper_method :search_params
9
+ end
10
+
11
+ def show_search
12
+ respond_to_action(:search)
13
+ end
14
+
15
+ protected
16
+ def search_respond_to_html
17
+ render(:action => "search")
18
+ end
19
+ def search_respond_to_js
20
+ render(:partial => "search")
21
+ end
22
+ def do_search
23
+ query = search_params.to_s.strip rescue ''
24
+ unless query.empty?
25
+ columns = active_scaffold_config.search.columns
26
+ text_search = active_scaffold_config.search.text_search
27
+ search_conditions = self.class.create_conditions_for_columns(query.split(' '), columns, text_search)
28
+ self.active_scaffold_conditions = merge_conditions(self.active_scaffold_conditions, search_conditions)
29
+ @filtered = !search_conditions.blank?
30
+
31
+ includes_for_search_columns = columns.collect{ |column| column.includes}.flatten.uniq.compact
32
+ self.active_scaffold_includes.concat includes_for_search_columns
33
+
34
+ active_scaffold_config.list.user.page = nil
35
+ end
36
+ end
37
+
38
+ private
39
+ def search_authorized_filter
40
+ link = active_scaffold_config.search.link || active_scaffold_config.search.class.link
41
+ raise ActiveScaffold::ActionNotAllowed unless self.send(link.security_method)
42
+ end
43
+ def search_formats
44
+ (default_formats + active_scaffold_config.formats + active_scaffold_config.search.formats).uniq
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ module ActiveScaffold::Actions
2
+ module Show
3
+ def self.included(base)
4
+ base.before_filter :show_authorized_filter, :only => :show
5
+ end
6
+
7
+ def show
8
+ # rest destroy falls back to rest show in case of disabled javascript
9
+ # just render action_confirmation message for destroy
10
+ unless params.delete :destroy_action
11
+ do_show
12
+ successful?
13
+ respond_to_action(:show)
14
+ else
15
+ @record = find_if_allowed(params[:id], :read) if params[:id] && params[:id] && params[:id].to_i > 0
16
+ action_confirmation_respond_to_html(:destroy)
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def show_respond_to_json
23
+ render :text => response_object.to_json(:only => active_scaffold_config.show.columns.names), :content_type => Mime::JSON, :status => response_status
24
+ end
25
+
26
+ def show_respond_to_yaml
27
+ render :text => Hash.from_xml(response_object.to_xml(:only => active_scaffold_config.show.columns.names)).to_yaml, :content_type => Mime::YAML, :status => response_status
28
+ end
29
+
30
+ def show_respond_to_xml
31
+ render :xml => response_object.to_xml(:only => active_scaffold_config.show.columns.names), :content_type => Mime::XML, :status => response_status
32
+ end
33
+
34
+ def show_respond_to_js
35
+ render :partial => 'show'
36
+ end
37
+
38
+ def show_respond_to_html
39
+ render :action => 'show'
40
+ end
41
+ # A simple method to retrieve and prepare a record for showing.
42
+ # May be overridden to customize show routine
43
+ def do_show
44
+ @record = find_if_allowed(params[:id], :read)
45
+ end
46
+
47
+ # The default security delegates to ActiveRecordPermissions.
48
+ # You may override the method to customize.
49
+ def show_authorized?(record = nil)
50
+ authorized_for?(:crud_type => :read)
51
+ end
52
+ private
53
+ def show_authorized_filter
54
+ link = active_scaffold_config.show.link || active_scaffold_config.show.class.link
55
+ raise ActiveScaffold::ActionNotAllowed unless self.send(link.security_method)
56
+ end
57
+ def show_formats
58
+ (default_formats + active_scaffold_config.formats + active_scaffold_config.show.formats).uniq
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ module ActiveScaffold::Actions
2
+ module Subform
3
+ def edit_associated
4
+ do_edit_associated
5
+ render :action => 'edit_associated'
6
+ end
7
+
8
+ protected
9
+
10
+ def do_edit_associated
11
+ @parent_record = params[:id].nil? ? new_model : find_if_allowed(params[:id], :update)
12
+ @column = active_scaffold_config.columns[params[:association]]
13
+
14
+ # NOTE: we don't check whether the user is allowed to update this record, because if not, we'll still let them associate the record. we'll just refuse to do more than associate, is all.
15
+ @record = @column.association.klass.find(params[:associated_id]) if params[:associated_id]
16
+ @record ||= if @column.singular_association?
17
+ @parent_record.send("build_#{@column.name}".to_sym)
18
+ else
19
+ @parent_record.send(@column.name).build
20
+ end
21
+
22
+ @scope = "[#{@column.name}]"
23
+ @scope += (@record.new_record?) ? "[#{(Time.now.to_f*1000).to_i.to_s}]" : "[#{@record.id}]" if @column.plural_association?
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,151 @@
1
+ module ActiveScaffold::Actions
2
+ module Update
3
+ def self.included(base)
4
+ base.before_filter :update_authorized_filter, :only => [:edit, :update]
5
+ base.verify :method => [:post, :put],
6
+ :only => :update,
7
+ :redirect_to => { :action => :index }
8
+ base.helper_method :update_refresh_list?
9
+ end
10
+
11
+ def edit
12
+ do_edit
13
+ respond_to_action(:edit)
14
+ end
15
+
16
+ def update
17
+ do_update
18
+ respond_to_action(:update)
19
+ end
20
+
21
+ # for inline (inlist) editing
22
+ def update_column
23
+ do_update_column
24
+ render :action => 'update_column', :locals => {:column_span_id => params[:editor_id] || params[:editorId]}
25
+ end
26
+
27
+ protected
28
+ def edit_respond_to_html
29
+ if successful?
30
+ render(:action => 'update')
31
+ else
32
+ return_to_main
33
+ end
34
+ end
35
+ def edit_respond_to_js
36
+ render(:partial => 'update_form')
37
+ end
38
+ def update_respond_to_html
39
+ if params[:iframe]=='true' # was this an iframe post ?
40
+ responds_to_parent do
41
+ render :action => 'on_update.js', :layout => false
42
+ end
43
+ else # just a regular post
44
+ if successful?
45
+ flash[:info] = as_(:updated_model, :model => @record.to_label)
46
+ return_to_main
47
+ else
48
+ render(:action => 'update')
49
+ end
50
+ end
51
+ end
52
+ def update_respond_to_js
53
+ if successful? && update_refresh_list? && !render_parent?
54
+ do_search if respond_to? :do_search
55
+ do_list
56
+ end
57
+ render :action => 'on_update'
58
+ end
59
+ def update_respond_to_xml
60
+ render :xml => response_object.to_xml(:only => active_scaffold_config.update.columns.names), :content_type => Mime::XML, :status => response_status
61
+ end
62
+ def update_respond_to_json
63
+ render :text => response_object.to_json(:only => active_scaffold_config.update.columns.names), :content_type => Mime::JSON, :status => response_status
64
+ end
65
+ def update_respond_to_yaml
66
+ render :text => Hash.from_xml(response_object.to_xml(:only => active_scaffold_config.update.columns.names)).to_yaml, :content_type => Mime::YAML, :status => response_status
67
+ end
68
+ # A simple method to find and prepare a record for editing
69
+ # May be overridden to customize the record (set default values, etc.)
70
+ def do_edit
71
+ register_constraints_with_action_columns(nested.constrained_fields, active_scaffold_config.update.hide_nested_column ? [] : [:update]) if nested?
72
+ @record = find_if_allowed(params[:id], :update)
73
+ end
74
+
75
+ # A complex method to update a record. The complexity comes from the support for subforms, and saving associated records.
76
+ # If you want to customize this algorithm, consider using the +before_update_save+ callback
77
+ def do_update
78
+ do_edit
79
+ update_save
80
+ end
81
+
82
+ def update_save(options = {})
83
+ begin
84
+ active_scaffold_config.model.transaction do
85
+ @record = update_record_from_params(@record, active_scaffold_config.update.columns, params[:record]) unless options[:no_record_param_update]
86
+ before_update_save(@record)
87
+ self.successful = [@record.valid?, @record.associated_valid?].all? {|v| v == true} # this syntax avoids a short-circuit
88
+ if successful?
89
+ @record.save! and @record.save_associated!
90
+ after_update_save(@record)
91
+ else
92
+ # some associations such as habtm are saved before saved is called on parent object
93
+ # we have to revert these changes if validation fails
94
+ raise ActiveRecord::Rollback, "don't save habtm associations unless record is valid"
95
+ end
96
+ end
97
+ rescue ActiveRecord::RecordInvalid
98
+ rescue ActiveRecord::StaleObjectError
99
+ @record.errors.add(:base, as_(:version_inconsistency))
100
+ self.successful=false
101
+ rescue ActiveRecord::RecordNotSaved
102
+ @record.errors.add(:base, as_(:record_not_saved)) if @record.errors.empty?
103
+ self.successful = false
104
+ end
105
+ end
106
+
107
+ def do_update_column
108
+ @record = active_scaffold_config.model.find(params[:id])
109
+ if @record.authorized_for?(:crud_type => :update, :column => params[:column])
110
+ column = active_scaffold_config.columns[params[:column].to_sym]
111
+ params[:value] ||= @record.column_for_attribute(params[:column]).default unless @record.column_for_attribute(params[:column]).nil? || @record.column_for_attribute(params[:column]).null
112
+ unless column.nil?
113
+ params[:value] = column_value_from_param_value(@record, column, params[:value])
114
+ params[:value] = [] if params[:value].nil? && column.form_ui && column.plural_association?
115
+ end
116
+ @record.send("#{params[:column]}=", params[:value])
117
+ before_update_save(@record)
118
+ @record.save
119
+ after_update_save(@record)
120
+ end
121
+ end
122
+
123
+ # override this method if you want to inject data in the record (or its associated objects) before the save
124
+ def before_update_save(record); end
125
+
126
+ # override this method if you want to do something after the save
127
+ def after_update_save(record); end
128
+
129
+ # should we refresh whole list after update operation
130
+ def update_refresh_list?
131
+ active_scaffold_config.update.refresh_list
132
+ end
133
+
134
+ # The default security delegates to ActiveRecordPermissions.
135
+ # You may override the method to customize.
136
+ def update_authorized?(record = nil)
137
+ (!nested? || !nested.readonly?) && authorized_for?(:crud_type => :update)
138
+ end
139
+ private
140
+ def update_authorized_filter
141
+ link = active_scaffold_config.update.link || active_scaffold_config.update.class.link
142
+ raise ActiveScaffold::ActionNotAllowed unless self.send(link.security_method)
143
+ end
144
+ def edit_formats
145
+ (default_formats + active_scaffold_config.formats).uniq
146
+ end
147
+ def update_formats
148
+ (default_formats + active_scaffold_config.formats + active_scaffold_config.update.formats).uniq
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,134 @@
1
+ # This module attempts to create permissions conventions for your ActiveRecord models. It supports english-based
2
+ # methods that let you restrict access per-model, per-record, per-column, per-action, and per-user. All at once.
3
+ #
4
+ # You may define instance methods in the following formats:
5
+ # def #{column}_authorized_for_#{action}?
6
+ # def #{column}_authorized?
7
+ # def authorized_for_#{action}?
8
+ #
9
+ # Your methods should allow for the following special cases:
10
+ # * cron scripts
11
+ # * guest users (or nil current_user objects)
12
+ module ActiveRecordPermissions
13
+ # ActiveRecordPermissions needs to know what method on your ApplicationController will return the current user,
14
+ # if available. This defaults to the :current_user method. You may configure this in your environment.rb if you
15
+ # have a different setup.
16
+ def self.current_user_method=(v); @@current_user_method = v; end
17
+ def self.current_user_method; @@current_user_method; end
18
+ @@current_user_method = :current_user
19
+
20
+ # Whether the default permission is permissive or not
21
+ # If set to true, then everything's allowed until configured otherwise
22
+ def self.default_permission=(v); @@default_permission = v; end
23
+ def self.default_permission; @@default_permission; end
24
+ @@default_permission = true
25
+
26
+ # This is a module aimed at making the current_user available to ActiveRecord models for permissions.
27
+ module ModelUserAccess
28
+ module Controller
29
+ def self.included(base)
30
+ base.prepend_before_filter :assign_current_user_to_models
31
+ end
32
+
33
+ # We need to give the ActiveRecord classes a handle to the current user. We don't want to just pass the object,
34
+ # because the object may change (someone may log in or out). So we give ActiveRecord a proc that ties to the
35
+ # current_user_method on this ApplicationController.
36
+ def assign_current_user_to_models
37
+ ActiveRecord::Base.current_user_proc = proc {send(ActiveRecordPermissions.current_user_method)}
38
+ end
39
+ end
40
+
41
+ module Model
42
+ def self.included(base)
43
+ base.extend ClassMethods
44
+ end
45
+
46
+ module ClassMethods
47
+ # The proc to call that retrieves the current_user from the ApplicationController.
48
+ attr_accessor :current_user_proc
49
+
50
+ # Class-level access to the current user
51
+ def current_user
52
+ ActiveRecord::Base.current_user_proc.call if ActiveRecord::Base.current_user_proc
53
+ end
54
+ end
55
+
56
+ # Instance-level access to the current user
57
+ def current_user
58
+ self.class.current_user
59
+ end
60
+ end
61
+ end
62
+
63
+ module Permissions
64
+ def self.included(base)
65
+ base.extend SecurityMethods
66
+ base.send :include, SecurityMethods
67
+ end
68
+
69
+ # Because any class-level queries get delegated to the instance level via a new record,
70
+ # it's useful to know when the authorization query is meant for a specific record or not.
71
+ # But using new_record? is confusing, even though accurate. So this is basically just a wrapper.
72
+ def existing_record_check?
73
+ !new_record?
74
+ end
75
+
76
+ module SecurityMethods
77
+ # A generic authorization query. This is what will be called programatically, since
78
+ # the actual permission methods can't be guaranteed to exist. And because we want to
79
+ # intelligently combine multiple applicable methods.
80
+ #
81
+ # options[:crud_type] should be a CRUD verb (:create, :read, :update, :destroy)
82
+ # options[:column] should be the name of a model attribute
83
+ # options[:action] is the name of a method
84
+ def authorized_for?(options = {})
85
+ raise ArgumentError, "unknown crud type #{options[:crud_type]}" if options[:crud_type] and ![:create, :read, :update, :delete].include?(options[:crud_type])
86
+
87
+ # column_authorized_for_crud_type? has the highest priority over other methods,
88
+ # you can disable a crud verb and enable that verb for a column
89
+ # (for example, disable update and enable inplace_edit in a column)
90
+ method = column_and_crud_type_security_method(options[:column], options[:crud_type])
91
+ return send(method) if method and respond_to?(method)
92
+
93
+ # authorized_for_action? has higher priority than other methods,
94
+ # you can disable a crud verb and enable an action with that crud verb
95
+ # (for example, disable update and enable an action with update as crud type)
96
+ method = action_security_method(options[:action])
97
+ return send(method) if method and respond_to?(method)
98
+
99
+ # collect other possibly-related methods that actually exist
100
+ methods = [
101
+ column_security_method(options[:column]),
102
+ crud_type_security_method(options[:crud_type]),
103
+ ].compact.select {|m| respond_to?(m)}
104
+
105
+ # if any method returns false, then return false
106
+ return false if methods.any? {|m| !send(m)}
107
+
108
+ # if any method actually exists then it must've returned true, so return true
109
+ return true unless methods.empty?
110
+
111
+ # if no method exists, return the default permission
112
+ return ActiveRecordPermissions.default_permission
113
+ end
114
+
115
+ private
116
+
117
+ def column_security_method(column)
118
+ "#{column}_authorized?" if column
119
+ end
120
+
121
+ def crud_type_security_method(crud_type)
122
+ "authorized_for_#{crud_type}?" if crud_type
123
+ end
124
+
125
+ def action_security_method(action)
126
+ "authorized_for_#{action}?" if action
127
+ end
128
+
129
+ def column_and_crud_type_security_method(column, crud_type)
130
+ "#{column}_authorized_for_#{crud_type}?" if column and crud_type
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,211 @@
1
+ module ActiveScaffold
2
+ # Provides support for param hashes assumed to be model attributes.
3
+ # Support is primarily needed for creating/editing associated records using a nested hash structure.
4
+ #
5
+ # Paradigm Params Hash (should write unit tests on this):
6
+ # params[:record] = {
7
+ # # a simple record attribute
8
+ # 'name' => 'John',
9
+ # # a plural association hash
10
+ # 'roles' => {
11
+ # # associate with an existing role
12
+ # '5' => {'id' => 5}
13
+ # # associate with an existing role and edit it
14
+ # '6' => {'id' => 6, 'name' => 'designer'}
15
+ # # create and associate a new role
16
+ # '124521' => {'name' => 'marketer'}
17
+ # }
18
+ # # a singular association hash
19
+ # 'location' => {'id' => 12, 'city' => 'New York'}
20
+ # }
21
+ #
22
+ # Simpler association structures are also supported, like:
23
+ # params[:record] = {
24
+ # # a simple record attribute
25
+ # 'name' => 'John',
26
+ # # a plural association ... all ids refer to existing records
27
+ # 'roles' => ['5', '6'],
28
+ # # a singular association ... all ids refer to existing records
29
+ # 'location' => '12'
30
+ # }
31
+ module AttributeParams
32
+ protected
33
+ # Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
34
+ # association attributes and attempts to instantiate them as associated objects.
35
+ #
36
+ # This is a secure way to apply params to a record, because it's based on a loop over the columns
37
+ # set. The columns set will not yield unauthorized columns, and it will not yield unregistered columns.
38
+ def update_record_from_params(parent_record, columns, attributes)
39
+ crud_type = parent_record.new_record? ? :create : :update
40
+ return parent_record unless parent_record.authorized_for?(:crud_type => crud_type)
41
+
42
+ multi_parameter_attributes = {}
43
+ attributes.each do |k, v|
44
+ next unless k.include? '('
45
+ column_name = k.split('(').first.to_sym
46
+ multi_parameter_attributes[column_name] ||= []
47
+ multi_parameter_attributes[column_name] << [k, v]
48
+ end
49
+
50
+ columns.each :for => parent_record, :crud_type => crud_type, :flatten => true do |column|
51
+ # Set any passthrough parameters that may be associated with this column (ie, file column "keep" and "temp" attributes)
52
+ unless column.params.empty?
53
+ column.params.each{|p| parent_record.send("#{p}=", attributes[p]) if attributes.has_key? p}
54
+ end
55
+
56
+ if multi_parameter_attributes.has_key? column.name
57
+ parent_record.send(:assign_multiparameter_attributes, multi_parameter_attributes[column.name])
58
+ elsif attributes.has_key? column.name
59
+ value = column_value_from_param_value(parent_record, column, attributes[column.name])
60
+
61
+ # we avoid assigning a value that already exists because otherwise has_one associations will break (AR bug in has_one_association.rb#replace)
62
+ parent_record.send("#{column.name}=", value) unless parent_record.send(column.name) == value
63
+
64
+ # plural associations may not actually appear in the params if all of the options have been unselected or cleared away.
65
+ # the "form_ui" check is necessary, becuase without it we have problems
66
+ # with subforms. the UI cuts out deep associations, which means they're not present in the
67
+ # params even though they're in the columns list. the result is that associations were being
68
+ # emptied out way too often.
69
+ elsif column.form_ui and column.plural_association?
70
+ parent_record.send("#{column.name}=", [])
71
+ end
72
+ end
73
+
74
+ if parent_record.new_record?
75
+ parent_record.class.reflect_on_all_associations.each do |a|
76
+ next unless [:has_one, :has_many].include?(a.macro) and not a.options[:through]
77
+ next unless association_proxy = parent_record.send(a.name)
78
+
79
+ raise ActiveScaffold::ReverseAssociationRequired, "Association #{a.name}: In order to support :has_one and :has_many where the parent record is new and the child record(s) validate the presence of the parent, ActiveScaffold requires the reverse association (the belongs_to)." unless a.reverse
80
+
81
+ association_proxy = [association_proxy] if a.macro == :has_one
82
+ association_proxy.each { |record| record.send("#{a.reverse}=", parent_record) }
83
+ end
84
+ end
85
+
86
+ parent_record
87
+ end
88
+
89
+ def manage_nested_record_from_params(parent_record, column, attributes)
90
+ record = find_or_create_for_params(attributes, column, parent_record)
91
+ if record
92
+ record_columns = active_scaffold_config_for(column.association.klass).subform.columns
93
+ update_record_from_params(record, record_columns, attributes)
94
+ record.unsaved = true
95
+ end
96
+ record
97
+ end
98
+
99
+ def column_value_from_param_value(parent_record, column, value)
100
+ # convert the value, possibly by instantiating associated objects
101
+ if value.is_a?(Hash)
102
+ column_value_from_param_hash_value(parent_record, column, value)
103
+ else
104
+ column_value_from_param_simple_value(parent_record, column, value)
105
+ end
106
+ end
107
+
108
+ def column_value_from_param_simple_value(parent_record, column, value)
109
+ if column.singular_association?
110
+ # it's a single id
111
+ column.association.klass.find(value) if value and not value.empty?
112
+ elsif column.plural_association?
113
+ column_plural_assocation_value_from_value(column, value)
114
+ elsif column.column && column.column.number? && [:i18n_number, :currency].include?(column.options[:format])
115
+ self.class.i18n_number_to_native_format(value)
116
+ else
117
+ # convert empty strings into nil. this works better with 'null => true' columns (and validations),
118
+ # and 'null => false' columns should just convert back to an empty string.
119
+ # ... but we can at least check the ConnectionAdapter::Column object to see if nulls are allowed
120
+ value = nil if value.is_a? String and value.empty? and !column.column.nil? and column.column.null
121
+ value
122
+ end
123
+ end
124
+
125
+ def column_plural_assocation_value_from_value(column, value)
126
+ # it's an array of ids
127
+ if value and not value.empty?
128
+ ids = value.select {|id| id.respond_to?(:empty?) ? !id.empty? : true}
129
+ ids.empty? ? [] : column.association.klass.find(ids)
130
+ end
131
+ end
132
+
133
+ def column_value_from_param_hash_value(parent_record, column, value)
134
+ # this is just for backwards compatibility. we should clean this up in 2.0.
135
+ if column.form_ui == :select
136
+ ids = if column.singular_association?
137
+ value[:id]
138
+ else
139
+ value.values.collect {|hash| hash[:id]}
140
+ end
141
+ (ids and not ids.empty?) ? column.association.klass.find(ids) : nil
142
+
143
+ elsif column.singular_association?
144
+ manage_nested_record_from_params(parent_record, column, value)
145
+ elsif column.plural_association?
146
+ value.collect {|key_value_pair| manage_nested_record_from_params(parent_record, column, key_value_pair[1])}.compact
147
+ else
148
+ value
149
+ end
150
+ end
151
+
152
+ # Attempts to create or find an instance of klass (which must be an ActiveRecord object) from the
153
+ # request parameters given. If params[:id] exists it will attempt to find an existing object
154
+ # otherwise it will build a new one.
155
+ def find_or_create_for_params(params, parent_column, parent_record)
156
+ current = parent_record.send(parent_column.name)
157
+ klass = parent_column.association.klass
158
+ return nil if parent_column.show_blank_record and attributes_hash_is_empty?(params, klass)
159
+
160
+ if params.has_key? :id
161
+ # modifying the current object of a singular association
162
+ if current and current.is_a? ActiveRecord::Base and current.id.to_s == params[:id]
163
+ return current
164
+ # modifying one of the current objects in a plural association
165
+ elsif current and current.respond_to?(:any?) and current.any? {|o| o.id.to_s == params[:id]}
166
+ return current.detect {|o| o.id.to_s == params[:id]}
167
+ # attaching an existing but not-current object
168
+ else
169
+ return klass.find(params[:id])
170
+ end
171
+ else
172
+ if klass.authorized_for?(:crud_type => :create)
173
+ if parent_column.singular_association?
174
+ return parent_record.send("build_#{parent_column.name}")
175
+ else
176
+ return parent_record.send(parent_column.name).build
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ # Determines whether the given attributes hash is "empty".
183
+ # This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
184
+ def attributes_hash_is_empty?(hash, klass)
185
+ ignore_column_types = [:boolean]
186
+ hash.all? do |key,value|
187
+ # convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
188
+ parts = key.to_s.split('(')
189
+ #old style date form management... ignore them too
190
+ ignore_column_types = [:boolean, :datetime, :date, :time] if parts.length > 1
191
+ column_name = parts.first
192
+ column = klass.columns_hash[column_name]
193
+
194
+ # booleans and datetimes will always have a value. so we ignore them when checking whether the hash is empty.
195
+ # this could be a bad idea. but the current situation (excess record entry) seems worse.
196
+ next true if column and ignore_column_types.include?(column.type)
197
+
198
+ # defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
199
+ next true if column and value == column.default.to_s
200
+
201
+ if value.is_a?(Hash)
202
+ attributes_hash_is_empty?(value, klass)
203
+ elsif value.is_a?(Array)
204
+ value.any? {|id| id.respond_to?(:empty?) ? !id.empty? : true}
205
+ else
206
+ value.respond_to?(:empty?) ? value.empty? : false
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end