drg_cms 0.6.0.3 → 0.6.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/drg_cms/drg_cms.js +259 -102
  3. data/app/assets/javascripts/drg_cms_cms.js +1 -1
  4. data/app/assets/stylesheets/drg_cms/drg_cms.css +314 -142
  5. data/app/assets/stylesheets/drg_cms/select-multiple.css +11 -2
  6. data/app/controllers/cmsedit_controller.rb +313 -236
  7. data/app/controllers/dc_application_controller.rb +29 -4
  8. data/app/controllers/dc_common_controller.rb +19 -16
  9. data/app/{controllers → controls}/browse_models_control.rb +0 -0
  10. data/app/{controllers → controls}/dc_page_control.rb +24 -8
  11. data/app/controls/dc_poll_result_control.rb +88 -0
  12. data/app/{controllers → controls}/design_element_settings_control.rb +0 -0
  13. data/app/forms/all_options.yml +11 -3
  14. data/app/forms/cms_menu.yml +22 -18
  15. data/app/forms/dc_design.yml +6 -3
  16. data/app/forms/dc_filter.yml +3 -6
  17. data/app/forms/dc_poll_result.yml +74 -0
  18. data/app/forms/dc_poll_result_export.yml +35 -0
  19. data/app/helpers/cmsedit_edit_helper.rb +471 -0
  20. data/app/helpers/cmsedit_helper.rb +151 -821
  21. data/app/helpers/cmsedit_index_helper.rb +567 -0
  22. data/app/helpers/dc_application_helper.rb +48 -31
  23. data/app/models/{dc_dummy.rb → __dc_dummy.rb} +0 -0
  24. data/app/models/dc_filter.rb +12 -5
  25. data/app/models/dc_memory.rb +8 -1
  26. data/app/models/dc_poll.rb +38 -19
  27. data/app/models/dc_poll_result.rb +44 -0
  28. data/app/models/dc_temp.rb +137 -0
  29. data/app/models/drgcms_form_fields/action.rb +61 -0
  30. data/app/models/drgcms_form_fields/comment.rb +8 -4
  31. data/app/models/drgcms_form_fields/date_picker.rb +7 -6
  32. data/app/models/drgcms_form_fields/date_select.rb +1 -1
  33. data/app/models/drgcms_form_fields/datetime_picker.rb +8 -7
  34. data/app/models/drgcms_form_fields/datetime_select.rb +1 -1
  35. data/app/models/drgcms_form_fields/drgcms_field.rb +39 -7
  36. data/app/models/drgcms_form_fields/embedded.rb +7 -2
  37. data/app/models/drgcms_form_fields/file_field.rb +52 -0
  38. data/app/models/drgcms_form_fields/html_field.rb +1 -1
  39. data/app/models/drgcms_form_fields/multitext_autocomplete.rb +7 -4
  40. data/app/models/drgcms_form_fields/number_field.rb +15 -6
  41. data/app/models/drgcms_form_fields/radio.rb +91 -0
  42. data/app/models/drgcms_form_fields/readonly.rb +1 -1
  43. data/app/models/drgcms_form_fields/select.rb +14 -2
  44. data/app/models/drgcms_form_fields/text_area.rb +1 -1
  45. data/app/models/drgcms_form_fields/text_autocomplete.rb +1 -1
  46. data/app/models/drgcms_form_fields/text_field.rb +1 -1
  47. data/app/models/drgcms_form_fields/text_with_select.rb +6 -3
  48. data/app/models/drgcms_form_fields/tree_select.rb +11 -3
  49. data/app/renderers/dc_poll_renderer.rb +29 -11
  50. data/app/views/cmsedit/{remove_edit_stuff.js.erb → __remove_edit_stuff.js.erb} +0 -0
  51. data/app/views/cmsedit/{show.html.erb → __show.html.erb} +0 -0
  52. data/app/views/cmsedit/_edit_stuff.html.erb +2 -4
  53. data/app/views/cmsedit/_form.html.erb +4 -3
  54. data/app/views/cmsedit/_result.html.erb +2 -3
  55. data/app/views/cmsedit/edit.html.erb +2 -1
  56. data/app/views/cmsedit/index.html.erb +6 -1
  57. data/app/views/cmsedit/new.html.erb +1 -1
  58. data/config/locales/drgcms_en.yml +7 -0
  59. data/config/locales/drgcms_sl.yml +8 -1
  60. data/config/locales/models_en.yml +13 -4
  61. data/config/locales/models_sl.yml +13 -2
  62. data/drg_cms.gemspec +1 -1
  63. data/lib/drg_cms.rb +1 -0
  64. data/lib/drg_cms/version.rb +1 -1
  65. data/lib/generators/new_drg_form/new_drg_form_generator.rb +7 -2
  66. metadata +20 -13
  67. data/app/assets/stylesheets/drg_cms/__jquery-ui.css +0 -339
  68. data/test/fixtures/drg_cms_test_data.rb +0 -87
@@ -23,871 +23,201 @@
23
23
 
24
24
  ###########################################################################
25
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.
26
+ # CmseditHelper module defines common methods used for DRGCMS forms.
28
27
  #
29
28
  ###########################################################################
30
29
  module CmseditHelper
31
30
  # javascript part created by form helpers
32
31
  attr_reader :js
33
32
 
