active_scaffold 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (272) hide show
  1. data/.autotest +27 -0
  2. data/CHANGELOG +152 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README +51 -0
  6. data/Rakefile +24 -0
  7. data/active_scaffold.gemspec +24 -0
  8. data/environment.rb +22 -0
  9. data/frontends/default/images/add.gif +0 -0
  10. data/frontends/default/images/arrow_down.gif +0 -0
  11. data/frontends/default/images/arrow_up.gif +0 -0
  12. data/frontends/default/images/close.gif +0 -0
  13. data/frontends/default/images/cross.png +0 -0
  14. data/frontends/default/images/indicator-small.gif +0 -0
  15. data/frontends/default/images/indicator.gif +0 -0
  16. data/frontends/default/images/magnifier.png +0 -0
  17. data/frontends/default/javascripts/jquery/active_scaffold.js +957 -0
  18. data/frontends/default/javascripts/jquery/jquery.editinplace.js +726 -0
  19. data/frontends/default/javascripts/prototype/active_scaffold.js +954 -0
  20. data/frontends/default/javascripts/prototype/dhtml_history.js +867 -0
  21. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  22. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  23. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  24. data/frontends/default/stylesheets/stylesheet.css +858 -0
  25. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  26. data/frontends/default/views/_base_form.html.erb +41 -0
  27. data/frontends/default/views/_create_form.html.erb +6 -0
  28. data/frontends/default/views/_create_form_on_list.html.erb +5 -0
  29. data/frontends/default/views/_field_search.html.erb +32 -0
  30. data/frontends/default/views/_form.html.erb +24 -0
  31. data/frontends/default/views/_form_association.html.erb +14 -0
  32. data/frontends/default/views/_form_association_footer.html.erb +40 -0
  33. data/frontends/default/views/_form_attribute.html.erb +15 -0
  34. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  35. data/frontends/default/views/_form_messages.html.erb +5 -0
  36. data/frontends/default/views/_horizontal_subform.html.erb +19 -0
  37. data/frontends/default/views/_horizontal_subform_header.html.erb +10 -0
  38. data/frontends/default/views/_horizontal_subform_record.html.erb +37 -0
  39. data/frontends/default/views/_human_conditions.html.erb +1 -0
  40. data/frontends/default/views/_list.html.erb +18 -0
  41. data/frontends/default/views/_list_actions.html.erb +16 -0
  42. data/frontends/default/views/_list_calculations.html.erb +16 -0
  43. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  44. data/frontends/default/views/_list_header.html.erb +12 -0
  45. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  46. data/frontends/default/views/_list_messages.html.erb +32 -0
  47. data/frontends/default/views/_list_pagination.html.erb +11 -0
  48. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  49. data/frontends/default/views/_list_record.html.erb +14 -0
  50. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  51. data/frontends/default/views/_list_with_header.html.erb +32 -0
  52. data/frontends/default/views/_messages.html.erb +10 -0
  53. data/frontends/default/views/_render_field.js.rjs +13 -0
  54. data/frontends/default/views/_row.html.erb +12 -0
  55. data/frontends/default/views/_search.html.erb +34 -0
  56. data/frontends/default/views/_search_attribute.html.erb +10 -0
  57. data/frontends/default/views/_show.html.erb +8 -0
  58. data/frontends/default/views/_show_columns.html.erb +12 -0
  59. data/frontends/default/views/_update_actions.html.erb +9 -0
  60. data/frontends/default/views/_update_form.html.erb +5 -0
  61. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  62. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  63. data/frontends/default/views/add_existing.js.rjs +17 -0
  64. data/frontends/default/views/add_existing_form.html.erb +5 -0
  65. data/frontends/default/views/create.html.erb +5 -0
  66. data/frontends/default/views/delete.html.erb +13 -0
  67. data/frontends/default/views/destroy.js.rjs +5 -0
  68. data/frontends/default/views/edit_associated.js.rjs +11 -0
  69. data/frontends/default/views/field_search.html.erb +5 -0
  70. data/frontends/default/views/form_messages.js.rjs +1 -0
  71. data/frontends/default/views/list.html.erb +1 -0
  72. data/frontends/default/views/list.js.rjs +1 -0
  73. data/frontends/default/views/on_action_update.js.rjs +8 -0
  74. data/frontends/default/views/on_create.js.rjs +24 -0
  75. data/frontends/default/views/on_update.js.rjs +15 -0
  76. data/frontends/default/views/search.html.erb +5 -0
  77. data/frontends/default/views/show.html.erb +5 -0
  78. data/frontends/default/views/update.html.erb +8 -0
  79. data/frontends/default/views/update_column.js.rjs +13 -0
  80. data/frontends/default/views/update_row.js.rjs +1 -0
  81. data/init.rb +1 -0
  82. data/install_assets.rb +44 -0
  83. data/lib/active_record_permissions.rb +134 -0
  84. data/lib/active_scaffold.rb +279 -0
  85. data/lib/active_scaffold/actions/common_search.rb +22 -0
  86. data/lib/active_scaffold/actions/core.rb +150 -0
  87. data/lib/active_scaffold/actions/create.rb +152 -0
  88. data/lib/active_scaffold/actions/delete.rb +72 -0
  89. data/lib/active_scaffold/actions/field_search.rb +82 -0
  90. data/lib/active_scaffold/actions/list.rb +128 -0
  91. data/lib/active_scaffold/actions/mark.rb +50 -0
  92. data/lib/active_scaffold/actions/nested.rb +241 -0
  93. data/lib/active_scaffold/actions/search.rb +47 -0
  94. data/lib/active_scaffold/actions/show.rb +54 -0
  95. data/lib/active_scaffold/actions/subform.rb +17 -0
  96. data/lib/active_scaffold/actions/update.rb +134 -0
  97. data/lib/active_scaffold/attribute_params.rb +207 -0
  98. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  99. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +38 -0
  100. data/lib/active_scaffold/bridges/bridge.rb +52 -0
  101. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  102. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +79 -0
  103. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +7 -0
  104. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +38 -0
  105. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +26 -0
  106. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +35 -0
  107. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  108. data/lib/active_scaffold/bridges/date_picker/bridge.rb +22 -0
  109. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +225 -0
  110. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  111. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  112. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  113. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  114. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  115. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  116. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  117. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  118. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  119. data/lib/active_scaffold/bridges/paperclip/bridge.rb +12 -0
  120. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  121. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  122. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  123. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  124. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  125. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  126. data/lib/active_scaffold/bridges/shared/date_bridge.rb +187 -0
  127. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  128. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +45 -0
  129. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  130. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  131. data/lib/active_scaffold/config/base.rb +54 -0
  132. data/lib/active_scaffold/config/core.rb +229 -0
  133. data/lib/active_scaffold/config/create.rb +43 -0
  134. data/lib/active_scaffold/config/delete.rb +25 -0
  135. data/lib/active_scaffold/config/field_search.rb +74 -0
  136. data/lib/active_scaffold/config/form.rb +46 -0
  137. data/lib/active_scaffold/config/list.rb +174 -0
  138. data/lib/active_scaffold/config/mark.rb +22 -0
  139. data/lib/active_scaffold/config/nested.rb +43 -0
  140. data/lib/active_scaffold/config/search.rb +68 -0
  141. data/lib/active_scaffold/config/show.rb +34 -0
  142. data/lib/active_scaffold/config/subform.rb +35 -0
  143. data/lib/active_scaffold/config/update.rb +38 -0
  144. data/lib/active_scaffold/configurable.rb +29 -0
  145. data/lib/active_scaffold/constraints.rb +179 -0
  146. data/lib/active_scaffold/data_structures/action_columns.rb +133 -0
  147. data/lib/active_scaffold/data_structures/action_link.rb +162 -0
  148. data/lib/active_scaffold/data_structures/action_links.rb +59 -0
  149. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  150. data/lib/active_scaffold/data_structures/column.rb +348 -0
  151. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  152. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  153. data/lib/active_scaffold/data_structures/nested_info.rb +108 -0
  154. data/lib/active_scaffold/data_structures/set.rb +62 -0
  155. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  156. data/lib/active_scaffold/finder.rb +333 -0
  157. data/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  158. data/lib/active_scaffold/helpers/controller_helpers.rb +40 -0
  159. data/lib/active_scaffold/helpers/country_helpers.rb +352 -0
  160. data/lib/active_scaffold/helpers/form_column_helpers.rb +343 -0
  161. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  162. data/lib/active_scaffold/helpers/id_helpers.rb +131 -0
  163. data/lib/active_scaffold/helpers/list_column_helpers.rb +363 -0
  164. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  165. data/lib/active_scaffold/helpers/search_column_helpers.rb +238 -0
  166. data/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  167. data/lib/active_scaffold/helpers/view_helpers.rb +315 -0
  168. data/lib/active_scaffold/locale/de.rb +113 -0
  169. data/lib/active_scaffold/locale/en.rb +118 -0
  170. data/lib/active_scaffold/locale/es.yml +112 -0
  171. data/lib/active_scaffold/locale/fr.rb +113 -0
  172. data/lib/active_scaffold/locale/hu.yml +63 -0
  173. data/lib/active_scaffold/locale/ja.yml +64 -0
  174. data/lib/active_scaffold/locale/ru.yml +62 -0
  175. data/lib/active_scaffold/marked_model.rb +38 -0
  176. data/lib/dhtml_confirm.rb +54 -0
  177. data/lib/extensions/action_controller_rendering.rb +20 -0
  178. data/lib/extensions/action_view_rendering.rb +113 -0
  179. data/lib/extensions/action_view_resolver.rb +7 -0
  180. data/lib/extensions/active_record_offset.rb +12 -0
  181. data/lib/extensions/array.rb +7 -0
  182. data/lib/extensions/localize.rb +10 -0
  183. data/lib/extensions/name_option_for_datetime.rb +12 -0
  184. data/lib/extensions/nil_id_in_url_params.rb +7 -0
  185. data/lib/extensions/paginator_extensions.rb +26 -0
  186. data/lib/extensions/reverse_associations.rb +62 -0
  187. data/lib/extensions/routing_mapper.rb +34 -0
  188. data/lib/extensions/to_label.rb +8 -0
  189. data/lib/extensions/unsaved_associated.rb +61 -0
  190. data/lib/extensions/unsaved_record.rb +20 -0
  191. data/lib/extensions/usa_state.rb +46 -0
  192. data/lib/generators/active_scaffold/USAGE +29 -0
  193. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  194. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  195. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +28 -0
  196. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  197. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  198. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +53 -0
  199. data/lib/paginator.rb +136 -0
  200. data/lib/responds_to_parent.rb +70 -0
  201. data/public/blank.html +33 -0
  202. data/shoulda_macros/macros.rb +136 -0
  203. data/test/bridges/bridge_test.rb +47 -0
  204. data/test/config/base_test.rb +15 -0
  205. data/test/config/create_test.rb +55 -0
  206. data/test/config/list_test.rb +74 -0
  207. data/test/config/show_test.rb +43 -0
  208. data/test/config/update_test.rb +17 -0
  209. data/test/const_mocker.rb +36 -0
  210. data/test/data_structures/action_columns_test.rb +113 -0
  211. data/test/data_structures/action_link_test.rb +78 -0
  212. data/test/data_structures/action_links_test.rb +78 -0
  213. data/test/data_structures/actions_test.rb +25 -0
  214. data/test/data_structures/association_column_test.rb +42 -0
  215. data/test/data_structures/column_test.rb +185 -0
  216. data/test/data_structures/columns_test.rb +69 -0
  217. data/test/data_structures/error_message_test.rb +28 -0
  218. data/test/data_structures/set_test.rb +86 -0
  219. data/test/data_structures/sorting_test.rb +126 -0
  220. data/test/data_structures/standard_column_test.rb +24 -0
  221. data/test/data_structures/virtual_column_test.rb +23 -0
  222. data/test/extensions/active_record_test.rb +45 -0
  223. data/test/extensions/array_test.rb +12 -0
  224. data/test/helpers/form_column_helpers_test.rb +31 -0
  225. data/test/helpers/list_column_helpers_test.rb +31 -0
  226. data/test/helpers/pagination_helpers_test.rb +55 -0
  227. data/test/misc/active_record_permissions_test.rb +154 -0
  228. data/test/misc/attribute_params_test.rb +110 -0
  229. data/test/misc/configurable_test.rb +96 -0
  230. data/test/misc/constraints_test.rb +193 -0
  231. data/test/misc/finder_test.rb +93 -0
  232. data/test/misc/lang_test.rb +12 -0
  233. data/test/mock_app/.gitignore +2 -0
  234. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  235. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  236. data/test/mock_app/config/boot.rb +110 -0
  237. data/test/mock_app/config/database.yml +16 -0
  238. data/test/mock_app/config/environment.rb +43 -0
  239. data/test/mock_app/config/environments/development.rb +17 -0
  240. data/test/mock_app/config/environments/production.rb +28 -0
  241. data/test/mock_app/config/environments/test.rb +28 -0
  242. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  243. data/test/mock_app/config/initializers/inflections.rb +10 -0
  244. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  245. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  246. data/test/mock_app/config/initializers/session_store.rb +15 -0
  247. data/test/mock_app/config/locales/en.yml +5 -0
  248. data/test/mock_app/config/routes.rb +43 -0
  249. data/test/mock_app/db/test.sqlite3 +1 -0
  250. data/test/mock_app/public/blank.html +33 -0
  251. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  252. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  253. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  254. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  255. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  256. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  257. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  258. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  259. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  260. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  261. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  262. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  263. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  264. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  265. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  266. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  267. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +839 -0
  268. data/test/model_stub.rb +55 -0
  269. data/test/run_all.rb +8 -0
  270. data/test/test_helper.rb +39 -0
  271. data/uninstall.rb +13 -0
  272. metadata +478 -0
