edgarj 0.01.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +23 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/javascripts/edgarj/base.js +49 -0
  6. data/app/assets/javascripts/edgarj/menu.js +39 -0
  7. data/app/assets/javascripts/edgarj/operator_selection.js +133 -0
  8. data/app/assets/stylesheets/edgarj/base.css +304 -0
  9. data/app/assets/stylesheets/edgarj/menu.css +65 -0
  10. data/app/controllers/edgarj/authentication_mixin.rb.sample +30 -0
  11. data/app/controllers/edgarj/controller_mixin_common.rb +80 -0
  12. data/app/controllers/edgarj/controller_mixin_for_app.rb +71 -0
  13. data/app/controllers/edgarj/edgarj_controller.rb +535 -0
  14. data/app/controllers/edgarj/model_permissions_controller.rb +8 -0
  15. data/app/controllers/edgarj/permission_mixin.rb +84 -0
  16. data/app/controllers/edgarj/popup_controller.rb +128 -0
  17. data/app/controllers/edgarj/rescue_mixin.rb +37 -0
  18. data/app/controllers/edgarj/user_group_users_controller.rb +8 -0
  19. data/app/controllers/edgarj/user_groups_controller.rb +9 -0
  20. data/app/controllers/edgarj/user_groups_popup_controller.rb +8 -0
  21. data/app/helpers/edgarj/assoc_helper.rb +369 -0
  22. data/app/helpers/edgarj/common_helper.rb +37 -0
  23. data/app/helpers/edgarj/edgarj_helper.rb +20 -0
  24. data/app/helpers/edgarj/field_helper.rb +397 -0
  25. data/app/helpers/edgarj/form_drawer.rb +322 -0
  26. data/app/helpers/edgarj/list_drawer.rb +169 -0
  27. data/app/helpers/edgarj/menu_helper.rb +92 -0
  28. data/app/helpers/edgarj/model_permissions_helper.rb +9 -0
  29. data/app/helpers/edgarj/popup_helper.rb +40 -0
  30. data/app/helpers/edgarj/search_helper.rb +14 -0
  31. data/app/helpers/edgarj/sessions_helper.rb +9 -0
  32. data/app/helpers/edgarj/user_group_users_helper.rb +9 -0
  33. data/app/helpers/edgarj/user_groups_helper.rb +9 -0
  34. data/app/helpers/edgarj/user_groups_popup_helper.rb +9 -0
  35. data/app/models/edgarj/drawer/base.rb +234 -0
  36. data/app/models/edgarj/drawer/normal.rb +6 -0
  37. data/app/models/edgarj/drawer/popup.rb +47 -0
  38. data/app/models/edgarj/drawer.rb +13 -0
  39. data/app/models/edgarj/model_permission.rb +41 -0
  40. data/app/models/edgarj/page_info.rb +47 -0
  41. data/app/models/edgarj/search.rb +81 -0
  42. data/app/models/edgarj/search_form.rb +255 -0
  43. data/app/models/edgarj/search_popup.rb +44 -0
  44. data/app/models/edgarj/sssn.rb +120 -0
  45. data/app/models/edgarj/user_group.rb +67 -0
  46. data/app/models/edgarj/user_group_user.rb +18 -0
  47. data/app/models/edgarj.rb +5 -0
  48. data/app/views/edgarj/edgarj/_form.html.erb +25 -0
  49. data/app/views/edgarj/edgarj/_list.html.erb +52 -0
  50. data/app/views/edgarj/edgarj/_message_popup.html.erb +15 -0
  51. data/app/views/edgarj/edgarj/_search_form.html.erb +64 -0
  52. data/app/views/edgarj/edgarj/_search_operator.html.erb +17 -0
  53. data/app/views/edgarj/edgarj/_search_operator_selection.html.erb +12 -0
  54. data/app/views/edgarj/edgarj/clear.js.erb +1 -0
  55. data/app/views/edgarj/edgarj/create.js.erb +4 -0
  56. data/app/views/edgarj/edgarj/destroy.js.erb +2 -0
  57. data/app/views/edgarj/edgarj/index.html.erb +37 -0
  58. data/app/views/edgarj/edgarj/index.js.erb +1 -0
  59. data/app/views/edgarj/edgarj/message_popup.js.erb +3 -0
  60. data/app/views/edgarj/edgarj/page_info_save.js.erb +1 -0
  61. data/app/views/edgarj/edgarj/search.js.erb +4 -0
  62. data/app/views/edgarj/edgarj/search_clear.js.erb +1 -0
  63. data/app/views/edgarj/edgarj/show.js.erb +1 -0
  64. data/app/views/edgarj/edgarj/top.html.erb +4 -0
  65. data/app/views/edgarj/edgarj/update.js.erb +4 -0
  66. data/app/views/edgarj/popup/_index.html.erb +16 -0
  67. data/app/views/edgarj/popup/_list.html.erb +42 -0
  68. data/app/views/edgarj/popup/_message_popup.html.erb +15 -0
  69. data/app/views/edgarj/popup/_search.html.erb +42 -0
  70. data/app/views/edgarj/popup/_search_save_popup.html.erb +16 -0
  71. data/app/views/edgarj/popup/index.js.erb +5 -0
  72. data/app/views/edgarj/popup/message_popup.js.erb +3 -0
  73. data/app/views/edgarj/popup/page_info_save.js.erb +1 -0
  74. data/app/views/edgarj/popup/search.js.erb +15 -0
  75. data/app/views/edgarj/popup/view_status_save.js.erb +1 -0
  76. data/config/routes.rb +15 -0
  77. data/config/settings.yml +5 -0
  78. data/db/migrate/20131118084600_create_edgar_sssns.rb +14 -0
  79. data/db/migrate/20131123124730_create_edgar_page_infos.rb +15 -0
  80. data/db/migrate/20140116062252_create_edgar_user_groups.rb +18 -0
  81. data/db/migrate/20140116062327_create_edgar_user_group_users.rb +10 -0
  82. data/db/migrate/20140206222308_create_edgar_model_permissions.rb +14 -0
  83. data/db/migrate/20141209053055_rename_edgar_to_edgarj.rb +17 -0
  84. data/lib/core_ext/active_record.rb +123 -0
  85. data/lib/core_ext/resources.rb +71 -0
  86. data/lib/edgarj/engine.rb +64 -0
  87. data/lib/edgarj/enum_cache.rb +46 -0
  88. data/lib/edgarj/templates/rails/helper/helper.rb +10 -0
  89. data/lib/edgarj/templates/test_unit/scaffold/functional_test.rb +575 -0
  90. data/lib/edgarj/version.rb +3 -0
  91. data/lib/edgarj.rb +14 -0
  92. data/lib/generators/edgarj/popup_scaffold/USAGE +6 -0
  93. data/lib/generators/edgarj/popup_scaffold/popup_scaffold_generator.rb +35 -0
  94. data/lib/generators/edgarj/popup_scaffold/templates/controller.rb +13 -0
  95. data/lib/generators/edgarj/popup_scaffold/templates/functional_test.rb +197 -0
  96. data/lib/generators/edgarj/popup_scaffold/templates/helper.rb +9 -0
  97. data/lib/generators/edgarj/scaffold/USAGE +17 -0
  98. data/lib/generators/edgarj/scaffold/scaffold_generator.rb +40 -0
  99. data/lib/generators/edgarj/scaffold/templates/controller.rb +13 -0
  100. data/lib/tasks/edgarj_tasks.rake +32 -0
  101. data/lib/tasks/pakcage.rake +18 -0
  102. data/locale/en.yml +94 -0
  103. data/locale/ja.yml +100 -0
  104. data/test/dummy/README.rdoc +28 -0
  105. data/test/dummy/Rakefile +6 -0
  106. data/test/dummy/app/assets/javascripts/application.js +21 -0
  107. data/test/dummy/app/assets/javascripts/authors.js +2 -0
  108. data/test/dummy/app/assets/stylesheets/application.css +16 -0
  109. data/test/dummy/app/assets/stylesheets/authors.css +4 -0
  110. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  111. data/test/dummy/app/controllers/application_controller.rb +15 -0
  112. data/test/dummy/app/controllers/authors_controller.rb +6 -0
  113. data/test/dummy/app/controllers/authors_popup_controller.rb +6 -0
  114. data/test/dummy/app/controllers/books_controller.rb +6 -0
  115. data/test/dummy/app/controllers/dummy_auth_mixin.rb +36 -0
  116. data/test/dummy/app/decorators/models/edgarj/user_group_decorator.rb +1 -0
  117. data/test/dummy/app/helpers/application_helper.rb +3 -0
  118. data/test/dummy/app/helpers/authors_helper.rb +7 -0
  119. data/test/dummy/app/helpers/authors_popup_helper.rb +7 -0
  120. data/test/dummy/app/helpers/books_helper.rb +7 -0
  121. data/test/dummy/app/models/author.rb +32 -0
  122. data/test/dummy/app/models/book.rb +5 -0
  123. data/test/dummy/app/models/user.rb +13 -0
  124. data/test/dummy/app/views/layouts/application.html.erb +32 -0
  125. data/test/dummy/app/views/layouts/login.html.erb +20 -0
  126. data/test/dummy/bin/bundle +3 -0
  127. data/test/dummy/bin/rails +4 -0
  128. data/test/dummy/bin/rake +4 -0
  129. data/test/dummy/config/application.rb +59 -0
  130. data/test/dummy/config/boot.rb +10 -0
  131. data/test/dummy/config/database.yml +15 -0
  132. data/test/dummy/config/edgarj/menu_config.rb +28 -0
  133. data/test/dummy/config/environment.rb +5 -0
  134. data/test/dummy/config/environments/development.rb +42 -0
  135. data/test/dummy/config/environments/production.rb +79 -0
  136. data/test/dummy/config/environments/test.rb +34 -0
  137. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  139. data/test/dummy/config/initializers/inflections.rb +16 -0
  140. data/test/dummy/config/initializers/mime_types.rb +5 -0
  141. data/test/dummy/config/initializers/secret_token.rb +12 -0
  142. data/test/dummy/config/initializers/session_store.rb +3 -0
  143. data/test/dummy/config/initializers/strong_parameter.rb +3 -0
  144. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  145. data/test/dummy/config/locales/en.yml +26 -0
  146. data/test/dummy/config/routes.rb +60 -0
  147. data/test/dummy/config.ru +4 -0
  148. data/test/dummy/db/migrate/20131107120635_create_authors.rb +9 -0
  149. data/test/dummy/db/migrate/20131218011851_create_books.rb +10 -0
  150. data/test/dummy/db/migrate/20140201000000_add_user_group_id_to_authors.rb +5 -0
  151. data/test/dummy/db/migrate/20140807065420_create_users.rb +10 -0
  152. data/test/dummy/db/schema.rb +93 -0
  153. data/test/dummy/public/404.html +58 -0
  154. data/test/dummy/public/422.html +58 -0
  155. data/test/dummy/public/500.html +57 -0
  156. data/test/dummy/public/favicon.ico +0 -0
  157. data/test/dummy/script/rails +6 -0
  158. data/test/dummy/test/functional/authors_controller_test.rb +631 -0
  159. data/test/dummy/test/functional/books_controller_test.rb +516 -0
  160. data/test/dummy/test/helpers/authors_helper_test.rb +4 -0
  161. data/test/dummy/test/unit/author_test.rb +7 -0
  162. data/test/dummy/test/unit/book_test.rb +7 -0
  163. data/test/edgar_test.rb +7 -0
  164. data/test/fixtures/authors.yml +32 -0
  165. data/test/fixtures/books.yml +56 -0
  166. data/test/fixtures/edgarj/model_permissions.yml +97 -0
  167. data/test/fixtures/edgarj/page_infos.yml +84 -0
  168. data/test/fixtures/edgarj/sssns.yml +38 -0
  169. data/test/fixtures/edgarj/user_group_users.yml +114 -0
  170. data/test/fixtures/edgarj/user_groups.yml +95 -0
  171. data/test/fixtures/users.yml +49 -0
  172. data/test/functional/edgarj/edgarj_controller_test.rb +24 -0
  173. data/test/functional/edgarj/model_permissions_controller_test.rb +554 -0
  174. data/test/functional/edgarj/user_group_users_controller_test.rb +567 -0
  175. data/test/integration/navigation_test.rb +10 -0
  176. data/test/support/edgarj/controller_supporter.rb +23 -0
  177. data/test/test_helper.rb +23 -0
  178. data/test/unit/edgarj/model_permission_test.rb +27 -0
  179. data/test/unit/edgarj/page_info_test.rb +7 -0
  180. data/test/unit/edgarj/sssn_test.rb +10 -0
  181. data/test/unit/edgarj/user_group_test.rb +32 -0
  182. data/test/unit/edgarj/user_group_user_test.rb +9 -0
  183. data/test/unit/helpers/edgarj/model_permissions_helper_test.rb +6 -0
  184. data/test/unit/helpers/edgarj/user_group_users_helper_test.rb +6 -0
  185. metadata +456 -0
