#-- # 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? 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 = < #{image_tag('drg_cms/spinner.gif')}" ' html.html_safe end ############################################################################ # Subroutine of dc_div_filter ############################################################################ def _get_field_def(name) #:nodoc: @form['form'].each do |tab| next if tab.first.match('actions') tab.each do |field| next if field.class == String field.each {|k,v| return v if v['name'] == name } end end nil end ############################################################################ # Finds field definition on form and use it for filter input. Subroutine of dc_div_filter. ############################################################################ def _get_field_div(name) #:nodoc: # 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 klas_string = field['type'].camelize klas = DrgcmsFormField::const_get(klas_string) 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 = [], '' # searching by id is added by default filter = (@form['index'] and @form['index']['filter']) ? @form['index']['filter'] + ',' : '' filter << 'id as text_field' 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 )} html =< #{ form_tag :table => @form['table'], filter: :on, action: :index, method: :post } #{ select(nil, 'filter_field', choices, { include_blank: true}) } #{ select(nil, 'filter_oper', choices4_operators) } #{ inputs }
EOT 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: method = yaml['method'] || yaml['request'] || 'get' caption = yaml['caption'] || yaml['text'] p "Form: result_set:action:text directive will be deprecated. Use caption instead of text." if yaml['text'] # 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 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? # 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 link_to( image_tag('drg_cms/edit.png', class: 'dc-link-img dc-animate'), parms ) when yaml['type'] == 'duplicate' then parms['id'] = record.id # duplicate string will be added to these fields. parms['dup_fields'] = yaml['dup_fields'] link_to( image_tag('drg_cms/copy.png', class: 'dc-link-img dc-animate'), parms, data: { confirm: t('drgcms.confirm_dup') }, method: :post ) when yaml['type'] == 'delete' then parms['action'] = 'destroy' parms['id'] = record.id link_to( image_tag('drg_cms/x.png', class: 'dc-link-img dc-animate'), 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};" link_to( image_tag('drg_cms/cols.png', class: 'dc-link-img dc-animate'), 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? 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 ############################################################################ 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!({' 4' => 'delete', ' 6' => 'new'} ) # delete is not OK here 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? if 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[#{image_tag('drg_cms/spinner.gif')}] 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 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 link_to(t('drgcms.back'), parms['return_to'] ) else parms['action'] = 'index' link_to(t('drgcms.back'), parms) end when v == 'delete' then parms['operation'] = v parms['id'] = @record.id link_to( t('drgcms.delete'), parms, data: { confirm: t('drgcms.confirm_delete') }, method: :delete ) when v == 'new' then parms['action'] = v #parms['id'] = @record.id link_to( t('drgcms.new'), parms) when (v == 'enable' or v == 'disable') then parms['operation'] = v parms['id'] = @record.id link_to( t("drgcms.#{v}"), parms, method: :delete ) else "err1 #{element[0]}=>#{v}" end c << '' # non standard actions else c << case # submit button when v['type'] == 'submit' caption = v['caption'] || 'drgcms.save' '' + submit_tag(t(caption, caption), :data => v['params'], :class => 'dc-submit') + '' # delete with some sugar added when v['type'] == 'delete' parms['id'] = @record.id parms.merge!(v['params']) caption = v['caption'] || 'drgcms.delete' '' + link_to( t(caption, caption), 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 missing!' caption = t("#{v['caption'].downcase}", v['caption']) url = url_for(parms) request = v['request'] || v['method'] || 'get' if v['type'] == 'ajax' # ajax button %Q[#{caption}] else # link button %Q[#{caption}] end end # Javascript action when v['type'] == 'script' v['caption'] ||= v['text'] || 'Caption missing!' caption = t("#{v['caption'].downcase}", v['caption']) data = {'request' => 'script', 'script' => v['js']} %Q[#{ link_to(caption, '#', 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 input field for one tab. Subroutine of dc_fields_for_form. ############################################################################ def dc_fields_for_tab(fields) #:nodoc: @js ||= '' 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 << DrgcmsFormField::HiddenField.new(self, @record, options).render next end # label 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') # create field object from class and call its render method klas_string = options['type'].camelize field_html = if DrgcmsFormField.const_defined?(klas_string) # check if field type is defined klas = DrgcmsFormField.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 # html << if labels_pos == 'top' %Q[] else %Q[] end end html << '
#{field_html}
#{field_html}
' end ############################################################################ # Creates edit form div. ############################################################################ def dc_fields_for_form() if @form['edit'] p "@form['edit']['title'] directive will be depricated. Use edit_title in form directive instead! #{@form['table']}" end 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 << "
" # 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[
' html.html_safe end end