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,1162 @@
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
+ # AgileApplicationHelper defines common helper methods for using with AgileRails.
27
+ #
28
+ ###########################################################################
29
+ module AgileApplicationHelper
30
+ # page document
31
+ attr_reader :page
32
+ # design document
33
+ attr_reader :design
34
+ # site document
35
+ attr_reader :site
36
+ # menu document
37
+ attr_reader :menu
38
+ # selected menu_item document
39
+ attr_reader :menu_item
40
+ # tables url parameter
41
+ attr_reader :tables
42
+ # ids url parameter
43
+ attr_reader :ids
44
+ # form object
45
+ attr_reader :form
46
+ # options object
47
+ attr_reader :options
48
+ # part
49
+ attr_reader :part
50
+
51
+ # page title
52
+ attr_accessor :page_title
53
+ # all parts read from page, design, ...
54
+ attr_accessor :parts
55
+ #
56
+ attr_accessor :record
57
+ #
58
+ attr_accessor :footer_record
59
+ # json_ld
60
+ attr_reader :json_ld
61
+
62
+ ############################################################################
63
+ # When @env is present then helper methods are called from @env object otherwise
64
+ # from self.
65
+ ############################################################################
66
+ def _origin #:nodoc:
67
+ @env || self
68
+ end
69
+
70
+ ############################################################################
71
+ # Writes out deprication msg. It also adds site_name to message, so it is easier to
72
+ # find where the message is comming from.
73
+ ############################################################################
74
+ def agile_deprecate(msg)
75
+ ActiveSupport::Deprecation.warn("#{agile_get_site.name}: #{msg}")
76
+ end
77
+
78
+ ############################################################################
79
+ # This is main method used for render parts of design into final HTML document.
80
+ #
81
+ # Parameters:
82
+ # [renderer] String or Symbol. Class name (in lowercase) that will be used to render final HTML code.
83
+ # If class name is provided without '_renderer' suffix it will be added automatically.
84
+ #
85
+ # When renderer has value :part, it is a shortcut for agile_render_part method which
86
+ # is used to draw parts of layout on design.
87
+ #
88
+ # [opts] Hash. Additional options that are passed to method. Options are merged with
89
+ # options set on site, design, page and passed to renderer object.
90
+ #
91
+ # Example:
92
+ # <%= agile_render(:ar_page, method: 'view', category: 'news') %>
93
+ ############################################################################
94
+ def agile_render(renderer, opts = {})
95
+ return agile_render_part(renderer[:part]) if renderer.instance_of?(Hash)
96
+
97
+ opts[:edit_mode] = session[:edit_mode]
98
+ opts[:edit_params] = {}
99
+
100
+ opts = @options.merge(opts) # merge options with parameters passed on site, page, design ...
101
+ opts.symbolize_keys! # this makes lots of things easier
102
+ # Create renderer object
103
+ klass = renderer.to_s.downcase
104
+ klass += '_renderer' unless klass.match('_renderer')
105
+ begin
106
+ renderer_object = Kernel.const_get(klass.classify, Class.new).new(self, opts)
107
+ rescue Exception => e
108
+ Agile.dump_exception(e)
109
+ # obj = nil
110
+ end
111
+
112
+ if renderer_object
113
+ html = renderer_object.render_html.to_s
114
+ @css += renderer_object.render_css.to_s
115
+ html.html_safe
116
+ else
117
+ I18n.t 'agile.no_class', class: klass
118
+ end
119
+ end
120
+
121
+ ########################################################################
122
+ # Used for designs with lots of common code and one (or more) part which differs.
123
+ # Point is to define design once and replace some parts of design dynamically.
124
+ # Design may be defined in site and design doc defines only parts that vary
125
+ # from page to page.
126
+ #
127
+ # Example: As used in design.
128
+ # <%= agile_render_part(@main) %>
129
+ #
130
+ # main variable is defined in design body for example:
131
+ #
132
+ # @main = Proc.new { render partial: 'parts/home' }
133
+ ########################################################################
134
+ def agile_render_part(part)
135
+ case
136
+ when part.nil?
137
+ logger.error('ERROR agile_render_part! part is NIL !'); ''
138
+ # Send as array. Part may be defined with options on page. First element has
139
+ # name of element which defines what to do. If not defined default behaviour is
140
+ # called. That is what is defined in second part of array.
141
+ when part.instance_of?(Array)
142
+ if @options.dig(:settings, part.first)
143
+ #TODO to be defined
144
+ else
145
+ result = part.last.call
146
+ result.instance_of?(Array) ? result.first : result
147
+ end
148
+ when part.instance_of?(Proc)
149
+ result = part.call
150
+ result.instance_of?(Array) ? result.first : result
151
+ # Send as string. Evaluate content of string
152
+ when part.instance_of?(String)
153
+ eval part
154
+ # For future maybe. Just call objects to_s method.
155
+ else
156
+ part.to_s
157
+ end.html_safe
158
+ end
159
+
160
+ ########################################################################
161
+ # Helper for rendering top CMS menu when in editing mode
162
+ ########################################################################
163
+ def agile_page_top
164
+ # Evaluate some code if defined in design
165
+ eval(@design.code) if @design&.code.present?
166
+
167
+ session[:edit_mode] > 0 ? render(partial: 'agile/edit_stuff') : ''
168
+ end
169
+
170
+ ########################################################################
171
+ # Helper for adding additional css and javascript code added by documents
172
+ # and renderers during page rendering.
173
+ ########################################################################
174
+ def agile_page_bottom
175
+ %(<style>#{@css}</style>#{javascript_tag @js}).html_safe
176
+ end
177
+
178
+ ############################################################################
179
+ # Creates title div for AgileRails dialogs. Title may also contain pagination section on right side if
180
+ # data_set is provided as parameter.
181
+ #
182
+ # Parameters:
183
+ # [text] String. Title caption.
184
+ # [data_set=nil] Document collection. If data_set is passed pagination links will be created.
185
+
186
+ # Returns:
187
+ # String. HTML code for title.
188
+ ############################################################################
189
+ def agile_dialog_title(text, data_set = nil)
190
+ c = %(<div class="ar-title">#{text})
191
+ c += agile_help_button(data_set)
192
+
193
+ if data_set&.respond_to?(:current_page)
194
+ c += %(<div class="ar-paginate">#{paginate(data_set, params: { action: 'index', clear: 'no', filter: nil })}</div>)
195
+ end
196
+ c += '<div style="clear: both;"></div></div>'
197
+ c.html_safe
198
+ end
199
+
200
+ ############################################################################
201
+ # Creates title for AgileRails forms edit dialog
202
+ #
203
+ # Returns:
204
+ # String. HTML code for title.
205
+ ############################################################################
206
+ def agile_edit_title
207
+ session[:form_processing] = 'form:title:'
208
+ title = @form['form']['title']
209
+ # defined as form:title:edit
210
+ if title && title['edit'] && !@form['readonly']
211
+ t( title['edit'], title['edit'] )
212
+ elsif title && title['show'] && @form['readonly']
213
+ t( title['show'], title['show'] )
214
+ else
215
+ # concatenate title
216
+ c = "#{@form['readonly'] ? t('agile.show') : t('agile.edit')} : "
217
+ c += (@form['title'].instance_of?(String) ? t( @form['title'], @form['title'] ) : t_table_name(@form['table']))
218
+ title = title.try('field')
219
+
220
+ c += "#{@record[title]}" if title && @record.respond_to?(title)
221
+ c
222
+ end
223
+ end
224
+
225
+ ############################################################################
226
+ # Creates title for AgileRails new dialog
227
+ #
228
+ # Returns:
229
+ # String. HTML code for title.
230
+ ############################################################################
231
+ def agile_new_title
232
+ session[:form_processing] = 'form:title:'
233
+ title = @form.dig('form', 'title')
234
+ if title.is_a?(String) # defined as form:title
235
+ t(title, title)
236
+ elsif title&.dig('new') # defined as form:title:new
237
+ t(title['new'], title['new'])
238
+ else
239
+ # in memory structures
240
+ if @form['table'] == 'agile_memory'
241
+ return t( @form['title'], @form['title'] ) if @form['title']
242
+
243
+ t("#{@form['i18n_prefix']}.table_title", '')
244
+ else
245
+ "#{t('agile.new')} : #{t_table_name(@form['table'])}"
246
+ end
247
+ end
248
+ end
249
+
250
+ ############################################################################
251
+ # Similar to rails submit_tag, but also takes care of link icon, translation, ...
252
+ ############################################################################
253
+ def agile_submit_tag(caption, icon, parms, rest = {})
254
+ icon_image = agile_icon_for_link(icon, nil)
255
+ %(<button type="submit" class="ar-submit" name="commit" value="#{t(caption, caption)}">#{icon_image} #{t(caption, caption)}</button>).html_safe
256
+ end
257
+
258
+ ############################################################################
259
+ # Returns icon code if icon is specified
260
+ ############################################################################
261
+ def agile_icon_for_link(icon, clas = 'ar-link-img')
262
+ return '' if icon.blank?
263
+
264
+ if icon.match(/\./)
265
+ _origin.image_tag(icon, class: clas)
266
+ elsif icon.match('<i')
267
+ icon
268
+ else
269
+ _origin.mi_icon(icon)
270
+ end
271
+ end
272
+
273
+ ############################################################################
274
+ # Similar to rails link_to, but also takes care of link icon, translation, ...
275
+ #
276
+ # Parameters:
277
+ # [String] caption : Caption or text created on link
278
+ # [String] icon : Icon used with the caption
279
+ # [Hash] parms : Standard parameters for link_to method (controller, action, id, table, form_name, ...)
280
+ # [Hash] rest : Standard rest parameters for link_to method (target, class, confirm ...)
281
+ ############################################################################
282
+ def agile_link_to(caption, icon, parms, rest = {})
283
+ url, body, parms, rest = _agile_link_to(caption, icon, parms, rest)
284
+ url ? _origin.link_to(body, url, rest) : _origin.link_to(body, parms, rest)
285
+ end
286
+
287
+ ############################################################################
288
+ # Creates link that will respond to json data returned by invoked action
289
+ #
290
+ # Parameters:
291
+ # [String] caption : Caption or text created on link
292
+ # [String] icon : Icon used with the caption
293
+ # [Hash] parms : Standard parameters for link_to method (controller, action, id, table, form_name, ...)
294
+ # [Hash] rest : Standard rest parameters for link_to method (target, class, confirm ...)
295
+ ############################################################################
296
+ def agile_ajax_link_to(caption, icon, parms, rest = {})
297
+ url, body, parms, rest = _agile_link_to(caption, icon, parms, rest)
298
+ url ||= _origin.url_for(parms)
299
+ clas = "ar-link-ajax #{rest.delete('class')}"
300
+ rest['data-confirm'] ||= rest.delete('confirm')
301
+ rest_data = rest.map { "#{_1}=\"#{_2}\"" }.join(', ')
302
+
303
+ %(<span class="#{clas}" data-url="#{url}" #{rest_data}>#{body}</span>).html_safe
304
+ end
305
+
306
+ ############################################################################
307
+ # Will use some default values and return data for previous link_to methods
308
+ ############################################################################
309
+ def _agile_link_to(caption, icon, parms, rest) # :nodoc:
310
+ icon_pos = 'first'
311
+ if parms.instance_of?(Hash)
312
+ parms.stringify_keys!
313
+ rest.stringify_keys!
314
+ url = parms.delete('url')
315
+ rest['target'] ||= parms.delete('target')
316
+ parms['controller'] ||= 'agile'
317
+ icon_pos = parms.delete('icon_pos') || 'first'
318
+ end
319
+
320
+ icon_image = agile_icon_for_link(icon)
321
+ if caption
322
+ caption = t(caption, caption)
323
+ icon_image += ' ' if icon_image
324
+ end
325
+ body = (%w[first left].include?(icon_pos) ? "#{icon_image}#{caption}" : "#{caption} #{icon_image}").html_safe
326
+ [url, body, parms, rest]
327
+ end
328
+
329
+ ####################################################################
330
+ # Returns flash messages formatted for display on message div.
331
+ #
332
+ # Returns:
333
+ # String. HTML code formatted for display.
334
+ ####################################################################
335
+ def agile_flash_messages()
336
+ err = _origin.flash[:error]
337
+ war = _origin.flash[:warning]
338
+ inf = _origin.flash[:info]
339
+ note = _origin.flash[:note]
340
+ html = ''
341
+ unless err.nil? and war.nil? and inf.nil? and note.nil?
342
+ html += "<div class=\"ar-form-error\">#{err}</div>" if err
343
+ html += "<div class=\"ar-form-warning\">#{war}</div>" if war
344
+ html += "<div class=\"ar-form-info\">#{inf}</div>" if inf
345
+ html += note if note
346
+ _origin.flash[:error] = nil
347
+ _origin.flash[:warning] = nil
348
+ _origin.flash[:info] = nil
349
+ _origin.flash[:note] = nil
350
+ end
351
+ # Update fields on the form
352
+ if _origin.flash[:update]
353
+ html += "<div class=\"ar-form-updates\">\n"
354
+ _origin.flash[:update].each do |field, value|
355
+ html += %(<div data-field="#{field}" data-value="#{value}"></div>\n)
356
+ end
357
+ html += '</div>'
358
+ _origin.flash[:update] = nil
359
+ end
360
+ html.html_safe
361
+ end
362
+
363
+ ########################################################################
364
+ # Decamelizes string. This probably doesn't work very good with non ascii chars.
365
+ # Therefore it is very unwise to use non ascii chars for table (table) names.
366
+ #
367
+ # Parameters:
368
+ # [Object] model_string. String or model to be converted into decamelized string.
369
+ #
370
+ # Returns:
371
+ # String. Decamelized string.
372
+ ########################################################################
373
+ def decamelize_type(model_string)
374
+ model_string&.to_s&.underscore
375
+ end
376
+
377
+ ####################################################################
378
+ # Returns validation error messages for the document (record) formatted for
379
+ # display on message div.
380
+ #
381
+ # Parameters:
382
+ # [doc] Document. Document record which will be checked for errors.
383
+ #
384
+ # Returns:
385
+ # String. HTML code formatted for display.
386
+ ####################################################################
387
+ def agile_error_messages_for(doc)
388
+ return '' unless doc&.errors.any?
389
+
390
+ msgs = doc.errors.inject('') do |r, error|
391
+ label = t("helpers.label.#{decamelize_type(doc.class)}.#{error.attribute}", error.attribute)
392
+ r + "<li>#{label} : #{error.message}</li>"
393
+ end
394
+
395
+ %(
396
+ <div class="ar-form-error">
397
+ <h2>#{t('agile.errors_no')} #{doc.errors.size}</h2>
398
+ <ul>#{msgs}</ul>
399
+ </div>).html_safe
400
+ end
401
+
402
+ ####################################################################
403
+ # Returns warning messages if any set in a model.
404
+ #
405
+ # When warnings array is added to model its content can be written on top of the form.
406
+ #
407
+ # Parameters:
408
+ # [doc] Document. Document record which will be checked for errors.
409
+ #
410
+ # Returns:
411
+ # String. HTML code formatted for display.
412
+ ####################################################################
413
+ def agile_warning_messages_for(doc)
414
+ return '' # NOT WORKING
415
+ return '' unless doc&.respond_to?(:warnings)
416
+
417
+ msgs = doc.warnings.inject('') do |r, error|
418
+ label = t("helpers.label.#{decamelize_type(doc.class)}.#{error.attribute}", error.attribute)
419
+ r + "<li>#{label} : #{error.message}</li>"
420
+ end
421
+
422
+ %(
423
+ <div class="ar-form-warning">
424
+ <h2>#{t('agile.warnings_no')} #{doc.warnings.size}</h2>
425
+ <ul>#{msgs}</ul>
426
+ </div>).html_safe
427
+ end
428
+
429
+ ####################################################################
430
+ # Checks if CMS is in edit mode (CMS menu bar is visible).
431
+ #
432
+ # Returns:
433
+ # Boolean. True if in edit mode
434
+ ####################################################################
435
+ def agile_edit_mode?
436
+ _origin.session[:edit_mode] > 1
437
+ end
438
+
439
+ ####################################################################
440
+ # Will create HTML code required to create new document.
441
+ #
442
+ # Parameters:
443
+ # [opts] Hash. Optional parameters for url_for helper. These options must provide at least table and form_name
444
+ # parameters.
445
+ #
446
+ # Example:
447
+ # if @opts[:edit_mode] > 1
448
+ # opts = {table: 'agile_page;agile_part', form_name: 'agile_part', ids: @doc.id }
449
+ # html += agile_link_for_create( opts.merge!({title: 'Add new part', 'agile_part.name' => 'initial name', 'agile_part.order' => 10}) )
450
+ # end
451
+ #
452
+ # Returns:
453
+ # String. HTML code which includes add image and javascript to invoke new document create action.
454
+ ####################################################################
455
+ def agile_link_for_create(opts)
456
+ opts.stringify_keys!
457
+ title = opts.delete('title') #
458
+ title = t(title, title) if title
459
+ target = opts.delete('target') || 'iframe_cms'
460
+ opts['form_name'] ||= opts['table'].to_s.split(';').last
461
+ opts['action'] = :new
462
+ opts['controller'] ||= :agile
463
+ url_forward_params(opts)
464
+ js = "$('##{target}').attr('src', '#{_origin.url_for(opts)}'); return false;"
465
+ agile_link_to(nil, _origin.mi_icon('plus-circle'), '#',
466
+ { onclick: js, title: title, alt: 'Create', class: 'ar-inline-link'}).html_safe
467
+ end
468
+
469
+ ####################################################################
470
+ # Will create HTML code required to edit document.
471
+ #
472
+ # Parameters:
473
+ # [opts] Hash. Optional parameters for url_for helper. These options must provide
474
+ # at least table, form_name and id parameters. Optional title, target and icon parameters
475
+ # can be set.
476
+ #
477
+ # Example:
478
+ # html += agile_link_for_edit( @options ) if @opts[:edit_mode] > 1
479
+ #
480
+ # Returns:
481
+ # String. HTML code which includes edit image and javascript to invoke edit document action.
482
+ ####################################################################
483
+ def agile_link_for_edit(opts)
484
+ opts.stringify_keys!
485
+ title = opts.delete('title') #
486
+ title = t(title)
487
+ target = opts.delete('target') || 'iframe_cms'
488
+ icon = opts.delete('icon') || 'edit-o'
489
+ opts['controller'] ||= :agile
490
+ opts['action'] ||= 'edit'
491
+ opts['form_name'] ||= opts['table'].to_s.split(';').last
492
+
493
+ js = "$('##{target}').attr('src', '#{_origin.url_for(opts)}'); return false;"
494
+ agile_link_to(nil, _origin.mi_icon(icon), '#',
495
+ { onclick: js, title: title, class: 'ar-inline-link', alt: 'Edit'})
496
+ end
497
+
498
+ ####################################################################
499
+ # Create edit link with edit picture. Subroutine of agile_page_edit_menu.
500
+ ####################################################################
501
+ def agile_link_menu_tag(title) #:nodoc:
502
+ html = %(
503
+ <dl>
504
+ <dt><div class='ar_popmenu ar-inline-link' href="#">
505
+ #{_origin.mi_icon('file-text-o', title: title)}
506
+ </div></dt>
507
+ <dd>
508
+ <ul class=' div-hidden ar_popmenu_class'>
509
+ )
510
+
511
+ yield html
512
+ html + '</ul></dd></dl>'
513
+ end
514
+
515
+ ####################################################################
516
+ # Create one option in page edit link. Subroutine of agile_page_edit_menu.
517
+ ####################################################################
518
+ def agile_link_for_edit1(opts, link_text) #:nodoc:
519
+ icon = opts.delete('icon')
520
+ url = _origin.url_for(opts)
521
+ "<li><div class='ar_popmenu_item' style='cursor: pointer;' data-url='#{url}'>
522
+ #{_origin.mi_icon(icon)} #{link_text}</div></li>\n"
523
+ end
524
+
525
+ ########################################################################
526
+ # Create edit menu for editing existing or creating new agile_page documents. Edit menu
527
+ # consists of for options.
528
+ # * Edit content. Will edit only body part od document.
529
+ # * Edit advanced. Will create edit form for editing all document fields.
530
+ # * New page. Will create new document and pass some initial data to it. Initial data is saved to cookie.
531
+ # * New part. Will create new part of document.
532
+ #
533
+ # Parameters:
534
+ # [opts] Hash. Optional parameters for url_for helper. These options must provide at least table and form_name
535
+ # and id parameters.
536
+ #
537
+ # Example:
538
+ # html += agile_page_edit_menu() if @opts[:edit_mode] > 1
539
+ #
540
+ # Returns:
541
+ # String. HTML code required for manipulation of currently processed document.
542
+ ########################################################################
543
+ def agile_page_edit_menu(opts = @opts)
544
+ opts[:edit_mode] ||= _origin.session[:edit_mode]
545
+ return '' if opts[:edit_mode] < 2
546
+
547
+ # save some data to cookie. This can not go to session.
548
+ page = opts[:page] || @page
549
+ table = _origin.site.page_class.underscore
550
+ kukis = { "#{table}.ar_design_id" => page.ar_design_id,
551
+ # "#{table}.menu_id" => page.menu_id,
552
+ # "#{table}.kats" => page.kats,
553
+ "#{table}.page_id" => page.id,
554
+ "#{table}.ar_site_id" => _origin.site.id
555
+ }
556
+ _origin.cookies[:record] = Marshal.dump(kukis)
557
+ title = "#{t('agile.edit')}: #{page.subject}"
558
+ opts[:edit_params] ||= {}
559
+ agile_link_menu_tag(title) do |html|
560
+ opts[:edit_params].merge!( controller: :agile, action: 'edit', 'icon' => 'edit-o' )
561
+ opts[:edit_params].merge!( id: page.id, table: _origin.site.page_class.underscore, form_name: opts[:form_name], edit_only: 'body' )
562
+ html += agile_link_for_edit1( opts[:edit_params], t('agile.edit_content') )
563
+
564
+ opts[:edit_params].merge!( edit_only: nil, 'icon' => 'edit-o' )
565
+ html += agile_link_for_edit1( opts[:edit_params], t('agile.edit_advanced') )
566
+
567
+ opts[:edit_params].merge!( action: 'new', 'icon' => 'plus' )
568
+ html += agile_link_for_edit1( opts[:edit_params], t('agile.edit_new_page') )
569
+
570
+ opts[:edit_params].merge!(ids: page.id, form_name: 'agile_part', 'icon' => 'plus',
571
+ table: "#{_origin.site.page_class.underscore};agile_part" )
572
+ html + agile_link_for_edit1( opts[:edit_params], t('agile.edit_new_part') )
573
+ end.html_safe
574
+ end
575
+
576
+ ########################################################################
577
+ # Return page class model defined in site document page_class field.
578
+ #
579
+ # Used in forms, when method must be called from page model and model is overwritten by
580
+ # user's own model.
581
+ #
582
+ # Example as used on form:
583
+ # 30:
584
+ # name: link
585
+ # type: text_with_select
586
+ # eval: 'agile_page_class.all_pages_for_site(@env.agile_get_site)'
587
+ ########################################################################
588
+ def agile_page_class
589
+ agile_get_site.page_klass
590
+ end
591
+
592
+ ########################################################################
593
+ # Return menu class model defined in site document menu_class field.
594
+ #
595
+ # Used in forms for providing menus class to the forms object.
596
+ #
597
+ # Example as used on form:
598
+ # 30:
599
+ # name: menu_id
600
+ # type: tree_view
601
+ # eval: 'agile_menu_class.all_menus_for_site(@env.agile_get_site)'
602
+ ########################################################################
603
+ def agile_menu_class
604
+ agile_get_site.menu_class.classify.constantize
605
+ end
606
+
607
+ ####################################################################
608
+ # Parse site name from url and return ar_site document. Site document will be cached in
609
+ # @site variable.
610
+ #
611
+ # If not in production environment and site document is not found
612
+ # method will search for 'test' document and return ar_site document found in alias_for field.
613
+ #
614
+ # Returns:
615
+ # ArSite. Site document.
616
+ ####################################################################
617
+ def agile_get_site
618
+ return @site if @site # already cached
619
+
620
+ reqst = _origin.request.url # different when called from renderer
621
+ uri = URI.parse(reqst)
622
+ @site = ArSite.find_by(name: uri.host)
623
+ # Site can be aliased
624
+ @site = ArSite.find_by(name: @site.alias_for) if @site&.alias_for.present?
625
+ # Development. If site with name test exists use alias_for field as pointer to real site data
626
+ if @site.nil? && ENV['RAILS_ENV'] != 'production'
627
+ @site = ArSite.find_by(name: 'development')
628
+ @site = ArSite.find_by(name: @site.alias_for) if @site
629
+ end
630
+ @site = nil unless @site&.active # might be disabled
631
+ @site
632
+ end
633
+
634
+ ############################################################################
635
+ # Return array of policies defined in a site document formated to be used
636
+ # as choices for select field. Method is used for selecting site policy where
637
+ # policy for displaying data is required.
638
+ #
639
+ # Example (as used in forms):
640
+ # name: policy_id
641
+ # type: select
642
+ # eval: agile_choices_for_site_policies
643
+ # include_blank: true
644
+ ############################################################################
645
+ def agile_choices_for_site_policies
646
+ agile_get_site.site_policies.map { |policy| [policy.name, policy.id] }
647
+ end
648
+
649
+ ############################################################################
650
+ # Returns list of all collections (tables) as array of choices for usage in select fields.
651
+ # List is collected from agile_menu.yml files and may not include all collections used in application.
652
+ # Currently list is only used for helping defining collection names on agile_permission form.
653
+ #
654
+ # Example (as used in forms):
655
+ # form:
656
+ # fields:
657
+ # 10:
658
+ # name: table_name
659
+ # type: text_with_select
660
+ # eval: agile_choices_for_all_tables
661
+ ############################################################################
662
+ def agile_choices_for_all_tables
663
+ choices = {}
664
+ Agile.paths(:forms).reverse.each do |path|
665
+ filename = "#{path}/agile_menu.yml"
666
+ next unless File.exist?(filename)
667
+
668
+ menu = YAML.load_file(filename) rescue nil # load menu
669
+ next unless menu['menu'] # not menu or error
670
+
671
+ menu['menu'].each do |section|
672
+ next unless section.last['items'] # next if no items
673
+
674
+ section.last['items'].each_value do |v|
675
+ key = v['table']
676
+ choices[key] ||= "#{key} - #{t(v['caption'], v['caption'])}"
677
+ end
678
+ end
679
+ end
680
+ choices.invert.to_a.sort # hash has to be inverted for values to be returned right
681
+ end
682
+
683
+ ##########################################################################
684
+ # html code for AgileRails application menu
685
+ ##########################################################################
686
+ def agile_application_menu
687
+ menus = {}
688
+ Agile.paths(:forms).reverse.each do |path|
689
+ filename = "#{path}/agile_menu.yml"
690
+ next unless File.exist?(filename)
691
+
692
+ menu = YAML.load_file(filename) rescue nil # load menu file
693
+ next unless menu['menu']
694
+
695
+ menus = AgileHelper.forms_merge(menu['menu'], menus) # ignore top level
696
+ end
697
+
698
+ html = '<ul>'
699
+ menus.to_a.sort.each do |index, menu| # sort menu numbers
700
+ next unless menu['caption']
701
+
702
+ icon = menu['icon'].match('/') ? image_tag(menu['icon']) : mi_icon(menu['icon']) #external or fa- image
703
+ html += %(<li class="agile-top-level-menu"><div>#{icon}#{t(menu['caption'])}</div><ul>)
704
+ menu['items'].to_a.sort.each do |index1, option| # sort by menu items
705
+ html += if option['link']
706
+ opts = { target: option['target'] || 'iframe_cms' }
707
+ "<li>#{agile_link_to(t(option['caption']), option['icon'], option['link'], opts)}</li>"
708
+ else
709
+ opts = { controller: option['controller'],
710
+ action: option['action'],
711
+ table: option['table'],
712
+ form_name: option['form_name'] || option['table'],
713
+ target: option['target'] || 'iframe_cms',
714
+ }
715
+ "<li>#{agile_link_to(t(option['caption']), option['icon'], opts)}</li>"
716
+ end
717
+ end
718
+ html += '</ul></li>'
719
+ end
720
+ html.html_safe
721
+ end
722
+
723
+ ############################################################################
724
+ # Returns list of directories as array of choices for use in select field
725
+ # on folder permission form. Directory root is determined from ar_site.files_directory field.
726
+ ############################################################################
727
+ def agile_choices_for_folders
728
+ public = File.join(Rails.root,'public')
729
+ home = File.join(public, agile_get_site.files_directory)
730
+ choices = Dir.glob("#{home}/**/*/").select { |fn| File.directory?(fn) }
731
+ choices << home # add home
732
+ choices.map { _1.gsub(public, '') }.sort # remove public part
733
+ end
734
+
735
+ ############################################################################
736
+ # Returns choices for select input field when choices are generated from
737
+ # all documents in collection.
738
+ #
739
+ # Parameters:
740
+ # [model] String. Collection (table) name in lowercase format.
741
+ # [name] String. Field name containing description text.
742
+ # [id] String. Field name containing id field. Default is '_id'
743
+ # [options] Hash. Various options. Currently site: (:only, :with_nil, :all) is used.
744
+ # Will return only documents belonging to current site, also with site not defined,
745
+ # or all documents.
746
+ #
747
+ # Example (as used in forms):
748
+ # 50:
749
+ # name: agile_poll_id
750
+ # type: select
751
+ # eval: agile_choices_for('agile_poll','name','_id')
752
+ ############################################################################
753
+ def agile_choices_for(model, name, id = 'id', options = {})
754
+ model = model.classify.constantize
755
+ qry = model.select(id, name)
756
+ if (param = options[:site])
757
+ sites = [agile_get_site.id] unless param == :all
758
+ sites << nil if param == :with_nil
759
+ qry = qry.where(ar_site_id: sites) if sites
760
+ end
761
+ qry = qry.where(active: true) if model.has_attribute?(:active)
762
+ qry.order(name => :asc).map { [_1[name], _1[id]] }
763
+ end
764
+
765
+ ############################################################################
766
+ # Returns list of choices for selection top level menu on agile_page form. Used for defining which
767
+ # top level menu will be highlited when page is displayed.
768
+ #
769
+ # Example (as used in forms):
770
+ # 20:
771
+ # name: menu_id
772
+ # type: select
773
+ # eval: agile_choices_for_menu
774
+ ############################################################################
775
+ def agile_choices_for_menu
776
+ menu_class = agile_get_site.menu_class
777
+ menu_class = 'ArMenu' if menu_class.blank?
778
+ klass = menu_class.classify.constantize
779
+ klass.choices_for_menu(agile_get_site)
780
+ end
781
+
782
+ ############################################################################
783
+ # Will add data to record cookie. Record cookie is used to preload some
784
+ # data on next create action. Create action will look for cookies[:record] and
785
+ # if found initialize fields on form with matching name to value found in cookie data.
786
+ #
787
+ # Example:
788
+ # kukis = {'agile_page.ar_design_id' => @page.ar_design_id,
789
+ # 'agile_page.agile_menu_id' => @page.menu_id)
790
+ # agile_add2_record_cookie(kukis)
791
+ ############################################################################
792
+ def agile_add2_record_cookie(hash)
793
+ kukis = if @env.cookies[:record]&.present?
794
+ Marshal.load(@env.cookies[:record])
795
+ else
796
+ {}
797
+ end
798
+ hash.each { |k, v| kukis[k] = v }
799
+ @env.cookies[:record] = Marshal.dump(kukis)
800
+ end
801
+
802
+ ############################################################################
803
+ # Will check if user roles allow user to view data in document with defined access_policy.
804
+ #
805
+ # Parameters:
806
+ # [ctrl] Controller object or object which holds methods to access environment. For example @env
807
+ # when called from renderer.
808
+ # [policy_id] Document or documents policy_id field value required to view data. Method will automatically
809
+ # check if parameter send has policy_id field defined and use value of that field.
810
+ #
811
+ # Example:
812
+ # can_view, message = agile_user_can_view(@env, @page)
813
+ # # or
814
+ # can_view, message = agile_user_can_view(@env, @page.policy_id)
815
+ # return message unless can_view
816
+ #
817
+ # Returns:
818
+ # True if access_policy allows user to view data.
819
+ # False and message from policy that is blocking view if access is not allowed.
820
+ ############################################################################
821
+ def agile_user_can_view(ctrl, policy_id)
822
+ @can_view_cache ||= {}
823
+ policy_id = policy_id.policy_id if policy_id&.respond_to?(:policy_id)
824
+ # Eventualy object without policy_id will be checked. This is to prevent error
825
+ policy_id = nil unless policy_id.instance_of?(Integer)
826
+ return @can_view_cache[policy_id] if @can_view_cache[policy_id]
827
+
828
+ # get site policies
829
+ site = ctrl.site
830
+ policies = (site.inherit_policy.blank? ? site : ArSite.find(site.inherit_policy)).site_policies.to_a
831
+ # get default policy
832
+ default_policy = policies.find(&:is_default)
833
+ return cache_can_view(policy_id, false, 'Default access policy not found for the site!') unless default_policy
834
+
835
+ permissions = {}
836
+ default_policy.ar_policy_rules.each { |v| permissions[v.ar_role_id] = v.permission }
837
+ # update permissions with defined policy
838
+ part_policy = nil
839
+ if policy_id
840
+ part_policy = policies.find { |policy| policy.id == policy_id }
841
+ return cache_can_view(policy_id, false, 'Access policy not found for part!') unless part_policy
842
+
843
+ part_policy.ar_policy_rules.each { |v| permissions[v.ar_role_id] = v.permission }
844
+ end
845
+ # apply guest role if no roles defined
846
+ if ctrl.session[:user_roles].nil?
847
+ guest_role = ArRole.get_role('guest')
848
+ return cache_can_view(policy_id, false, 'System guest role not defined!') unless guest_role
849
+
850
+ ctrl.session[:user_roles] = [guest_role.id]
851
+ end
852
+ # Check if user has any role that allows him to view part
853
+ can_view = ctrl.session[:user_roles].find{ |role| permissions[role]&.to_i > 0 }
854
+ msg = ''
855
+ unless can_view
856
+ msg = part_policy ? t(part_policy.message, part_policy.message) : t(default_policy.message, default_policy.message)
857
+ # message may have variable content
858
+ msg = _origin.render(inline: msg, layout: nil) if msg.match('<%=')
859
+ end
860
+ cache_can_view(policy_id, can_view, msg)
861
+ end
862
+
863
+ ####################################################################
864
+ # Check if user has required role assigned to its user profile. If role is passed as
865
+ # string method will check roles for name and system name.
866
+ #
867
+ # Parameters:
868
+ # [role] ArRole/String. Required role. If passed as string role will be searched in agile_policy_roles collection.
869
+ # [user] User id. Defaults to session[:user_id].
870
+ # [roles] Array of roles that will be searched. Default session[:user_roles].
871
+ #
872
+ # Example:
873
+ # if agile_user_has_role?('decision_maker', session[:user_id), session[:user_roles])
874
+ # do_something_important
875
+ # end
876
+ #
877
+ # Returns:
878
+ # Boolean. True if user has required role.
879
+ ####################################################################
880
+ def agile_user_has_role?( role, user = nil, roles = nil )
881
+ roles = _origin.session[:user_roles] if roles.nil?
882
+ user = _origin.session[:user_id] if user.nil?
883
+ return false if user.nil? || roles.nil?
884
+
885
+ role = ArRole.get_role(role)
886
+ return false if role.nil?
887
+
888
+ # role is included in roles array
889
+ roles.include?(role.id)
890
+ end
891
+
892
+ ####################################################################
893
+ # Returns true if parameter has value of 0, false, no, none or -.
894
+ # Returns value of default if parameter has nil value.
895
+ #
896
+ # Parameters:
897
+ # [what] String/boolean/Integer.
898
+ # [default] Default value when what has value of nil. False by default.
899
+ #
900
+ # Example:
901
+ # agile_dont?('none') # => true
902
+ # agile_dont?('-') # => true
903
+ # agile_dont?(1) # => false
904
+ # agile_dont?(nil, true) # => true
905
+ ####################################################################
906
+ def agile_dont?(what, default = false)
907
+ AgileHelper.dont?(what, default)
908
+ end
909
+
910
+ ############################################################################
911
+ # Truncates string length maximal to the size required and takes care, that words are not broken in middle.
912
+ # Used for output text summary with texts that can be longer then allowed space.
913
+ #
914
+ # Parameters:
915
+ # [string] String of any size.
916
+ # [size] Maximal size of the string to be returned.
917
+ #
918
+ # Example:
919
+ # agile_limit_string(description, 100)
920
+ #
921
+ # Returns:
922
+ # String, truncated to required size. If string is truncated '...' will be added to the end.
923
+ ############################################################################
924
+ def agile_limit_string(string, size)
925
+ return string if string.size <= size
926
+
927
+ string = string[0, size]
928
+ string.chop! until string[-1, 1] == ' ' || string == ''
929
+ "#{string}..."
930
+ end
931
+
932
+ ############################################################################
933
+ # Returns key defined in ArBigTable as array of choices for use in select fields.
934
+ # ArBigTable can be used like a key/value store for all kind of predefined values
935
+ # which can be linked to site and or locale.
936
+ #
937
+ # Parameters:
938
+ # [key] String. Key name to be searched in agile_big_tables documents.
939
+ #
940
+ # Example:
941
+ # 10:
942
+ # name: category
943
+ # type: select
944
+ # eval: agile_big_table_choices 'categories_for_page' # as used on form
945
+ #
946
+ # Returns:
947
+ # Array of choices ready for select field.
948
+ ############################################################################
949
+ def agile_big_table_choices(key)
950
+ ret = []
951
+ bt = ArBigTable.find_by(key: key, site_id: agile_get_site.id, active: true) ||
952
+ ArBigTable.find_by(key: key, site_id: nil, active: true)
953
+ return ret if bt.nil?
954
+
955
+ bt.ar_big_table_values.order(description: 'asc').each do |ar_value| # iterate values
956
+ next unless ar_value.active
957
+
958
+ desc = ar_value.get_localized_description
959
+ ret << [desc, ar_value.value]
960
+ end
961
+ ret
962
+ end
963
+
964
+ ########################################################################
965
+ # Will return html code required for load AgileRails form into iframe. If parameters
966
+ # are passed to method iframe url will have initial value and thus enabling automatic form
967
+ # load on page display.
968
+ #
969
+ # Parameters:
970
+ # [table] String: Collection (table) name used to load initial form.
971
+ # [opts] Hash: Optional parameters which define url for loading AgileRails form.
972
+ # These parameters are :action, :oper, :table, :form_name, :id, :readonly
973
+ #
974
+ # Example:
975
+ # # just iframe code
976
+ # <%= agile_edit_frame(nil) %>
977
+ # # load note form for note collection into iframe with name iframe_name
978
+ # <%= agile_edit_frame('note', iframe: 'iframe_name') %>
979
+ # # on register collection use reg_adresses form_name to display data with id @register.id
980
+ # <%= agile_edit_frame('register', action: :show, form_name: 'reg_adresses', readonly: 1, id: @register.id ) %>
981
+ #
982
+ # Returns:
983
+ # Html code for edit iframe
984
+ ########################################################################
985
+ def agile_edit_frame(table, opts = {})
986
+ iframe_name = opts[:iframe] || 'iframe_edit'
987
+ if params.to_unsafe_h.size > 2 && table # controller, action, path is minimal
988
+ params[:controller] = :agile
989
+ params[:action] = params[:oper] == 'edit' ? 'edit' : 'index'
990
+ params[:action] = opts[:action] unless params[:oper]
991
+ params[:table] ||= table
992
+ params[:form_name] ||= opts[:form_name] || table
993
+ params[:id] ||= params[:idp] || opts[:id]
994
+ params[:readonly] ||= opts[:readonly]
995
+ params[:path] = nil
996
+ params.permit! # rails 5 request
997
+ "<iframe id='#{iframe_name}' name='#{iframe_name}' src='#{url_for params}'></iframe>"
998
+ else
999
+ "<iframe id='#{iframe_name}' name='#{iframe_name}'></iframe>"
1000
+ end.html_safe
1001
+ end
1002
+
1003
+ ########################################################################
1004
+ # Will return value from Rails and AgileRails environment objects.
1005
+ # This objects can be params, session, record, site, page
1006
+ #
1007
+ # Parameters:
1008
+ # [object] String: Internal object holding variable. Possible values are session, params, record, site, page, class
1009
+ # [var_name] String[symbol]: Variable name (:user_name, 'user_id', ...)
1010
+ # [current_record] Object: If passed and object is 'record' then current active record it will be used for retrieving data.
1011
+ #
1012
+ # Example:
1013
+ # # called when constructing iframe for display
1014
+ # agile_internal_var('session', :user_id)
1015
+ # agile_internal_var('params', :some_external_parameter)
1016
+ # agile_internal_var('site', :name)
1017
+ # # or even
1018
+ # agile_internal_var('class', 'ClassName.class_method_name')
1019
+ #
1020
+ #
1021
+ # Returns:
1022
+ # Value of variable or error when not found
1023
+ ########################################################################
1024
+ def agile_internal_var(object, var_name, current_record = nil)
1025
+ begin
1026
+ case object.to_s
1027
+ when 'session' then _origin.session[var_name]
1028
+ when 'params' then _origin.params[var_name]
1029
+ when 'site' then _origin.agile_get_site.send(var_name)
1030
+ when 'page' then _origin.page.send(var_name)
1031
+ when 'record'
1032
+ current_record ? current_record.send(var_name) : _origin.record.send(var_name)
1033
+ when 'class'
1034
+ clas, method_name = var_name.split('.')
1035
+ klas = clas.classify.constantize
1036
+ # call method. Error will be caught below.
1037
+ klas.send(method_name)
1038
+ else
1039
+ 'VARIABLE: UNKNOWN OBJECT'
1040
+ end
1041
+ rescue Exception => e
1042
+ Rails.logger.debug "\nagile_internal_var. Runtime error. #{e.message}\n"
1043
+ Rails.logger.debug(e.backtrace.join($/)) if Rails.env.development?
1044
+ 'VARIABLE: ERROR'
1045
+ end
1046
+ end
1047
+
1048
+ ########################################################################
1049
+ # Will return formated code for embedding json+ld data into page
1050
+ #
1051
+ # Returns:
1052
+ # HTML data to be embedded into page header
1053
+ #######################################################################
1054
+ def agile_get_json_ld
1055
+ return '' if @json_ld.nil? || @json_ld.size == 0
1056
+
1057
+ %(
1058
+ <script type="application/ld+json">
1059
+ #{JSON.pretty_generate({ '@context' => 'http://schema.org', '@graph' => @json_ld })}
1060
+ </script>
1061
+ ).html_safe
1062
+ end
1063
+
1064
+ ########################################################################
1065
+ # Will add new element to json_ld structure
1066
+ #
1067
+ # Parameters:
1068
+ # [element] Hash or Array of hashes: json+ld element
1069
+ #######################################################################
1070
+ def agile_add_json_ld(element)
1071
+ @json_ld ||= []
1072
+ if element.instance_of?(Array)
1073
+ @json_ld << element
1074
+ else
1075
+ @json_ld << element
1076
+ end
1077
+ end
1078
+
1079
+ ########################################################################
1080
+ # Will return meta data for SEO optimizations
1081
+ #
1082
+ # Returns:
1083
+ # HTML data to be embedded into page header
1084
+ #######################################################################
1085
+ def agile_get_seo_meta_tags
1086
+ html = ''
1087
+ html += %(<link rel="canonical" href="#{@page.canonical_link}">\n ) unless @page&.canonical_link.blank?
1088
+ if @meta_tags
1089
+ html += @meta_tags.inject('') do |r, hash|
1090
+ r + %(<meta #{hash.first} content="#{hash.last}">\n )
1091
+ end
1092
+ end
1093
+ html.html_safe
1094
+ end
1095
+
1096
+ ########################################################################
1097
+ # Will add a meta tag to internal hash structure. If meta tag already exists it
1098
+ # will be overwritten.
1099
+ #
1100
+ # Parameters:
1101
+ # [name] String: meta name
1102
+ # [content] String: meta content
1103
+ ########################################################################
1104
+ def agile_add_meta_tag(type, name, content)
1105
+ return if content.blank?
1106
+
1107
+ @meta_tags ||= {}
1108
+ key = "#{type}=\"#{name}\""
1109
+ @meta_tags[key] = content
1110
+ end
1111
+
1112
+ #######################################################################
1113
+ # Will return alt image option when text is provided. When text is blank
1114
+ # it will extract alt name from picture file_name. This method returns
1115
+ # together with alt="image-tag" tag.
1116
+ #
1117
+ # Parameters:
1118
+ # [file_name] String: Filename of a picture
1119
+ # [text] String: Alt text name
1120
+ #
1121
+ # Returns:
1122
+ # [String] alt="image-tag"
1123
+ #######################################################################
1124
+ def agile_img_alt_tag(file_name, text = nil)
1125
+ %( alt="#{agile_img_alt(file_name, text)}" ).html_safe
1126
+ end
1127
+
1128
+ #######################################################################
1129
+ # Will return alt image option when text is provided. When text is blank
1130
+ # it will extract alt name from picture file_name. This method returns just
1131
+ # alt name.
1132
+ #
1133
+ # Parameters:
1134
+ # [file_name] String: Filename of a picture
1135
+ # [text] String: Alt text name
1136
+ #
1137
+ # Returns:
1138
+ # [String] alt_image_name
1139
+ #######################################################################
1140
+ def agile_img_alt(file_name, text = nil)
1141
+ return text if text.present?
1142
+
1143
+ name = File.basename(file_name.to_s)
1144
+ name[0, name.index('.')].downcase rescue name
1145
+ end
1146
+
1147
+ ########################################################################
1148
+ # Will return name for value defined in ar_big_table
1149
+ ########################################################################
1150
+ def agile_big_table_name_for_value(key, value)
1151
+ agile_big_table_choices(key).each { |k, val| return k if val.to_s == value.to_s}
1152
+ '???'
1153
+ end
1154
+
1155
+ private
1156
+
1157
+ # will cache agile_user_can_view response
1158
+ def cache_can_view(id, can_view, msg)
1159
+ @can_view_cache[id] = [can_view, msg]
1160
+ end
1161
+
1162
+ end