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,677 @@
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
+ #
26
+ # AgileIndexHelper module defines helper methods used by AgileRails actions. Output is controlled by
27
+ # data found in 3 major sections of AgileRails form: index, data_set and form sections.
28
+ #
29
+ ###########################################################################
30
+ module AgileIndexHelper
31
+
32
+ ############################################################################
33
+ # Creates action div for AgileRails index action.
34
+ ############################################################################
35
+ def agile_actions_for_index
36
+ @js = @form['script'] || @form['js'] || ''
37
+ @css = @form['css'] || ''
38
+ return '' if @form['index'].nil?
39
+
40
+ actions = @form['index']['actions']
41
+ return '' if actions.nil?
42
+
43
+ std_actions = Agile.config(:index_standard_actions)
44
+ if actions.instance_of?(String)
45
+ actions = process_standard_actions(actions, std_actions)
46
+ elsif actions['standard']
47
+ actions.merge!(std_actions)
48
+ actions.delete('standard')
49
+ end
50
+ # remove new if readonly
51
+ actions_remove_action(actions, 'new') if @form['readonly']
52
+
53
+ html_left, html_right = '', ''
54
+ # Remove actions settings and sort
55
+ only_actions = []
56
+ actions.each { |key, value| only_actions << [key, value] if key.instance_of?(Integer) }
57
+ only_actions.sort_by!(&:first)
58
+ only_actions.each do |key, options|
59
+ next if options.nil? # must be
60
+
61
+ url = @form_params.clone
62
+ yaml = options.instance_of?(String) ? { 'type' => options } : options # if single definition simulate type parameter
63
+ action = yaml['type'].to_s.downcase
64
+ # if return_to is present link directly to URL
65
+ if action == 'link' && yaml['url']
66
+ url = yaml['url']
67
+ else
68
+ url['controller'] = yaml['controller'] if yaml['controller']
69
+ url['action'] = yaml['action'] || action
70
+ url['table'] = yaml['table'] if yaml['table']
71
+ url['form_name'] = yaml['form_name'] if yaml['form_name']
72
+ url['control'] = yaml['control'] if yaml['control']
73
+ end
74
+ # html link options
75
+ html_options = yaml['html'] || {}
76
+ html_options['title'] = yaml['title'] if yaml['title']
77
+ case action
78
+ # new
79
+ when 'new'
80
+ caption = yaml['caption'] || 'agile.new'
81
+ html_options['class'] = 'ar-link'
82
+ html_left += "<li>#{agile_link_to(caption, 'add', url, html_options)}</li>"
83
+ # filter
84
+ when 'filter'
85
+ # filter off is not present
86
+ no_off = session.dig(:filters, @form['table'], :filter, :no_off)
87
+ next if no_off
88
+
89
+ url = ''
90
+ if session.dig(:filters, @form['table'], :filter)
91
+ url = url_for(controller: :agile, action: :run, control: 'agile.filter_off', t: @form['table'], f: AgileHelper.form_param(params))
92
+ end
93
+ html_right += %(
94
+ <li>
95
+ <div class="ar-filter" title="#{ArFilter.title_for_filter_off(session.dig(:filters, @form['table']))}" data-url="#{url.html_safe}">
96
+ #{mi_icon(url.blank? ? 'search' : 'filter_alt_off') }#{ArFilter.filter_menu(self).html_safe}
97
+ </div>
98
+ </li>#{ArFilter.get_filter_input_field(self)}).html_safe
99
+
100
+ when 'close'
101
+ html_left += %(<li><div class="ar-link" onclick="window.close();"'>#{mi_icon('close')} #{t('agile.close')}</div></li>)
102
+
103
+ when 'back'
104
+ html_left += %(<li><div class="ar-link" onclick="history.back();"'>#{mi_icon('arrow_back')} #{t('agile.back')}</div></li>)
105
+
106
+ # menu
107
+ when 'menu'
108
+ code = if options['caption']
109
+ caption = "#{t(options['caption'], options['caption'])}&nbsp;#{mi_icon('caret-down')}"
110
+ caption + agile_process_eval(options['eval'], self)
111
+ else # when caption is false, provide own actions
112
+ agile_process_eval(options['eval'], self)
113
+ end
114
+ html_left += %(<li><div class="ar-link">#{code}</div></li>)
115
+
116
+ =begin
117
+ # reorder
118
+ when action == 'reorder' then
119
+ caption = t('agile.reorder')
120
+ parms = @form_params.clone
121
+ parms['operation'] = v
122
+ parms['id'] = params[:ids]
123
+ parms['table'] = @form['table']
124
+ agile_link_to( caption, 'reorder', parms, method: :delete )
125
+ =end
126
+
127
+ when 'script'
128
+ html_left += agile_script_action(options)
129
+
130
+ when 'field'
131
+ if yaml['position'] == 'left'
132
+ html_left += agile_field_action(yaml)
133
+ else
134
+ html_right += agile_field_action(yaml)
135
+ end
136
+
137
+ when 'ajax', 'link', 'window', 'popup', 'submit'
138
+ html_left += agile_link_ajax_window_submit_action(options, nil)
139
+
140
+ # sort
141
+ when 'sort'
142
+ choices = [%w[id id]]
143
+ @form['index']['sort']&.split(',')&.each do |e|
144
+ e.strip!
145
+ choices << [ t("helpers.label.#{@form['table']}.#{e}"), e ]
146
+ end
147
+ data = mi_icon('sort') + select('sort', 'sort', choices, { include_blank: true },
148
+ { class: 'ar-sort-select', 'data-table' => @form['table'], 'data-form' => AgileHelper.form_param(params)} )
149
+ html_right += %(<li title="#{t('agile.sort')}"><div class="ar-sort">#{data}</li>)
150
+ else
151
+ caption = agile_get_caption(yaml) || t("agile.#{action}")
152
+ icon = yaml['icon'] || action
153
+ html_options['class'] = 'ar-link'
154
+ code = agile_link_to(caption, icon, url, html_options)
155
+ html_left += %(<li>#{code}</li>)
156
+ end
157
+ end
158
+
159
+ %(
160
+ <form id="ar-action-menu">
161
+ <span class="ar-spinner">#{mi_icon('settings-o spin')}</span>
162
+
163
+ <div class="ar-action-menu">
164
+ <ul class="ar-left">#{html_left}</ul>
165
+ <ul class="ar-right">#{html_right}</ul>
166
+ </div>
167
+ <div style="clear: both;"></div>
168
+ </form>
169
+ ).html_safe
170
+ end
171
+
172
+ ############################################################################
173
+ # Paints div for set filter popup
174
+ ############################################################################
175
+ def agile_div_filter
176
+ choices = []
177
+ filter = (@form.dig('index', 'filter') || '').split(',').delete_if { _1 == 'id' }.map(&:strip)
178
+ filter << 'id as text_field' # filter for id is added by default
179
+ filter.each do |f|
180
+ f = f.strip
181
+ name = f.match(' as ') ? f.split(' ').first : f
182
+ # like another field on the form
183
+ choices << [t("helpers.label.#{@form['table']}.#{name}", name), f]
184
+ end
185
+ choices_for_operators = t('agile.choices_for_filter_operators').chomp.split(',').map do |v|
186
+ v.match(':') ? v.split(':') : v
187
+ end
188
+ # currently selected options
189
+ field_name, operators_value = nil, nil
190
+ filter = session.dig(:filters, @form['table'], :filter)
191
+ if filter
192
+ field_name = filter[:field]
193
+ operators_value = filter[:value]
194
+ end
195
+ url_on = url_for(controller: :agile, action: :run, control: 'agile.filter_on' ,
196
+ t: AgileHelper.table_param(params), f: AgileHelper.form_param(params), filter_input: 1)
197
+ url_off = url_for(controller: :agile, action: :run, control: 'agile.filter_off',
198
+ t: AgileHelper.table_param(params), f: AgileHelper.form_param(params))
199
+ %(
200
+ <div id="ar_filter" class="div-hidden">
201
+ <h1>#{t('agile.filter_set')}</h1>
202
+
203
+ #{ select(nil, 'filter_field1', options_for_select(choices, field_name), { include_blank: true }) }
204
+ #{ select(nil, 'filter_oper', options_for_select(choices_for_operators, operators_value)) }
205
+ <div class="ar-edit-menu">
206
+ <div class="ar-link ar-filter-set" data-url="#{url_on}">#{mi_icon('done')} #{t('agile.filter_on')}</div>
207
+ <div class="ar-link-ajax" data-url="#{url_off}">
208
+ #{mi_icon('close')}#{t('agile.filter_off')}
209
+ </div>
210
+ </div>
211
+ </div>).html_safe
212
+ end
213
+
214
+ ############################################################################
215
+ # Creates popup div for setting filter on dataset header.
216
+ ############################################################################
217
+ def agile_filter_popup
218
+ html = %(<div class="filter-popup" style="display: none;"><div>#{t('agile.filter_set')}</div><ul>)
219
+ url = url_for(controller: :agile, action: :run, control: 'agile.filter_on',
220
+ t: @form['table'], f: params['form_name'], filter_input: 1)
221
+
222
+ t('agile.choices_for_filter_operators').chomp.split(',').each do |operator_choice|
223
+ caption, choice = operator_choice.split(':')
224
+ html += %(<li data-operator="#{choice}" data-url="#{url}">#{caption}</li>)
225
+ end
226
+ "#{html}</ul></div>".html_safe
227
+ end
228
+
229
+ ############################################################################
230
+ # Will return title based on @form['title']
231
+ ############################################################################
232
+ def agile_form_title
233
+ return t("helpers.label.#{@form['table']}.table_title", @form['table']) if @form['title'].nil?
234
+ return t(@form['title'], @form['title']) if @form['title'].instance_of?(String)
235
+
236
+ # Hash
237
+ agile_process_eval(@form['title']['eval'], [@form['title']['caption'] || @form['title']['text'], params])
238
+ end
239
+
240
+ ############################################################################
241
+ # Creates title div for index action. Title div also includes paging options
242
+ # and help link
243
+ ############################################################################
244
+ def agile_title_for_index(result = nil)
245
+ agile_dialog_title(agile_form_title(), result)
246
+ end
247
+
248
+ ############################################################################
249
+ # Determines actions and width of actions column
250
+ ############################################################################
251
+ def agile_dataset_actions
252
+ actions = @form['index']['data_set']['actions']
253
+ return [{}, 0, false] if actions.nil? || agile_dont?(actions)
254
+
255
+ std_actions = Agile.config(:dataset_standard_actions)
256
+ if actions.instance_of?(String)
257
+ actions = process_standard_actions(actions, std_actions)
258
+ elsif actions['standard']
259
+ actions.merge!(std_actions)
260
+ actions.delete('standard')
261
+ end
262
+
263
+ # check must be 0 action
264
+ has_check = actions[0] && actions[0] == 'check'
265
+ width = actions.size == 1 ? 22 : 44
266
+ width = 22 if actions.size > 2 && !has_check
267
+ [actions, width, has_check]
268
+ end
269
+
270
+ ############################################################################
271
+ # Calculates (blank) space required for actions when @footer_record is rendered
272
+ ############################################################################
273
+ def agile_dataset_actions_for_footer
274
+ return '' unless @form['index']['data_set']['actions']
275
+
276
+ ignore, width, ignore2 = agile_dataset_actions()
277
+ %(<div class="ar-result-actions" style="width: #{width}px;"></div>).html_safe
278
+ end
279
+
280
+ ############################################################################
281
+ # Creates actions that could be performed on single row of dataset.
282
+ ############################################################################
283
+ def agile_actions_for_dataset(record)
284
+ actions = @form['index']['data_set']['actions']
285
+ return '' if actions.nil? || @form['readonly']
286
+
287
+ actions, width, has_check = agile_dataset_actions()
288
+ has_sub_menu = actions.size > 2 || (has_check && actions.size > 1)
289
+
290
+ main_menu, sub_menu = '', ''
291
+ actions.sort_by(&:first).each do |num, action|
292
+ session[:form_processing] = "data_set:actions: #{num}=#{action}"
293
+ parms = @form_params.clone
294
+
295
+ # if single definition simulate type parameter
296
+ yaml = action.instance_of?(String) ? { 'type' => action } : action
297
+
298
+ if %w[ajax link window popup submit].include?(yaml['type'])
299
+ @record = record # otherwise record fields can't be used as parameters
300
+ html = agile_link_ajax_window_submit_action(yaml, record)
301
+ else
302
+ caption = agile_get_caption(yaml) || "agile.#{yaml['type']}"
303
+ title = t(yaml['help'] || caption, '')
304
+ caption = has_sub_menu ? t(caption, '') : nil
305
+ html = '<li>'
306
+ html += case yaml['type']
307
+ when 'check'
308
+ main_menu += "<li>#{check_box_tag("check-#{record.id}", false, false, { class: 'ar-check' })}</li>"
309
+ next
310
+
311
+ when 'edit'
312
+ parms['action'] = 'edit'
313
+ parms['id'] = record.id
314
+ agile_link_to( caption, 'edit-o', parms, title: title )
315
+
316
+ when 'show'
317
+ parms['action'] = 'show'
318
+ parms['id'] = record.id
319
+ parms['readonly'] = true
320
+ agile_link_to( caption, 'eye', parms, title: title )
321
+
322
+ when 'duplicate'
323
+ parms['id'] = record.id
324
+ # duplicate string will be added to these fields.
325
+ parms['dup_fields'] = yaml['dup_fields']
326
+ parms['action'] = 'create'
327
+ agile_link_to( caption, 'content_copy-o', parms, data: { confirm: t('agile.confirm_dup') }, method: :post, title: title )
328
+
329
+ when 'delete'
330
+ parms['action'] = 'destroy'
331
+ parms['id'] = record.id
332
+ agile_link_to( caption, 'delete-o', parms, data: { confirm: t('agile.confirm_delete') }, method: :delete, title: title )
333
+
334
+ else # error.
335
+ yaml['type'].to_s
336
+ end
337
+ html += '</li>'
338
+ end
339
+
340
+ if has_sub_menu
341
+ sub_menu += html
342
+ else
343
+ main_menu += html
344
+ end
345
+ end
346
+
347
+ if has_sub_menu
348
+ %(
349
+ <ul class="ar-result-actions" style="width: #{width}px;">#{main_menu}
350
+ <li><div class="ar-result-submenu">#{mi_icon('more_vert')}
351
+ <ul id="menu-#{record.id}">#{sub_menu}</ul>
352
+ </div></li>
353
+ </ul>)
354
+ else
355
+ %(<ul class="ar-result-actions" style="width: #{width}px;">#{main_menu}</ul>)
356
+ end.html_safe
357
+ end
358
+
359
+ ############################################################################
360
+ # Creates header div for dataset.
361
+ ############################################################################
362
+ def agile_header_for_dataset
363
+ html = '<div class="ar-result-header">'
364
+ if @form['index']['data_set']['actions'] && !@form['readonly']
365
+ ignore, width, has_check = agile_dataset_actions()
366
+ check_all = mi_icon('check-box-o', class: 'ar-check-all') if has_check
367
+ html += %(<div class="ar-result-actions" style="width:#{width}px;">#{check_all}</div>)
368
+ end
369
+ # preparation for sort icon
370
+ sort_data = session.dig(:filters, @form['table'], :sort)
371
+ sort_field, sort_direction = sort_data.to_s.split(' ')
372
+ filter_fields = (@form.dig('index', 'filter') || '').split(',').map(&:strip)
373
+ if (columns = @form['index']['data_set']['columns'])
374
+ columns.sort.each do |key, options|
375
+ session[:form_processing] = "data_set:columns: #{key}=#{options}"
376
+ next if options['width'].to_s.match(/hidden|none/i)
377
+
378
+ th = %(<div class="th" style="width:#{options['width'] || '15%'};text-align:#{options['align'] || 'left'};" data-name="#{options['name']}")
379
+ label = t_label_for_column(options)
380
+ # no sorting when embedded records or custom filter is active
381
+ sort_ok = !agile_dont?(@form['index']['data_set']['sort'], false)
382
+ sort_ok ||= @form['index'] && @form['index']['sort']
383
+ sort_ok &&= !agile_dont?(options['sort'], false)
384
+ if sort_ok
385
+ icon = 'sort_unset md-18'
386
+ # add filter helper only if field name exists in filter option and field is defined in form
387
+ filter_class = can_include_filter_shortcut?(options['name']) ? nil : 'no-filter'
388
+ if options['name'] == sort_field
389
+ icon = sort_direction == 'desc' ? 'sort_down md-18' : 'sort_up md-18'
390
+ else
391
+ # no icon if filter can not be set
392
+ icon = nil if filter_class || session.dig(:filters, @form['table'], :filter, :no_off)
393
+ end
394
+ # sort and filter icon
395
+ icon = mi_icon(icon, class: filter_class) if icon
396
+ url = url_for(controller: :agile, action: :run, control: 'agile.sort', sort: options['name'],
397
+ t: AgileHelper.table_param(params), f: AgileHelper.form_param(params))
398
+ th += %(><span data-url="#{url}">#{label}</span>#{icon}</div>)
399
+ else
400
+ th += ">#{label}</div>"
401
+ end
402
+ html += "<div class=\"spacer\"></div>#{th}"
403
+ end
404
+ end
405
+ "#{html}</div>".html_safe
406
+ end
407
+
408
+ ############################################################################
409
+ # Creates link for single or double click on dataset column
410
+ ############################################################################
411
+ def dblclick_on_dataset_action(record)
412
+ html = ''
413
+ if @form['index']['data_set']['dblclick']
414
+ yaml = @form['index']['data_set']['dblclick']
415
+ opts = { id: record.id }
416
+ opts[:controller] = yaml['controller'] || :agile
417
+ opts[:action] = yaml['action']
418
+ opts[:table] = yaml['table'] || AgileHelper.table_param(params)
419
+ opts[:form_name] = yaml['form_name'] || AgileHelper.form_param(params) || opts[:table]
420
+ opts[:method] = yaml['method'] || 'get'
421
+ opts[:readonly] = yaml['readonly'] if yaml['readonly']
422
+ opts[:window_close] = yaml['window_close'] if yaml['window_close']
423
+ url_forward_params(opts)
424
+
425
+ html += " data-dblclick=#{url_for(opts)}"
426
+ else
427
+ opts = { action: :show,
428
+ controller: :agile,
429
+ id: record.id,
430
+ ids: params[:ids],
431
+ readonly: (params[:readonly] ? 2 : 1),
432
+ table: AgileHelper.table_param(params),
433
+ form_name: AgileHelper.form_param(params) }
434
+ url_forward_params(opts)
435
+ html += " data-dblclick=#{url_for(opts)}" if @form['form']
436
+ end
437
+ html
438
+ end
439
+
440
+ ############################################################################
441
+ # Formats value according to format supplied or data type. There is lots of things missing here.
442
+ ############################################################################
443
+ def agile_format_value(value, format = nil)
444
+ return '' if value.blank?
445
+
446
+ klass = value.class.to_s
447
+ return AgileHelper.format_date_time(value, format) if klass.match(/time|date/i)
448
+
449
+ format = format.to_s.upcase
450
+ if format[0] == 'N'
451
+ return '' if value.to_f == 0.0 && format.match('Z')
452
+
453
+ format.gsub!('Z', '')
454
+ dec = format[1].blank? ? nil : format[1].to_i
455
+ sep = format[2].blank? ? nil : format[2]
456
+ del = format[3].blank? ? nil : format[3]
457
+ cur = format[4].blank? ? nil : format[4]
458
+ agile_format_number(value, dec, sep, del, cur)
459
+ else
460
+ value.to_s
461
+ end
462
+ end
463
+
464
+ ############################################################################
465
+ # Creates tr code for each row of dataset.
466
+ ############################################################################
467
+ def agile_row_for_dataset(record)
468
+ clas = "ar-#{cycle('odd','even')} " + agile_style_or_class(nil, @form['index']['data_set']['tr_class'], nil, record)
469
+ style = agile_style_or_class('style', @form['index']['data_set']['tr_style'], nil, record)
470
+ %(<div id="#{record.id}" class="ar-result-data #{clas}" #{dblclick_on_dataset_action(record)} #{style}>).html_safe
471
+ end
472
+
473
+ ############################################################################
474
+ # Creates column for each field of dataset record.
475
+ ############################################################################
476
+ def agile_columns_for_dataset(record)
477
+ data_set = @form['index']['data_set']
478
+ return '' unless data_set['columns']
479
+
480
+ html, index = '', 0
481
+ data_set['columns'].sort.each do |k, v|
482
+ session[:form_processing] = "data_set:columns: #{k}=#{v}"
483
+ next if v['width'].to_s.match(/hidden|none/i)
484
+
485
+ # convert shortcut to hash
486
+ v = { 'name' => v } if v.instance_of?(String)
487
+ begin
488
+ # as Array (footer)
489
+ value = if record.instance_of?(Array)
490
+ agile_format_value(record[index], v['format']) if record[index]
491
+ # as Hash (ar_memory)
492
+ elsif record.instance_of?(Hash)
493
+ agile_format_value(record[ v['name'] ], v['format'])
494
+ # eval
495
+ elsif v['eval']
496
+ agile_process_column_eval(v, record)
497
+ # as field
498
+ elsif record.respond_to?(v['name'])
499
+ agile_format_value(record.send( v['name'] ), v['format'])
500
+ else
501
+ "??? #{v['name']}"
502
+ end
503
+ rescue Exception => e
504
+ agile_log_exception(e, 'agile_columns_for_dataset')
505
+ value = '!!!Error'
506
+ end
507
+ html += '<div class="spacer"></div>'
508
+ # set column class
509
+ class_ = agile_style_or_class(nil, v['td_class'], value, record)
510
+ # set width and align an additional style
511
+ style = agile_style_or_class(nil, v['td_style'] || v['style'], value, record)
512
+ flex_align = v['align'].to_s == 'right' ? 'flex-direction:row-reverse;' : ''
513
+ width_align = "width:#{v['width'] || '15%'};#{flex_align}"
514
+ style = %(style="#{width_align}#{style}" )
515
+
516
+ html += %(<div class="td #{class_}" #{style}>#{value}</div>)
517
+ index += 1
518
+ end
519
+ html.html_safe
520
+ end
521
+
522
+ ############################################################################
523
+ # Split eval expression to array by parameters.
524
+ # Ex. Will split agile_text_for_value(one ,"two") => ['agile_text_for_value', 'one', 'two']
525
+ ############################################################################
526
+ def agile_eval_to_array(expression)
527
+ expression.split(/[ ,()]/).select(&:present?).map { _1.gsub(/['"]/, '').strip }
528
+ end
529
+
530
+ private
531
+
532
+ ############################################################################
533
+ # Process eval. Breaks eval option and calls with send method.
534
+ # Parameters:
535
+ # evaluate : String : Expression to be evaluated
536
+ # parameters : Array : array of parameters which will be send to method
537
+ ############################################################################
538
+ def agile_process_eval(evaluate, parameters)
539
+ # evaluate by calling send method
540
+ clas, method = evaluate.split('.')
541
+ return send(clas, *parameters) if method.nil? && respond_to?(clas)
542
+
543
+ if method
544
+ klass = clas.camelize.constantize rescue nil
545
+ p klass, clas
546
+ return klass.send(method, *parameters) if klass&.respond_to?(method)
547
+ end
548
+ agile_log_exception(nil, "#{evaluate} not defined!")
549
+ '???'
550
+ end
551
+
552
+ ############################################################################
553
+ # Process eval option for data_set field or form head field.
554
+ ############################################################################
555
+ def agile_process_column_eval(yaml, document)
556
+ if yaml['params'].blank?
557
+ parms = agile_eval_to_array(yaml['eval'])
558
+ method = parms.shift
559
+
560
+ # prepare parameters for agile_name_for_* methods
561
+ parms << 'id' if method == 'agile_name_for_id' && parms.size == 2
562
+ parms = [@form['table'], yaml['name']] if method == 'agile_text_for_value' && parms.size < 2
563
+
564
+ parms << document[yaml['name']]
565
+ parms.map! { %w[record document].include?(_1.to_s) ? document : _1 }
566
+ if method.match(/^agile_/)
567
+ send(method, *parms)
568
+ elsif respond_to?(method)
569
+ parms = [document] + parms
570
+ send(method, *parms)
571
+ # model method
572
+ elsif document.respond_to?(method)
573
+ document.send(method)
574
+ # some class method
575
+ elsif method.match('.')
576
+ klass, method = method.split('.')
577
+ klass.classify.constantize.send(method, *parms)
578
+ else
579
+ '?????'
580
+ end
581
+ # eval with params
582
+ else
583
+ parms = {}
584
+ if yaml['params'].instance_of?(String)
585
+ parms = agile_value_for_parameter(yaml['params'], document)
586
+ elsif yaml['params'].instance_of?(Hash)
587
+ yaml['params'].each { |k, v| parms[k] = agile_value_for_parameter(v) }
588
+ else
589
+ parms = document[ yaml['name'] ]
590
+ end
591
+ agile_process_eval(yaml['eval'], parms)
592
+ end
593
+ end
594
+
595
+ ############################################################################
596
+ # Defines style or class for row (tr) or column (td) on data_set.
597
+ ############################################################################
598
+ def agile_style_or_class(selector, yaml, value, record)
599
+ return '' if yaml.nil?
600
+
601
+ field = value # alias value as field so both names can be used in eval
602
+ document = record
603
+ html = selector ? "#{selector}=\"" : ''
604
+ begin
605
+ html += if yaml.instance_of?(String)
606
+ yaml
607
+ elsif yaml['eval']
608
+ eval(yaml['eval'])
609
+ elsif yaml['method']
610
+ agile_process_eval(yaml['method'], record)
611
+ end
612
+ rescue Exception => e
613
+ agile_log_exception(e, 'agile_style_or_class')
614
+ end
615
+ html += '"' if selector
616
+ html
617
+ end
618
+
619
+ ############################################################################
620
+ # Get standard actions when actions directive contains single line.
621
+ # Subroutine of agile_actions_for_index
622
+ #
623
+ # Allows for actions: new, filter, standard syntax
624
+ ############################################################################
625
+ def process_standard_actions(actions_params, std_actions)
626
+ actions, index = {}, 1
627
+
628
+ actions_params.split(',').map(&:strip).each do |an_action|
629
+ if an_action == 'standard'
630
+ actions.merge!(std_actions)
631
+ index += 2 * std_actions.size
632
+ else
633
+ actions[index] = an_action
634
+ index += 2
635
+ end
636
+ end
637
+ actions
638
+ end
639
+
640
+ ############################################################################
641
+ # Removes action from list of actions
642
+ ############################################################################
643
+ def actions_remove_action(actions, *what)
644
+ what.each do |action_to_remove|
645
+ index = actions.find { |k, v| v == action_to_remove }
646
+ actions.delete(index.first) if index
647
+ end
648
+ end
649
+
650
+
651
+ ############################################################################
652
+ # When dataset is to be drawn by Rails helper method.
653
+ ############################################################################
654
+ def agile_process_data_set_method
655
+ data_set = @form['index']['data_set']
656
+ return render partial: data_set['view'] if data_set['view']
657
+
658
+ method = data_set['eval'] || 'data_set_eval_misssing'
659
+ return send method if respond_to?(method)
660
+
661
+ I18n.t('agile.no_method', method: method)
662
+ end
663
+
664
+ ############################################################################
665
+ # Check if form has defined input field for field_name and that is not redefined as as
666
+ ############################################################################
667
+ def can_include_filter_shortcut?(field_name)
668
+ field = AgileHelper.get_field_form_definition(field_name, @form)
669
+ return unless field
670
+
671
+ filters = @form.dig('index', 'filter')
672
+ return if filters.nil?
673
+
674
+ filters.split(',').map(&:strip).find { _1 == field_name }
675
+ end
676
+
677
+ end