drg_cms 0.5.52.16 → 0.6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/drg_cms/drg_cms.js +446 -144
  3. data/app/assets/javascripts/drg_cms/jstree.min.js +6 -6
  4. data/app/assets/javascripts/drg_cms_application.js +1 -1
  5. data/app/assets/stylesheets/drg_cms/drg_cms.css +625 -243
  6. data/app/assets/stylesheets/drg_cms/jstree.css +6 -12
  7. data/app/assets/stylesheets/drg_cms/select-multiple.css +18 -12
  8. data/app/assets/stylesheets/drg_cms_cms.css +1 -1
  9. data/app/controllers/cmsedit_controller.rb +536 -331
  10. data/app/controllers/dc_application_controller.rb +380 -117
  11. data/app/controllers/dc_common_controller.rb +105 -14
  12. data/app/{controllers → controls}/browse_models_control.rb +0 -0
  13. data/app/controls/dc_help_control.rb +126 -0
  14. data/app/{controllers → controls}/dc_page_control.rb +24 -8
  15. data/app/controls/dc_poll_result_control.rb +88 -0
  16. data/app/controls/dc_report.rb +225 -0
  17. data/app/{controllers → controls}/design_element_settings_control.rb +0 -0
  18. data/app/forms/all_options.yml +50 -9
  19. data/app/forms/cms_menu.yml +28 -18
  20. data/app/forms/dc_ad.yml +11 -22
  21. data/app/forms/dc_big_table.yml +1 -0
  22. data/app/forms/dc_big_table_value.yml +1 -0
  23. data/app/forms/dc_category.yml +2 -1
  24. data/app/forms/dc_design.yml +20 -16
  25. data/app/forms/dc_filter.yml +3 -6
  26. data/app/forms/dc_help_1.yml +109 -0
  27. data/app/forms/dc_journal.yml +3 -1
  28. data/app/forms/dc_json_ld.yml +59 -0
  29. data/app/forms/dc_key_value.yml +32 -0
  30. data/app/forms/dc_link.yml +1 -1
  31. data/app/forms/dc_menu.yml +2 -0
  32. data/app/forms/dc_menu_item.yml +2 -0
  33. data/app/forms/dc_page.yml +3 -5
  34. data/app/forms/dc_part.yml +1 -0
  35. data/app/forms/dc_piece.yml +1 -0
  36. data/app/forms/dc_poll.yml +19 -9
  37. data/app/forms/dc_poll_item.yml +2 -1
  38. data/app/forms/dc_poll_result.yml +83 -0
  39. data/app/forms/dc_poll_result_export.yml +35 -0
  40. data/app/forms/dc_seo.yml +33 -0
  41. data/app/forms/dc_simple_menu.yml +2 -0
  42. data/app/forms/dc_site.yml +4 -12
  43. data/app/forms/dc_user.yml +27 -11
  44. data/app/forms/dc_user_role.yml +3 -0
  45. data/app/forms/json_ld_schema.yml +168 -0
  46. data/app/helpers/cms_common_helper.rb +376 -0
  47. data/app/helpers/cms_edit_helper.rb +506 -0
  48. data/app/helpers/cms_helper.rb +268 -0
  49. data/app/helpers/cms_index_helper.rb +576 -0
  50. data/app/helpers/dc_application_helper.rb +183 -271
  51. data/app/models/concerns/dc_page_concern.rb +40 -6
  52. data/app/models/concerns/dc_policy_rule_concern.rb +20 -8
  53. data/app/models/concerns/dc_seo_concern.rb +66 -0
  54. data/app/models/concerns/dc_site_concern.rb +67 -44
  55. data/app/models/concerns/dc_user_concern.rb +62 -21
  56. data/app/models/dc_design.rb +30 -18
  57. data/app/models/dc_filter.rb +22 -13
  58. data/app/models/dc_json_ld.rb +152 -0
  59. data/app/models/dc_key_value.rb +48 -0
  60. data/app/models/dc_key_value_store.rb +1 -0
  61. data/app/models/dc_memory.rb +8 -1
  62. data/app/models/dc_page.rb +0 -1
  63. data/app/models/dc_permission.rb +49 -9
  64. data/app/models/dc_policy.rb +25 -14
  65. data/app/models/dc_policy_role.rb +22 -11
  66. data/app/models/dc_poll.rb +39 -19
  67. data/app/models/{drgcms_form_fields/drgcms_form_fields.rb → dc_poll_result.rb} +23 -2
  68. data/app/models/dc_temp.rb +140 -0
  69. data/app/models/dc_user_role.rb +2 -2
  70. data/app/models/drgcms_form_fields.rb +14 -6
  71. data/app/models/drgcms_form_fields/action.rb +61 -0
  72. data/app/models/drgcms_form_fields/check_box.rb +3 -0
  73. data/app/models/drgcms_form_fields/comment.rb +8 -4
  74. data/app/models/drgcms_form_fields/date_picker.rb +11 -9
  75. data/app/models/drgcms_form_fields/date_select.rb +1 -1
  76. data/app/models/drgcms_form_fields/{date_time_picker.rb → datetime_picker.rb} +12 -10
  77. data/app/models/drgcms_form_fields/datetime_select.rb +1 -1
  78. data/app/models/drgcms_form_fields/drgcms_field.rb +55 -9
  79. data/app/models/drgcms_form_fields/embedded.rb +26 -16
  80. data/app/models/drgcms_form_fields/file_field.rb +52 -0
  81. data/app/models/drgcms_form_fields/file_select.rb +2 -2
  82. data/app/models/drgcms_form_fields/hash_field.rb +90 -0
  83. data/app/models/drgcms_form_fields/hidden_field.rb +1 -1
  84. data/app/models/drgcms_form_fields/html_field.rb +1 -1
  85. data/app/models/drgcms_form_fields/link_to.rb +2 -2
  86. data/app/models/drgcms_form_fields/method.rb +66 -0
  87. data/app/models/drgcms_form_fields/multitext_autocomplete.rb +23 -14
  88. data/app/models/drgcms_form_fields/number_field.rb +15 -6
  89. data/app/models/drgcms_form_fields/radio.rb +96 -0
  90. data/app/models/drgcms_form_fields/readonly.rb +2 -2
  91. data/app/models/drgcms_form_fields/select.rb +92 -29
  92. data/app/models/drgcms_form_fields/text_area.rb +1 -1
  93. data/app/models/drgcms_form_fields/text_autocomplete.rb +29 -17
  94. data/app/models/drgcms_form_fields/text_field.rb +1 -1
  95. data/app/models/drgcms_form_fields/text_with_select.rb +6 -3
  96. data/app/models/drgcms_form_fields/tree_select.rb +29 -10
  97. data/app/{helpers → renderers}/dc_ad_renderer.rb +0 -0
  98. data/app/{helpers → renderers}/dc_big_menu_renderer.rb +1 -0
  99. data/app/{helpers → renderers}/dc_captcha_renderer.rb +0 -0
  100. data/app/{helpers → renderers}/dc_common_renderer.rb +0 -0
  101. data/app/{helpers → renderers}/dc_gallery_renderer.rb +1 -0
  102. data/app/{helpers → renderers}/dc_menu_renderer.rb +11 -4
  103. data/app/{helpers → renderers}/dc_page_renderer.rb +1 -0
  104. data/app/{helpers → renderers}/dc_part_renderer.rb +5 -4
  105. data/app/{helpers → renderers}/dc_piece_renderer.rb +1 -1
  106. data/app/{helpers → renderers}/dc_poll_renderer.rb +84 -48
  107. data/app/{helpers → renderers}/dc_renderer.rb +1 -0
  108. data/app/{helpers → renderers}/dc_simple_menu_renderer.rb +1 -0
  109. data/app/views/cmsedit/{remove_edit_stuff.js.erb → __remove_edit_stuff.js.erb} +0 -0
  110. data/app/views/cmsedit/{show.html.erb → __show.html.erb} +0 -0
  111. data/app/views/cmsedit/_edit_stuff.html.erb +4 -25
  112. data/app/views/cmsedit/_form.html.erb +4 -3
  113. data/app/views/cmsedit/_result.html.erb +2 -3
  114. data/app/views/cmsedit/edit.html.erb +4 -1
  115. data/app/views/cmsedit/index.html.erb +7 -1
  116. data/app/views/cmsedit/new.html.erb +3 -1
  117. data/app/views/dc_common/_help.html.erb +8 -0
  118. data/app/views/layouts/models.html.erb +2 -1
  119. data/config/locales/drgcms_en.yml +26 -3
  120. data/config/locales/drgcms_sl.yml +31 -5
  121. data/config/locales/models_en.yml +66 -18
  122. data/config/locales/models_sl.yml +71 -20
  123. data/drg_cms.gemspec +3 -3
  124. data/lib/drg_cms.rb +63 -1
  125. data/lib/drg_cms/version.rb +2 -2
  126. data/lib/generators/new_drg_form/new_drg_form_generator.rb +9 -2
  127. data/lib/tasks/database.rake +6 -56
  128. metadata +81 -63
  129. data/app/assets/stylesheets/drg_cms/__jquery-ui.css +0 -339
  130. data/app/helpers/application_helper.rb +0 -2
  131. data/app/helpers/cmsedit_helper.rb +0 -885
  132. data/app/models/dc_dummy.rb +0 -102
  133. data/test/fixtures/drg_cms_test_data.rb +0 -87
