Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 390
- Log:
Initial import of Canvass, a donations-based poll application.
- Author:
- rool
- Date:
- Mon Mar 21 14:58:04 +0000 2011
- Size:
- 8804 Bytes
1 | ######################################################################## |
2 | # File:: auditers_helper.rb |
3 | # (C):: Hipposoft 2011 |
4 | # |
5 | # Purpose:: Utility methods for audit-related views. |
6 | # ---------------------------------------------------------------------- |
7 | # 23-Feb-2011 (ADH): Created. |
8 | ######################################################################## |
9 | |
10 | module AuditersHelper |
11 | |
12 | # Return an HTML-safe description of a user's name from a given Auditer |
13 | # record, linking to the User Controller's "show" action for that User if |
14 | # the object still exists and dealing with 'nil' names in passing. |
15 | # |
16 | def auditershelp_user_link( record ) |
17 | if ( record.user.nil? ) |
18 | if ( record.username.blank? ) |
19 | apphelp_generic( :unknown ) |
20 | else |
21 | h( record.username ) |
22 | end |
23 | else |
24 | link_to( h( record.user.name ), record.user ) |
25 | end |
26 | end |
27 | |
28 | # Return an HTML table describing the changes in the given Auditer record |
29 | # in human readable form. |
30 | # |
31 | def auditershelp_changes( record ) |
32 | type = record.auditable_type |
33 | changes = record.changes |
34 | model = type.constantize |
35 | translate = model.respond_to?( :columns_for_translation ) |
36 | |
37 | 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' |
38 | |
39 | output = '<table border="1" cellspacing="1" cellpadding="1" class="audit_changes">' |
40 | output << '<tr><th>' << |
41 | apphelp_view_hint( :change_details_field ) << |
42 | '</th><th>' |
43 | |
44 | if ( record.action == "create" ) |
45 | output << apphelp_view_hint( :change_details_became ) |
46 | elsif ( record.action == "destroy" ) |
47 | output << apphelp_view_hint( :change_details_was ) |
48 | else |
49 | output << apphelp_view_hint( :change_details_from ) << |
50 | '</th><th>' << |
51 | apphelp_view_hint( :change_details_to ) |
52 | end |
53 | |
54 | output << '</th></tr>' |
55 | |
56 | # A sorted change table is easier to follow on the whole |
57 | |
58 | keys = changes.keys.sort do | a, b | |
59 | if ( translate ) |
60 | a = model.untranslated_column( a ) |
61 | b = model.untranslated_column( b ) |
62 | end |
63 | |
64 | model.human_attribute_name( a ) <=> model.human_attribute_name( b ) |
65 | end |
66 | |
67 | # There's special support here for models containing a single currency |
68 | # value split as integer and fraction strings. |
69 | |
70 | have_had_currency_value = false |
71 | currency_keys = %w{ amount_integer amount_fraction total_integer total_fraction } |
72 | |
73 | keys.each do | key | |
74 | cdata = changes[ key ] |
75 | key = model.untranslated_column( key ) if ( translate ) |
76 | |
77 | # Deal with amounts of money. |
78 | |
79 | if ( currency_keys.include?( key ) ) |
80 | next if ( have_had_currency_value ) |
81 | |
82 | if ( model == Poll ) |
83 | integer_key = 'total_integer' |
84 | fraction_key = 'total_fraction' |
85 | else # Assume any other model uses "amount" |
86 | integer_key = 'amount_integer' |
87 | fraction_key = 'amount_fraction' |
88 | end |
89 | |
90 | # We need to find the model instance in question in order to determine |
91 | # its currency. If we can't, then we have to drop through to the 'raw' |
92 | # display of change values below. |
93 | |
94 | model_instance = model.find_by_id( record.auditable_id ) |
95 | |
96 | unless ( model_instance.nil? || model_instance.currency.nil? ) |
97 | |
98 | integer_changes = changes[ integer_key ] |
99 | fraction_changes = changes[ fraction_key ] |
100 | |
101 | # Changes may occur only in the integer or fraction part. In that case, |
102 | # treat the unchanged part as a change from and to the current value |
103 | # for the model. |
104 | |
105 | if ( integer_changes.nil? ) |
106 | integer_value = model_instance.send( integer_key ) |
107 | integer_changes = [ integer_value, integer_value ] |
108 | elsif ( fraction_changes.nil? ) |
109 | fraction_value = model_instance.send( fraction_key ) |
110 | fraction_changes = [ fraction_value, fraction_value ] |
111 | end |
112 | |
113 | # The above code means that we might have synthesized change arrays |
114 | # for an integer or fractin part, because only the corresponding |
115 | # fraction or integer part had a change recorded. But if that change |
116 | # was from 'unset'/nil to a value, the change record will be a single |
117 | # item, not an array. We need to deal with the combination of arrays |
118 | # and non-arrays. |
119 | |
120 | if ( ! integer_changes.is_a?( Array ) || ! fraction_changes.is_a?( Array ) ) |
121 | integer_changes = integer_changes[ 0 ] if ( integer_changes.is_a?( Array ) ) |
122 | fraction_changes = fraction_changes[ 0 ] if ( fraction_changes.is_a?( Array ) ) |
123 | |
124 | cdata = currencyhelp_compose( model_instance.currency, integer_changes, fraction_changes ) |
125 | else |
126 | cdata = [] |
127 | cdata[ 0 ] = currencyhelp_compose( model_instance.currency, integer_changes[ 0 ], fraction_changes[ 0 ] ) |
128 | cdata[ 1 ] = currencyhelp_compose( model_instance.currency, integer_changes[ 1 ], fraction_changes[ 1 ] ) |
129 | end |
130 | |
131 | if ( model == Poll ) |
132 | key = 'total_for_sorting' |
133 | else # Assume any other model uses "amount" |
134 | key = 'amount_for_sorting' |
135 | end |
136 | |
137 | have_had_currency_value = true |
138 | |
139 | end # 'unless ( model_instance.nil? )' |
140 | end # 'if ( currency_keys.include?( key.to_sym ) )' |
141 | |
142 | # Sort out human-readable versions of the change string. Change data and |
143 | # even the value of 'key' may have been modified by code above if amounts |
144 | # of money were detected in the change set. |
145 | |
146 | if ( cdata.instance_of?( Array ) ) |
147 | cfrom = auditershelp_to_descriptive_s( model, key, cdata[ 0 ] ) |
148 | cto = auditershelp_to_descriptive_s( model, key, cdata[ 1 ] ) |
149 | else |
150 | cfrom = '—' |
151 | cto = auditershelp_to_descriptive_s( model, key, cdata ) |
152 | end |
153 | |
154 | # Ignore changes from nil to empty strings, or similar. |
155 | |
156 | next if ( cfrom == '—' && cto == '—' ) |
157 | |
158 | # Add the hopefully easily human-readable descriptive change row. |
159 | |
160 | output << "<tr valign=\"top\"><td>#{ model.human_attribute_name( key ) }</td>" |
161 | |
162 | if ( record.action == "create" || record.action == "destroy" ) |
163 | output << "<td>#{ cto }</td></tr>" |
164 | else |
165 | output << "<td>#{ cfrom }</td><td>#{ cto }</td></tr>" |
166 | end |
167 | end |
168 | |
169 | return output << '</table>' |
170 | end |
171 | |
172 | # Return a more descriptive version of a string. Pass the model the string |
173 | # is for, the field it's for and its value (which may be from before or after |
174 | # a recorded change, so it's not read from the model automatically using the |
175 | # field name you give). The field name must be given as a string not a symbol |
176 | # and must be the 'untranslated' form for translatable models (e.g. pass |
177 | # "title" rather than "title_en"). |
178 | # |
179 | # Returns a more descriptive string including special case handlers for known |
180 | # fields which contain e.g. Textile data. The returned value is HTML safe. |
181 | # |
182 | def auditershelp_to_descriptive_s( model, field, str ) |
183 | str = str.to_s |
184 | |
185 | return '—' if ( str.blank? ) # Note this will also catch non-empty, but all whitespace strings |
186 | |
187 | if ( model == Poll ) |
188 | if ( field == 'description' ) |
189 | return RedCloth.new( str ).to_html |
190 | elsif ( field == 'currency_id' ) |
191 | return auditershelp_link_to( Currency, str, :name ) |
192 | elsif ( field == 'user_id' ) |
193 | return auditershelp_link_to( User, str, :name ) |
194 | end |
195 | elsif ( model == Donation ) |
196 | if ( field == 'currency_id' ) |
197 | return auditershelp_link_to( Currency, str, :name ) |
198 | elsif ( field == 'poll_id' ) |
199 | return auditershelp_link_to( Poll, str, :title ) |
200 | elsif ( field == 'user_id' ) |
201 | return auditershelp_link_to( User, str, :name ) |
202 | elsif ( field == 'workflow_state' ) |
203 | return apphelp_state( str, DonationsController ) |
204 | end |
205 | end |
206 | |
207 | return h( str ) |
208 | end |
209 | |
210 | # Pass a model class (e.g. User, Currency), an ID as a string or integer |
211 | # and a method. Tries to find an instance of the given model with the given |
212 | # ID. If found, it returns a link to the 'show' view for that instance using |
213 | # the visible link text obtained by sending the given method to the instance. |
214 | # Otherwise, returns the ID again. |
215 | # |
216 | # Example: |
217 | # |
218 | # auditershelp_ink_to( User, 2, :email ) |
219 | # |
220 | # Tries to find a User with ID 2. If not found, returns 2. If found, returns |
221 | # a link to the 'show' action for that user with "user.email" as the visible |
222 | # link text. |
223 | # |
224 | def auditershelp_link_to( model, id, method ) |
225 | object = model.find_by_id( id ) |
226 | |
227 | return id if ( object.nil? ) |
228 | return link_to( object.send( method ), object ) |
229 | end |
230 | end |