34
- ############################################################################
35
- # Get standard actions when actions directive contains single line.
36
- # Subroutine of dc_actions_for_index
37
- #
38
- # Allows for actions: new, filter, standard syntax
39
- ############################################################################
40
- def define_standard_actions(actions_params, standard)
41
- actions = {}
42
- actions_params.split(',').each do |an_action|
43
- an_action.strip!
44
- if an_action == 'standard'
45
- actions.merge!(standard)
46
- else
47
- standard.each do |index, action|
48
- (actions[index] = action; break) if action == an_action
49
- end
50
- end
51
- end
52
- actions
53
- end
54
-
55
- ############################################################################
56
- # Creates action div for cmsedit index action.
57
- ############################################################################
58
- def dc_actions_for_index()
59
- return '' if @form['index'].nil? or @form['readonly']
60
- actions = @form['index']['actions']
61
- return '' if actions.nil? or actions.size == 0
62
- std_actions = {2 => 'new', 3 => 'sort', 4 => 'filter' }
63
- if actions.class == String
64
- actions = define_standard_actions(actions, std_actions)
65
- elsif actions['standard']
66
- actions.merge!(std_actions)
67
- actions['standard'] = nil
68
- end
69
-
70
- # start div with hidden spinner image
71
- html = <<EOT
72
- <div id="dc-action-menu">
73
- <span class="dc-spinner div-hidden">#{fa_icon('spinner lg spin')}</span>
74
- <ul class="dc-action-menu">
75
- EOT
76
- # Remove actions settings and sort
77
- only_actions = []
78
- actions.each { |key, value| only_actions << [key, value] if key.class == Integer }
79
- only_actions.sort_by!(&:first)
80
- only_actions.each do |element|
81
- k,v = element
82
- session[:form_processing] = "index:actions: #{k}=#{v}"
83
- next if v.nil? # must be
84
- url = @parms.clone
85
- yaml = v.class == String ? {'type' => v} : v # if single definition simulate type parameter
86
- action = yaml['type'].to_s.downcase
87
- if action == 'url'
88
- dc_deprecate "action: url will be deprecated. Use action: link in index: actions"
89
- action = 'link'
90
- end
91
- # if return_to is present link directly to URL
92
- if action == 'link' and yaml['url']
93
- url = yaml['url']
94
- else
95
- url['controller'] = yaml['controller'] if yaml['controller']
96
- url['action'] = yaml['action'] || action
97
- url['table'] = yaml['table'] if yaml['table']
98
- url['form_name'] = yaml['form_name'] if yaml['form_name']
99
- end
100
- # html link options
101
- yhtml = yaml['html'] || {}
102
- yhtml['title'] = yaml['title'] if yaml['title']
103
- code = case
104
- # sort
105
- when action == 'sort' then
106
- choices = [['id','id']]
107
- if @form['index']['sort']
108
- @form['index']['sort'].split(',').each do |s|
109
- s.strip!
110
- choices << [ t("helpers.label.#{@form['table']}.#{s}"), s ]
111
- end
112
- end
113
- fa_icon('sort-alpha-asc') + ' ' + t('drgcms.sort') + ' ' +
114
- select('sort', 'sort', choices, { include_blank: true },
115
- { class: 'drgcms_sort', 'data-table' => @form['table']} )
116
- # filter
117
- when action == 'filter' then
118
- caption = t('drgcms.filter')
119
- caption << '&nbsp;' + fa_icon('caret-down lg') + DcFilter.menu_filter(self)
120
- # add filter OFF link
121
- s = session[@form['table']]
122
- if s and s[:filter]
123
- caption << '&nbsp;&nbsp;' + dc_link_to(nil,'remove lg', {controller: 'cmsedit',
124
- filter: 'off', table: @form['table']}, { title: DcFilter.title4_filter_off(s[:filter]) })
125
- end
126
- caption
127
- # new
128
- when action == 'new' then
129
- caption = yaml['caption'] || 'drgcms.new'
130
- dc_link_to(caption,'plus', url, yhtml )
131
- # menu
132
- when action == 'menu' then
133
- caption = t(v['caption'], v['caption']) + '&nbsp;' + fa_icon('caret-down lg')
134
- caption + eval(v['eval'])
135
- =begin
136
- # reorder
137
- when action == 'reorder' then
138
- caption = t('drgcms.reorder')
139
- parms = @parms.clone
140
- parms['operation'] = v
141
- parms['id'] = params[:ids]
142
- parms['table'] = @form['table']
143
- dc_link_to( caption, 'reorder', parms, method: :delete )
144
- =end
145
- when action == 'script'
146
- html << dc_script_action(v)
147
- next
148
- else
149
- caption = yaml['caption'] || yaml['text']
150
- icon = yaml['icon'] ? yaml['icon'] : action
151
- dc_link_to(caption, icon, url, yhtml)
152
- end
153
- html << "<li class=\"dc-animate\">#{code}</li>"
154
- end
155
- html << '</ul>'
156
- html << DcFilter.get_filter_field(self)
157
- html << '</div>'
158
- html.html_safe
159
- end
160
-
161
- ############################################################################
162
- # Creates filter div for cmsedit index/filter action.
163
- ############################################################################
164
- def dc_div_filter()
165
- choices = []
166
- filter = (@form['index'] and @form['index']['filter']) ? @form['index']['filter'] + ',' : ''
167
- filter << 'id as text_field' # filter id is added by default
168
- filter.split(',').each do |f|
169
- f.strip!
170
- name = f.match(' as ') ? f.split(' ').first : f
171
- # like another field on the form
172
- if f.match(' like ')
173
- a = f.split(' ')
174
- name = a.first
175
- f = a.last
176
- end
177
- choices << [ t("helpers.label.#{@form['table']}.#{name}", name), f ]
178
- end
179
- choices4_operators = t('drgcms.choices4_filter_operators').chomp.split(',').inject([]) {|r,v| r << (v.match(':') ? v.split(':') : v )}
180
- # currently selected options
181
- if session[@form['table']] and session[@form['table']][:filter]
182
- field_name, operators_value, dummy = session[@form['table']][:filter].split("\t")
183
- else
184
- field_name, operators_value = nil, nil
185
- end
186
- #{ form_tag :table => @form['table'], filter: :on, filter_input: 1, action: :index, method: :post }
187
- url = url_for(table: @form['table'],form_name: params['form_name'], filter: :on, filter_input: 1, action: :index, controller: :cmsedit)
188
- html =<<EOT
189
- <div id="drgcms_filter" class="div-hidden">
190
- <h1>#{t('drgcms.filter_set')}</h1>
191
-
192
- #{ select(nil, 'filter_field1', options_for_select(choices, field_name), { include_blank: true }) }
193
- #{ select(nil, 'filter_oper', options_for_select(choices4_operators, operators_value)) }
194
- <div class="dc-menu">
195
- <div class="dc-link dc-animate drgcms_popup_submit" data-url="#{url}">#{fa_icon('check-square-o')} #{t('drgcms.filter_on')}</div>
196
- <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>
197
- </div>
198
- </div>
199
- EOT
200
- html.html_safe
201
- end
202
-
203
- ############################################################################
204
- # Creates title div for cmsedit index result set records. Title div also includes paging
205
- # options.
206
- ############################################################################
207
- def dc_table_title_for_result(result=nil)
208
- title = if @form['title'] # form has title section
209
- t(@form['title'], @form['title'])
210
- else # get name from translations
211
- t("helpers.label.#{@form['table']}.tabletitle", @form['table'])
212
- end
213
- dc_table_title(title, result)
214
- end
215
-
216
- ############################################################################
217
- # Creates code for link or ajax action type. Subroutine of dc_actions_for_result.
218
- ############################################################################
219
- def dc_link_or_ajax_action(yaml, parms) #:nodoc:
220
- rest = {}
221
- rest['method'] = yaml['method'] || yaml['request'] || 'get'
222
- rest['caption'] = yaml['caption'] || yaml['text']
223
- rest['class'] = 'dc-animate'
224
- rest['title'] = yaml['title']
225
-
226
- if yaml['type'] == 'link'
227
- dc_link_to(yaml['caption'], yaml['icon'], parms, rest )
228
- else
229
- rest['data-url'] = url_for(parms)
230
- rest['class'] << " fa fa-#{yaml['icon']}"
231
- fa_icon(yaml['icon'], rest )
232
- end
233
- end
234
-
235
33
  ############################################################################
