#-- # 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 # javascript part created by form helpers attr_reader :js ############################################################################ # Creates action div for cmsedit index action. ############################################################################ def dc_actions_for_index() return '' if @form['index'].nil? or @form['readonly'] actions = @form['index']['actions'] return '' if actions.nil? or actions.size == 0 # Simulate standard actions actions = {'standard' => true} if actions.class == String && actions == 'standard' std_actions = {' 2' => 'new', ' 3' => 'sort', ' 4' => 'filter' } if actions['standard'] actions.merge!(std_actions) actions['standard'] = nil end # start div with hidden spinner image html = < #{fa_icon('spinner lg spin')} ' html.html_safe end ############################################################################ # Will return field form definition if field is defined on form. Subroutine of dc_div_filter ############################################################################ def _get_field_def(name) #:nodoc: @form['form']['tabs'].each do |tab| tab.each do |field| next if field.class == String # tab name field.each {|k,v| return v if v['name'] == name } end end if @form['form']['tabs'] # I know. But nice. # @form['form']['fields'].each do |field| return field.last if field.last['name'] == name end if @form['form']['fields'] nil end ############################################################################ # Finds field definition on form and use it for filter input. Subroutine of dc_div_filter. ############################################################################ def _get_field_div(name) #:nodoc: filter = nil # old filter saved to session if session[@form['table']] and session[@form['table']][:filter] filter, operation, value = session[@form['table']][:filter].split("\t") end # field not defined on form. Must be defined: name as form_field_type if name.match(' as ') name, dummy, type = name.split(' ') field = {"name" => name, "type" => type, "html"=>{"size"=>20}} else field = _get_field_def(name) field = {"name" => name, "type" => 'text_field', "html"=>{"size"=>20}} if field.nil? end # div_hidden = 'div-hidden' if filter div_hidden = '' if name == filter if field['html'] field['html']['value'] = value else field['html'] = { "value" => value } end end klas_string = field['type'].camelize klas = DrgcmsFormFields::const_get(klas_string) || nil return 'error' if klas.nil? # o = klas.new(self, @record, field).render "" << o.html << (o.js.size > 2 ? javascript_tag(o.js) : '') << '' end ############################################################################ # Creates filter div for cmsedit index/filter action. ############################################################################ def dc_div_filter() choices, inputs = [], '' filter = (@form['index'] and @form['index']['filter']) ? @form['index']['filter'] + ',' : '' filter << 'id as text_field' # filter id is added by default filter.split(',').each do |f| f.strip! name = f.match(' as ') ? f.split(' ').first : f choices << [ t("helpers.label.#{@form['table']}.#{name}", name), name ] # find field definition on form and use it for input field definition inputs << _get_field_div(f) end choices4_operators = t('drgcms.choices4_filter_operators').chomp.split(',').inject([]) {|r,v| r << (v.match(':') ? v.split(':') : v )} s = session[@form['table']] is_hidden = (s and s[:filter]) ? '' : ' class="div-hidden" ' # if session[@form['table']] and session[@form['table']][:filter] field_name, operators_value, dummy = session[@form['table']][:filter].split("\t") else field_name, operators_value = nil, nil end html =<
#{ form_tag :table => @form['table'], filter: :on, action: :index, method: :post } #{ select(nil, 'filter_field', options_for_select(choices, field_name), { include_blank: true }) } #{ select(nil, 'filter_oper', options_for_select(choices4_operators, operators_value)) } #{ inputs }
EOT # #{link_to t('drgcms.filter_off'), action: 'index', filter: 'off', :table => @form['table']} html.html_safe end ############################################################################ # Creates title div for cmsedit index result set records. Title div also includes paging # options. ############################################################################ def dc_table_title_for_result(result=nil) title = if @form['title'] # form has title section t(@form['title'],@form['title']) else # get name from translations t('helpers.label.' + @form['table'] + '.tabletitle', @form['table']) end dc_table_title(title, result) end ############################################################################ # Creates code for link or ajax action type. Subroutine of dc_actions_for_result. ############################################################################ def dc_link_or_ajax(yaml, parms) #:nodoc: rest = {} rest['method'] = yaml['method'] || yaml['request'] || 'get' rest['caption'] = yaml['caption'] || yaml['text'] rest['class'] = rest['class'].to_s + ' dc-animate' rest['title'] = yaml['title'] # method = yaml['method'] || yaml['request'] || 'get' # caption = yaml['caption'] || yaml['text'] dc_deprecate "Form: result_set:action:text directive will be deprecated. Use caption instead of text." if yaml['text'] # =begin if yaml['type'] == 'link' if yaml['icon'] # icon link_to( image_tag(yaml['icon'], class: 'dc-link-img dc-link-ajax dc-animate'), parms, method: method, title: t(yaml['title'],yaml['title']) ) else # caption '' + link_to(" #{t(caption, caption)} ", parms, method: method, title: t(yaml['title'],yaml['title']) ) + '' end else # ajax url = url_for(parms) if yaml['icon'] # icon image_tag(yaml['icon'], class: 'dc-link-img dc-link-ajax dc-animate', 'data-url' => url, 'data-request' => method) else # caption %Q[#{caption}] end end ##### caption = '' if yaml['type'] == 'link' if yaml['icon'] # icon caption = if yaml['icon'].match('.') image_tag(yaml['icon'], class: 'dc-link-img dc-link-ajax dc-animate') else fa_icon("#{yaml['icon']} 2x" , class: 'dc-link-ajax dc-animate') end end if yaml['caption'] caption << ' ' if caption.size > 0 caption << t(yaml['caption'],yaml['caption']) end link_to(caption, parms, method: method, title: t(yaml['title'],yaml['title']) ) else '' end =end if yaml['type'] == 'link' dc_link_to(yaml['caption'], yaml['icon'], parms, rest ) else '' end end ############################################################################ # Creates actions that could be performed on single row of result set. ############################################################################ def dc_actions_for_result(record) actions = @form['result_set']['actions'] return '' if actions.nil? or @form['readonly'] # standard actions actions = {'standard' => true} if actions.class == String && actions == 'standard' std_actions = {' 2' => 'edit', ' 3' => 'delete'} actions.merge!(std_actions) if actions['standard'] # width = @form['result_set']['actions_width'] || 22*actions.size html = "" actions.each do |k,v| session[:form_processing] = "result_set:actions: #{k}=#{v}" next if k == 'standard' # ignore standard definition parms = @parms.clone yaml = v.class == String ? {'type' => v} : v # if single definition simulate type parameter html << case when yaml['type'] == 'edit' then parms['action'] = 'edit' parms['id'] = record.id dc_link_to( nil, 'pencil lg', parms ) when yaml['type'] == 'duplicate' then parms['id'] = record.id # duplicate string will be added to these fields. parms['dup_fields'] = yaml['dup_fields'] parms['action'] = 'create' dc_link_to( nil, 'copy lg', parms, data: { confirm: t('drgcms.confirm_dup') }, method: :post ) when yaml['type'] == 'delete' then parms['action'] = 'destroy' parms['id'] = record.id dc_link_to( nil, 'remove lg', parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete ) # undocumented so far when yaml['type'] == 'edit_embedded' parms['controller'] = 'cmsedit' parms['table'] += ";#{yaml['table']}" parms['ids'] ||= '' parms['ids'] += "#{record.id};" dc_link_to( nil, 'table lg', parms, method: :get ) when yaml['type'] == 'link' || yaml['type'] == 'ajax' then if yaml['url'] parms['controller'] = yaml['url'] parms['idr'] = record.id else parms['id'] = record.id end parms['controller'] = yaml['controller'] if yaml['controller'] parms['action'] = yaml['action'] if yaml['action'] parms['table'] = yaml['table'] if yaml['table'] parms['formname'] = yaml['formname'] if yaml['formname'] parms['target'] = yaml['target'] if yaml['target'] dc_link_or_ajax(yaml, parms) else # error. yaml['type'].to_s end end html << '' html.html_safe end ############################################################################ # Creates header div for result set. ############################################################################ def dc_header_for_result() c = '' actions = @form['result_set']['actions'] c = ' ' unless actions.nil? or @form['readonly'] if (columns = @form['result_set']['columns']) columns.each do |k,v| session[:form_processing] = "result_set:columns: #{k}=#{v}" th = ' v} if v.class == String caption = v['caption'] || t("helpers.label.#{@form['table']}.#{v['name']}") th << "style=\"#{v['style']}\" " if v['style'] th << "class=\"#{v['class']}\" " if v['class'] # no sorting when embedded field or custom filter is active if @tables.size == 1 and @form['result_set']['filter'].nil? th << ">#{link_to(caption, sort: v['name'], table: @tables[0][1], action: :index )}" else th << ">#{caption}" end c << th end end c.html_safe end ############################################################################ # Formats value according to format supplied or data type. There is lots of things missing here. ############################################################################ def dc_format_value(value, format=nil) # :TODO: Enable formating numbers. return '' if value.nil? klass = value.class.to_s if klass.match('Time') format ||= t('time.formats.default') value.strftime(format) elsif klass.match('Date') format ||= t('date.formats.default') value.strftime(format) else value.to_s end end ############################################################################ # Creates div with documents of current result set. ############################################################################ def dc_columns_for_result(document) html = '' if (columns = @form['result_set']['columns']) columns.each do |k,v| session[:form_processing] = "result_set:columns: #{k}=#{v}" # convert shortcut to hash v = {'name' => v} if v.class == String td = '#{value}" html << td << ">#{value}" end end html.html_safe 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() # 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.id.nil? std_actions.merge!({' 6' => 'new'} ) std_actions.merge!(@record.active ? {' 5' => 'disable'} : {' 5' => 'enable'} ) if @record.respond_to?('active') end actions = @form['form']['actions'] # shortcut for actions: standard actions = nil if actions.class == String && actions == 'standard' # standard actions actions = std_actions if actions.nil? # readonly actions = {' 1' => 'back'} if @form['readonly'] # Actions are strictly forbidden if @form['form']['actions'] and dc_dont?(@form['form']['actions']) actions = [] elsif actions['standard'] actions.merge!(std_actions) actions['standard'] = nil 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 # Sort so that standard actions come first actions = actions.to_a.sort {|x,y| x[0].to_s <=> y[0].to_s} # Add spinner to the beginning c = %Q[#{fa_icon('spinner lg spin')}] actions.each do |element| session[:form_processing] = "form:actions: #{element}" v = element[1] next if v.nil? # yes it happends p "Using text option in actions_for form is replaced with caption. Table #{@form['table']}" if v['text'] # on_save_ok should't go inside td tags if (element[0] == 'on_save_ok') then c << hidden_field_tag(:on_save_ok, v) next end # parms = @parms.clone if v.class == String next if params[:readonly] and !(v == 'back') c << '' c << case when (v == 'back' or v == 'cancle') then # If return_to is present link directly to URL if parms['xreturn_to'] # disabled for now dc_link_to( 'drgcms.back','arrow-left', parms['return_to'] ) else parms['action'] = 'index' parms['readonly'] = parms['readonly'].to_s.to_i < 2 ? nil : 1 dc_link_to( 'drgcms.back','arrow-left', parms ) end when v == 'delete' then parms['operation'] = v parms['id'] = @record.id dc_link_to( 'drgcms.delete','remove', parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete ) when v == 'new' then parms['action'] = v dc_link_to( 'drgcms.new', 'plus', parms) when (v == 'enable' or v == 'disable') then parms['operation'] = v parms['id'] = @record.id icon = (v == 'enable' ? 'thumbs-o-up' : 'thumbs-o-down') dc_link_to( "drgcms.#{v}",icon, parms, method: :delete ) when v == 'edit' then parms['operation'] = v parms['id'] = @record.id dc_link_to( "drgcms.#{v}",v, parms ) else "err1 #{element[0]}=>#{v}" end c << '' # non standard actions else c << case # submit button when v['type'] == 'submit' caption = v['caption'] || 'drgcms.save' icon = v['icon'] || 'save' '' + dc_submit_tag(caption, icon, {:data => v['params'], :title => v['title']}) + '' # delete with some sugar added when v['type'] == 'delete' parms['id'] = @record.id parms.merge!(v['params']) caption = v['caption'] || 'drgcms.delete' icon = v['icon'] || 'remove' '' + dc_link_to( caption, icon, parms, data: t('drgcms.confirm_delete'), method: :delete ) + '' # ajax or link button when v['type'] == 'ajax' || v['type'] == 'link' parms = {} # direct url if v['url'] parms['controller'] = v['url'] parms['idr'] = @record.id # make url else parms['controller'] = v['controller'] parms['action'] = v['action'] parms['table'] = v['table'] parms['formname'] = v['formname'] parms['id'] = @record.id # additional parameters v['params'].each { |k,v| parms[k] = v } if v['params'] end # Error if controller param is missing if parms['controller'].nil? "#{t('drgcms.error')}" else v['caption'] ||= v['text'] caption = t("#{v['caption'].downcase}", v['caption']) url = url_for(parms) request = v['request'] || v['method'] || 'get' icon = v['icon'] ? "#{fa_icon(v['icon'])} " : '' if v['type'] == 'ajax' # ajax button %Q[#{icon}#{caption}] else # link button %Q[#{icon}#{caption}] end end # Javascript action when v['type'] == 'script' # v['caption'] ||= 'Caption missing!' # caption = t("#{v['caption'].downcase}", v['caption']) data = {'request' => 'script', 'script' => v['js']} %Q[#{ dc_link_to(v['caption'],v['icon'], '#', data: data ) }] else 'err2' end end end c.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 top or bottom horizontal line on form. ############################################################################ def top_bottom_line(yaml) if yaml.class == Hash clas = yaml['class'] style = yaml['style'] end clas ||= 'dc-separator' "" end ############################################################################ # Creates input field for one tab. Subroutine of dc_fields_for_form. ############################################################################ def dc_fields_for_tab(fields) #:nodoc: @js ||= '' double_field = 0 html = '' labels_pos = dc_check_and_default(@form['form']['labels_pos'], 'right', ['top','left','right']) reset_cycle() # sort fields by name fields.to_a.sort.each do |element| options = element.last session[:form_processing] = "form:fields: #{element.first}=#{options}" # ignore if edit_only singe field is required next if params[:edit_only] and params[:edit_only] != options['name'] # hidden_fields. Ignore description text, otherwise it will be seen on screen if options['type'] == 'hidden_field' html << DrgcmsFormFields::HiddenField.new(self, @record, options).render next end # label text = if options['text'] t(options['text'], options['text']) else t_name(options['name'], options['name'].capitalize.gsub('_',' ') ) end # options['text'] ||= options['name'].capitalize.gsub('_',' ') # text = options['text'].match('helpers.') ? t(options['text']) : t_name(options['name'], options['text']) # help text can be defined in form or in translations starting with helpers. or as helpers.help.collection.field help = if options['help'] options['help'].match('helpers.') ? t(options['help']) : options['help'] end help ||= t('helpers.help.' + @form['table'] + '.' + options['name'],' ') odd_even = cycle('odd','even') odd_even = cycle('odd','even') if double_field == 2 # it should be same style as first # create field object from class and call its render method klas_string = options['type'].camelize field_html = if DrgcmsFormFields.const_defined?(klas_string) # check if field type is defined klas = DrgcmsFormFields.const_get(klas_string) field = klas.new(self, @record, options).render @js << field.js field.html else # litle error string "Error: Code for field type #{options['type']} not defined!" end # Separator html << top_bottom_line(options['top-line']) if options['top-line'] # Double entry fields in one row double_field = 1 if options['double'] html << '' if double_field < 2 # html << if labels_pos == 'top' %Q[] else %Q[ ] end html << '' if double_field != 1 double_field = 0 if double_field == 2 double_field = 2 if double_field == 1 html << top_bottom_line(options['bottom-line']) if options['bottom-line'] end html << '
#{field_html}
#{field_html}
' end ############################################################################ # Creates edit form div. ############################################################################ def dc_fields_for_form() html, tabs, tdata = '',[], '' # Only fields defined if (fields = @form['form']['fields']) html << "
" : '>') html << dc_fields_for_tab(fields) + '
' else # there are multiple tabs on form first = true # first tab @form['form']['tabs'].keys.sort.each do |tabname| next if tabname.match('actions') # Tricky. If field name is not on the tab skip to next tab if params[:edit_only] is_on_tab = false @form['form']['tabs'][tabname].each {|k,v| is_on_tab = true if params[:edit_only] == v['name'] } next unless is_on_tab end # first div is displayed all other are hidden tdata << "
#{dc_fields_for_tab(@form['form']['tabs'][tabname])}
" tabs << tabname first = false end # make it all work together html << '' html << tdata end # add last_updated_at hidden field so controller can check if record was updated in during editing html << hidden_field(nil, :last_updated_at, :value => @record.updated_at.to_i) if @record.respond_to?(:updated_at) html.html_safe end ############################################################################ # Returns username for id. Subroutine of dc_document_statistics ############################################################################ def _get_user_for(field_name) #:nodoc: if @record[field_name] u = DcUser.find(@record[field_name]) return u ? u.name : @record[field_name] end # nil 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.new_record? or dc_dont?(@form['form']['info']) html = "
#{t('drgcms.doc_info')}
" # html = "
#{fa_icon 'info-circle 2x'}
" html = '
' + fa_icon('info-circle lg') + '
' # html << "
" u = _get_user_for('created_by') html << "" if u u = _get_user_for('updated_by') html << "" if u html << "" if @record['created_at'] html << "" if @record['updated_at'] html << '
#{t('drgcms.created_by', 'Created by')}: #{u}
#{t('drgcms.updated_by', 'Updated by')}: #{u}
#{t('drgcms.created_at', 'Created at')}: #{dc_format_value(@record.created_at)}
#{t('drgcms.updated_at', 'Updated at')}: #{dc_format_value(@record.updated_at)}
' # Copy to clipboard icon parms = params.clone parms[:controller] = 'dc_common' parms[:action] = 'copy_clipboard' url = url_for(parms) # caption = image_tag('drg_cms/copy.png', title: t('drgcms.doc_copy_clipboard')) # html << %Q[
url, 'data-request' => 'get', title: t('drgcms.doc_copy_clipboard') ) (html << '
').html_safe # html.html_safe end end