@@ -0,0 +1,535 @@
1
+ require 'csv'
2
+
3
+ module Edgarj
4
+ # Generic CRUD(Create/Read/Update/Delte) controller with Ajax.
5
+ #
6
+ # === EdgarjController v.s. ApplicationController
7
+ # When concreate controller (e.g. CustomersController) inherits
8
+ # EdgarjController, it has the following features:
9
+ #
10
+ # * CRUD with default form on Ajax
11
+ # * form can be customized.
12
+ # * QBE (Query By Example) search form
13
+ # * popup-selection on 'belongs_to' column
14
+ # * sort on list
15
+ # * saving search-conditions and reuse it
16
+ #
17
+ # If these are not necessary, just inherits ApplicationController.
18
+ #
19
+ # === Tasks when adding new model which is handled under EdgarjController
20
+ # For example, when want to add Post model:
21
+ # 1. generate edgarj:scaffold:
22
+ # rails generate edgarj:scaffold post name:string body:text published:boolean
23
+ # 2. add controller entry to CONTROLLER_MENU (config/initializers/edgarj.rb)
24
+ #
25
+ # It will take about ~3 min.
26
+ #
27
+ # For the detail of customization, please see:
28
+ # http://sourceforge.net/apps/trac/jjedgarj/wiki/customize
29
+ #
30
+ # === Architecture
31
+ # see {architecture}[link:../architecture.odp] (OpenOffice Presentation)
32
+ #
33
+ # === Access Control
34
+ # There are two dimentions of access control:
35
+ # 1. Page level (Controller level) access control.
36
+ # * Edgarj::UserGroup with kind==ROLE and Edgarj::ModelPermission
37
+ # represents access control on each controller.
38
+ # * Admin user, who belongs to 'admin' user_group, can access any page.
39
+ # * When a user belongs to a user_group (kind==ROLE) and
40
+ # a model_permission belongs to the user_group, the user can
41
+ # access the controller which model is the model in model_permission.
42
+ # * More precisely, 4 kind of access controls, CREATE,
43
+ # READ, UPDATE, and DELETE can be set with any conbination on the
44
+ # controller.
45
+ # * See Edgarj::ModelPermission for more detail.
46
+ # 1. Data scope.
47
+ # * data scope access is controlled by 'user_scoped(user, context)'
48
+ # scope defined at each model.
49
+ # * Where, user is currently accessing person to the model.
50
+ # * context is any kind of 2nd parameter. Default is session object of
51
+ # Edgarj::Sssn, but it can be overwritten 'scope_context' method
52
+ # at the target controller.
53
+ # * See Author.user_scoped as an example.
54
+ #
55
+ # == Naming Convention
56
+ #
57
+ # * 'record' is an instance of 'Model'.
58
+ # * 'drawer' is an instance of 'Drawer' class.
59
+ #
60
+ # === Implementation Note
61
+ # Why not to choose mixin rather than class is because
62
+ # it is easier to use edgarj's view at client controller. For example,
63
+ # AuthorsController, which inherits from EdgarjController, can
64
+ # automatically use edgarj's view by rails view-selection feature.
65
+ #
66
+ # === SEE ALSO
67
+ # PopupController:: 'belongs_to' popup for EdgarjController
68
+ #
69
+ class EdgarjController < ApplicationController
70
+ include ControllerMixinCommon
71
+ include PermissionMixin
72
+ #include TopicPathControllerMixin
73
+
74
+ helper_method :model, :model_name, :drawer,
75
+ :draw_flash, :csv_proc
76
+
77
+ READ_ACTIONS = %w(
78
+ index show clear search search_clear search_save search_load
79
+ zip_complete csv_download file_download map page_info_save)
80
+
81
+ before_filter :require_create_permission, only: :create
82
+ before_filter :require_read_permission, only: READ_ACTIONS
83
+ before_filter :require_update_permission, only: :update
84
+ before_filter :require_delete_permission, only: :destroy
85
+ before_filter :require_other_permission, except: READ_ACTIONS + %w(
86
+ create update destroy top)
87
+ #after_filter :log_topic_path
88
+ after_filter :enum_cache_stat
89
+
90
+ # This page is for following purposes:
91
+ #
92
+ # * top page which contain latest info (TBD)
93
+ # * any error message on HTML format access
94
+ # * on Ajax access, rather edgarj_error_popup is used
95
+ def top
96
+ render :action => 'top'
97
+ end
98
+
99
+ # draw search result in whole page.
100
+ # default update DOM id and template is as follows:
101
+ #
102
+ # DOM id:: 'edgarj_list'
103
+ # template:: 'edgarj/list'
104
+ #
105
+ # However, these can be replaced by params[:update] and params[:template]
106
+ #
107
+ # === Permission
108
+ # ModelPermission::READ on this controller is required.
109
+ #
110
+ # === SEE ALSO
111
+ # popup():: draw popup
112
+ def index
113
+ page_info
114
+
115
+ # update @page_info.page when page is specified.
116
+ # Otherwise, reset page to 1.
117
+ #
118
+ # Just set, not save. It will be done later when saving sssn with
119
+ # 'has_many page_infos ... autosave: true'
120
+ @page_info.page = (params[:page] || 1)
121
+
122
+ #clear_topic_path
123
+ prepare_list
124
+
125
+ @search = page_info.record
126
+ @record = model.new
127
+ end
128
+
129
+ # save new record
130
+ #
131
+ # === Permission
132
+ # ModelPermission::CREATE on this controller is required.
133
+ #
134
+ def create
135
+ upsert do
136
+ # NOTE: create!() is not used because assign to @record to draw form.
137
+ # Otherwise, @record would be in nil so failure at edgarj/_form rendering.
138
+ #
139
+ # NOTE2: valid? after create() calls validate_on_update. This is not
140
+ # an expected behavior. So, new, valid?, then save.
141
+ @record = model.new(permitted_params(:create))
142
+ on_upsert
143
+ #upsert_files
144
+ raise ActiveRecord::RecordNotSaved if !@record.valid?
145
+ @record.save
146
+
147
+ # clear @record values for next data-entry
148
+ @record = model.new
149
+ end
150
+ end
151
+
152
+ # Show detail of one record. Format of html & js should be supported.
153
+ #
154
+ # === Permission
155
+ # ModelPermission::READ on this controller is required.
156
+ #
157
+ def show
158
+ @record = user_scoped.find(params[:id])
159
+ #add_topic_path
160
+ respond_to do |format|
161
+ format.html {
162
+ prepare_list
163
+ @search = page_info.record
164
+ render :action=>'index'
165
+ }
166
+ format.js
167
+ end
168
+ end
169
+
170
+ # save existence modified record
171
+ #
172
+ # === Permission
173
+ # ModelPermission::UPDATE on this controller is required.
174
+ #
175
+ def update
176
+ upsert do
177
+ # NOTE:
178
+ # 1. update ++then++ valid to set new values in @record to draw form.
179
+ # 1. user_scoped may have joins so that record could be
180
+ # ActiveRecord::ReadOnlyRecord so that's why access again from
181
+ # model.
182
+ @record = model.find(user_scoped.find(params[:id]).id)
183
+ #upsert_files
184
+ if !@record.update_attributes(permitted_params(:update))
185
+ raise ActiveRecord::RecordInvalid.new(@record)
186
+ end
187
+ end
188
+ end
189
+
190
+ # === Permission
191
+ # ModelPermission::DELETE on this controller is required.
192
+ def destroy
193
+ m = model.find(user_scoped.find(params[:id]).id)
194
+ m.destroy
195
+
196
+ prepare_list
197
+ @record = model.new
198
+ @flash_notice = t('delete_ok')
199
+ end
200
+
201
+ # Ajax method to clear form
202
+ #
203
+ # === Permission
204
+ # ModelPermission::READ on this controller is required.
205
+ #
206
+ def clear
207
+ @record = model.new
208
+ end
209
+
210
+ # Ajax method to execute search
211
+ #
212
+ # Actually, this doesn't execute search. Rather, this just saves
213
+ # condition. Search will be executed at any listing action
214
+ # like 'index', 'create', or 'update'.
215
+ #
216
+ # === Permission
217
+ # ModelPermission::READ on this controller is required.
218
+ #
219
+ def search
220
+ pi = page_info
221
+ pi.record = SearchForm.new(model, params[:edgarj_search_form])
222
+ pi.page = 1
223
+ pi.save!
224
+ @search = pi.record
225
+ prepare_list if @search.valid?
226
+ end
227
+
228
+ # Ajax method to clear search conditions
229
+ #
230
+ # === Permission
231
+ # ModelPermission::READ on this controller is required.
232
+ #
233
+ def search_clear
234
+ @search = SearchForm.new(model)
235
+ end
236
+
237
+ # Ajax method to save search conditions
238
+ #
239
+ # === call flow
240
+ # Edgarj.SearchSavePopup.open() (javascript)
241
+ # (show $('search_save_popup'))
242
+ # Edgarj.SearchSavePopup.submit() (javascript)
243
+ # (copy entered name into $('saved_page_info_name') in form)
244
+ # call :action=>'search_save'
245
+ #
246
+ # ==== TRICKY PART
247
+ # There are two requirements:
248
+ # 1. use modal-dialog to enter name to decrese busy in search form.
249
+ # 1. send Search Form with name to server.
250
+ #
251
+ # To comply these, Edgarj.SearchSavePopup copies the entered name to
252
+ # 'saved_page_info_name' hidden field and then sends the form which includes
253
+ # the copied name.
254
+ #
255
+ # === Permission
256
+ # ModelPermission::READ on this controller is required.
257
+ #
258
+ def search_save
259
+ svc = SavedVcontext.save(current_user, nil,
260
+ params[:saved_page_info_name], page_info)
261
+
262
+ render :update do |page|
263
+ page << "Edgarj.SearchSavePopup.close();"
264
+ page.replace 'edgarj_load_condition_menu',
265
+ :partial=>'edgarj/load_condition_menu'
266
+ end
267
+ rescue ActiveRecord::ActiveRecordError => ex
268
+ app_rescue
269
+ render :update do |page|
270
+ page.replace_html 'search_save_popup_flash_error', :text=>t('save_fail')
271
+ end
272
+ end
273
+
274
+ # Ajax method to load search condition, lines, order_by, dir, and page.
275
+ #
276
+ def search_load
277
+ @search = current_user.saved_page_infos.find(params[:id]).load(@sssn).model
278
+ draw_search_form
279
+ end
280
+
281
+ # zip -> address completion
282
+ #
283
+ # === INPUTS
284
+ # params[:zip]:: key to find address info. hyphen is supported.
285
+ # params[:adrs_prefix]:: address fields DOM id prefix. e.g. 'org_adrs_0_'
286
+ #
287
+ # === call flow
288
+ # ==== on drawing
289
+ # EdgarjHelper.draw_adrs() app/helpers/edgarj_helper.rb
290
+ # app/views/edgarj/_address.html.erb
291
+ # Example:
292
+ # :
293
+ # <input type=text name='org[adrs_0_zip]'>
294
+ # :
295
+ #
296
+ # ==== on clicking zip->address button
297
+ # Edgarj.zip_complete() public/javascripts/edgarj.js
298
+ # Ajax.Request()
299
+ # EdgarjController.zip_complete app/controllers/edgarj_controller.rb
300
+ # inline RJS to fill address info
301
+ #
302
+ =begin
303
+ def zip_complete
304
+ zip = params[:zip].gsub(/\D/, '')
305
+ @address = ZipAddress.find_by_zip(zip) || ZipAddress.new(
306
+ :prefecture => '?',
307
+ :city => '',
308
+ :other => '')
309
+
310
+ # sanitize
311
+ @adrs_prefix = params[:adrs_prefix].gsub(/[^a-zA-Z_0-9]/, '')
312
+ end
313
+ =end
314
+
315
+ # download model under current condition
316
+ #
317
+ # <tt>respond_to...format.csv</tt> approach was not used since
318
+ # \@list is different as follows:
319
+ # * csv returns all of records under the conditions
320
+ # * HTML returns *just* in specified 'page'.
321
+ #
322
+ # === Permission
323
+ # ModelPermission::READ on this controller is required.
324
+ #
325
+ # FIXME: file.close(true) deletes files *BEFORE* actually send file
326
+ # so that comment it out. Need to clean these work files.
327
+ def csv_download
328
+ filename = sprintf("%s-%s.csv",
329
+ model_name,
330
+ Time.now.strftime("%Y%m%d-%H%M%S"))
331
+ file = Tempfile.new(filename, Settings.edgarj.csv_dir)
332
+ csv_visitor = EdgarjHelper::CsvVisitor.new(view_context)
333
+ cond = {:conditions=>page_info.record.conditions}
334
+ file.write CSV.generate_line(model.columns.map{|c| c.name})
335
+ for rec in user_scoped.where(page_info.record.conditions).
336
+ order(
337
+ page_info.order_by.blank? ?
338
+ nil :
339
+ page_info.order_by + ' ' + page_info.dir) do
340
+ array = []
341
+ for col in model.columns do
342
+ array << csv_visitor.visit_column(rec, col)
343
+ end
344
+ file.write CSV.generate_line(array)
345
+ end
346
+ file.close
347
+ send_file(file.path, {
348
+ type: 'text/csv',
349
+ filename: filename})
350
+ #file.close(true)
351
+ end
352
+
353
+ # To prevent unassociated file access, do:
354
+ #
355
+ # 1. check if it is in model object
356
+ # 1. check if it is a edgarj_file column
357
+ #
358
+ # === Permission
359
+ # ModelPermission::READ on this controller is required.
360
+ def file_download
361
+ if !model.edgarj_file?(params[:column])
362
+ flash[:error] = t('edgarj_file.no_assoc')
363
+ return
364
+ end
365
+
366
+ file_info_id = user_scoped.find(params[:id]).send(params[:column])
367
+ if file_info_id
368
+ file_info = FileInfo.find(file_info_id)
369
+ if file_info
370
+ send_file(file_info.full_filename, :filename => file_info.filename)
371
+ return
372
+ end
373
+ end
374
+ logger.warn 'invalid file_info'
375
+ end
376
+
377
+ # draw Google map.
378
+ #
379
+ # === Permission
380
+ # ModelPermission::READ on this controller is required.
381
+ #
382
+ def map
383
+ render :template=>'edgarj/map', :layout=>false
384
+ end
385
+
386
+ private
387
+ # derive model class from this controller.
388
+ #
389
+ # If controller cannot guess model class, overwrite this.
390
+ #
391
+ # === Examples:
392
+ # * AuthorsController -> Author
393
+ # * UsersController -> User
394
+ def model
395
+ @_model ||=
396
+ if self.class == Edgarj::EdgarjController
397
+ # dummy model for 'top' action:
398
+ Edgarj::Sssn
399
+ else
400
+ self.class.name.gsub(/Controller$/, '').singularize.constantize
401
+ end
402
+ end
403
+
404
+ # return model name.
405
+ #
406
+ # if each concreate controller cannot guess model name from its
407
+ # controller name, overwrite this.
408
+ #
409
+ # === Examples:
410
+ # * UsersController -> 'edgarj_user'
411
+ # * AuthorsController -> 'author'
412
+ #
413
+ def model_name
414
+ @_model_name ||= ActiveModel::Naming.param_key(model.new)
415
+ end
416
+
417
+ # return permitted params. Default is all.
418
+ #
419
+ # Derived Controller *MUST* customize this.
420
+ def permitted_params(action, kind=nil)
421
+ params.require(model_name).permit!
422
+ end
423
+
424
+ # return search-form object.
425
+ #
426
+ # called from page_info
427
+ def search_form
428
+ SearchForm.new(model)
429
+ end
430
+
431
+ # derive drawer class from this controller.
432
+ #
433
+ # If controller cannot guess drawer class, overwrite this.
434
+ #
435
+ # === Examples:
436
+ # * AuthorsController -> AuthorDrawer
437
+ # * UsersController -> UserDrawer
438
+ def drawer_class
439
+ @_drawer_cache ||=
440
+ if self.class == Edgarj::EdgarjController
441
+ Edgarj::Drawer::Normal
442
+ else
443
+ (self.class.name.gsub(/Controller$/, '').singularize +
444
+ 'Drawer').constantize
445
+ end
446
+ end
447
+
448
+ # set drawer instance as drawer for later use on rendering view
449
+ def set_drawer
450
+ @drawer = drawer_class.new(view_context, params, page_info, model)
451
+ end
452
+
453
+ def drawer
454
+ @drawer
455
+ end
456
+
457
+ # additional behavior on upsert (default does nothing).
458
+ #
459
+ # Derived controller may overwrite this method if necessary, for example:
460
+ # * to upsert protected attributes.
461
+ # * to upsert server-side calculated values
462
+ def on_upsert
463
+ #
464
+ end
465
+
466
+ # update/insert common
467
+ def upsert(&block)
468
+ ActiveRecord::Base.transaction do
469
+ yield
470
+ @flash_notice = t('save_ok')
471
+ end
472
+ prepare_list
473
+ rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => ex
474
+ logger.info "#{ex.class.to_s}: #{@record.class.to_s}: #{@record.errors.full_messages.join(' / ')}"
475
+ app_rescue
476
+ @flash_error = t('save_fail')
477
+ end
478
+
479
+ # At public action, just set any message into @flash_notice and/or
480
+ # \@flash_error. Then, any render in EdgarjController will display it
481
+ # by calling draw_flash().
482
+ #
483
+ # draw_flash() must be a helper because it is called in render block, which
484
+ # is a kind of view.
485
+ def draw_flash(page)
486
+ page.replace_html 'flash_notice', :text=>@flash_notice
487
+ page.replace_html 'flash_error', :text=>@flash_error
488
+ end
489
+
490
+ # insert/update uploaded file and point to it via file_NN column
491
+ =begin
492
+ def upsert_files
493
+ #@record.upsert_files(params[:file_info], params[model_name])
494
+ end
495
+
496
+ # Since 'render :text=>proc...' cannot be tested at functional test
497
+ # (see http://lightyearsoftware.com/2009/10/rails-bug-with-render-text-proc-in-tests/),
498
+ # move the logic inside the proc here to test
499
+ def csv_proc(output)
500
+ csv_visitor = EdgarjHelper::CsvVisitor.new(@template)
501
+ find_args = {:conditions=>page_info.record.conditions}
502
+ if !page_info.order_by.blank?
503
+ find_args.merge!(:order => page_info.order_by + ' ' + page_info.dir)
504
+ end
505
+ output.write CSV.generate_line(model.columns.map{|c| c.name}) + "\n"
506
+ for rec in model.find(:all, find_args) do
507
+ array = []
508
+ for col in model.columns do
509
+ array << csv_visitor.visit_column(rec, col)
510
+ end
511
+ output.write CSV.generate_line(array) + "\n"
512
+ end
513
+ end
514
+ =end
515
+
516
+ # This works as:
517
+ #
518
+ # before_render :set_drawer
519
+ #
520
+ # to set drawer just before rendering.
521
+ #
522
+ # See http://stackoverflow.com/questions/9281224/filter-to-execute-before-render-but-after-controller
523
+ #
524
+ def render(*args)
525
+ # set_drawer should be called only when finishing before_filters.
526
+ set_drawer if current_user
527
+ super
528
+ end
529
+
530
+ def enum_cache_stat
531
+ logger.debug 'EnumCache stat (hit/out/out_of_enum): ' +
532
+ EnumCache.instance.stat.inspect
533
+ end
534
+ end
535
+ end
@@ -0,0 +1,8 @@
1
+ module Edgarj
2
+ class ModelPermissionsController < EdgarjController
3
+ private
4
+ def drawer_class
5
+ ModelPermissionsHelper::ModelPermissionDrawer
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,84 @@
1
+ # permission check before-filters
2
+ #
3
+ # authorization('require_login' before_filter) should be prior to this.
4
+ module Edgarj
5
+ module PermissionMixin
6
+ def self.included(klass)
7
+ klass.helper_method(
8
+ :current_user_roles,
9
+ :current_model_permissions
10
+ )
11
+ end
12
+
13
+ private
14
+ def respond_to_permission_error
15
+ respond_to do |format|
16
+ format.html {
17
+ flash[:error] = v('permission_no')
18
+
19
+ # FIXME: flash[:error] is not passed at redirected page
20
+ # so taht set it to parmas[:error].
21
+ # However, this change causes the test failure so that
22
+ # this change is applied *ONLY* on dev. & prod.
23
+ if Rails.env=='test'
24
+ redirect_to main_app.top_path
25
+ else
26
+ redirect_to main_app.top_path(error: v('permission_no'))
27
+ end
28
+ }
29
+ format.js {
30
+ flash.now[:error] = v('permission_no')
31
+ render 'message_popup'
32
+ }
33
+ end
34
+ end
35
+
36
+ def current_user_roles
37
+ @_edgarj_current_user_roles ||= Edgarj::UserGroup.joins(:user_group_users).
38
+ where(
39
+ 'edgarj_user_groups.kind' => Edgarj::UserGroup::Kind::ROLE,
40
+ 'edgarj_user_group_users.user_id'=> current_user.id)
41
+ end
42
+
43
+ def current_model_permissions
44
+ @_edgarj_current_model_permissions ||= Edgarj::ModelPermission.
45
+ joins(user_group: :user_group_users).
46
+ where(
47
+ 'model' => model.to_s,
48
+ 'edgarj_user_groups.kind' => Edgarj::UserGroup::Kind::ROLE,
49
+ 'edgarj_user_group_users.user_id'=> current_user.id)
50
+ end
51
+
52
+ # common method for all of 'require_*_permission' before_filter
53
+ def require_x_permission(flag)
54
+ if current_user && current_user_roles.any?{|ug| ug.admin?}
55
+ # if role is admin, then ok
56
+ elsif current_user && current_model_permissions.any?{|cp| cp.permitted?(flag)}
57
+ # if enough permission, then ok
58
+ else
59
+ respond_to_permission_error
60
+ end
61
+ end
62
+
63
+ def require_create_permission
64
+ require_x_permission(Edgarj::ModelPermission::FlagsBitset::CREATE)
65
+ end
66
+
67
+ def require_read_permission
68
+ require_x_permission(Edgarj::ModelPermission::FlagsBitset::READ)
69
+ end
70
+
71
+ def require_update_permission
72
+ require_x_permission(Edgarj::ModelPermission::FlagsBitset::UPDATE)
73
+ end
74
+
75
+ def require_delete_permission
76
+ require_x_permission(Edgarj::ModelPermission::FlagsBitset::DELETE)
77
+ end
78
+
79
+ # fallback to catch public action which permisson is not declared
80
+ def require_other_permission
81
+ respond_to_permission_error
82
+ end
83
+ end
84
+ end