236
34
  # Creates code for script action type.
237
35
  ############################################################################
238
36
  def dc_script_action(yaml)
239
- data = {'request' => 'script', 'script' => yaml['js']}
240
- %Q[<li class="dc-link-ajax dc-animate">#{ dc_link_to(yaml['caption'], yaml['icon'], '#', data: data ) }</li>]
241
- end
242
-
243
- ############################################################################
244
- # Determines actions and width of actions column
245
- ############################################################################
246
- def dc_actions_column()
247
- actions = @form['result_set']['actions']
248
- # standard actions
249
- actions = {'standard' => true} if actions.class == String && actions == 'standard'
250
- std_actions = {' 2' => 'edit', ' 3' => 'delete'}
251
- actions.merge!(std_actions) if actions['standard']
252
- #
253
- width = @form['result_set']['actions_width'] || 20*actions.size
254
- [ actions, "<div class=\"actions\" style=\"width: #{width}px;\">" ]
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>]
255
42
  end
256
43
 
257
44
  ############################################################################
258
- # Creates actions that could be performed on single row of result set.
45
+ # Will return field form definition if field is defined on form.
46
+ # Field definition will be used for input field on the form.
259
47
  ############################################################################
260
- def dc_actions_for_result(document)
261
- actions = @form['result_set']['actions']
262
- return '' if actions.nil? or @form['readonly']
263
- #
264
- actions, html = dc_actions_column()
265
- actions.each do |k,v|
266
- session[:form_processing] = "result_set:actions: #{k}=#{v}"
267
- next if k == 'standard' # ignore standard definition
268
- parms = @parms.clone
269
- yaml = v.class == String ? {'type' => v} : v # if single definition simulate type parameter
270
- html << case
271
- when yaml['type'] == 'edit' then
272
- parms['action'] = 'edit'
273
- parms['id'] = document.id
274
- dc_link_to( nil, 'pencil lg', parms )
275
- when yaml['type'] == 'duplicate' then
276
- parms['id'] = document.id
277
- # duplicate string will be added to these fields.
278
- parms['dup_fields'] = yaml['dup_fields']
279
- parms['action'] = 'create'
280
- dc_link_to( nil, 'copy lg', parms, data: { confirm: t('drgcms.confirm_dup') }, method: :post )
281
- when yaml['type'] == 'delete' then
282
- parms['action'] = 'destroy'
283
- parms['id'] = document.id
284
- parms['return_to'] = request.url
285
- dc_link_to( nil, 'remove lg', parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete )
286
- # undocumented so far
287
- when yaml['type'] == 'edit_embedded'
288
- parms['controller'] = 'cmsedit'
289
- parms['table'] += ";#{yaml['table']}"
290
- parms['ids'] ||= ''
291
- parms['ids'] += "#{document.id};"
292
- dc_link_to( nil, 'table lg', parms, method: :get )
293
- when yaml['type'] == 'link' || yaml['type'] == 'ajax' then
294
- if yaml['url']
295
- parms['controller'] = yaml['url']
296
- parms['idr'] = document.id
297
- else
298
- parms['id'] = document.id
299
- end
300
- parms['controller'] = yaml['controller'] if yaml['controller']
301
- parms['action'] = yaml['action'] if yaml['action']
302
- parms['table'] = yaml['table'] if yaml['table']
303
- parms['form_name'] = yaml['form_name'] if yaml['form_name']
304
- parms['target'] = yaml['target'] if yaml['target']
305
- dc_link_or_ajax_action(yaml, parms)
306
- else # error.
307
- yaml['type'].to_s
308
- end
309
- end
310
- html << '</div>'
311
- html.html_safe
48
+ def dc_get_field_form_definition(name) #:nodoc:
49
+ return nil 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
312
62
  end
313
63
 