@@ -0,0 +1,268 @@
1
+ #--
2
+ # Copyright (c) 2012+ 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
+ # CmseditHelper module defines common methods used for DRGCMS forms.
27
+ #
28
+ ###########################################################################
29
+ module CmsHelper
30
+ # javascript part created by form helpers
31
+ attr_reader :js
32
+
33
+ ############################################################################
34
+ # Creates code for script action type.
35
+ ############################################################################
36
+ def dc_script_action(yaml)
37
+ # data = {'request' => 'script', 'script' => yaml['js'] || yaml['script'] }
38
+ # %Q[<li class="dc-link-ajax with-link dc-animate">#{ dc_link_to(yaml['caption'], yaml['icon'], '#', data: data ) }</li>]
39
+ icon = dc_icon_for_link yaml['icon']
40
+ data = %Q[data-request="script" data-script="#{yaml['js'] || yaml['script']}"]
41
+ %Q[<li class="dc-link-ajax dc-animate" #{data}>#{icon} #{ t(yaml['caption'],yaml['caption']) }</li>]
42
+ end
43
+
44
+ ############################################################################
45
+ # Will return field form definition if field is defined on form.
46
+ # Field definition will be used for input field on the form.
47
+ ############################################################################
48
+ def dc_get_field_form_definition(name) #:nodoc:
49
+ return if @form['form'].nil?
50
+
51
+ @form['form']['tabs'].each do |tab|
52
+ # Array with 2 elements. First is tabname, second is data
53
+ my_fields = tab.last
54
+ my_fields.each {|k,v| return v if (k.class == Integer and v['name'] == name) }
55
+ end if @form['form']['tabs'] # I know. But nice.
56
+ #
57
+ @form['form']['fields'].each do |field|
58
+ next unless field.first.class == Integer # options
59
+ return field.last if field.last['name'] == name
60
+ end if @form['form']['fields']
61
+ nil
62
+ end
63
+
64
+ ############################################################################
65
+ # Return field code, label and help text for a field defined on a DRG Form.
66
+ #
67
+ # Parameters:
68
+ # options : Hash : Field definition
69
+ #
70
+ # Returns: Array[3]
71
+ # field_html : String : HTML code for field definition
72
+ # label : String : Label text
73
+ # help : String : Help text
74
+ ############################################################################
75
+ def dc_field_label_help(options)
76
+ label, help = dc_label_help(options)
77
+ # create field object from type and call its render method
78
+ if options['type'].present?
79
+ klass_string = options['type'].camelize
80
+ field_html = if DrgcmsFormFields.const_defined?(klass_string) # when field type defined
81
+ klass = DrgcmsFormFields.const_get(klass_string)
82
+ field = klass.new(self, @record, options).render
83
+ @js << field.js
84
+ @css << field.css
85
+ field.html
86
+ else
87
+ "Error: Field type #{options['type']} not defined!"
88
+ end
89
+ else
90
+ "Error: Field type missing!"
91
+ end
92
+ [field_html, label, help]
93
+ end
94
+
95
+ ############################################################################
96
+ # Return label and help text for a field defined on Form.
97
+ #
98
+ # Parameters:
99
+ # options : Hash : Field definition
100
+ #
101
+ # Returns:
102
+ # label : String : Label text
103
+ # help : String : Help text
104
+ ############################################################################
105
+ def dc_label_help(options)
106
+ # no label or help in comments
107
+ return [nil, nil] if %w(comment action).include?(options['type'])
108
+
109
+ label = options['caption'] || options['text'] || options['label']
110
+ label = if label.blank?
111
+ t_name(options['name'], options['name'].capitalize.gsub('_',' ') )
112
+ elsif options['name']
113
+ t(label, label)
114
+ end
115
+ # help text can be defined in form or in translations starting with helpers. or as helpers.help.collection.field
116
+ help = options['help']
117
+ help ||= "helpers.help.#{@form['table']}.#{options['name']}" if options['name']
118
+ help = t(help, ' ') if help.to_s.match(/helpers\./)
119
+ [label, help]
120
+ end
121
+
122
+ ############################################################################
123
+ # Return label and help for tab on Form.
124
+ #
125
+ # Parameters:
126
+ # options : String : Tab name on form
127
+ #
128
+ # Returns:
129
+ # label : String : Label text
130
+ # help : String : Help text
131
+ ############################################################################
132
+ def dc_tab_label_help(tab_name)
133
+ label = @form['form']['tabs'][tab_name]['caption'] || tab_name
134
+ label = t(label, t_name(label, label))
135
+
136
+ help = @form['form']['tabs'][tab_name]['help'] || "helpers.help.#{@form['table']}.#{tab_name}"
137
+ help = t(help, t_name(help, help))
138
+ help = nil if help.match('helpers.') # help not found in translation
139
+
140
+ [label, help]
141
+ end
142
+
143
+ ############################################################################
144
+ # Creates code for including data entry field in index actions.
145
+ ############################################################################
146
+ def dc_field_action(yaml)
147
+ # assign value if value found in parameters
148
+ if params['record']
149
+ value = params['record'][yaml['name']]
150
+ params["p_#{yaml['name']}"] = value
151
+ end
152
+ # find field definition on form
153
+ if ( field_definition = dc_get_field_form_definition(yaml['name']) )
154
+ # some options may be redefined
155
+ field_definition['size'] = yaml['size'] if yaml['size']
156
+ field, label, help = dc_field_label_help(field_definition)
157
+ else
158
+ yaml['type'] = yaml['field_type']
159
+ field, label, help = dc_field_label_help(yaml)
160
+ end
161
+ # input field will have label as placeholder
162
+ field = field.sub('input',"input placeholder=\"#{label}\"")
163
+ %Q[<li class="no-background">#{field}</li>]
164
+ end
165
+
166
+ ############################################################################
167
+ # Create ex. class="my-class" html code from html options for action
168
+ ############################################################################
169
+ def dc_html_data(yaml)
170
+ return '' if yaml.blank?
171
+ yaml.inject(' ') {|result, e| result = e.last.nil? ? result : result << "#{e.first}=\"#{e.last}\" "}
172
+ end
173
+
174
+ ############################################################################
175
+ # Creates code for link, ajax or windows action for index or form actions.
176
+ #
177
+ # Parameters:
178
+ # yaml: Hash : Action definition
179
+ # record : Object : Currently selected record if available
180
+ # action_active : Boolean : Is action active or disabled
181
+ #
182
+ # Returns:
183
+ # String : HTML code for action
184
+ ############################################################################
185
+ def dc_link_ajax_window_submit_action(yaml, record=nil, action_active=true)
186
+ parms = {}
187
+ caption = yaml['caption'] || yaml['text']
188
+ caption = caption ? t("#{caption.downcase}", caption) : nil
189
+ icon = yaml['icon'] ? "#{fa_icon(yaml['icon'])}" : ''
190
+ # action is not active
191
+ unless dc_is_action_active?(yaml)
192
+ return "<li class=\"dc-link-no\">#{icon} #{caption}</li>"
193
+ end
194
+ # set data-confirm when confirm
195
+ yaml['html'] ||= {}
196
+ confirm = yaml['html']['data-confirm'] || yaml['confirm']
197
+ yaml['html']['data-confirm'] = t(confirm) if confirm.present?
198
+ yaml['html']['title'] ||= yaml['title']
199
+ yaml['html']['title'] = t(yaml['title']) if yaml['title'].present?
200
+ yaml['html']['target'] ||= yaml['target']
201
+ # direct url
202
+ if yaml['url']
203
+ parms['url'] = yaml['url']
204
+ parms['idr'] = dc_document_path(record) if record
205
+ # make url from action controller
206
+ else
207
+ parms['controller'] = yaml['controller'] || 'cmsedit'
208
+ parms['action'] = yaml['action']
209
+ parms['table'] = yaml['table'] || @form['table']
210
+ parms['form_name'] = yaml['form_name']
211
+ parms['control'] = yaml['control'] if yaml['control']
212
+ parms['id'] = record.id if record
213
+ end
214
+ # add current id to parameters
215
+ parms['id'] = dc_document_path(record) if record
216
+ # overwrite with or add additional parameters from environment or record
217
+ yaml['params'].each { |k, v| parms[k] = dc_value_for_parameter(v, record) } if yaml['params']
218
+
219
+ parms['table'] = parms['table'].underscore if parms['table'] # might be CamelCase
220
+ # error if controller parameter is missing
221
+ if parms['controller'].nil? && parms['url'].nil?
222
+ "<li>#{'Controller not defined'}</li>"
223
+ else
224
+ #yaml['caption'] ||= yaml['text']
225
+ html_data = dc_html_data(yaml['html'])
226
+ url = url_for(parms) rescue 'URL error'
227
+ url = nil if parms['url'] == '#'
228
+
229
+ request = yaml['request'] || yaml['method'] || 'get'
230
+ if yaml['type'] == 'ajax' # ajax button
231
+ clas = "dc-link-ajax dc-animate"
232
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}
233
+ data-request="#{request}" title="#{yaml['title']}">#{icon}#{caption}</li>]
234
+
235
+ elsif yaml['type'] == 'submit' # submit button
236
+ # It's dirty hack, but will prevent not authorized message and render index action correctly
237
+ parms[:filter] = 'on'
238
+ url = url_for(parms) rescue 'URL error'
239
+ clas = "dc-action-submit"
240
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}
241
+ data-request="#{request}" title="#{yaml['title']}">#{icon}#{caption}</li>]
242
+
243
+ elsif yaml['type'] == 'link' # link button
244
+ clas = "dc-link dc-animate"
245
+ link = dc_link_to(caption, yaml['icon'], parms, yaml['html'] )
246
+ %Q[<li class="#{clas}">#{action_active ? link : caption}</li>]
247
+
248
+ elsif yaml['type'] == 'window'
249
+ clas = "dc-link dc-animate dc-window-open"
250
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}>#{icon}#{caption}</li>]
251
+
252
+ else
253
+ '<li>Action Type error</li>'
254
+ end
255
+ end
256
+ end
257
+
258
+ ############################################################################
259
+ #
260
+ ############################################################################
261
+ def dc_log_exception(exception)
262
+ log = exception ? "\n!!!Error: #{exception.message}\n#{exception.backtrace.first.inspect}\n" : ''
263
+ log << "DRG Form processing line: #{session[:form_processing]}\n"
264
+
265
+ logger.error log
266
+ end
267
+
268
+ end
@@ -0,0 +1,576 @@
1
+ #--
2
+ # Copyright (c) 2012+ 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
+ # CmseditHelper module defines helper methods used by cmsedit actions. Output is controlled by
27
+ # data found in 3 major sections of DRG CMS form: index, result_set and form sections.
28
+ #
29
+ ###########################################################################
30
+ module CmsIndexHelper
31
+
32
+ ############################################################################
33
+ # Creates action div for cmsedit index action.
34
+ ############################################################################
35
+ def dc_actions_for_index()
36
+ @js = @form['script'] || @form['js'] || ''
37
+ @css = @form['css'] || ''
38
+ return '' if @form['index'].nil? or @form['readonly']
39
+ actions = @form['index']['actions']
40
+ return '' if actions.blank?
41
+
42
+ std_actions = {2 => 'new', 3 => 'sort', 4 => 'filter' }
43
+ if actions.class == String
44
+ actions = dc_define_standard_actions(actions, std_actions)
45
+ elsif actions['standard']
46
+ actions.merge!(std_actions)
47
+ actions['standard'] = nil
48
+ end
49
+
50
+ # start div with hidden spinner image
51
+ html = %(
52
+ <form id="dc-action-menu">
53
+ <span class="dc-spinner">#{fa_icon('spinner lg spin')}</span>
54
+ <ul class="dc-action-menu">)
55
+
56
+ # Remove actions settings and sort
57
+ only_actions = []
58
+ actions.each { |key, value| only_actions << [key, value] if key.class == Integer }
59
+ only_actions.sort_by!(&:first)
60
+ only_actions.each do |key, options|
61
+ session[:form_processing] = "index:actions: #{key}=#{options}"
62
+ next if options.nil? # must be
63
+ url = @parms.clone
64
+ yaml = options.class == String ? {'type' => options} : options # if single definition simulate type parameter
65
+ action = yaml['type'].to_s.downcase
66
+ if action == 'url'
67
+ dc_deprecate "action: url will be deprecated. Use action: link in index: actions! Form #{params['form_name']}"
68
+ action = 'link'
69
+ end
70
+ # if return_to is present link directly to URL
71
+ if action == 'link' and yaml['url']
72
+ url = yaml['url']
73
+ else
74
+ url['controller'] = yaml['controller'] if yaml['controller']
75
+ url['action'] = yaml['action'] || action
76
+ url['table'] = yaml['table'] if yaml['table']
77
+ url['form_name'] = yaml['form_name'] if yaml['form_name']
78
+ url['control'] = yaml['control'] if yaml['control']
79
+ end
80
+ # html link options
81
+ yhtml = yaml['html'] || {}
82
+ yhtml['title'] = yaml['title'] if yaml['title']
83
+
84
+ code = case
85
+ # sort
86
+ when action == 'sort' then
87
+ choices = [['id','id']]
88
+ if @form['index']['sort']
89
+ @form['index']['sort'].split(',').each do |s|
90
+ s.strip!
91
+ choices << [ t("helpers.label.#{@form['table']}.#{s}"), s ]
92
+ end
93
+ end
94
+ fa_icon('sort-alpha-asc') + ' ' + t('drgcms.sort') + ' ' +
95
+ select('sort', 'sort', choices, { include_blank: true },
96
+ { class: 'drgcms_sort', 'data-table' => @form['table'], 'data-form' => params['form_name']} )
97
+
98
+ # filter
99
+ when action == 'filter' then
100
+ caption = t('drgcms.filter')
101
+ caption << '&nbsp;' + fa_icon('caret-down lg') + DcFilter.menu_filter(self)
102
+ # add filter OFF link
103
+ sess = session[@form['table']]
104
+ if sess and sess[:filter]
105
+ caption << '&nbsp;&nbsp;' + dc_link_to(nil,'remove lg',
106
+ { controller: 'cmsedit', filter: 'off', table: @form['table'], form_name: params['form_name'] },
107
+ { title: DcFilter.title4_filter_off(sess[:filter]) })
108
+ end
109
+ caption
110
+
111
+ # new
112
+ when action == 'new' then
113
+ caption = yaml['caption'] || 'drgcms.new'
114
+ dc_link_to(caption,'plus', url, yhtml )
115
+
116
+ # menu
117
+ when action == 'menu' then
118
+ caption = t(options['caption'], options['caption']) + '&nbsp;' + fa_icon('caret-down lg')
119
+ caption + eval(options['eval'])
120
+ =begin
121
+ # reorder
122
+ when action == 'reorder' then
123
+ caption = t('drgcms.reorder')
124
+ parms = @parms.clone
125
+ parms['operation'] = v
126
+ parms['id'] = params[:ids]
127
+ parms['table'] = @form['table']
128
+ dc_link_to( caption, 'reorder', parms, method: :delete )
129
+ =end
130
+ when action == 'script'
131
+ html << dc_script_action(options)
132
+ next
133
+
134
+ when action == 'field'
135
+ html << dc_field_action(yaml)
136
+ next
137
+
138
+ when %w(ajax link window submit).include?(action)
139
+ html << dc_link_ajax_window_submit_action(options, nil)
140
+ next
141
+
142
+ else
143
+ caption = yaml['caption'] || yaml['text']
144
+ icon = yaml['icon'] ? yaml['icon'] : action
145
+ dc_link_to(caption, icon, url, yhtml)
146
+ end
147
+ html << "<li class=\"dc-link dc-animate\">#{code}</li>"
148
+ html << DcFilter.get_filter_field(self) if action == 'filter'
149
+ end
150
+ html << '</ul></form>'
151
+ html.html_safe
152
+ end
153
+
154
+ ############################################################################
155
+ # Creates filter div for cmsedit index/filter action.
156
+ ############################################################################
157
+ def dc_div_filter
158
+ choices = []
159
+ filter = (@form['index'] and @form['index']['filter']) ? @form['index']['filter'] + ',' : ''
160
+ filter << 'id as text_field' # filter id is added by default
161
+ filter.split(',').each do |f|
162
+ f.strip!
163
+ name = f.match(' as ') ? f.split(' ').first : f
164
+ # like another field on the form
165
+ if f.match(' like ')
166
+ a = f.split(' ')
167
+ name = a.first
168
+ f = a.last
169
+ end
170
+ choices << [ t("helpers.label.#{@form['table']}.#{name}", name), f ]
171
+ end
172
+ choices4_operators = t('drgcms.choices4_filter_operators').chomp.split(',').inject([]) {|r,v| r << (v.match(':') ? v.split(':') : v )}
173
+ # currently selected options
174
+ if session[@form['table']] and session[@form['table']][:filter]
175
+ field_name, operators_value, dummy = session[@form['table']][:filter].split("\t")
176
+ else
177
+ field_name, operators_value = nil, nil
178
+ end
179
+ #{ form_tag :table => @form['table'], filter: :on, filter_input: 1, action: :index, method: :post }
180
+ url = url_for(table: @form['table'],form_name: params['form_name'], filter: :on, filter_input: 1, action: :index, controller: :cmsedit)
181
+ html =<<EOT
182
+ <div id="drgcms_filter" class="div-hidden">
183
+ <h1>#{t('drgcms.filter_set')}</h1>
184
+
185
+ #{ select(nil, 'filter_field1', options_for_select(choices, field_name), { include_blank: true }) }
186
+ #{ select(nil, 'filter_oper', options_for_select(choices4_operators, operators_value)) }
187
+ <div class="dc-menu">
188
+ <div class="dc-link dc-animate drgcms_popup_submit" data-url="#{url}">#{fa_icon('check-square-o')} #{t('drgcms.filter_on')}</div>
189
+ <div class="dc-link dc-animate">#{dc_link_to('drgcms.filter_off','close', {action: :index, filter: 'off', table: @form['table'], form_name: params['form_name']}) }</div>
190
+ </div>
191
+ </div>
192
+ EOT
193
+ html.html_safe
194
+ end
195
+
196
+ ############################################################################
197
+ # Creates popup div for setting filter on result set header.
198
+ ############################################################################
199
+ def dc_filter_popup
200
+ html = %Q[<div class="filter-popup" style="display: none;">
201
+ <div>#{t('drgcms.filter_set')}</div>
202
+ <ul>]
203
+ url = url_for(table: @form['table'],form_name: params['form_name'], filter: :on, filter_input: 1, action: :index, controller: :cmsedit)
204
+ t('drgcms.choices4_filter_operators').chomp.split(',').each do |operator_choice|
205
+ caption,choice = operator_choice.split(':')
206
+ html << %Q[<li data-operator="#{choice}" data-url="#{url}">#{caption}</li>]
207
+ end
208
+ html << "</ul></div>"
209
+ html.html_safe
210
+ end
211
+
212
+ ############################################################################
213
+ # Will return title based on @form['title']
214
+ ############################################################################
215
+ def dc_form_title
216
+ return t("helpers.label.#{@form['table']}.tabletitle", @form['table']) if @form['title'].nil?
217
+ return t(@form['title'], @form['title']) if @form['title'].class == String
218
+
219
+ # Hash
220
+ dc_process_eval(@form['title']['eval'], [@form['title']['caption'] || @form['title']['text'], params])
221
+ end
222
+
223
+ ############################################################################
224
+ # Creates title div for index action. Title div also includes paging options
225
+ # and help link
226
+ ############################################################################
227
+ def dc_title_for_index(result = nil)
228
+ dc_table_title(dc_form_title(), result)
229
+ end
230
+
231
+ ############################################################################
232
+ # Determines actions and width of actions column
233
+ ############################################################################
234
+ def dc_actions_column
235
+ actions = @form['result_set']['actions']
236
+ return [{}, 0] if actions.nil? || dc_dont?(actions)
237
+
238
+ # standard actions
239
+ actions = {'standard' => true} if actions.class == String && actions == 'standard'
240
+ std_actions = { 2 => 'edit', 5 => 'delete' }
241
+ if actions['standard']
242
+ actions.merge!(std_actions)
243
+ actions.delete('standard')
244
+ end
245
+
246
+ width = @form['result_set']['actions_width'] || 18*actions.size
247
+ [actions, width]
248
+ end
249
+
250
+ ############################################################################
251
+ # Calculates (blank) space required for actions when @record_footer is rendered
252
+ ############################################################################
253
+ def dc_actions_column_for_footer
254
+ return '' unless @form['result_set']['actions']
255
+
256
+ ignore, width = dc_actions_column
257
+ %Q[<div class="actions" style="width: #{width}px;"></div>].html_safe
258
+ end
259
+
260
+ ############################################################################
261
+ # Creates actions that could be performed on single row of result set.
262
+ ############################################################################
263
+ def dc_actions_for_result(document)
264
+ actions = @form['result_set']['actions']
265
+ return '' if actions.nil? or @form['readonly']
266
+
267
+ actions, width = dc_actions_column()
268
+ html = %Q[<ul class="actions" style="width: #{width}px;">]
269
+ actions.sort_by(&:first).each do |k, v|
270
+ session[:form_processing] = "result_set:actions: #{k}=#{v}"
271
+ parms = @parms.clone
272
+ # if single definition simulate type parameter
273
+ yaml = v.class == String ? { 'type' => v } : v
274
+ # code already includes li tag
275
+ if %w(ajax link window submit).include?(yaml['type'])
276
+ @record = document # otherwise document fields can't be used as parameters
277
+ html << dc_link_ajax_window_submit_action(yaml, document)
278
+ else
279
+ html << '<li class="dc-link">'
280
+ html << case
281
+ when yaml['type'] == 'edit' then
282
+ parms['action'] = 'edit'
283
+ parms['id'] = document.id
284
+ dc_link_to( nil, 'pencil lg', parms )
285
+ when yaml['type'] == 'duplicate' then
286
+ parms['id'] = document.id
287
+ # duplicate string will be added to these fields.
288
+ parms['dup_fields'] = yaml['dup_fields']
289
+ parms['action'] = 'create'
290
+ dc_link_to( nil, 'copy lg', parms, data: { confirm: t('drgcms.confirm_dup') }, method: :post )
291
+ when yaml['type'] == 'delete' then
292
+ parms['action'] = 'destroy'
293
+ parms['id'] = document.id
294
+ parms['return_to'] = request.url
295
+ dc_link_to( nil, 'remove lg', parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete )
296
+ # undocumented so far
297
+ when yaml['type'] == 'edit_embedded'
298
+ parms['controller'] = 'cmsedit'
299
+ parms['table'] += ";#{yaml['table']}"
300
+ parms['ids'] ||= ''
301
+ parms['ids'] += "#{document.id};"
302
+ dc_link_to( nil, 'table lg', parms, method: :get )
303
+
304
+ else # error.
305
+ yaml['type'].to_s
306
+ end
307
+ html << '</li>'
308
+ end
309
+ end
310
+ html << '</ul>'
311
+ html.html_safe
312
+ end
313
+
314
+ ############################################################################
315
+ # Creates header div for result set.
316
+ ############################################################################
317
+ def dc_header_for_result()
318
+ html = '<div class="dc-result-header">'
319
+ if @form['result_set']['actions'] and !@form['readonly']
320
+ ignore, width = dc_actions_column()
321
+ html << %Q[<div class="actions" style="width:#{width}px;"></div>]
322
+ end
323
+ # preparation for sort icon
324
+ sort_field, sort_direction = nil, nil
325
+ if session[@form['table']]
326
+ sort_field, sort_direction = session[@form['table']][:sort].to_s.split(' ')
327
+ end
328
+
329
+ if (columns = @form['result_set']['columns'])
330
+ columns.sort.each do |k, v|
331
+ session[:form_processing] = "result_set:columns: #{k}=#{v}"
332
+ next if v['width'].to_s.match(/hidden|none/i)
333
+
334
+ th = %Q[<div class="th" style="width:#{v['width'] || '15%'};text-align:#{v['align'] || 'left'};" data-name="#{v['name']}"]
335
+ label = v['caption'] || v['label']
336
+ label = (v['name'] ? "helpers.label.#{@form['table']}.#{v['name']}" : '') if label.nil?
337
+ label = t(label) if label.match(/\./)
338
+ # no sorting when embedded documents or custom filter is active
339
+ sort_ok = @form['result_set'].nil? || (@form['result_set'] && @form['result_set']['filter'].nil?)
340
+ sort_ok = sort_ok || (@form['index'] && @form['index']['sort'])
341
+ sort_ok = sort_ok && !dc_dont?(v['sort'], false)
342
+ if @tables.size == 1 and sort_ok
343
+ icon = 'sort lg'
344
+ if v['name'] == sort_field
345
+ icon = sort_direction == '1' ? 'sort-alpha-asc lg' : 'sort-alpha-desc lg'
346
+ end
347
+ th << ">#{dc_link_to(label, icon, sort: v['name'], table: params[:table], form_name: params[:form_name], action: :index, icon_pos: :last )}</div>"
348
+ else
349
+ th << ">#{label}</div>"
350
+ end
351
+ html << "<div class=\"spacer\"></div>" + th
352
+ end
353
+ end
354
+ (html << '</div>').html_safe
355
+ end
356
+
357
+ ############################################################################
358
+ # Creates link for single or double click on result column
359
+ ############################################################################
360
+ def dc_clicks_for_result(document)
361
+ html = ''
362
+ if @form['result_set']['dblclick']
363
+ yaml = @form['result_set']['dblclick']
364
+ opts = {}
365
+ opts[:controller] = yaml['controller'] || 'cmsedit'
366
+ opts[:action] = yaml['action']
367
+ opts[:table] = yaml['table']
368
+ opts[:form_name] = yaml['form_name']
369
+ opts[:method] = yaml['method'] || 'get'
370
+ opts[:id] = document['id']
371
+ opts[:readonly] = yaml['readonly'] if yaml['readonly']
372
+ opts[:window_close] = yaml['window_close'] if yaml['window_close']
373
+ html << ' data-dblclick=' + url_for(opts)
374
+ else
375
+ html << (' data-dblclick=' +
376
+ url_for(action: 'show', controller: 'cmsedit', id: document.id, ids: params[:ids],
377
+ readonly: (params[:readonly] ? 2 : 1), table: params[:table],
378
+ form_name: params[:form_name]) ) if @form['form']
379
+ end
380
+ html
381
+ end
382
+
383
+ ############################################################################
384
+ # Formats value according to format supplied or data type. There is lots of things missing here.
385
+ ############################################################################
386
+ def dc_format_value(value, format=nil)
387
+ return '' if value.nil?
388
+
389
+ klass = value.class.to_s
390
+ if klass.match(/time|date/i)
391
+ CmsCommonHelper.dc_format_date_time(value, format)
392
+ elsif format.to_s[0] == 'N'
393
+ return '' if value == 0 and format.match('z')
394
+
395
+ dec = format[1].blank? ? nil : format[1].to_i
396
+ sep = format[2].blank? ? nil : format[2]
397
+ del = format[3].blank? ? nil : format[3]
398
+ cur = format[4].blank? ? nil : format[4]
399
+ dc_format_number(value, dec, sep, del, cur)
400
+ else
401
+ value.to_s
402
+ end
403
+ end
404
+
405
+ ############################################################################
406
+ # Creates tr code for each row of result set.
407
+ ############################################################################
408
+ def dc_row_for_result(document)
409
+ clas = "dc-#{cycle('odd','even')} " + dc_style_or_class(nil, @form['result_set']['tr_class'], nil, document)
410
+ style = dc_style_or_class('style', @form['result_set']['tr_style'], nil, document)
411
+ "<div id=\"#{document.id}\" class=\"dc-result-data #{clas}\" #{dc_clicks_for_result(document)} #{style}>".html_safe
412
+ end
413
+
414
+ ############################################################################
415
+ # Creates column for each field of result set document.
416
+ ############################################################################
417
+ def dc_columns_for_result(document)
418
+ return '' unless @form['result_set']['columns']
419
+ html = ''
420
+ @form['result_set']['columns'].sort.each do |k,v|
421
+ session[:form_processing] = "result_set:columns: #{k}=#{v}"
422
+ next if v['width'].to_s.match(/hidden|none/i)
423
+
424
+ # convert shortcut to hash
425
+ v = {'name' => v} if v.class == String
426
+ begin
427
+ # eval
428
+ value = if v['eval']
429
+ dc_process_column_eval(v, document)
430
+ # as field
431
+ elsif document.respond_to?(v['name'])
432
+ dc_format_value(document.send( v['name'] ), v['format'])
433
+ # as hash (dc_memory)
434
+ elsif document.class == Hash
435
+ dc_format_value(document[ v['name'] ], v['format'])
436
+ # error
437
+ else
438
+ "??? #{v['name']}"
439
+ end
440
+ rescue Exception => e
441
+ dc_log_exception(e)
442
+ value = '!!!Error'
443
+ end
444
+ html << '<div class="spacer"></div>'
445
+ # set class
446
+ clas = dc_style_or_class(nil, v['td_class'], value, document)
447
+ # set width and align an additional style
448
+ style = dc_style_or_class(nil, v['td_style'] || v['style'], value, document)
449
+ flex_align = v['align'].to_s == 'right' ? 'flex-direction:row-reverse;' : ''
450
+ width_align = %Q[width:#{v['width'] || '15%'};#{flex_align}]
451
+ style = "style=\"#{width_align}#{style}\" "
452
+
453
+ html << "<div class=\"td #{clas}\" #{style}>#{value}</div>"
454
+ end
455
+ html.html_safe
456
+ end
457
+
458
+ private
459
+
460
+ ############################################################################
461
+ # Process eval. Breaks eval option and calls with send method.
462
+ # Parameters:
463
+ # evaluate : String : Expression to be evaluated
464
+ # parameters : Array : array of parameters which will be send to method
465
+ ############################################################################
466
+ def dc_process_eval(evaluate, parameters)
467
+ # evaluate by calling send method
468
+ clas, method = evaluate.split('.')
469
+ if method.nil?
470
+ send(clas, *parameters)
471
+ else
472
+ klass = clas.camelize.constantize
473
+ klass.send(method, *parameters)
474
+ end
475
+ end
476
+
477
+ ############################################################################
478
+ # Split eval expression to array by parameters.
479
+ # Will split dc_name4_value(one ,"two") => ['dc_name4_value', 'one', 'two']
480
+ ############################################################################
481
+ def dc_eval_to_array(expression)
482
+ expression.split(/\ |\,|\(|\)/).delete_if {|e| e.blank? }.map {|e| e.gsub(/\'|\"/,'').strip }
483
+ end
484
+
485
+ ############################################################################
486
+ # Process eval option for field value.
487
+ # Used for processing single field column on result_set or form head.
488
+ ############################################################################
489
+ def dc_process_column_eval(yaml, document)
490
+ # dc_name_for_id
491
+ if yaml['eval'].match(/dc_name4_id|dc_name_for_id/)
492
+ parms = dc_eval_to_array(yaml['eval'])
493
+ if parms.size == 3
494
+ dc_name_for_id(parms[1], parms[2], nil, document[ yaml['name'] ])
495
+ else
496
+ dc_name_for_id(parms[1], parms[2], parms[3], document[ yaml['name'] ])
497
+ end
498
+
499
+ # dc_name_for_value from locale definition
500
+ elsif yaml['eval'].match(/dc_name4_value|dc_name_for_value/)
501
+ parms = dc_eval_to_array(yaml['eval'])
502
+ if parms.size == 1
503
+ dc_name_for_value( @form['table'], yaml['name'], document[ yaml['name'] ] )
504
+ else
505
+ dc_name_for_value( parms[1], parms[2], document[ yaml['name'] ] )
506
+ end
507
+
508
+ # defined in helpers. For example dc_icon_for_boolean
509
+ elsif respond_to?(yaml['eval'])
510
+ send(yaml['eval'], document[yaml['name']])
511
+
512
+ # defined in model
513
+ elsif document.respond_to?(yaml['eval'])
514
+ document.send(yaml['eval'])
515
+
516
+ # special eval
517
+ elsif yaml['eval'].match('eval ')
518
+ # TO DO evaluate with specified parameters
519
+
520
+ # eval with params
521
+ else
522
+ parms = {}
523
+ if yaml['params'].class == String
524
+ parms = dc_value_for_parameter(yaml['params'], document)
525
+ elsif yaml['params'].class == Hash
526
+ yaml['params'].each { |k, v| parms[k] = dc_value_for_parameter(v) }
527
+ else
528
+ parms = document[ yaml['name'] ]
529
+ end
530
+ dc_process_eval(yaml['eval'], parms)
531
+ end
532
+ end
533
+
534
+ ############################################################################
535
+ # Defines style or class for row (tr) or column (td)
536
+ ############################################################################
537
+ def dc_style_or_class(selector, yaml, value, record)
538
+ return '' if yaml.nil?
539
+
540
+ # alias record and value so both names can be used in eval
541
+ field, document = value, record
542
+ html = selector ? "#{selector}=\"" : ''
543
+ html << if yaml.class == String
544
+ yaml
545
+ # direct evaluate expression
546
+ elsif yaml['eval']
547
+ eval(yaml['eval']) rescue 'background-color:red;'
548
+ elsif yaml['method']
549
+ dc_process_eval(yaml['method'], record)
550
+ end
551
+ html << '"' if selector
552
+ html
553
+ end
554
+
555
+ ############################################################################
556
+ # Get standard actions when actions directive contains single line.
557
+ # Subroutine of dc_actions_for_index
558
+ #
559
+ # Allows for actions: new, filter, standard syntax
560
+ ############################################################################
561
+ def dc_define_standard_actions(actions_params, standard)
562
+ actions = {}
563
+ actions_params.split(',').each do |an_action|
564
+ an_action.strip!
565
+ if an_action == 'standard'
566
+ actions.merge!(standard)
567
+ else
568
+ standard.each do |index, action|
569
+ (actions[index] = action; break) if action == an_action
570
+ end
571
+ end
572
+ end
573
+ actions
574
+ end
575
+
576
+ end