agile_rails 0.0.0.1

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 (259) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +285 -0
  5. data/Rakefile +39 -0
  6. data/agile_rails.gemspec +36 -0
  7. data/app/assets/fonts/ibm-plex-sans-300.woff2 +0 -0
  8. data/app/assets/fonts/ibm-plex-sans-400.woff2 +0 -0
  9. data/app/assets/fonts/ibm-plex-sans-500.woff2 +0 -0
  10. data/app/assets/fonts/ibm-plex-sans-600.woff2 +0 -0
  11. data/app/assets/fonts/ibm-plex-sans-700.woff2 +0 -0
  12. data/app/assets/fonts/ibm-plex-sans-italic.woff2 +0 -0
  13. data/app/assets/images/32px.png +0 -0
  14. data/app/assets/images/throbber.gif +0 -0
  15. data/app/assets/javascripts/agile/agile.js +1489 -0
  16. data/app/assets/javascripts/agile/jquery-migrate.js +702 -0
  17. data/app/assets/javascripts/agile/jquery.bpopup.js +372 -0
  18. data/app/assets/javascripts/agile/jquery.datetimepicker.js +1353 -0
  19. data/app/assets/javascripts/agile/jstree.min.js +6 -0
  20. data/app/assets/javascripts/agile/select-multiple.js +459 -0
  21. data/app/assets/javascripts/agile/some_scripts.js +33 -0
  22. data/app/assets/javascripts/agile.js +22 -0
  23. data/app/assets/javascripts/agile_application.js +22 -0
  24. data/app/assets/javascripts/agile_editor.js +22 -0
  25. data/app/assets/stylesheets/agile/agile.css +1882 -0
  26. data/app/assets/stylesheets/agile/agile_apps.css +149 -0
  27. data/app/assets/stylesheets/agile/jquery.datetimepicker.css +304 -0
  28. data/app/assets/stylesheets/agile/jstree.css +1107 -0
  29. data/app/assets/stylesheets/agile/select-multiple.css +110 -0
  30. data/app/assets/stylesheets/agile/th-bg.png +0 -0
  31. data/app/assets/stylesheets/agile/theme.css +49 -0
  32. data/app/assets/stylesheets/agile.css +21 -0
  33. data/app/assets/stylesheets/agile_application.css +20 -0
  34. data/app/assets/stylesheets/agile_editor.css +19 -0
  35. data/app/controllers/agile_application_controller.rb +735 -0
  36. data/app/controllers/agile_common_controller.rb +345 -0
  37. data/app/controllers/agile_controller.rb +977 -0
  38. data/app/controllers/agile_main_controller.rb +36 -0
  39. data/app/controls/agile_control.rb +120 -0
  40. data/app/controls/agile_report.rb +364 -0
  41. data/app/controls/ar_category_control.rb +39 -0
  42. data/app/controls/ar_help_control.rb +139 -0
  43. data/app/controls/ar_image_control.rb +180 -0
  44. data/app/controls/ar_journal_control.rb +47 -0
  45. data/app/controls/ar_menu_item_control.rb +55 -0
  46. data/app/controls/ar_page_control.rb +64 -0
  47. data/app/controls/ar_poll_result_control.rb +84 -0
  48. data/app/controls/ar_setup_control.rb +62 -0
  49. data/app/controls/belongs_to_control.rb +61 -0
  50. data/app/controls/browse_models_control.rb +98 -0
  51. data/app/controls/settings_form_control.rb +137 -0
  52. data/app/forms/agile_help.yml +112 -0
  53. data/app/forms/agile_menu.yml +140 -0
  54. data/app/forms/agile_report_defaults.yml +39 -0
  55. data/app/forms/all_options.yml +810 -0
  56. data/app/forms/ar_ad.yml +121 -0
  57. data/app/forms/ar_big_table.yml +60 -0
  58. data/app/forms/ar_big_table_value.yml +52 -0
  59. data/app/forms/ar_browse_fields.yml +35 -0
  60. data/app/forms/ar_browse_models.yml +48 -0
  61. data/app/forms/ar_category.yml +73 -0
  62. data/app/forms/ar_category_as_tree.yml +31 -0
  63. data/app/forms/ar_design.yml +75 -0
  64. data/app/forms/ar_filter.yml +52 -0
  65. data/app/forms/ar_folder_permission.yml +56 -0
  66. data/app/forms/ar_folder_rule.yml +48 -0
  67. data/app/forms/ar_gallery.yml +55 -0
  68. data/app/forms/ar_image.yml +126 -0
  69. data/app/forms/ar_image_search.yml +83 -0
  70. data/app/forms/ar_journal.yml +76 -0
  71. data/app/forms/ar_json_ld.yml +56 -0
  72. data/app/forms/ar_key_value.yml +33 -0
  73. data/app/forms/ar_key_value_store.yml +33 -0
  74. data/app/forms/ar_link.yml +60 -0
  75. data/app/forms/ar_menu.yml +67 -0
  76. data/app/forms/ar_menu_item.yml +141 -0
  77. data/app/forms/ar_page.yml +187 -0
  78. data/app/forms/ar_part.yml +91 -0
  79. data/app/forms/ar_permission.yml +52 -0
  80. data/app/forms/ar_permission_rule.yml +40 -0
  81. data/app/forms/ar_piece.yml +106 -0
  82. data/app/forms/ar_policy.yml +64 -0
  83. data/app/forms/ar_policy_rule.yml +42 -0
  84. data/app/forms/ar_policy_rule_nocms.yml +40 -0
  85. data/app/forms/ar_poll.yml +118 -0
  86. data/app/forms/ar_poll_item.yml +78 -0
  87. data/app/forms/ar_poll_result.yml +88 -0
  88. data/app/forms/ar_poll_result_export.yml +35 -0
  89. data/app/forms/ar_removed_url.yml +41 -0
  90. data/app/forms/ar_role.yml +43 -0
  91. data/app/forms/ar_seo.yml +32 -0
  92. data/app/forms/ar_setup.yml +45 -0
  93. data/app/forms/ar_site.yml +149 -0
  94. data/app/forms/ar_steps_template.yml +51 -0
  95. data/app/forms/ar_user.yml +140 -0
  96. data/app/forms/ar_user_role.yml +57 -0
  97. data/app/forms/help/dc_category_as_tree.en +4 -0
  98. data/app/forms/help/dc_category_as_tree.sl +5 -0
  99. data/app/forms/json_ld_schema.yml +168 -0
  100. data/app/helpers/agile_application_helper.rb +1162 -0
  101. data/app/helpers/agile_category_helper.rb +128 -0
  102. data/app/helpers/agile_common_helper.rb +308 -0
  103. data/app/helpers/agile_edit_helper.rb +645 -0
  104. data/app/helpers/agile_helper.rb +509 -0
  105. data/app/helpers/agile_index_helper.rb +677 -0
  106. data/app/helpers/ar_image_helper.rb +128 -0
  107. data/app/models/agile_form_fields/action.rb +61 -0
  108. data/app/models/agile_form_fields/agile_form_field.rb +322 -0
  109. data/app/models/agile_form_fields/belongs_to.rb +112 -0
  110. data/app/models/agile_form_fields/check_box.rb +73 -0
  111. data/app/models/agile_form_fields/comment.rb +62 -0
  112. data/app/models/agile_form_fields/date_picker.rb +104 -0
  113. data/app/models/agile_form_fields/date_select.rb +68 -0
  114. data/app/models/agile_form_fields/datetime_picker.rb +88 -0
  115. data/app/models/agile_form_fields/datetime_select.rb +73 -0
  116. data/app/models/agile_form_fields/file_field.rb +52 -0
  117. data/app/models/agile_form_fields/file_select.rb +69 -0
  118. data/app/models/agile_form_fields/hidden_field.rb +51 -0
  119. data/app/models/agile_form_fields/html_field.rb +69 -0
  120. data/app/models/agile_form_fields/journal_diff.rb +62 -0
  121. data/app/models/agile_form_fields/link_to.rb +69 -0
  122. data/app/models/agile_form_fields/method.rb +66 -0
  123. data/app/models/agile_form_fields/multitext_autocomplete.rb +215 -0
  124. data/app/models/agile_form_fields/number_field.rb +92 -0
  125. data/app/models/agile_form_fields/password_field.rb +63 -0
  126. data/app/models/agile_form_fields/radio_button.rb +95 -0
  127. data/app/models/agile_form_fields/readonly.rb +77 -0
  128. data/app/models/agile_form_fields/select.rb +281 -0
  129. data/app/models/agile_form_fields/submit_tag.rb +58 -0
  130. data/app/models/agile_form_fields/text_area.rb +61 -0
  131. data/app/models/agile_form_fields/text_autocomplete.rb +171 -0
  132. data/app/models/agile_form_fields/text_field.rb +55 -0
  133. data/app/models/agile_form_fields/text_with_select.rb +94 -0
  134. data/app/models/agile_form_fields/tree_select.rb +170 -0
  135. data/app/models/ar_big_table.rb +82 -0
  136. data/app/models/ar_big_table_value.rb +53 -0
  137. data/app/models/ar_category.rb +109 -0
  138. data/app/models/ar_design.rb +116 -0
  139. data/app/models/ar_filter.rb +200 -0
  140. data/app/models/ar_folder_permission.rb +50 -0
  141. data/app/models/ar_folder_rule.rb +47 -0
  142. data/app/models/ar_gallery.rb +53 -0
  143. data/app/models/ar_image.rb +198 -0
  144. data/app/models/ar_internals.rb +60 -0
  145. data/app/models/ar_journal.rb +46 -0
  146. data/app/models/ar_json_ld.rb +131 -0
  147. data/app/models/ar_key_value_store.rb +128 -0
  148. data/app/models/ar_link.rb +48 -0
  149. data/app/models/ar_memory.rb +172 -0
  150. data/app/models/ar_menu.rb +144 -0
  151. data/app/models/ar_menu_item.rb +106 -0
  152. data/app/models/ar_page.rb +74 -0
  153. data/app/models/ar_part.rb +66 -0
  154. data/app/models/ar_permission.rb +180 -0
  155. data/app/models/ar_permission_rule.rb +65 -0
  156. data/app/models/ar_policy.rb +78 -0
  157. data/app/models/ar_policy_rule.rb +65 -0
  158. data/app/models/ar_poll.rb +74 -0
  159. data/app/models/ar_poll_item.rb +47 -0
  160. data/app/models/ar_poll_result.rb +38 -0
  161. data/app/models/ar_removed_url.rb +42 -0
  162. data/app/models/ar_role.rb +84 -0
  163. data/app/models/ar_setup.rb +115 -0
  164. data/app/models/ar_site.rb +68 -0
  165. data/app/models/ar_temp.rb +150 -0
  166. data/app/models/ar_user.rb +72 -0
  167. data/app/models/ar_user_group.rb +38 -0
  168. data/app/models/ar_user_role.rb +54 -0
  169. data/app/models/ar_visit.rb +41 -0
  170. data/app/models/concerns/ar_page_concern.rb +128 -0
  171. data/app/models/concerns/ar_part_concern.rb +48 -0
  172. data/app/models/concerns/ar_piece_concern.rb +48 -0
  173. data/app/models/concerns/ar_policy_rule_concern.rb +87 -0
  174. data/app/models/concerns/ar_seo_concern.rb +66 -0
  175. data/app/models/concerns/ar_site_concern.rb +103 -0
  176. data/app/models/concerns/ar_user_concern.rb +195 -0
  177. data/app/renderers/agile_common_renderer.rb +93 -0
  178. data/app/renderers/agile_renderer.rb +59 -0
  179. data/app/renderers/ar_ad_renderer.rb +219 -0
  180. data/app/renderers/ar_captcha_renderer.rb +113 -0
  181. data/app/renderers/ar_common_renderer.rb +90 -0
  182. data/app/renderers/ar_gallery_renderer.rb +107 -0
  183. data/app/renderers/ar_menu_renderer.rb +195 -0
  184. data/app/renderers/ar_page_renderer.rb +147 -0
  185. data/app/renderers/ar_part_renderer.rb +235 -0
  186. data/app/renderers/ar_piece_renderer.rb +119 -0
  187. data/app/renderers/ar_poll_renderer.rb +272 -0
  188. data/app/views/agile/_edit_stuff.html.erb +57 -0
  189. data/app/views/agile/_form.html.erb +24 -0
  190. data/app/views/agile/_result.html.erb +28 -0
  191. data/app/views/agile/edit.html.erb +13 -0
  192. data/app/views/agile/error.html.erb +2 -0
  193. data/app/views/agile/index.html.erb +14 -0
  194. data/app/views/agile/login.html.erb +19 -0
  195. data/app/views/agile/new.html.erb +12 -0
  196. data/app/views/agile_common/_help.html.erb +18 -0
  197. data/app/views/agile_common/_iframe_edit.html.erb +2 -0
  198. data/app/views/agile_common/paste_clipboard.html.erb +17 -0
  199. data/app/views/layouts/agile.html.erb +17 -0
  200. data/app/views/layouts/content.html.erb +20 -0
  201. data/app/views/models/dump_models.html.erb +47 -0
  202. data/config/initializers/kaminari_patch.rb +56 -0
  203. data/config/locales/agile_de.yml +138 -0
  204. data/config/locales/agile_en.yml +162 -0
  205. data/config/locales/agile_sl.yml +163 -0
  206. data/config/locales/datetimepicker.yml +19 -0
  207. data/config/locales/de.yml +231 -0
  208. data/config/locales/en.yml +13 -0
  209. data/config/locales/kaminari.yml +26 -0
  210. data/config/locales/models_en.yml +1032 -0
  211. data/config/locales/models_sl.yml +1065 -0
  212. data/config/locales/sl.yml +211 -0
  213. data/db/migrate/20240120160001_add_sessions_table.rb +12 -0
  214. data/db/migrate/20240120160002_ar_big_table.rb +17 -0
  215. data/db/migrate/20240120160003_ar_big_table_value.rb +18 -0
  216. data/db/migrate/20240120160004_ar_category.rb +22 -0
  217. data/db/migrate/20240120160005_ar_design.rb +20 -0
  218. data/db/migrate/20240120160006_ar_filter.rb +17 -0
  219. data/db/migrate/20240120160007_ar_gallery.rb +21 -0
  220. data/db/migrate/20240120160008_ar_journal.rb +19 -0
  221. data/db/migrate/20240120160009_ar_key_value_store.rb +11 -0
  222. data/db/migrate/20240120160010_ar_link.rb +19 -0
  223. data/db/migrate/20240120160011_ar_memory.rb +8 -0
  224. data/db/migrate/20240120160012_ar_menu.rb +21 -0
  225. data/db/migrate/20240120160013_ar_menu_item.rb +28 -0
  226. data/db/migrate/20240120160014_ar_page.rb +50 -0
  227. data/db/migrate/20240120160015_ar_part.rb +33 -0
  228. data/db/migrate/20240120160016_ar_permission.rb +16 -0
  229. data/db/migrate/20240120160017_ar_permission_rule.rb +17 -0
  230. data/db/migrate/20240120160018_ar_piece.rb +28 -0
  231. data/db/migrate/20240120160019_ar_policy.rb +21 -0
  232. data/db/migrate/20240120160020_ar_policy_rule.rb +18 -0
  233. data/db/migrate/20240120160021_ar_poll.rb +27 -0
  234. data/db/migrate/20240120160022_ar_poll_item.rb +23 -0
  235. data/db/migrate/20240120160023_ar_poll_result.rb +14 -0
  236. data/db/migrate/20240120160024_ar_removed_url.rb +16 -0
  237. data/db/migrate/20240120160025_ar_role.rb +17 -0
  238. data/db/migrate/20240120160026_ar_site.rb +37 -0
  239. data/db/migrate/20240120160027_ar_temp.rb +11 -0
  240. data/db/migrate/20240120160028_ar_user.rb +42 -0
  241. data/db/migrate/20240120160029_ar_user_group.rb +12 -0
  242. data/db/migrate/20240120160030_ar_user_role.rb +18 -0
  243. data/db/migrate/20240120160031_ar_visit.rb +15 -0
  244. data/db/migrate/20240703016001_ar_setup.rb +16 -0
  245. data/db/migrate/20240703016002_ar_folder_permission.rb +15 -0
  246. data/db/migrate/20240703016003_ar_folder_rule.rb +14 -0
  247. data/db/migrate/20250115000001_ar_image.rb +25 -0
  248. data/lib/agile/configuration.rb +43 -0
  249. data/lib/agile/engine.rb +29 -0
  250. data/lib/agile/version.rb +27 -0
  251. data/lib/agile.rb +282 -0
  252. data/lib/agile_rails.rb +1 -0
  253. data/lib/generators/agile/USAGE +11 -0
  254. data/lib/generators/agile/new_form_generator.rb +369 -0
  255. data/lib/generators/convert_to_ar/convert_to_ar_generator.rb +158 -0
  256. data/lib/tasks/agile_db_clone.rake +132 -0
  257. data/lib/tasks/agile_db_export_to_yaml.rake +37 -0
  258. data/lib/tasks/agile_db_migrate.rake +35 -0
  259. metadata +414 -0