@@ -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,279 @@
1
+ require 'active_record_permissions'
2
+ require 'dhtml_confirm'
3
+ require 'paginator'
4
+ require 'responds_to_parent'
5
+ require 'active_scaffold/attribute_params'
6
+ require 'active_scaffold/configurable'
7
+ require 'active_scaffold/constraints'
8
+ require 'active_scaffold/finder'
9
+ require 'active_scaffold/marked_model'
10
+ require 'active_scaffold/config/base'
11
+ require 'active_scaffold/data_structures/column'
12
+ require 'active_scaffold/data_structures/actions'
13
+ require 'active_scaffold/data_structures/action_links'
14
+ require 'active_scaffold/config/form'
15
+
16
+ module ActiveScaffold
17
+ class ControllerNotFound < RuntimeError; end
18
+ class DependencyFailure < RuntimeError; end
19
+ class MalformedConstraint < RuntimeError; end
20
+ class RecordNotAllowed < SecurityError; end
21
+ class ActionNotAllowed < SecurityError; end
22
+ class ReverseAssociationRequired < RuntimeError; end
23
+
24
+ def self.included(base)
25
+ base.extend(ClassMethods)
26
+ base.module_eval do
27
+ # TODO: these should be in actions/core
28
+ before_filter :handle_user_settings
29
+ end
30
+ end
31
+
32
+ def self.set_defaults(&block)
33
+ ActiveScaffold::Config::Core.configure &block
34
+ end
35
+
36
+ def active_scaffold_config
37
+ self.class.active_scaffold_config
38
+ end
39
+
40
+ def active_scaffold_config_for(klass)
41
+ self.class.active_scaffold_config_for(klass)
42
+ end
43
+
44
+ def active_scaffold_session_storage
45
+ id = params[:eid] || params[:controller]
46
+ session_index = "as:#{id}"
47
+ session[session_index] ||= {}
48
+ session[session_index]
49
+ end
50
+
51
+ # at some point we need to pass the session and params into config. we'll just take care of that before any particular action occurs by passing those hashes off to the UserSettings class of each action.
52
+ def handle_user_settings
53
+ if self.class.uses_active_scaffold?
54
+ active_scaffold_config.actions.each do |action_name|
55
+ conf_instance = active_scaffold_config.send(action_name) rescue next
56
+ next if conf_instance.class::UserSettings == ActiveScaffold::Config::Base::UserSettings # if it hasn't been extended, skip it
57
+ active_scaffold_session_storage[action_name] ||= {}
58
+ conf_instance.user = conf_instance.class::UserSettings.new(conf_instance, active_scaffold_session_storage[action_name], params)
59
+ end
60
+ end
61
+ end
62
+
63
+ def self.js_framework=(framework)
64
+ @@js_framework = framework
65
+ end
66
+
67
+ def self.js_framework
68
+ @@js_framework ||= :prototype
69
+ end
70
+
71
+ module ClassMethods
72
+ def active_scaffold(model_id = nil, &block)
73
+ # initialize bridges here
74
+ ActiveScaffold::Bridges::Bridge.run_all
75
+
76
+ # converts Foo::BarController to 'bar' and FooBarsController to 'foo_bar' and AddressController to 'address'
77
+ model_id = self.to_s.split('::').last.sub(/Controller$/, '').pluralize.singularize.underscore unless model_id
78
+
79
+ # run the configuration
80
+ @active_scaffold_config = ActiveScaffold::Config::Core.new(model_id)
81
+ @active_scaffold_config_block = block
82
+ self.links_for_associations
83
+
84
+ @active_scaffold_overrides = []
85
+ ActionController::Base.view_paths.each do |dir|
86
+ active_scaffold_overrides_dir = File.join(dir.to_s,"active_scaffold_overrides")
87
+ @active_scaffold_overrides << active_scaffold_overrides_dir if File.exists?(active_scaffold_overrides_dir)
88
+ end
89
+ @active_scaffold_overrides.uniq! # Fix rails duplicating some view_paths
90
+ @active_scaffold_frontends = []
91
+ if active_scaffold_config.frontend.to_sym != :default
92
+ active_scaffold_custom_frontend_path = File.join(ActiveScaffold::Config::Core.plugin_directory, 'frontends', active_scaffold_config.frontend.to_s , 'views')
93
+ @active_scaffold_frontends << active_scaffold_custom_frontend_path
94
+ end
95
+ active_scaffold_default_frontend_path = File.join(ActiveScaffold::Config::Core.plugin_directory, 'frontends', 'default' , 'views')
96
+ @active_scaffold_frontends << active_scaffold_default_frontend_path
97
+ @active_scaffold_custom_paths = []
98
+
99
+ self.active_scaffold_superclasses_blocks.each {|superblock| self.active_scaffold_config.configure &superblock}
100
+ self.active_scaffold_config.configure &block if block_given?
101
+ self.active_scaffold_config._configure_sti unless self.active_scaffold_config.sti_children.nil?
102
+ self.active_scaffold_config._load_action_columns
103
+
104
+ # defines the attribute read methods on the model, so record.send() doesn't find protected/private methods instead
105
+ klass = self.active_scaffold_config.model
106
+ klass.define_attribute_methods unless klass.attribute_methods_generated?
107
+ # include the rest of the code into the controller: the action core and the included actions
108
+ module_eval do
109
+ include ActiveScaffold::Finder
110
+ include ActiveScaffold::Constraints
111
+ include ActiveScaffold::AttributeParams
112
+ include ActiveScaffold::Actions::Core
113
+ active_scaffold_config.actions.each do |mod|
114
+ name = mod.to_s.camelize
115
+ include "ActiveScaffold::Actions::#{name}".constantize
116
+
117
+ # sneak the action links from the actions into the main set
118
+ if link = active_scaffold_config.send(mod).link rescue nil
119
+ active_scaffold_config.action_links << link
120
+ end
121
+ end
122
+ end
123
+ active_scaffold_paths.each do |path|
124
+ self.append_view_path(ActionView::ActiveScaffoldResolver.new(path))
125
+ end
126
+ self.active_scaffold_config._add_sti_create_links if self.active_scaffold_config.add_sti_create_links?
127
+ end
128
+
129
+ # Create the automatic column links. Note that this has to happen when configuration is *done*, because otherwise the Nested module could be disabled. Actually, it could still be disabled later, couldn't it?
130
+ def links_for_associations
131
+ return unless active_scaffold_config.actions.include? :list and active_scaffold_config.actions.include? :nested
132
+ active_scaffold_config.columns.each do |column|
133
+ next unless column.link.nil? and column.autolink?
134
+ action_link = link_for_association(column)
135
+ column.set_link(action_link) unless action_link.nil?
136
+ end
137
+ end
138
+
139
+ def link_for_association(column, options = {})
140
+ begin
141
+ controller = column.polymorphic_association? ? :polymorph : active_scaffold_controller_for(column.association.klass)
142
+ rescue ActiveScaffold::ControllerNotFound
143
+ controller = nil
144
+ end
145
+
146
+ unless controller.nil?
147
+ options.reverse_merge! :label => column.label, :position => :after, :type => :member, :controller => (controller == :polymorph ? controller : controller.controller_path), :column => column
148
+ options[:parameters] ||= {}
149
+ options[:parameters].reverse_merge! :parent_model => column.active_record_class.to_s.underscore, :association => column.association.name
150
+ if column.plural_association?
151
+ # note: we can't create nested scaffolds on :through associations because there's no reverse association.
152
+
153
+ ActiveScaffold::DataStructures::ActionLink.new('index', options) #unless column.through_association?
154
+ else
155
+ actions = [:create, :update, :show]
156
+ actions = controller.active_scaffold_config.actions unless controller == :polymorph
157
+ column.actions_for_association_links.delete :new unless actions.include? :create
158
+ column.actions_for_association_links.delete :edit unless actions.include? :update
159
+ column.actions_for_association_links.delete :show unless actions.include? :show
160
+ ActiveScaffold::DataStructures::ActionLink.new(:none, options.merge({:crud_type => nil, :html_options => {:class => column.name}}))
161
+ end
162
+ end
163
+ end
164
+
165
+ def link_for_association_as_scope(scope, options = {})
166
+ options.reverse_merge! :label => scope, :position => :after, :type => :member, :controller => controller_path
167
+ options[:parameters] ||= {}
168
+ options[:parameters].reverse_merge! :parent_model => active_scaffold_config.model.to_s.underscore, :named_scope => scope
169
+ ActiveScaffold::DataStructures::ActionLink.new('index', options)
170
+ end
171
+
172
+ def add_active_scaffold_path(path)
173
+ @active_scaffold_paths = nil # Force active_scaffold_paths to rebuild
174
+ @active_scaffold_custom_paths << path
175
+ end
176
+
177
+ def add_active_scaffold_override_path(path)
178
+ @active_scaffold_paths = nil # Force active_scaffold_paths to rebuild
179
+ @active_scaffold_overrides.unshift path
180
+ end
181
+
182
+ def active_scaffold_paths
183
+ return @active_scaffold_paths unless @active_scaffold_paths.nil?
184
+
185
+ #@active_scaffold_paths = ActionView::PathSet.new
186
+ @active_scaffold_paths = []
187
+ @active_scaffold_paths.concat @active_scaffold_overrides unless @active_scaffold_overrides.nil?
188
+ @active_scaffold_paths.concat @active_scaffold_custom_paths unless @active_scaffold_custom_paths.nil?
189
+ @active_scaffold_paths.concat @active_scaffold_frontends unless @active_scaffold_frontends.nil?
190
+ @active_scaffold_paths
191
+ end
192
+
193
+ def active_scaffold_config
194
+ if @active_scaffold_config.nil?
195
+ self.superclass.active_scaffold_config if self.superclass.respond_to? :active_scaffold_config
196
+ else
197
+ @active_scaffold_config
198
+ end
199
+ end
200
+
201
+ def active_scaffold_config_block
202
+ @active_scaffold_config_block
203
+ end
204
+
205
+ def active_scaffold_superclasses_blocks
206
+ blocks = []
207
+ klass = self.superclass
208
+ while klass.respond_to? :active_scaffold_superclasses_blocks
209
+ blocks << klass.active_scaffold_config_block
210
+ klass = klass.superclass
211
+ end
212
+ blocks.compact.reverse
213
+ end
214
+
215
+ def active_scaffold_config_for(klass)
216
+ begin
217
+ controller = active_scaffold_controller_for(klass)
218
+ rescue ActiveScaffold::ControllerNotFound
219
+ config = ActiveScaffold::Config::Core.new(klass)
220
+ config._load_action_columns
221
+ config
222
+ else
223
+ controller.active_scaffold_config
224
+ end
225
+ end
226
+
227
+ # Tries to find a controller for the given ActiveRecord model.
228
+ # Searches in the namespace of the current controller for singular and plural versions of the conventional "#{model}Controller" syntax.
229
+ # You may override this method to customize the search routine.
230
+ def active_scaffold_controller_for(klass)
231
+ controller_namespace = self.to_s.split('::')[0...-1].join('::') + '::'
232
+ error_message = []
233
+ [controller_namespace, ''].each do |namespace|
234
+ ["#{klass.to_s.underscore.pluralize}", "#{klass.to_s.underscore.pluralize.singularize}"].each do |controller_name|
235
+ begin
236
+ controller = "#{namespace}#{controller_name.camelize}Controller".constantize
237
+ rescue NameError => error
238
+ # Only rescue NameError associated with the controller constant not existing - not other compile errors
239
+ if error.message["uninitialized constant #{controller}"]
240
+ error_message << "#{namespace}#{controller_name.camelize}Controller"
241
+ next
242
+ else
243
+ raise
244
+ end
245
+ end
246
+ raise ActiveScaffold::ControllerNotFound, "#{controller} missing ActiveScaffold", caller unless controller.uses_active_scaffold?
247
+ raise ActiveScaffold::ControllerNotFound, "ActiveScaffold on #{controller} is not for #{klass} model.", caller unless controller.active_scaffold_config.model == klass
248
+ return controller
249
+ end
250
+ end
251
+ raise ActiveScaffold::ControllerNotFound, "Could not find " + error_message.join(" or "), caller
252
+ end
253
+
254
+ def uses_active_scaffold?
255
+ !active_scaffold_config.nil?
256
+ end
257
+ end
258
+ end
259
+
260
+ ##
261
+ ## Initialize the environment
262
+ ##
263
+ unless Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR >= 0
264
+ raise "This version of ActiveScaffold requires Rails 3.0 or higher. Please use an earlier version."
265
+ end
266
+
267
+ require File.dirname(__FILE__) + '/../environment'
268
+
269
+ ##
270
+ ## Run the install assets script, too, just to make sure
271
+ ## But at least rescue the action in production
272
+ ##
273
+ Rails::Application.initializer("active_scaffold_install_assets") do
274
+ begin
275
+ require File.dirname(__FILE__) + '/../install_assets'
276
+ rescue
277
+ raise $! unless Rails.env == 'production'
278
+ end
279
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveScaffold::Actions
2
+ module CommonSearch
3
+ protected
4
+ def store_search_params_into_session
5
+ active_scaffold_session_storage[:search] = params.delete :search if params[:search]
6
+ end
7
+
8
+ def search_params
9
+ active_scaffold_session_storage[:search]
10
+ end
11
+
12
+ def search_ignore?
13
+ active_scaffold_config.list.always_show_search
14
+ end
15
+
16
+ # The default security delegates to ActiveRecordPermissions.
17
+ # You may override the method to customize.
18
+ def search_authorized?
19
+ authorized_for?(:crud_type => :read)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,150 @@
1
+ module ActiveScaffold::Actions
2
+ module Core
3
+ def self.included(base)
4
+ base.class_eval do
5
+ after_filter :clear_flashes
6
+ end
7
+ base.helper_method :nested?
8
+ base.helper_method :beginning_of_chain
9
+ end
10
+ def render_field
11
+ @record ||= if params[:in_place_editing]
12
+ active_scaffold_config.model.find params[:id]
13
+ else
14
+ active_scaffold_config.model.new
15
+ end
16
+ column = active_scaffold_config.columns[params[:column]]
17
+ if params[:in_place_editing]
18
+ render :inline => "<%= active_scaffold_input_for(active_scaffold_config.columns[params[:update_column].to_sym]) %>"
19
+ elsif !column.nil?
20
+ value = column_value_from_param_value(@record, column, params[:value])
21
+ @record.send "#{column.name}=", value
22
+ after_render_field(@record, column)
23
+ render :partial => "render_field", :collection => Array(params[:update_columns]), :content_type => 'text/javascript'
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def nested?
30
+ false
31
+ end
32
+
33
+ # override this method if you want to do something after render_field
34
+ def after_render_field(record, column); end
35
+
36
+ def authorized_for?(options = {})
37
+ active_scaffold_config.model.authorized_for?(options)
38
+ end
39
+
40
+ def clear_flashes
41
+ if request.xhr?
42
+ flash.keys.each do |flash_key|
43
+ flash[flash_key] = nil
44
+ end
45
+ end
46
+ end
47
+
48
+ def default_formats
49
+ [:html, :js, :json, :xml, :yaml]
50
+ end
51
+ # Returns true if the client accepts one of the MIME types passed to it
52
+ # ex: accepts? :html, :xml
53
+ def accepts?(*types)
54
+ for priority in request.accepts.compact
55
+ if priority == Mime::ALL
56
+ # Because IE always sends */* in the accepts header and we assume
57
+ # that if you really wanted XML or something else you would say so
58
+ # explicitly, we will assume */* to only ask for :html
59
+ return types.include?(:html)
60
+ elsif types.include?(priority.to_sym)
61
+ return true
62
+ end
63
+ end
64
+ false
65
+ end
66
+
67
+ def response_status
68
+ if successful?
69
+ action_name == 'create' ? 201 : 200
70
+ else
71
+ 422
72
+ end
73
+ end
74
+
75
+ # API response object that will be converted to XML/YAML/JSON using to_xxx
76
+ def response_object
77
+ @response_object = successful? ? (@record || @records) : @record.errors
78
+ end
79
+
80
+ # Success is the existence of certain variables and the absence of errors (when applicable).
81
+ # Success can also be defined.
82
+ def successful?
83
+ if @successful.nil?
84
+ @records or (@record and @record.errors.count == 0 and @record.no_errors_in_associated?)
85
+ else
86
+ @successful
87
+ end
88
+ end
89
+
90
+ def successful=(val)
91
+ @successful = (val) ? true : false
92
+ end
93
+
94
+ # Redirect to the main page (override if the ActiveScaffold is used as a component on another controllers page) for Javascript degradation
95
+ def return_to_main
96
+ redirect_to main_path_to_return
97
+ end
98
+
99
+ # Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
100
+ def conditions_for_collection
101
+ end
102
+
103
+ # Override this method on your controller to define joins to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
104
+ def joins_for_collection
105
+ end
106
+
107
+ # Override this method on your controller to provide custom finder options to the find() call. The return of this method should be a hash.
108
+ def custom_finder_options
109
+ {}
110
+ end
111
+
112
+ #Overide this method on your controller to provide model with named scopes
113
+ def beginning_of_chain
114
+ active_scaffold_config.model
115
+ end
116
+
117
+ # Builds search conditions by search params for column names. This allows urls like "contacts/list?company_id=5".
118
+ def conditions_from_params
119
+ conditions = nil
120
+ params.reject {|key, value| [:controller, :action, :id, :page, :sort, :sort_direction].include?(key.to_sym)}.each do |key, value|
121
+ next unless active_scaffold_config.model.column_names.include?(key)
122
+ if value.is_a?(Array)
123
+ conditions = merge_conditions(conditions, ["#{active_scaffold_config.model.table_name}.#{key.to_s} in (?)", value])
124
+ else
125
+ conditions = merge_conditions(conditions, ["#{active_scaffold_config.model.table_name}.#{key.to_s} = ?", value])
126
+ end
127
+ end
128
+ conditions
129
+ end
130
+ private
131
+ def respond_to_action(action)
132
+ respond_to do |type|
133
+ send("#{action}_formats").each do |format|
134
+ type.send(format){ send("#{action}_respond_to_#{format}") }
135
+ end
136
+ end
137
+ end
138
+
139
+ def response_code_for_rescue(exception)
140
+ case exception
141
+ when ActiveScaffold::RecordNotAllowed
142
+ "403 Record Not Allowed"
143
+ when ActiveScaffold::ActionNotAllowed
144
+ "403 Action Not Allowed"
145
+ else
146
+ super
147
+ end
148
+ end
149
+ end
150
+ end