314
64
  ############################################################################
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, code = dc_actions_column()
321
- html << code + '</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.each do |k,v|
331
- session[:form_processing] = "result_set:columns: #{k}=#{v}"
332
- #
333
- th = %Q[<div class="th" style="width: #{v['width'] || '15%'};text-align: #{v['align'] || 'left'};"]
334
- v = {'name' => v} if v.class == String
335
- caption = v['caption'] || t("helpers.label.#{@form['table']}.#{v['name']}")
336
- # no sorting when embedded documents or custom filter is active
337
- sort_ok = @form['result_set'].nil? || (@form['result_set'] && @form['result_set']['filter'].nil?)
338
- sort_ok = sort_ok || (@form['index'] && @form['index']['sort'])
339
- if @tables.size == 1 and sort_ok
340
- icon = 'sort lg'
341
- if v['name'] == sort_field
342
- icon = sort_direction == '1' ? 'sort-alpha-asc lg' : 'sort-alpha-desc lg'
343
- end
344
- th << ">#{dc_link_to(caption, icon, sort: v['name'], table: params[:table], form_name: params[:form_name], action: :index, icon_pos: :last )}</div>"
345
- else
346
- th << ">#{caption}</div>"
347
- end
348
- html << "<div class=\"spacer\"></div>" + th
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:
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
+ # no label or help in comments
77
+ unless %w(comment action).include?(options['type'])
78
+ caption = options['caption'] || options['text']
79
+ label = if caption.blank?
80
+ t_name(options['name'], options['name'].capitalize.gsub('_',' ') )
81
+ elsif options['name']
82
+ t(caption, caption)
349
83
  end
84
+ # help text can be defined in form or in translations starting with helpers. or as helpers.help.collection.field
85
+ help = if options['help']
86
+ options['help'].match('helpers.') ? t(options['help']) : options['help']
87
+ end
88
+ help ||= t('helpers.help.' + @form['table'] + '.' + options['name'],' ') if options['name']
350
89
  end
351
- (html << '</div>').html_safe
352
- end
353
-
354
- ############################################################################
355
- # Creates link for single or double click on result column
356
- ############################################################################
357
- def dc_clicks_for_result(document)
358
- html = ''
359
- if @form['result_set']['dblclick']
360
- yaml = @form['result_set']['dblclick']
361
- opts = {}
362
- opts[:controller] = yaml['controller'] || 'cmsedit'
363
- opts[:action] = yaml['action']
364
- opts[:table] = yaml['table']
365
- opts[:form_name] = yaml['form_name']
366
- opts[:method] = yaml['method'] || 'get'
367
- opts[:id] = document['id']
368
- html << ' data-dblclick=' + url_for(opts)
369
- else
370
- html << (' data-dblclick=' +
371
- url_for(action: 'show', controller: 'cmsedit', id: document.id,
372
- readonly: (params[:readonly] ? 2 : 1), table: params[:table],
373
- form_name: params[:form_name], ids: params[:ids]) ) if @form['form']
90
+ # create field object from class and call its render method
91
+ klass_string = options['type'].camelize
92
+ field_html = if DrgcmsFormFields.const_defined?(klass_string) # when field type defined
93
+ klass = DrgcmsFormFields.const_get(klass_string)
94
+ field = klass.new(self, @record, options).render
95
+ @js << field.js
96
+ @css << field.css
97
+ field.html
98
+ else # litle error string
99
+ "Error: Field type #{options['type']} not defined!"
374
100
  end
375
- html
101
+ [field_html, label, help]
376
102
  end
377
103
 
378
104
  ############################################################################
379
- # Formats value according to format supplied or data type. There is lots of things missing here.
105
+ # Creates code for including data entry field in index actions.
380
106
  ############################################################################
381
- def dc_format_value(value, format=nil)
382
- return '' if value.nil?
383
- klass = value.class.to_s
384
- case when klass.match('Time') then
385
- format ||= t('time.formats.default')
386
- value.strftime(format)
387
- when klass.match('Date') then
388
- format ||= t('date.formats.default')
389
- value.strftime(format)
390
- when format.to_s[0] == 'N' then
391
- dec = format[1].blank? ? nil : format[1].to_i
392
- sep = format[2].blank? ? nil : format[2]
393
- del = format[3].blank? ? nil : format[3]
394
- cur = format[4].blank? ? nil : format[4]
395
- dc_format_number(value, dec, sep, del, cur)
396
- else
397
- value.to_s
107
+ def dc_field_action(yaml)
108
+ # assign value if value found in parameters
109
+ if params['record']
110
+ value = params['record'][yaml['name']]
111
+ params["p_#{yaml['name']}"] = value
398
112
  end
399
- end
400
-
401
- ############################################################################
402
- # Defines style or class for row (tr) or column (td)
403
- ############################################################################
404
- def dc_style_or_class(selector, yaml, value, record)
405
- return '' if yaml.nil?
406
- # alias record and value so both names can be used in eval
407
- field = value
408
- document = record
409
- html = selector ? "#{selector}=\"" : ''
410
- html << if yaml.class == String
411
- yaml
113
+ #
114
+ if ( field_definition = dc_get_field_form_definition(yaml['name']) )
115
+ field, label, help = dc_field_label_help(field_definition)
412
116
  else
413
- (yaml['eval'] ? eval(yaml['eval']) : '') rescue 'background-color:red;'
117
+ yaml['type'] = yaml['field_type']
118
+ field, label, help = dc_field_label_help(yaml)
414
119
  end