@@ -0,0 +1,977 @@
1
+ #--
2
+ # Copyright (c) 2024+ Damjan Rems
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #+
23
+
24
+ ########################################################################
25
+ # This is main controller for processing actions by AgileRails forms. It provides
26
+ # CRUD actions for editing database records. AgileRails does not require controller
27
+ # to be made for every table model but implements all actions in single
28
+ # controller. Logic required to control data entry is provided within AgileRails
29
+ # forms which are loaded dynamically for every action.
30
+ #
31
+ # Data entry validations must therefore reside in document models definitions or can be implemented in forms.
32
+ # There are always validations that cannot be done in models. Like validations
33
+ # which include url parameters or accessing session variables. This is hard to be done
34
+ # in model therefore AgileRails controls had to be invented. AgileRails controls are
35
+ # modules with methods that are injected into agile controller and act in runtime like
36
+ # they are part of Agile controller.
37
+ #
38
+ # Since Ruby and Rails provide some "automagic" loading of modules AgileRails controls must be saved
39
+ # into app/controls folder. Every model can have its own controls file.
40
+ # ar_page model's controls live in ar_page_controls.rb file. By convention module names
41
+ # are declared in camel case, so our ar_page_controls.rb declares ArPageControls module.
42
+ #
43
+ # Controls (among other) may contain 8 callback methods.
44
+ # These methods are:
45
+ # - before_new
46
+ # - new_record
47
+ # - dup_record
48
+ # - before_edit
49
+ # - before_save
50
+ # - after_save
51
+ # - before_delete
52
+ # - after_delete
53
+ #
54
+ # Methods before_new, before_edit, before_save or before_delete may also effect flow of the application. If
55
+ # method return false (not nil but FalseClass) normal flow of the program is interrupted and last operation
56
+ # is canceled.
57
+ #
58
+ # Second control methods that can be declared in AgileRails controls are filters for
59
+ # viewing and sorting documents. It is often required that dynamic filters are
60
+ # applied to data_set documents.
61
+ #
62
+ # data_set:
63
+ # filter: current_users_documents
64
+ #
65
+ # Example implemented controls method:
66
+ #
67
+ # def current_users_documents
68
+ # if agile_user_can(ArPermission::CAN_READ)
69
+ # ArPage.where(created_by: session[:user_id])
70
+ # else
71
+ # flash[:error] = 'User can not perform this operation!'
72
+ # false
73
+ # end
74
+ # end
75
+ #
76
+ # If filter method returns false user will be presented with flash error.
77
+ ########################################################################
78
+ class AgileController < AgileApplicationController
79
+ before_action :authorization_check, :except => [:login, :logout, :test, :run]
80
+ protect_from_forgery with: :null_session, only: Proc.new { _1.request.format.json? }
81
+
82
+ layout 'agile'
83
+
84
+ ########################################################################
85
+ # Index action
86
+ ########################################################################
87
+ def index
88
+ @form['index']['data_set'] ||= {}
89
+ redirected = (@form['table'] == 'ar_memory' ? process_in_memory : process_data_set)
90
+ return if redirected
91
+
92
+ callback_method_call(@form.dig('index', 'data_set', 'footer') || 'update_footer')
93
+ respond_to do |format|
94
+ format.html { render action: :index }
95
+ format.js { render partial: :result }
96
+ end
97
+ end
98
+
99
+ ########################################################################
100
+ # Filter action.
101
+ ########################################################################
102
+ def _filter
103
+ index
104
+ end
105
+
106
+ ########################################################################
107
+ # Show displays record in readonly mode.
108
+ ########################################################################
109
+ def show
110
+ find_record
111
+ # before_show callback
112
+ if (m = callback_method('before_show') )
113
+ ret = callback_method_call(m)
114
+ if ret.class == FalseClass
115
+ @form['readonly'] = nil # must be
116
+ return index
117
+ end
118
+ end
119
+
120
+ render action: 'edit'
121
+ end
122
+
123
+ ########################################################################
124
+ # Login action. Used to login direct to CMS. It is mostly used when first time
125
+ # creating site and when something goes so wrong, that common login procedure
126
+ # is not available.
127
+ #
128
+ # Login can be called directly with url http://site.com/agile/login
129
+ ########################################################################
130
+ def login
131
+ return set_development_site if params[:id] == 'test'
132
+
133
+ session[:edit_mode] = 0 unless params[:ok]
134
+ render action: 'login'
135
+ end
136
+
137
+ ########################################################################
138
+ # Logout action. Used to logout direct from CMS.
139
+ #
140
+ # Logout can be called directly with url http://site.com/agile/logout
141
+ ########################################################################
142
+ def logout
143
+ session[:edit_mode] = 0
144
+ session[:user_id] = nil
145
+ session[:user_roles] = nil
146
+ render action: 'login'
147
+ end
148
+
149
+ ########################################################################
150
+ # Shortcut for setting currently selected site in development. Will search
151
+ # for ar_site document with site name 'development' and set alias_for to site
152
+ # url parameter.
153
+ ########################################################################
154
+ def set_development_site
155
+ # only in development
156
+ return agile_render_404 unless Rails.env.development?
157
+
158
+ alias_site = ArSite.find_by(name: params[:site])
159
+ return agile_render_404 unless alias_site
160
+
161
+ # update alias for
162
+ site = ArSite.find_by(name: 'development')
163
+ site.alias_for = params[:site]
164
+ site.save
165
+ redirect_to '/'
166
+ end
167
+
168
+ ########################################################################
169
+ # New action.
170
+ ########################################################################
171
+ def new
172
+ flash[:error] = flash[:warning] = flash[:info] = nil
173
+ # not authorized
174
+ unless agile_user_can(ArPermission::CAN_CREATE)
175
+ flash[:error] = t('agile.not_authorized')
176
+ return index
177
+ end
178
+ create_new_empty_record()
179
+
180
+ if (m = callback_method('before_new') )
181
+ ret = callback_method_call(m)
182
+ return index if ret.class == FalseClass
183
+ end
184
+ load_initial_values()
185
+
186
+ # new_record callback. Set default values for new record
187
+ if (m = callback_method('new_record') ) then callback_method_call(m) end
188
+ @form_params['action'] = 'create'
189
+ end
190
+
191
+ ########################################################################
192
+ # Will duplicate source document into new record. This method is used for
193
+ # duplicating record and is subroutine of create action.
194
+ ########################################################################
195
+ def duplicate_record(source)
196
+ duplicates = params['dup_fields'].split(',').map(&:strip)
197
+ dest = {}
198
+ source.attribute_names.each do |attribute_name|
199
+ next if attribute_name == 'id' # don't duplicate _id
200
+
201
+ dest[attribute_name] = source[attribute_name]
202
+ # if duplicate, string dup is added. For unique fields
203
+ dest[attribute_name] += ' dup' if duplicates.include?(attribute_name)
204
+ end
205
+ dest['created_at'] = Time.now if dest['created_at']
206
+ dest['updated_at'] = Time.now if dest['updated_at']
207
+ dest
208
+ end
209
+
210
+ ########################################################################
211
+ # Create (or duplicate) action.
212
+ ########################################################################
213
+ def create
214
+ # not authorized
215
+ unless agile_user_can(ArPermission::CAN_CREATE)
216
+ flash[:error] = t('agile.not_authorized')
217
+ return index
218
+ end
219
+
220
+ # create document
221
+ if params['id'].nil?
222
+ # Prevent double form submit
223
+ return index if double_form_submit?
224
+
225
+ create_new_empty_record
226
+ if save_data
227
+ flash[:info] = t('agile.record_saved')
228
+ params[:return_to] = 'index' if params[:commit] == t('agile.save&back') # save & back
229
+ return process_return_to(params[:return_to]) if params[:return_to]
230
+
231
+ @form_params['id'] = @record.id # must be set, for proper update link
232
+ params[:id] = @record.id # must be set, for find_record
233
+ edit
234
+ else # error
235
+ return process_return_to(params[:return_to]) if params[:return_to]
236
+
237
+ render action: :new
238
+ end
239
+ else # duplicate record
240
+ find_record
241
+ new_record = duplicate_record(@record)
242
+ create_new_empty_record(new_record)
243
+ if (m = callback_method('dup_record')) then callback_method_call(m) end
244
+ update_standards
245
+ @record.save!
246
+ index
247
+ end
248
+ end
249
+
250
+ ########################################################################
251
+ # Edit action.
252
+ ########################################################################
253
+ def edit
254
+ find_record
255
+ if (m = callback_method('before_edit') )
256
+ ret = callback_method_call(m)
257
+ # don't do anything if return is false
258
+ return index if ret.class == FalseClass
259
+ end
260
+ @form_params['action'] = 'update'
261
+ render action: :edit
262
+ end
263
+
264
+ ########################################################################
265
+ # Update action.
266
+ ########################################################################
267
+ def update
268
+ find_record
269
+ # check if record was not updated in mean time
270
+ if @record.respond_to?(:updated_at)
271
+ if params[:last_updated_at].to_i != @record.updated_at.to_i
272
+ flash[:error] = t('agile.updated_by_other')
273
+ return render(action: :edit)
274
+ end
275
+ end
276
+
277
+ if agile_user_can(ArPermission::CAN_EDIT_ALL) ||
278
+ (@record.respond_to?('created_by') && @record.created_by == session[:user_id] && agile_user_can(ArPermission::CAN_EDIT))
279
+
280
+ if save_data
281
+ params[:return_to] = 'index' if params[:commit] == t('agile.save&back') # save & back
282
+ @form_params['action'] = 'update'
283
+ # Process return_to
284
+ return process_return_to(params[:return_to]) if params[:return_to]
285
+ else
286
+ # do not forget before_edit callback
287
+ if m = callback_method('before_edit') then callback_method_call(m) end
288
+ return render action: :edit
289
+ end
290
+ else
291
+ flash[:error] = t('agile.not_authorized')
292
+ end
293
+ edit
294
+ end
295
+
296
+ ########################################################################
297
+ # Destroy action. Used also for enabling and disabling record.
298
+ ########################################################################
299
+ def destroy
300
+ find_record
301
+ # check permission required to delete
302
+ permission = if params['operation'].nil?
303
+ if @record.respond_to?('created_by') # needs can_delete_all if created_by is present and not owner
304
+ (@record.created_by == session[:user_id]) ? ArPermission::CAN_DELETE : ArPermission::CAN_DELETE_ALL
305
+ else
306
+ ArPermission::CAN_DELETE # by default
307
+ end
308
+ else # enable or disable record
309
+ if @record.respond_to?('created_by')
310
+ (@record.created_by == session[:user_id]) ? ArPermission::CAN_EDIT : ArPermission::CAN_EDIT_ALL
311
+ else
312
+ ArPermission::CAN_EDIT # by default
313
+ end
314
+ end
315
+ ok2delete = agile_user_can(permission)
316
+
317
+ case
318
+ # not authorized
319
+ when !ok2delete then
320
+ flash[:error] = t('agile.not_authorized')
321
+ return index
322
+
323
+ # delete document
324
+ when params['operation'].nil? then
325
+ # before_delete callback
326
+ if (m = callback_method('before_delete') )
327
+ ret = callback_method_call(m)
328
+ # don't do anything if return is false
329
+ return index if ret.class == FalseClass
330
+ end
331
+
332
+ # take care of transaction
333
+ begin
334
+ transaction_begin()
335
+ if @record.destroy
336
+ save_journal(:delete)
337
+ flash[:info] = t('agile.record_deleted')
338
+ # after_delete callback
339
+ if (m = callback_method('after_delete') )
340
+ callback_method_call(m)
341
+ elsif params['after-delete'].to_s.match('return_to')
342
+ params[:return_to] = params['after-delete']
343
+ end
344
+ # Process return_to link
345
+ if params[:return_to]
346
+ transaction_end()
347
+ return process_return_to(params[:return_to])
348
+ end
349
+ else
350
+ flash[:error] = agile_error_messages_for(@record)
351
+ transaction_abort('')
352
+ end
353
+ rescue Exception => e
354
+ transaction_abort()
355
+ transaction_end()
356
+ logger.error(%(#{e.message}\n\n#{e.backtrace.join("\n")}))
357
+ return if Rails.env.test? # or test will fail
358
+
359
+ raise
360
+ end
361
+ # end transaction normaly
362
+ transaction_end()
363
+ return index
364
+
365
+ # deactivate document
366
+ when params['operation'] == 'disable' then
367
+ if @record.respond_to?('active')
368
+ @record.active = false
369
+ save_journal(:update, @record.changes)
370
+ update_standards()
371
+ @record.save
372
+ flash[:info] = t('agile.record_disabled')
373
+ end
374
+
375
+ # reactivate document
376
+ when params['operation'] == 'enable' then
377
+ if @record.respond_to?('active')
378
+ @record.active = true
379
+ update_standards()
380
+ save_journal(:update, @record.changes)
381
+ @record.save
382
+ flash[:info] = t('agile.record_enabled')
383
+ end
384
+
385
+ #TODO reorder documents
386
+ when params['operation'] == 'reorder' then
387
+
388
+ end
389
+
390
+ @form_params['action'] = 'update'
391
+ render action: :edit
392
+ end
393
+
394
+ ########################################################################
395
+ # Run action
396
+ ########################################################################
397
+ def run
398
+ # determine control file name and method
399
+ control_name, method_name = params[:control].split('.')
400
+ if method_name.nil?
401
+ method_name = control_name
402
+ control_name = AgileHelper.table_param(params)
403
+ end
404
+ # extend with control methods
405
+ extend_with_control_module(control_name)
406
+ if respond_to?(method_name)
407
+ # can it be called
408
+ return return_run_error t('agile.not_authorized') unless can_process_run
409
+ # call method
410
+ respond_to do |format|
411
+ format.json { send method_name }
412
+ format.html { send method_name }
413
+ end
414
+ else # Error message
415
+ return_run_error "Method #{method_name} not defined in #{control_name}_control"
416
+ end
417
+ end
418
+
419
+ protected
420
+
421
+ ########################################################################
422
+ # Respond with error on run action
423
+ ########################################################################
424
+ def return_run_error(text)
425
+ respond_to do |format|
426
+ format.json { render json: { msg_error: text } }
427
+ format.html { render plain: text }
428
+ end
429
+ end
430
+
431
+ ########################################################################
432
+ # Can run call be processed
433
+ ########################################################################
434
+ def can_process_run
435
+ if respond_to?( :can_process?)
436
+ response = send( :can_process?)
437
+ return response unless response.class == Array
438
+ else
439
+ response = [ArPermission::CAN_VIEW, AgileHelper.table_param(params) || 'ar_memory']
440
+ end
441
+ agile_user_can *response
442
+ end
443
+
444
+ ########################################################################
445
+ # Checks if user has permissions to perform operation on table and if not
446
+ # prepares response for not authorized message.
447
+ #
448
+ # @param [Integer] permission : Permission level defined in ArPermission constants eg. ArPermission::CAN_EDIT
449
+ # @param [String] collection_name : Table name on which user must have permission
450
+ #
451
+ # @return [Boolean] true when user has required permission otherwise false
452
+ ########################################################################
453
+ def user_has_permission?(permission, collection_name)
454
+ unless agile_user_can(permission, collection_name.to_s)
455
+ respond_to do |format|
456
+ format.json { render json: { msg_error: t('agile.not_authorized') } }
457
+ format.html { render plain: t('agile.not_authorized') }
458
+ end
459
+ return true
460
+ end
461
+ true
462
+ end
463
+
464
+ ############################################################################
465
+ # Load module if available. Try not to mask errors in control module
466
+ ############################################################################
467
+ def controls_module_load(controls_string)
468
+ begin
469
+ controls_string.classify.constantize
470
+ rescue NameError => e
471
+ return if e.message.match('uninitialized constant') || e.message.match('wrong constant name')
472
+ # report errors when loading existing module
473
+ raise e
474
+ end
475
+ end
476
+
477
+ ############################################################################
478
+ # Dynamically extend ra controller class with methods defined in controls module.
479
+ ############################################################################
480
+ def extend_with_control_module(control_name = @form['controls'] || @form['control'])
481
+ # May include embedded forms so ; => _
482
+ control_name ||= AgileHelper.table_param(params).gsub(';', '_')
483
+ control_name += '_control' unless control_name.match(/control$|report$/i)
484
+
485
+ controls = controls_module_load(control_name)
486
+ if controls
487
+ # extend first with agile_report when report
488
+ if control_name.match(/report$/i)
489
+ extend AgileReport
490
+ report_init(control_name)
491
+ end
492
+ extend controls
493
+ # Form may be dynamically updated before processed
494
+ send(:update_form) if respond_to?(:update_form)
495
+ end
496
+ end
497
+
498
+ ############################################################################
499
+ # Check if user is authorized for the action. If authorization is in order it will also
500
+ # load AgileRails form.
501
+ ############################################################################
502
+ def authorization_check
503
+ params[:table] ||= params[:t] || AgileHelper.form_param(params)
504
+ # Only show menu
505
+ return login if params[:id].in?(%w[login logout test])
506
+
507
+ table = params[:table].to_s.strip.downcase
508
+ set_default_guest_user_role if session[:user_roles].nil?
509
+ # request shouldn't pass
510
+ if table != 'ar_memory' &&
511
+ (table.size < 3 || !agile_user_can(ArPermission::CAN_VIEW))
512
+ return render(action: 'error', locals: { error: t('agile.not_authorized')} )
513
+ end
514
+ agile_form_read
515
+
516
+ # Permissions can be also defined on form
517
+ #TODO So far only can_view is used. Think about if using other permissions has sense
518
+ can_view = @form.dig('permissions', 'can_view')
519
+ if can_view.nil? || can_view.split(',').find{ agile_user_has_role?(_1) }
520
+ extend_with_control_module
521
+ else
522
+ render(action: 'error', locals: { error: t('agile.not_authorized')} )
523
+ end
524
+ end
525
+
526
+ ########################################################################
527
+ # Find current record for edit, update or delete.
528
+ ########################################################################
529
+ def find_record #:nodoc:
530
+ @record = @tables.last[0].find(params[:id])
531
+ end
532
+
533
+ ########################################################################
534
+ # Creates new empty record for new and create action.
535
+ ########################################################################
536
+ def create_new_empty_record(initial_data = nil) #:nodoc:
537
+ if @tables.size == 1
538
+ @record = @tables.first[0].new(initial_data)
539
+ else
540
+ rec = @tables.first[0].find(@ids.first) # top most record
541
+ 1.upto(@tables.size - 2) { |i| rec = rec.send(@tables[i][1].pluralize).find(@ids[i]) } # find embedded children by ids
542
+ @record = @tables.last[0].new(initial_data) # new record
543
+ end
544
+ end
545
+
546
+ ########################################################################
547
+ # Update standard fields like updated_by, created_by, site_id
548
+ ########################################################################
549
+ def update_standards(record = @record)
550
+ record.updated_by = session[:user_id] if record.respond_to?('updated_by')
551
+ if record.new_record?
552
+ record.created_by = session[:user_id] if record.respond_to?('created_by')
553
+ # set this only initially. Allow to be set to nil on updates. Record can then belong to all sites
554
+ # and will be directly visible only to admins
555
+ record.ar_site_id = agile_get_site.id if record.respond_to?('ar_site_id') && record.ar_site_id.nil?
556
+ end
557
+ record.send(:set_history, self) if record.respond_to?(:set_history)
558
+ end
559
+
560
+ ########################################################################
561
+ # Save record's changes to journal table. Saves all parameters to retrieve record if needed.
562
+ #
563
+ # [Parameters:]
564
+ # [operation] 'delete' or 'update'.
565
+ # [changes] Current record's changed fields.
566
+ ########################################################################
567
+ def save_journal(operation, changes = {})
568
+ if operation == :delete
569
+ @record.attributes.each { |k, v| changes[k] = v }
570
+ end
571
+ changes.except!('created_at', 'updated_at', 'created_by', 'updated_by')
572
+
573
+ if (operation != :update) || changes.size > 0
574
+ # determine site_id
575
+ site_id = @record.site_id if @record.respond_to?('site_id')
576
+ site_id = site_id || agile_get_site&.id
577
+ ArJournal.create(site_id: site_id,
578
+ operation: operation,
579
+ user_id: session[:user_id],
580
+ tables: @form['table'],
581
+ ids: params[:ids],
582
+ record_id: params[:id],
583
+ ip: request.remote_ip,
584
+ time: Time.now,
585
+ diff: changes.to_json)
586
+ end
587
+ end
588
+
589
+ ########################################################################
590
+ # Determines if callback method is defined in parameters or in control module.
591
+ # Returns callback method name or nil if not defined.
592
+ ########################################################################
593
+ def callback_method(key) #:nodoc:
594
+ data_key = key.gsub('_', '-') # convert _ to -
595
+ callback = case
596
+ when params['data'] && params['data'][data_key] then params['data'][data_key]
597
+ # method is present then call it automatically
598
+ when @form.dig('form', key) then @form['form'][key]
599
+ when respond_to?(key) then key
600
+ when params[data_key] then params[data_key]
601
+ else nil
602
+ end
603
+
604
+ ret = case
605
+ when callback.nil? then callback # otherwise there will be errors in next lines
606
+ when callback.match('eval ') then callback.sub('eval ', '')
607
+ when callback.match('return_to ')
608
+ params[:return_to] = callback.sub('return_to ', '')
609
+ return nil
610
+ else callback
611
+ end
612
+ ret
613
+ end
614
+
615
+ ########################################################################
616
+ # Calls callback method.
617
+ ########################################################################
618
+ def callback_method_call(m) #:nodoc:
619
+ send(m) if respond_to?(m)
620
+ end
621
+
622
+ ########################################################################
623
+ # Same as javascript_tag helper. Ajax form actions may results in javascript code to be returned.
624
+ # This will add javascript tag to code.
625
+ ########################################################################
626
+ def js_tag(script) #:nodoc:
627
+ "<script>#{script}</script>"
628
+ end
629
+
630
+ ########################################################################
631
+ # Process return_to parameter when defined on form or set by controls methods.
632
+ # params['return_to'] may contain 'index', 'reload' or 'parent.reload' or any valid url to
633
+ # return to, after successful controls method call.
634
+ ########################################################################
635
+ def process_return_to(return_to)
636
+ script = case
637
+ when return_to == 'index' then return index
638
+ when return_to.match(/eval=/i) then return_to.sub('eval=', '')
639
+ when return_to.match(/parent\.reload/i) then 'parent.location.href=parent.location.href;'
640
+ when return_to.match(/reload/i) then 'location.href=location.href;'
641
+ when return_to.match(/window\.close/i) then 'window.close();'
642
+ when return_to.match(/none/i) then return
643
+ else "location.href='#{return_to}'"
644
+ end
645
+ render html: js_tag(script).html_safe, layout: false
646
+ end
647
+
648
+ ########################################################################
649
+ # Since tabs have been introduced on form it is a little more complicated
650
+ # to collect all edit fields on form. This method does it. Subroutine of save_data.
651
+ ########################################################################
652
+ def fields_on_form #:nodoc:
653
+ form_fields = []
654
+ if @form['form']['fields']
655
+ # read only field elements (key is Integer)
656
+ @form['form']['fields'].each { |key, options| form_fields << options if key.class == Integer }
657
+ else
658
+ @form['form']['tabs'].keys.each do |tab|
659
+ @form['form']['tabs'][tab].each { |key, options| form_fields << options if key.class == Integer }
660
+ end
661
+ end
662
+ form_fields
663
+ end
664
+
665
+ ########################################################################
666
+ # Save edited data. Take care that only fields defined on form are affected.
667
+ # It also saves journal data and calls before_save and after_save callbacks.
668
+ ########################################################################
669
+ def save_data
670
+ form_fields = fields_on_form()
671
+ return true if form_fields.size == 0
672
+
673
+ form_fields.each do |v|
674
+ session[:form_processing] = v['name'] # for debuging
675
+ next if v['type'].nil? or v['name'].nil? or
676
+ v['type'].match('belongs_to') or # don't wipe embedded types
677
+ (params[:edit_only] and params[:edit_only] != v['name']) or # otherwise other fields would be wiped
678
+ v['readonly'] or # fields with readonly option don't return value and would be wiped
679
+ !@record.respond_to?(v['name']) # there are temporary fields on the form
680
+ # return value from form field definition
681
+ value = AgileFormFields.const_get(v['type'].camelize).get_data(params, v['name'])
682
+ @record.send("#{v['name']}=", value)
683
+ end
684
+
685
+ # before_save callback
686
+ if (m = callback_method('before_save') )
687
+ ret = callback_method_call(m)
688
+ # don't save if callback returns false
689
+ return false if ret.class == FalseClass
690
+ end
691
+
692
+ # save data. Take care for active transaction and end it properly
693
+ begin
694
+ changes = @record.changes
695
+ transaction_begin()
696
+ update_standards() if changes.size > 0 # update only if there has been some changes
697
+ operation = @record.new_record? ? :new : :update
698
+ if (saved = @record.save)
699
+ save_journal(operation, @record.previous_changes)
700
+ # after_save callback
701
+ if (m = callback_method('after_save') ) then callback_method_call(m) end
702
+ else
703
+ transaction_abort()
704
+ end
705
+ rescue Exception => e
706
+ transaction_abort()
707
+ transaction_end()
708
+ logger.error(%(\n#{e.message}\n\n#{e.backtrace.join("\n")}))
709
+ return if Rails.env.test?
710
+
711
+ raise
712
+ end
713
+ transaction_end()
714
+ saved
715
+ end
716
+
717
+ ########################################################################
718
+ # Begins database transaction if requested
719
+ ########################################################################
720
+ def transaction_begin
721
+ return unless %w[1 yes true].include?(@form['transaction'].to_s.downcase)
722
+
723
+ manager = ActiveRecord::Base.connection.transaction_manager
724
+ manager.begin_transaction
725
+ @transaction = 'OK'
726
+ end
727
+
728
+ ########################################################################
729
+ # Aborts database transaction. It will rollback in transaction_end
730
+ ########################################################################
731
+ def transaction_abort(message = 'Transaction aborted')
732
+ return if @transaction.nil?
733
+
734
+ @transaction = @transaction == 'OK' ? message : "#{@transaction}; #{message}"
735
+ end
736
+
737
+ ########################################################################
738
+ # Commits or rollbacks the transaction. Operation depend on the value of
739
+ # @transaction variable. If value is OK, then transaction is commited, otherwise
740
+ # transaction is aborted with the message added
741
+ ########################################################################
742
+ def transaction_end
743
+ return if @transaction.nil?
744
+
745
+ manager = ActiveRecord::Base.connection.transaction_manager
746
+ if @transaction == 'OK'
747
+ manager.commit_transaction
748
+ else
749
+ manager.rollback_transaction
750
+ flash[:error] = (flash[:error].present? ? flash[:error].to_s + "<br>" : '') + @transaction
751
+ flash[:info] = nil # must be
752
+ end
753
+ end
754
+ ########################################################################
755
+ # Will return comma separated data (field names) as array of symbols. For usage
756
+ # in select_fields and deny_fields
757
+ ########################################################################
758
+ def separated_to_symbols(data)
759
+ data.chomp.split(',').map { _1.strip.downcase.to_sym }
760
+ end
761
+
762
+ ########################################################################
763
+ # Will process data_set['select'] option
764
+ # @TODO drops No route matches error, for no apparent reason
765
+ ########################################################################
766
+ def process_select_options(model) #:nodoc:
767
+ data_set = @form['index']['data_set']
768
+ return model unless data_set['select']
769
+
770
+ model.select(*data_set['select'].chomp.split(',').map(&:to_sym))
771
+ end
772
+
773
+ ########################################################################
774
+ # Will check and set sorting options for current dataset. Subroutine of index method.
775
+ ########################################################################
776
+ def check_sort_options #:nodoc:
777
+ table_name = @tables.first[1]
778
+ sort_data = session.dig(:filters, table_name, :sort)
779
+ return unless sort_data && @records.kind_of?(ActiveRecord::Relation)
780
+
781
+ sort, direction = sort_data.split(' ')
782
+ @records = @records.order(sort => direction.to_sym)
783
+ end
784
+
785
+ ########################################################################
786
+ # Return currently defined filter on a table.
787
+ #
788
+ # Use in custom form filter: agile_filter_options(ArPage).and(ar_site_id: params[:site_id])
789
+ ########################################################################
790
+ def agile_filter_options(model)
791
+ table_name = @tables.first[1]
792
+ filter_data = session.dig(:filters, table_name)
793
+ ArFilter.get_filter(filter_data) || model.all
794
+ end
795
+
796
+ ########################################################################
797
+ # Return current sort options for model (table)
798
+ ########################################################################
799
+ def agile_sort_options(model) #:nodoc:
800
+ table_name = model.to_s.underscore
801
+ sort_data = session.dig(:filters, table_name, :sort)
802
+ return if sort_data.nil?
803
+
804
+ field, direction = sort_data.split(' ')
805
+ { field.to_sym => direction.to_sym }
806
+ end
807
+
808
+ ########################################################################
809
+ # Will check and set current filter options for data_set. Subroutine of index method.
810
+ ########################################################################
811
+ def check_filter_options #:nodoc:
812
+ table_name = AgileHelper.table_param(params).strip.split(';').first.underscore
813
+ model = table_name.classify.constantize
814
+ save_filter_value(params[:page], table_name, :page) if params[:page]
815
+ # if data model has field ar_site_id ensure that only records which belong to the site are selected.
816
+ site_id = agile_get_site.id if agile_get_site
817
+
818
+ # don't filter site if no ar_site_id field or user is ADMIN
819
+ site_id = nil if !model.method_defined?('ar_site_id') || agile_user_can(ArPermission::CAN_ADMIN)
820
+ site_id = nil if session.dig(:filters, table_name, 'filter').to_s.match('ar_site_id')
821
+
822
+ if @records = ArFilter.get_filter(session.dig(:filters, table_name))
823
+ @records = @records.where(ar_site_id: site_id) if site_id
824
+ else
825
+ @records = site_id ? model.where(ar_site_id: site_id) : model.all
826
+ end
827
+ # pagination if required
828
+ per_page = (@form['index']['data_set']['per_page'] || 25).to_i
829
+ current_page = session.dig(:filters, table_name, :page) || 1
830
+ @records = @records.page(current_page).per(per_page) if per_page > 0
831
+ end
832
+
833
+ ######################################################################
834
+ # Save specified filter value to session
835
+ ######################################################################
836
+ def save_filter_value(value, table_name, *parms)
837
+ session[:filters] ||= {}
838
+ session[:filters][table_name] ||= {}
839
+ case parms.size
840
+ when 0 then session[:filters][table_name] = value
841
+ when 1 then session[:filters][table_name][parms[0]] = value
842
+ when 2 then
843
+ session[:filters][table_name][parms[0]] ||= {}
844
+ session[:filters][table_name][parms[0]][parms[1]] = value
845
+ end
846
+ session[:filters][table_name][:updated] = Time.now
847
+ end
848
+
849
+ ######################################################################
850
+ # Save specified filter value to session
851
+ ######################################################################
852
+ def read_filter_value(table_name, *parms)
853
+ case parms.size
854
+ when 0 then session.dig(:filters, table_name)
855
+ when 1 then session.dig(:filters, table_name, parms[0])
856
+ when 2 then session.dig(:filters, table_name, parms[0], parms[1])
857
+ end
858
+ end
859
+
860
+ ########################################################################
861
+ # Select data from table for index action
862
+ ########################################################################
863
+ def process_data_set #:nodoc
864
+ # If data_set is not defined on form, then it will fail. :return_to should know where to go
865
+ data_set = @form['index']['data_set']
866
+ if data_set.nil?
867
+ process_return_to(params[:return_to] || 'reload')
868
+ return true
869
+ end
870
+ # when data_set is evaluated as Rails helper
871
+ data_set['type'] ||= 'default'
872
+ return unless data_set['type'] == 'default'
873
+
874
+ # for now enable only filtering of top level records
875
+ if @tables.size == 1
876
+ check_filter_options()
877
+ check_sort_options()
878
+ end
879
+ # dataset is defined by filter method in control object
880
+ form_filter = data_set['filter'] || 'default_filter'
881
+ if respond_to?(form_filter)
882
+ @records = send(form_filter)
883
+ # something went wrong. flash[:error] should have explanation.
884
+ if @records.class == FalseClass
885
+ @records = []
886
+ render(action: :index)
887
+ return true
888
+ end
889
+ # process_select_options
890
+ # pagination but only if not already set
891
+ unless (@form['table'] == 'ar_memory') #TODO || @records.options[:limit])
892
+ per_page = (data_set['per_page'] || 25).to_i
893
+ @records = @records.page(params[:page]).per(per_page) if per_page > 0
894
+ end
895
+ elsif form_filter != 'default_filter'
896
+ Rails.logger.error "Error: data_set:filter: #{data_set['filter']} not found in controls!"
897
+ end
898
+ false
899
+ end
900
+
901
+ ########################################################################
902
+ # Process index action for in memory data. default_filter method must fill @records array
903
+ # with data, that will be displayed in the browser object.
904
+ ########################################################################
905
+ def process_in_memory #:nodoc
906
+ @records = []
907
+ # dataset is defined by filter method in control object
908
+ if (method = @form['index']['data_set']['filter'] || default_filter)
909
+ send(method) if respond_to?(method)
910
+ end
911
+ # ensure that record has id field
912
+ if @records.size > 0
913
+ raise "Exception: id field must be set in ar_memory record!" unless @records.first.id
914
+ end
915
+ false
916
+ end
917
+
918
+ ########################################################################
919
+ # Prevent double form submit
920
+ #
921
+ # Program will save form_time_stamp to session. If form is saved with
922
+ # the same form_time_stamp, program will block create action.
923
+ ########################################################################
924
+ def double_form_submit?
925
+ form_name = AgileHelper.form_param(params) || AgileHelper.table_param(params)
926
+ session[:dfs] ||= {}
927
+ params[:form_time_stamp] = params[:form_time_stamp].to_i
928
+ if params[:form_time_stamp] <= update_dfs_time(form_name) && !Rails.env.test? # test must be excluded
929
+ flash[:error] = I18n.t('agile.dfs')
930
+ return true
931
+ end
932
+
933
+ update_dfs_time(form_name, params[:form_time_stamp])
934
+ false
935
+ end
936
+
937
+ ########################################################################
938
+ # Updates double_form_submit timings.
939
+ ########################################################################
940
+ def update_dfs_time(form_name, time = nil)
941
+ if time.nil?
942
+ session[:dfs][form_name] || 0
943
+ else
944
+ session[:dfs][form_name] = time
945
+ if session[:dfs].size > 3
946
+ oldest = session[:dfs].invert.min
947
+ session[:dfs].delete(oldest.last)
948
+ end
949
+ end
950
+ end
951
+
952
+ ########################################################################
953
+ # Loads initial values for new action from cookie or params. Initial values
954
+ # are provided in cookies[:record] or in params as table_name.field_name=value
955
+ ########################################################################
956
+ def load_initial_values
957
+ table = @tables.last[1] + '.'
958
+ # initial values set on page
959
+ if cookies[:record].present?
960
+ Marshal.load(cookies[:record]).each do |k, v|
961
+ k = k.to_s
962
+ if k.match(table + '.')
963
+ field = k.split('.').last
964
+ @record.send("#{field}=", v) if @record.respond_to?(field)
965
+ end
966
+ end
967
+ end
968
+ # initial values set in url (params)
969
+ params.each do |k, v|
970
+ if k.match(table + '.')
971
+ field = k.split('.').last
972
+ @record.send("#{field}=", v) if @record.respond_to?(field)
973
+ end
974
+ end
975
+ end
976
+
977
+ end