edgarj 0.01.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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