#--
# Copyright (c) 2012+ Damjan Rems
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
###########################################################################
#
# CmseditHelper module defines helper methods used by cmsedit actions. Output is controlled by
# data found in 3 major sections of DRG CMS form: index, result_set and form sections.
#
###########################################################################
module CmsEditHelper
############################################################################
# Will return value when internal or additional parameters are defined in action
# Subroutine of dc_actions_for_form.
############################################################################
def dc_value_for_parameter(param, current_document = nil)#:nodoc:
if param.class == Hash
dc_internal_var(param['object'] ||= 'record', param['method'], current_document)
elsif param.to_s.match(/record|document/)
current_document ? current_document : @record
else
param
end
end
############################################################################
# Creates actions div for edit form.
#
# Displaying readonly form turned out to be challenge. For now when readonly parameter
# has value 2, back link will force readonly form. Value 1 or not set will result in
# normal link.
############################################################################
def dc_is_action_active?(options)
if options['when_new']
dc_deprecate("when_option will be deprecated and replaced by active: not_new_record! Form #{CmsHelper.form_param(params)}")
return !(dc_dont?(options['when_new']) && @record.new_record?)
end
return true unless options['active']
# alias record and document so both can be used in eval
record = document = @record
option = options['active']
case
# usually only for test
when option.class == TrueClass || option['eval'].class == TrueClass then true
when option.class == String then
if option.match(/new_record/i)
(@record.new_record? && option == 'new_record') || (!@record.new_record? && option == 'not_new_record')
elsif option.match(/\./)
# shortcut for method and eval option
dc_process_eval(option, self)
else
eval(option['eval'])
end
# direct evaluate expression
when option['eval'] then
eval(option['eval'])
when option['method'] then
# if record present send record otherwise send self as parameter
dc_process_eval(option['method'], self)
else
false
end
end
############################################################################
# Creates actions div for edit form.
#
# Displaying readonly form turned out to be challenge. For now when readonly parameter
# has value 2, back link will force readonly form. Value 1 or not set will result in
# normal link.
############################################################################
def dc_actions_for_form(position)
# create standard actions
std_actions = {1 => 'back', 2 => {'type' => 'submit', 'caption' => 'drgcms.save'},
3 => {'type' => 'submit', 'caption' => 'drgcms.save&back'} }
# when edit only
unless @record.try(:id).nil?
std_actions.merge!({6 => 'new'} )
std_actions.merge!(@record.active ? {5 => 'disable'} : {5 => 'enable'} ) if @record.respond_to?('active')
std_actions.merge!({7 => 'refresh'} )
end
# readonly
std_actions = { 1 => 'back' } if @form['readonly']
actions = @form['form']['actions']
# shortcut for actions: standard
actions = nil if actions.class == String && actions == 'standard'
actions = std_actions if actions.nil?
# Actions are strictly forbidden
if @form['form']['actions'] && dc_dont?(@form['form']['actions'])
actions = []
elsif actions['standard']
actions.merge!(std_actions)
actions['standard'] = nil
end
# request for close window button present
if actions.class == Hash
case params[:window_close]
when '0' then actions[1] = 'close'; actions[3] = nil
when '1' then actions = { 1 => 'close' }
when '2' then actions = { 1 => 'close' }
end
end
# Update save and save&back
actions.each do |k, v|
if v.class == String
if v.match(/save\&back/i)
actions[k] = {'type' => 'submit', 'caption' => 'drgcms.save&back'}
elsif v == 'save'
actions[k] = {'type' => 'submit', 'caption' => 'drgcms.save'}
end
end
end
# remove standard option and sort so that standard actions come first
actions.delete('standard')
actions = actions.to_a.sort { |x, y| x[0] <=> y[0] }
# Add spinner to the beginning
html = %Q[#{fa_icon('settings-o spin')}
').html_safe
end
############################################################################
# Create background div and table definitions for result set.
############################################################################
def dc_background_for_result(start)
if start == :start
html = '' : '>')
html << "\n
" : '>')
else
html = '
'
end
html.html_safe
end
############################################################################
# Checks if value is defined and sets default. If values are sent it also checks
# if value is found in values. If not it will report error and set value to default.
# Subroutine of dc_fields_for_tab.
############################################################################
def dc_check_and_default(value, default, values=nil) #:nodoc:
return default if value.nil?
# check if value is within allowed values
if values
if !values.index(value)
# parameters should be in downcase. Check downcase version.
if n = values.index(value.downcase)
return values[n]
else
logger.error("DRG Forms: Value #{value} not within values [#{values.join(',')}]. Default #{default} used!")
return default
end
end
end
value
end
############################################################################
# Creates input fields defined in form options
############################################################################
def dc_fields_for_form
html = "" : '>')
@js ||= ''
@css ||= ''
# fields
if (form_fields = @form['form']['fields'])
html << dc_input_form_create(form_fields) + '
'
# tabs
elsif @form['form']['tabs']
html = dc_tabs_form_create()
end
# add last_updated_at hidden field so controller can check if record was updated in db during editing
html << hidden_field(nil, :last_updated_at, value: @record.updated_at.to_i) if @record.respond_to?(:updated_at)
# add form time stamp to prevent double form submit
html << hidden_field(nil, :form_time_stamp, value: Time.now.to_i)
# add javascript code if defined by form
@js << "\n#{@form['script']} #{@form['js']}"
@css << "\n#{@form['css']}\n#{@form['form']['css']}"
html.html_safe
end
############################################################################
# Creates head form div. Head form div is used to display header data usefull
# to be seen even when tabs are switched.
############################################################################
def dc_head_for_form
@css ||= ''
head = @form['form']['head']
return '' if head.nil?
html = %(\n
)
split = head['split'] || 4
percent = 100/split
current = 0
head_fields = head.select {|field| field.class == Integer }
head_fields.to_a.sort.each do |number, options|
session[:form_processing] = "form: head: #{number}=#{options}"
# Label
caption = options['caption']
span = options['span'] || 1
@css << "\n#{options['css']}" unless options['css'].blank?
label = if caption.blank?
''
elsif options['name'] == caption
t_label_for_field(options['name'], options['name'].capitalize.gsub('_',' ') )
else
t(caption, caption)
end
# Field value
begin
field = if options['eval']
dc_process_column_eval(options, @record)
else
@record.send(options['name'])
end
rescue Exception => e
dc_log_exception(e, 'dc_head_for_form')
field = '!!!Error'
end
#
klass = dc_style_or_class(nil, options['class'], field, @record)
style = dc_style_or_class(nil, options['style'], field, @record)
html << %(
#{label.blank? ? '' : "#{label}"}
#{field}
)
current += span
if current == split
html << %(
\n
)
current = 0
end
end
html << '
'
html.html_safe
end
############################################################################
# Returns username for id. Subroutine of dc_document_statistics
###########################################################################
def dc_document_user_for(field_name) #:nodoc:
return if @record[field_name].nil?
user = DcUser.find(@record[field_name])
user ? user.name : @record[field_name]
end
############################################################################
# Creates current document statistics div (created_by, created_at, ....) at the bottom of edit form.
# + lots of more. At the moment also adds icon for dumping current document as json text.
############################################################################
def dc_document_statistics
return '' if @record.id.nil? || dc_dont?(@form['form']['info'])
html = %(#{fa_icon('info md-18')}
').html_safe
end
############################################################################
# Updates form prior to processing form
############################################################################
def dc_form_update
# update form for steps options
if @form.dig('form', 'steps')
dc_form_update_steps
end
end
############################################################################
# If form is divided into two parts, this method gathers html to be painted
# on right side of the form pane.
############################################################################
def dc_form_left
yaml = @form.dig('form', 'form_left')
return '' unless yaml
html = ''
html << dc_process_eval(yaml['eval'], self) if yaml['eval']
html.html_safe
end
private
############################################################################
# Creates top or bottom horizontal line on form.
#
# @param [String] location (top or bottom)
# @param [Object] options yaml field definition
#
# @return [String] html code for drawing a line
############################################################################
def dc_top_bottom_line(location, options)
if options["#{location}-line"] || options['line'].to_s == location.to_s
''
else
''
end
end
############################################################################
# Creates input fields for one tab. Subroutine of dc_fields_for_form.
############################################################################
def dc_input_form_create(fields_on_tab) #:nodoc:
html = '' << hidden_fields
end
############################################################################
# Will create html code required for input form with steps defined
############################################################################
def dc_steps_one_element(element, tab_name = nil)
def add_one_step(key, tab_name, key_number)
fields = tab_name ? @form['form']['tabs'][tab_name] : @form['form']['fields']
{ key_number => fields[key] }
end
key_number, fields = 0, {}
element.to_s.split(',').each do |particle|
if particle.match('-')
tabs_fields = tab_name ? @form['form']['tabs'][tab_name] : @form['form']['fields']
next if tabs_fields.nil?
start, to_end = particle.split('-').map(&:to_i)
tabs_fields.each { |key, data| fields.merge!(add_one_step(key, tab_name, key_number += 10)) if (start..to_end).include?(key) }
else
fields.merge!(add_one_step(particle.to_i, tab_name, key_number += 10))
end
end
fields
end
############################################################################
# Will create html code required for input form with steps defined and update
# actions.
############################################################################
def dc_form_update_steps
def add_step_to_form(index, step, next_step)
@form['form']['actions'][index]['params']['step'] = step
@form['form']['actions'][index]['params']['next_step'] = next_step
end
form = {}
step = params[:step].to_i
step_data = @form['form']['steps'].to_a[step - 1]
step_data.last.each do |element|
if element.first == 'fields'
form.merge!(dc_steps_one_element(element.second))
elsif element.first == 'tabs'
element.last.each do |tab_name, data|
form.merge!(dc_steps_one_element(data, tab_name))
end
end
end
# fraction updates of newly created form
form.deep_merge!(step_data.last['update']) if step_data.last['update']
# update steps data on form
add_step_to_form(10, step, step - 1)
add_step_to_form(20, step, step + 1)
add_step_to_form(100, step, step + 1)
# remove not needed steps
if step < 2
@form['form']['actions'].delete(10)
elsif step == @form['form']['steps'].size
@form['form']['actions'].delete(20)
end
@form['form']['actions'].delete(100) unless step == @form['form']['steps'].size
# update form_name and control name if defined
%w[1 10 20 100].each do |i|
next unless @form['form']['actions'][i.to_i]
@form['form']['actions'][i.to_i]['form_name'] = CmsHelper.form_param(params)
control = @form['control'] ? @form['control'] : @form['table']
@form['form']['actions'][i.to_i]['control'].sub!('x.', "#{control}.")
end
@form['form']['form_left'] ||= { 'eval' => 'dc_steps_menu_get'}
@form['form']['fields'] = form
end
############################################################################
# Will create html code required for input form with tabs
############################################################################
def dc_tabs_form_create
html, tabs, tab_data = '', [], ''
# there are multiple tabs on form
first = true # first tab
@form['form']['tabs'].keys.sort.each do |tab_name|
next if tab_name.match('actions')
# Tricky when editing single field. If field is not present on the tab skip to next tab
if params[:edit_only]
is_on_tab = false
@form['form']['tabs'][tab_name].each { |k, v| is_on_tab = true if params[:edit_only] == v['name'] }
next unless is_on_tab
end
# first div is displayed, all others are hidden
tab_data << "#{dc_input_form_create(@form['form']['tabs'][tab_name])}
"
tab_label, tab_title = dc_tab_label_help(tab_name)
tabs << [tab_name, tab_label, tab_title]
first = false
end
# make it all work together
html << ''
html << tab_data
end
end