415
- html << '"' if selector
416
- html
417
- end
418
-
419
- ############################################################################
420
- # Creates tr code for each row of result set.
421
- ############################################################################
422
- def dc_row_for_result(document)
423
- clas = "dc-#{cycle('odd','even')} " + dc_style_or_class(nil, @form['result_set']['tr_class'], nil, document)
424
- style = dc_style_or_class('style', @form['result_set']['tr_style'], nil, document)
425
- "<div class=\"dc-result-data #{clas}\" #{dc_clicks_for_result(document)} #{style}>".html_safe
120
+ # input field will have label as placeholder
121
+ field = field.sub('input',"input placeholder=\"#{label}\"")
122
+ %Q[<li class="no-background">#{field}</li>]
123
+
426
124
  end
427
125
 
428
126
  ############################################################################
429
- # Creates column for each field of result set document.
430
- ############################################################################
431
- def dc_columns_for_result(document)
432
- html = ''
433
- return html unless @form['result_set']['columns']
434
- #
435
- @form['result_set']['columns'].each do |k,v|
436
- session[:form_processing] = "result_set:columns: #{k}=#{v}"
437
- # convert shortcut to hash
438
- v = {'name' => v} if v.class == String
439
- # eval
440
- value = if v['eval']
441
- if v['eval'].match('dc_name4_id')
442
- a = v['eval'].split(',')
443
- if a.size == 3
444
- dc_name4_id(a[1], a[2], nil, document[ v['name'] ])
445
- else
446
- dc_name4_id(a[1], a[2], a[3], document[ v['name'] ])
447
- end
448
- elsif v['eval'].match('dc_name4_value')
449
- dc_name4_value( @form['table'], v['name'], document[ v['name'] ] )
450
- elsif v['eval'].match('eval ')
451
- # evaluate with specified parameters
452
- else
453
- if v['params']
454
- if v['params'] == 'document' # pass document as parameter when all
455
- eval( "#{v['eval']} document")
456
- else # list of fields delimeted by ,
457
- params = v['params'].chomp.split(',').inject('') do |result,e|
458
- result << (e.match(/\.|\:|\(/) ? e : "document['#{e.strip}']") + ','
459
- end
460
- params.chomp!(',')
461
- eval( "#{v['eval']} #{params}")
462
- end
463
- else
464
- eval( "#{v['eval']} '#{document[ v['name'] ]}'")
465
- end
466
- end
467
- # as field
468
- elsif document.respond_to?(v['name'])
469
- dc_format_value(document.send( v['name'] ), v['format'])
470
- # as hash (dc_memory)
471
- elsif document.class == Hash
472
- dc_format_value(document[ v['name'] ], v['format'])
473
- # error
474
- else
475
- "!!! #{v['name']}"
476
- end
477
127
  #
478
- td = '<div class="spacer"></div><div class="td" '
479
- td << dc_style_or_class('class', v['td_class'], value, document)
480
-
481
- width_align = %Q[width: #{v['width'] || '15%'};text-align: #{v['align'] || 'left'};]
482
- style = dc_style_or_class('style', v['td_style'] || v['style'], value, document)
483
- style = if style.size > 1
484
- # remove trailing " add width and add trailing " back
485
- style.delete_suffix('"') + width_align + '"'
486
- else
487
- # create style string
488
- "style=\"#{width_align}\""
489
- end
490
- html << "#{td} #{style}>#{value}</div>"
491
- end
492
- html.html_safe
493
- end
494
-
495
- ############################################################################
496
- # Will return value for parameter required on form
497
128
  ############################################################################
498
- def dc_value_for_parameter(param)
499
- if param.class == Hash
500
- dc_internal_var(param['object'] || 'record', param['method'])
501
- else
502
- param
503
- end
129
+ def dc_html_data(yaml)
130
+ return '' if yaml.blank?
131
+ yaml.inject(' ') {|result, e| result << "#{e.first}=\"#{e.last}\" "}
504
132
  end
505
133
 
506
134
  ############################################################################
507
- # Creates actions div for edit form.
135
+ # Creates code for link, ajax or windows action for index or form actions.
508
136
  #
509
- # Displaying readonly form turned out to be challenge. For now when readonly parameter
510
- # has value 2, back link will force readonly form. Value 1 or not set will result in
511
- # normal link.
512
- ############################################################################
513
- def dc_actions_for_form()
514
- # create standard actions
515
- std_actions = {' 1' => 'back', ' 2' => {'type' => 'submit', 'caption' => 'drgcms.save'},
516
- ' 3' => {'type' => 'submit', 'caption' => 'drgcms.save&back'} }
517
- # when edit only
518
- unless @record.id.nil?
519
- std_actions.merge!({' 6' => 'new'} )
520
- std_actions.merge!(@record.active ? {' 5' => 'disable'} : {' 5' => 'enable'} ) if @record.respond_to?('active')
521
- std_actions.merge!({' 7' => 'refresh'} )
522
- end
523
- actions = @form['form']['actions']
524
- # shortcut for actions: standard
525
- actions = nil if actions.class == String && actions == 'standard'
526
- # standard actions
527
- actions = std_actions if actions.nil?
528
- # readonly
529
- actions = {' 1' => 'back'} if @form['readonly']
530
- # Actions are strictly forbidden
531
- if @form['form']['actions'] and dc_dont?(@form['form']['actions'])
532
- actions = []
533
- elsif actions['standard']
534
- actions.merge!(std_actions)
535
- actions['standard'] = nil
536
- end
537
- # Update save and save&back
538
- actions.each do |k,v|
539
- if v.class == String
540
- if v.match(/save\&back/i)
541
- actions[k] = {'type' => 'submit', 'caption' => 'drgcms.save&back'}
542
- elsif v == 'save'
543
- actions[k] = {'type' => 'submit', 'caption' => 'drgcms.save'}
544
- end
545
- end
546
- end
547
- # Sort so that standard actions come first
548
- actions = actions.to_a.sort {|x,y| x[0].to_s <=> y[0].to_s}
549
- # Add spinner to the beginning
550
- c = %Q[<span class="dc-spinner div-hidden">#{fa_icon('spinner lg spin')}</span><ul class="dc-menu">]
551
-
552
- actions.each do |element|
553
- session[:form_processing] = "form:actions: #{element}"
554
- v = element[1]
555
- next if v.nil? # yes it happends
556
- # on_save_ok should't go inside td tags
557
- if (element[0] == 'on_save_ok') then
558
- c << hidden_field_tag(:on_save_ok, v)
559
- next
560
- end
561
- #
562
- action_active = !(dc_dont?(v['when_new']) and @record.new_record?)
563
- # p [v['caption'], action_active]
564
- parms = @parms.clone
565
- if v.class == String
566
- next if params[:readonly] and !(v == 'back')
567
-
568
- c << '<li class="dc-link dc-animate">'
569
- c << case
570
- when (v == 'back' or v == 'cancle') then
571
- # If return_to is present link directly to URL
572
- if parms['xreturn_to'] # disabled for now
573
- dc_link_to( 'drgcms.back','arrow-left', parms['return_to'] )
574
- else
575
- parms['action'] = 'index'
576
- parms['readonly'] = parms['readonly'].to_s.to_i < 2 ? nil : 1
577
- dc_link_to( 'drgcms.back','arrow-left', parms )
578
- end
579
- when v == 'delete' then
580
- parms['operation'] = v
581
- parms['id'] = @record.id
582
- dc_link_to( 'drgcms.delete','remove', parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete )
583
- when v == 'new' then
584
- parms['action'] = v
585
- dc_link_to( 'drgcms.new', 'plus', parms)
586
- when (v == 'enable' or v == 'disable') then
587
- parms['operation'] = v
588
- parms['id'] = @record.id
589
- icon = (v == 'enable' ? 'thumbs-o-up' : 'thumbs-o-down')
590
- dc_link_to( "drgcms.#{v}",icon, parms, method: :delete )
591
- when v == 'edit' then
592
- parms['operation'] = v
593
- parms['id'] = @record.id
594
- dc_link_to( "drgcms.#{v}",v, parms )
595
- when v == 'refresh' then
596
- "<span onclick='window.location.href=window.location.href;'>#{fa_icon('refresh')} #{t('drgcms.refresh')}</span></li>"
597
- else
598
- "err1 #{element[0]}=>#{v}"
599
- end
600
- c << '</td>'
601
- # non standard actions
602
- else
603
- c << case
604
- # submit button
605
- when v['type'] == 'submit'
606
- caption = v['caption'] || 'drgcms.save'
607
- icon = v['icon'] || 'save'
608
- if action_active
609
- '<li class="dc-link-submit dc-animate">' +
610
- dc_submit_tag(caption, icon, {:data => v['params'], :title => v['title'] }) +
611
- '</li>'
612
- else
613
- "<li class=\"dc-link-no\">#{fa_icon(icon)} #{caption}</li>"
614
- end
615
-
616
- # delete with some sugar added
617
- when v['type'] == 'delete'
618
- parms['id'] = @record.id
619
- parms.merge!(v['params'])
620
- caption = v['caption'] || 'drgcms.delete'
621
- icon = v['icon'] || 'remove'
622
- '<li class="dc-link dc-animate">' +
623
- dc_link_to( caption, icon, parms, data: t('drgcms.confirm_delete'), method: :delete ) +
624
- '</li>'
625
-
626
- # ajax or link button
627
- when v['type'] == 'ajax' || v['type'] == 'link' || v['type'] == 'window'
628
- parms = {}
629
- # direct url
630
- if v['url']
631
- parms['controller'] = v['url']
632
- parms['idr'] = dc_document_path(@record)
633
- # make url from action controller
634
- else
635
- parms['controller'] = v['controller']
636
- parms['action'] = v['action']
637
- parms['table'] = v['table']
638
- parms['form_name'] = v['form_name']
639
- end
640
- # add current id to parameters
641
- parms['id'] = dc_document_path(@record)
642
- # overwrite with or add additional parameters when params defined
643
- v['params'].each { |k,v| parms[k] = dc_value_for_parameter(v) } if v['params']
644
- parms['table'] = parms['table'].underscore if parms['table'] # might be CamelCase
645
- # error if controller parameter is missing
646
- if parms['controller'].nil?
647
- "<li>#{t('drgcms.error')}</li>"
648
- else
649
- v['caption'] ||= v['text']
650
- caption = t("#{v['caption'].downcase}", v['caption'])
651
- #
652
- url = url_for(parms) rescue ''
653
- request = v['request'] || v['method'] || 'get'
654
- icon = v['icon'] ? "#{fa_icon(v['icon'])} " : ''
655
- if v['type'] == 'ajax' # ajax button
656
- clas = action_active ? "dc-link-ajax dc-animate" : "dc-link-no"
657
- %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}"
658
- data-request="#{request}" title="#{v['title']}">#{icon}#{caption}</li>]
659
- elsif v['type'] == 'link' # link button
660
- clas = action_active ? "dc-link dc-animate" : "dc-link-no"
661
- %Q[<li class="#{clas}">#{action_active ? dc_link_to(v['caption'],v['icon'], parms, {target: v['target']} ) : caption}</li>]
662
- elsif v['type'] == 'window'
663
- clas = action_active ? "dc-link dc-animate dc-window-open" : "dc-link-no"
664
- %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}">#{icon}#{caption}</li>]
665
- else
666
- 'Action Type error'
667
- end
668
- end
669
-
670
- # Javascript action
671
- when v['type'] == 'script'
672
- dc_script_action(v)
673
- else
674
- '<li>err2</li>'
675
- end
676
- end
677
- end
678
- (c << '</ul>').html_safe
679
- end
680
-
681
- ############################################################################
682
- # Create background div and table definitions for result set.
683
- ############################################################################
684
- def dc_background_for_result(start)
685
- if start == :start
686
- html = '<div class="dc-result-div" '
687
- html << (@form['result_set']['table_style'] ? 'style="overflow-x: scroll;" >' : '>')
688
- #
689
- html << "\n<div class=\"dc-result #{@form['result_set']['table_class']}\" "
690
- html << (@form['result_set']['table_style'] ? "style=\"#{@form['result_set']['table_style']}\" >" : '>')
137
+ # Parameters:
138
+ # yaml: Hash : Action definition
139
+ # record : Object : Currently selected record if available
140
+ # action_active : Boolean : Is action active or disabled
141
+ #
142
+ # Returns:
143
+ # String : HTML code for action
144
+ ############################################################################
145
+ def dc_link_ajax_window_submit_action(yaml, record=nil, action_active=true)
146
+ parms = {}
147
+ caption = yaml['caption'] ? t("#{yaml['caption'].downcase}", yaml['caption']) : nil
148
+ icon = yaml['icon'] ? "#{fa_icon(yaml['icon'])}" : ''
149
+ # action is not active
150
+ unless dc_is_action_active?(yaml)
151
+ return "<li class=\"dc-link-no\">#{icon} #{caption}</li>"
152
+ end
153
+ # set data-confirm when confirm
154
+ yaml['html'] ||= {}
155
+ confirm = yaml['html']['data-confirm'] || yaml['confirm']
156
+ yaml['html']['data-confirm'] = t(confirm) unless confirm.blank?
157
+ # direct url
158
+ if yaml['url']
159
+ parms['controller'] = yaml['url']
160
+ parms['idr'] = dc_document_path(record) if record
161
+ # make url from action controller
691
162
  else
692
- html = '</div></div>'
693
- end
694
- html.html_safe
695
- end
696
-
697
- ############################################################################
698
- # Checks if value is defined and sets default. If values are sent it also checks
699
- # if value is found in values. If not it will report error and set value to default.
700
- # Subroutine of dc_fields_for_tab.
701
- ############################################################################
702
- def dc_check_and_default(value, default, values=nil) #:nodoc:
703
- return default if value.nil?
704
- # check if value is within allowed values
705
- if values
706
- if !values.index(value)
707
- # parameters should be in downcase. Check downcase version.
708
- if n = values.index(value.downcase)
709
- return values[n]
710
- else
711
- logger.error("DRG Forms: Value #{value} not within values [#{values.join(',')}]. Default #{default} used!")
712
- return default
713
- end
714
- end
715
- end
716
- value
717
- end
718
-
719
- ############################################################################
720
- # Creates top or bottom horizontal line on form.
721
- ############################################################################
722
- def top_bottom_line(options)
723
- '<div class="dc-separator"></div>'
724
- end
163
+ parms['controller'] = yaml['controller'] || 'cmsedit'
164
+ parms['action'] = yaml['action']
165
+ parms['table'] = yaml['table']
166
+ parms['form_name'] = yaml['form_name']
167
+ parms['control'] = yaml['control'] if yaml['control']
168
+ parms['id'] = record.id if record
169
+ end
170
+ # add current id to parameters
171
+ parms['id'] = dc_document_path(record) if record
172
+ # overwrite with or add additional parameters from environment or record
173
+ yaml['params'].each { |k,v| parms[k] = dc_value_for_parameter(v) } if yaml['params']
174
+ parms['table'] = parms['table'].underscore if parms['table'] # might be CamelCase
175
+ # error if controller parameter is missing
176
+ if parms['controller'].nil? && parms['url'].nil?
177
+ "<li>#{'Controller not defined'}</li>"
178
+ else
179
+ yaml['caption'] ||= yaml['text']
180
+
181
+ html_data = dc_html_data(yaml['html'])
182
+ #
183
+ url = url_for(parms) rescue 'URL error'
184
+ request = yaml['request'] || yaml['method'] || 'get'
185
+ if yaml['type'] == 'ajax' # ajax button
186
+ clas = "dc-link-ajax dc-animate"
187
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}
188
+ data-request="#{request}" title="#{yaml['title']}">#{icon}#{caption}</li>]
189
+
190
+ elsif yaml['type'] == 'submit' # submit button
191
+ # It's dirty hack, but will prevent not authorized message and render index action correctly
192
+ parms[:filter] = 'on'
193
+ url = url_for(parms) rescue 'URL error'
194
+ clas = "dc-action-submit"
195
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}
196
+ data-request="#{request}" title="#{yaml['title']}">#{icon}#{caption}</li>]
197
+
198
+ elsif yaml['type'] == 'link' # link button
199
+ clas = "dc-link dc-animate"
200
+ link = dc_link_to(yaml['caption'],yaml['icon'], parms, {target: yaml['target'], html: yaml['html']} )
201
+ %Q[<li class="#{clas}">#{action_active ? link : caption}</li>]
202
+
203
+ elsif yaml['type'] == 'window'
204
+ clas = "dc-link dc-animate dc-window-open"
205
+ %Q[<li class="#{clas}" data-url="#{action_active ? url : ''}" #{html_data}>#{icon}#{caption}</li>]
725
206
 
726
- ############################################################################
727
- # Creates input fields for one tab. Subroutine of dc_fields_for_form.
728
- ############################################################################
729
- def dc_fields_for_tab(fields_on_tab) #:nodoc:
730
- @js ||= ''
731
- html = '<div class="dc-form">'
732
- labels_pos = dc_check_and_default(@form['form']['labels_pos'], 'right', ['top','left','right'])
733
- hidden_fields = ''
734
- odd_even = nil
735
- group_option, group_count = 0, 0
736
- reset_cycle()
737
- # options and fields must be separated before sorting
738
- # form_options = fields.select {|field| field.class != Integer }
739
- # columns = form_options.try(:[],'columns') || 1
740
- # Select form fields and sort them by key
741
- form_fields = fields_on_tab.select {|field| field.class == Integer }
742
- form_fields.to_a.sort.each do |element|
743
- options = element.last
744
- session[:form_processing] = "form:fields: #{element.first}=#{options}"
745
- # ignore if edit_only singe field is required
746
- next if params[:edit_only] and params[:edit_only] != options['name']
747
- # hidden_fields. Add them at the end
748
- if options['type'] == 'hidden_field'
749
- hidden_fields << DrgcmsFormFields::HiddenField.new(self, @record, options).render
750
- next
751
- end
752
- # label
753
- caption = options['caption'] || options['text']
754
- label = if !caption.blank?
755
- t(caption, caption)
756
- elsif options['name']
757
- t_name(options['name'], options['name'].capitalize.gsub('_',' ') )
758
- end
759
- # help text can be defined in form or in translations starting with helpers. or as helpers.help.collection.field
760
- help = if options['help']
761
- options['help'].match('helpers.') ? t(options['help']) : options['help']
762
- end
763
- help ||= t('helpers.help.' + @form['table'] + '.' + options['name'],' ') if options['name']
764
- # create field object from class and call its render method
765
- klas_string = options['type'].camelize
766
- field_html = if DrgcmsFormFields.const_defined?(klas_string) # check if field type is defined
767
- klas = DrgcmsFormFields.const_get(klas_string)
768
- field = klas.new(self, @record, options).render
769
- @js << field.js
770
- field.html
771
- else # litle error string
772
- "Error: Code for field type #{options['type']} not defined!"
773
- end
774
- # Line separator
775
- html << top_bottom_line(options['top-line']) if options['top-line']
776
- # Begining of new row
777
- if group_count == 0
778
- html << '<div class="row-div">'
779
- odd_even = cycle('odd','even')
780
- group_count = options['group'] || 1
781
- group_option = options['group'] || 1
782
- end
783
- #
784
- html << if labels_pos == 'top'
785
- %Q[
786
- <div class="dc-form-label-top dc-color-#{odd_even} dc-align-left" title="#{help}">
787
- <label for="record_#{options['name']}">#{label} </label>
788
- <div id="td_record_#{options['name']}">#{field_html}</div>
789
- </div> ]
790
207
  else
791
- label_width = 14
792
- # less place for label when more then 1 field per row
793
- label_width = 10 if group_option > 1 and group_option != group_count
794
- data_width = (94 - 10*group_option)/group_option
795
- %Q[
796
- <div class="dc-form-label dc-color-#{odd_even} dc-align-#{labels_pos}" style="width:#{label_width}%;" title="#{help}">
797
- <label for="record_#{options['name']}">#{label} </label>
798
- </div>
799
- <div id="td_record_#{options['name']}" class="dc-form-field dc-color-#{odd_even}" style="width:#{data_width}%;">#{field_html}</div>
800
- ]
208
+ '<li>Action Type error</li>'
801
209
  end
802
- # check if must go to next row
803
- group_count -= 1
804
- html << '</div>' if group_count == 0
805
- html << top_bottom_line(options['bottom-line']) if options['bottom-line']
806
210
  end
807
- html << '</div>' << hidden_fields
808
211
  end
809
212
 
810
213
  ############################################################################
811
- # Creates edit form div.
812
- ############################################################################
813
- def dc_fields_for_form()
814
- html, tabs, tab_data = '',[], ''
815
- # Only fields defined
816
- if (form_fields = @form['form']['fields'])
817
- html << "<div id='data_fields' " + (@form['form']['height'] ? "style=\"height: #{@form['form']['height']}px;\">" : '>')
818
- html << dc_fields_for_tab(form_fields) + '</div>'
819
- else
820
- # there are multiple tabs on form
821
- first = true # first tab
822
- @form['form']['tabs'].keys.sort.each do |tab_name|
823
- next if tab_name.match('actions')
824
- # Tricky. If field name is not on the tab skip to next tab
825
- if params[:edit_only]
826
- is_on_tab = false
827
- @form['form']['tabs'][tab_name].each {|k,v| is_on_tab = true if params[:edit_only] == v['name'] }
828
- next unless is_on_tab
829
- end
830
- # first div is displayed, all others are hidden
831
- tab_data << "<div id=\"data_#{tab_name.delete("\s\n")}\""
832
- tab_data << ' class="div-hidden"' unless first
833
- tab_data << " style=\"height: #{@form['form']['height']}px;\"" if @form['form']['height']
834
- tab_data << ">#{dc_fields_for_tab(@form['form']['tabs'][tab_name])}</div>"
835
- tab_label = @form['form']['tabs'][tab_name]['caption'] || tab_name
836
- tabs << [tab_name, tab_label]
837
- first = false
838
- end
839
- # make it all work together
840
- html << '<ul class="dc-form-ul" >'
841
- first = true # first tab must be selected
842
- tabs.each do |tab_name, tab_label|
843
- html << "<li id=\"li_#{tab_name}\" data-div=\"#{tab_name.delete("\s\n")}\" class=\"dc-form-li"
844
- html << ' dc-form-li-selected' if first
845
- html << "\">#{t(tab_label, t_name(tab_label))}</li>"
846
- first = false
847
- end
848
- html << '</ul>'
849
- html << tab_data
850
- end
851
- # add last_updated_at hidden field so controller can check if record was updated in db during editing
852
- html << hidden_field(nil, :last_updated_at, value: @record.updated_at.to_i) if @record.respond_to?(:updated_at)
853
- # add form time stamp to prevent double form submit
854
- html << hidden_field(nil, :form_time_stamp, value: Time.now.to_i)
855
- html.html_safe
856
- end
857
-
858
- ############################################################################
859
- # Returns username for id. Subroutine of dc_document_statistics
860
- ############################################################################
861
- def _get_user_for(field_name) #:nodoc:
862
- if @record[field_name]
863
- u = DcUser.find(@record[field_name])
864
- return u ? u.name : @record[field_name]
865
- end
866
- # nil
867
- end
868
-
869
- ############################################################################
870
- # Creates current document statistics div (created_by, created_at, ....) at the bottom of edit form.
871
- # + lots of more. At the moment also adds icon for dumping current document as json text.
872
- ############################################################################
873
- def dc_document_statistics
874
- return '' if @record.new_record? or dc_dont?(@form['form']['info'])
875
- html = %Q[<div id="dc-document-info">#{fa_icon('info-circle lg')}</div> <div id="dc-document-info-popup" class="div-hidden"> ]
876
214
  #
877
- u = _get_user_for('created_by')
878
- html << %Q[<div><span>#{t('drgcms.created_by', 'Created by')}: </span><span>#{u}</span></div>] if u
879
- u = _get_user_for('updated_by')
880
- html << %Q[<div><span>#{t('drgcms.updated_by', 'Updated by')}: </span><span>#{u}</span></div>] if u
881
- html << %Q[<div><span>#{t('drgcms.created_at', 'Created at')}: </span><span>#{dc_format_value(@record.created_at)}</span></div>] if @record['created_at']
882
- html << %Q[<div><span>#{t('drgcms.updated_at', 'Updated at')}: </span><span>#{dc_format_value(@record.updated_at)}</span></div>] if @record['updated_at']
883
- # copy to clipboard icon
884
- parms = params.clone
885
- parms[:controller] = 'dc_common'
886
- parms[:action] = 'copy_clipboard'
887
- url = url_for(parms.permit!)
888
- html << fa_icon('copy 2x', class: 'dc-link-img dc-link-ajax dc-animate',
889
- 'data-url' => url, 'data-request' => 'get', title: t('drgcms.doc_copy_clipboard') )
890
- (html << '</div></div>').html_safe
215
+ ############################################################################
216
+ def dc_log_exception(exception)
217
+ log = exception ? "\n!!!Error: #{exception.message}\n#{exception.backtrace.first.inspect}\n" : ''
218
+ log << "DRG Form processing line: #{session[:form_processing]}\n"
219
+
220
+ logger.error log
891
221
  end
892
-
222
+
893
223
  end