######################################################################## # File:: auditers_helper.rb # (C):: Hipposoft 2011 # # Purpose:: Utility methods for audit-related views. # ---------------------------------------------------------------------- # 23-Feb-2011 (ADH): Created. ######################################################################## module AuditersHelper # Return an HTML-safe description of a user's name from a given Auditer # record, linking to the User Controller's "show" action for that User if # the object still exists and dealing with 'nil' names in passing. # def auditershelp_user_link( record ) if ( record.user.nil? ) if ( record.username.blank? ) apphelp_generic( :unknown ) else h( record.username ) end else link_to( h( record.user.name ), record.user ) end end # Return an HTML table describing the changes in the given Auditer record # in human readable form. # def auditershelp_changes( record ) type = record.auditable_type changes = record.changes model = type.constantize translate = model.respond_to?( :columns_for_translation ) return apphelp_view_hint( :no_other_details ) if ( changes.nil? or changes.empty? ) # Not 'changes.blank?', as this would ignore changes to 'false' since 'false.blank?' => 'true' output = '' output << '' # A sorted change table is easier to follow on the whole keys = changes.keys.sort do | a, b | if ( translate ) a = model.untranslated_column( a ) b = model.untranslated_column( b ) end model.human_attribute_name( a ) <=> model.human_attribute_name( b ) end # There's special support here for models containing a single currency # value split as integer and fraction strings. have_had_currency_value = false currency_keys = %w{ amount_integer amount_fraction total_integer total_fraction } keys.each do | key | cdata = changes[ key ] key = model.untranslated_column( key ) if ( translate ) # Deal with amounts of money. if ( currency_keys.include?( key ) ) next if ( have_had_currency_value ) if ( model == Poll ) integer_key = 'total_integer' fraction_key = 'total_fraction' else # Assume any other model uses "amount" integer_key = 'amount_integer' fraction_key = 'amount_fraction' end # We need to find the model instance in question in order to determine # its currency. If we can't, then we have to drop through to the 'raw' # display of change values below. model_instance = model.find_by_id( record.auditable_id ) unless ( model_instance.nil? || model_instance.currency.nil? ) integer_changes = changes[ integer_key ] fraction_changes = changes[ fraction_key ] # Changes may occur only in the integer or fraction part. In that case, # treat the unchanged part as a change from and to the current value # for the model. if ( integer_changes.nil? ) integer_value = model_instance.send( integer_key ) integer_changes = [ integer_value, integer_value ] elsif ( fraction_changes.nil? ) fraction_value = model_instance.send( fraction_key ) fraction_changes = [ fraction_value, fraction_value ] end # The above code means that we might have synthesized change arrays # for an integer or fractin part, because only the corresponding # fraction or integer part had a change recorded. But if that change # was from 'unset'/nil to a value, the change record will be a single # item, not an array. We need to deal with the combination of arrays # and non-arrays. if ( ! integer_changes.is_a?( Array ) || ! fraction_changes.is_a?( Array ) ) integer_changes = integer_changes[ 0 ] if ( integer_changes.is_a?( Array ) ) fraction_changes = fraction_changes[ 0 ] if ( fraction_changes.is_a?( Array ) ) cdata = currencyhelp_compose( model_instance.currency, integer_changes, fraction_changes ) else cdata = [] cdata[ 0 ] = currencyhelp_compose( model_instance.currency, integer_changes[ 0 ], fraction_changes[ 0 ] ) cdata[ 1 ] = currencyhelp_compose( model_instance.currency, integer_changes[ 1 ], fraction_changes[ 1 ] ) end if ( model == Poll ) key = 'total_for_sorting' else # Assume any other model uses "amount" key = 'amount_for_sorting' end have_had_currency_value = true end # 'unless ( model_instance.nil? )' end # 'if ( currency_keys.include?( key.to_sym ) )' # Sort out human-readable versions of the change string. Change data and # even the value of 'key' may have been modified by code above if amounts # of money were detected in the change set. if ( cdata.instance_of?( Array ) ) cfrom = auditershelp_to_descriptive_s( model, key, cdata[ 0 ] ) cto = auditershelp_to_descriptive_s( model, key, cdata[ 1 ] ) else cfrom = '—' cto = auditershelp_to_descriptive_s( model, key, cdata ) end # Ignore changes from nil to empty strings, or similar. next if ( cfrom == '—' && cto == '—' ) # Add the hopefully easily human-readable descriptive change row. output << "" if ( record.action == "create" || record.action == "destroy" ) output << "" else output << "" end end return output << '
' << apphelp_view_hint( :change_details_field ) << '' if ( record.action == "create" ) output << apphelp_view_hint( :change_details_became ) elsif ( record.action == "destroy" ) output << apphelp_view_hint( :change_details_was ) else output << apphelp_view_hint( :change_details_from ) << '' << apphelp_view_hint( :change_details_to ) end output << '
#{ model.human_attribute_name( key ) }#{ cto }
#{ cfrom }#{ cto }
' end # Return a more descriptive version of a string. Pass the model the string # is for, the field it's for and its value (which may be from before or after # a recorded change, so it's not read from the model automatically using the # field name you give). The field name must be given as a string not a symbol # and must be the 'untranslated' form for translatable models (e.g. pass # "title" rather than "title_en"). # # Returns a more descriptive string including special case handlers for known # fields which contain e.g. Textile data. The returned value is HTML safe. # def auditershelp_to_descriptive_s( model, field, str ) str = str.to_s return '—' if ( str.blank? ) # Note this will also catch non-empty, but all whitespace strings if ( model == Poll ) if ( field == 'description' ) return RedCloth.new( str ).to_html elsif ( field == 'currency_id' ) return auditershelp_link_to( Currency, str, :name ) elsif ( field == 'user_id' ) return auditershelp_link_to( User, str, :name ) end elsif ( model == Donation ) if ( field == 'currency_id' ) return auditershelp_link_to( Currency, str, :name ) elsif ( field == 'poll_id' ) return auditershelp_link_to( Poll, str, :title ) elsif ( field == 'user_id' ) return auditershelp_link_to( User, str, :name ) elsif ( field == 'workflow_state' ) return apphelp_state( str, DonationsController ) end end return h( str ) end # Pass a model class (e.g. User, Currency), an ID as a string or integer # and a method. Tries to find an instance of the given model with the given # ID. If found, it returns a link to the 'show' view for that instance using # the visible link text obtained by sending the given method to the instance. # Otherwise, returns the ID again. # # Example: # # auditershelp_ink_to( User, 2, :email ) # # Tries to find a User with ID 2. If not found, returns 2. If found, returns # a link to the 'show' action for that user with "user.email" as the visible # link text. # def auditershelp_link_to( model, id, method ) object = model.find_by_id( id ) return id if ( object.nil? ) return link_to( object.send( method ), object ) end end