Changesets can be listed by changeset number.
The Git repository is here.
Changeset 489
Jan 2024 bounty system updates
- Comitted by: rool
- Date: Sunday January 28 09:30:54 2024 (10 months ago)
Affected files:
- rool/rails/canvass/trunk/db/migrate/20240126085900_add_targets_to_poll.rb
- rool/rails/canvass/trunk/README.md (diff)
- rool/rails/canvass/trunk/app/helpers/currencies_helper.rb (diff)
- rool/rails/canvass/trunk/app/models/poll.rb (diff)
- rool/rails/canvass/trunk/app/views/layouts/application.html.erb (diff)
- rool/rails/canvass/trunk/app/views/polls/_edit.html.erb (diff)
- rool/rails/canvass/trunk/app/views/polls/_list.html.erb (diff)
- rool/rails/canvass/trunk/app/views/polls/show.html.erb (diff)
- rool/rails/canvass/trunk/config/locales/en.yml (diff)
- rool/rails/canvass/trunk/db/schema.rb (diff)
rool/rails/canvass/trunk/README.md:
prev. | current | |
1 | ||
1 | # Canvass v1.1.0 2024-01-26 | |
Canvass lets you define polls which are 'voted' on by users who make | ||
financial donations to a cause. | ||
... | ... | |
## Changes | ||
13 | ||
13 | * v1.1.0: Adds concept of a guide target amount to a Poll. | |
14 | ||
15 | * v1.0.2: Allow administrators to register external donations (e.g. made by | |
cheque) rather than only supporting through-Canvass payments. | ||
* v1.0.1: Add administrator ability to revert an underway bounty to an |
rool/rails/canvass/trunk/app/helpers/currencies_helper.rb:
prev. | current | |
# non-numeric characters might appear in the output because they're present | ||
# in the formatting template for the given currency). | ||
# | ||
19 | | |
19 | # The two optional parameters are passed through to currencyhelp_print - | |
# see there for details. | ||
# | ||
# See also: currencyhelp_print() | ||
# | ||
24 | | |
24 | def currencyhelp_compose( currency, integer, fraction, converter = false, omit_zero_fraction = false ) | |
return currencyhelp_print( | ||
currency, | ||
Currency.simplify( integer, fraction ), | ||
28 | | |
28 | converter, | |
29 | omit_zero_fraction | |
) | ||
end | ||
... | ... | |
# if there's a currently logged in user (so the target currency is known) | ||
# and only if the currency requested differs from the target. | ||
# | ||
43 | # If the optional "omit_zero_fraction" is set to 'true', then for currencies | |
44 | # with a fractional component and where that component is zero, the fraction | |
45 | # will be omitted (e.g. generate "£750" instead of the default "$750.00"). | |
46 | # | |
# See also: currencyhelp_compose() | ||
# | ||
44 | | |
49 | def currencyhelp_print( currency, value, converter = false, omit_zero_fraction = false ) | |
rounding = Currency::ROUNDING_ALGORITHMS[ currency.rounding_algorithm ] || | ||
Currency::ROUNDING_ALGORITHMS[ Currency::DEFAULT_ROUNDING_ALGORITHM ] | ||
numeric = rounding[ :proc ].call( currency, value.to_s ) | ||
... | ... | |
) | ||
formatted_value = formatted_integer | ||
74 | | |
79 | formatted_value += currency.delimiter + formatted_fraction unless ( formatted_fraction.empty? || ( omit_zero_fraction == true && fraction.to_i == 0 ) ) | |
# Canvass doesn't use Locations like Artisan. | ||
# | ||
# # Build a currency converter link? | ||
79 | | |
84 | # | |
# if ( converter && | ||
# logged_in? && | ||
# ! current_user.location.nil? && | ||
# ! current_user.location.currency.nil? && | ||
# current_user.location.currency_id != currency.id ) | ||
85 | | |
90 | # | |
# converter_link = " " + link_to( | ||
# image_tag( | ||
# 'famfamfam_silk_icons/currency.png', | ||
... | ... | |
# :target => '_blank', | ||
# :class => 'help' | ||
# ) | ||
98 | | |
103 | # | |
# end # (Else converter_link ends up 'nil' by default) | ||
converter_link = nil | ||
# Compile the result | ||
if ( currency.show_after_number ) | ||
105 | | |
110 | return "#{ formatted_value }#{ currency.symbol }#{ converter_link }" | |
else | ||
107 | | |
112 | return "#{ currency.symbol }#{ formatted_value }#{ converter_link }" | |
end | ||
end | ||
rool/rails/canvass/trunk/app/models/poll.rb:
prev. | current | |
class Poll < ActiveRecord::Base | ||
13 | | |
13 | acts_as_audited :protect => false, :except => [ :total_for_sorting, :target_for_sorting ] | |
belongs_to :user | ||
belongs_to :currency | ||
... | ... | |
:only_integer => true | ||
33 | validates_numericality_of :target_integer, | |
34 | :target_fraction, | |
35 | ||
36 | :allow_nil => true, | |
37 | :allow_blank => true, | |
38 | :only_integer => true | |
39 | ||
validate :currency_alteration_is_permitted | ||
def currency_alteration_is_permitted | ||
... | ... | |
attr_accessible :title, | ||
:description, | ||
43 | | |
50 | :currency_id, | |
51 | :target_integer, | |
52 | :target_fraction | |
45 | | |
54 | # Keep a for-sorting cache columns up to date. | |
# | ||
47 | | |
56 | def update_sorting_amounts | |
amount = Currency.simplify( | ||
self.total_integer, | ||
self.total_fraction | ||
).to_f | ||
self.total_for_sorting = amount | ||
63 | ||
64 | effective_target_integer = self.target_integer.to_i | |
65 | effective_target_fraction = self.target_fraction.to_i | |
66 | ||
67 | if effective_target_integer == 0 && effective_target_fraction == 0 | |
68 | self.target_integer = nil | |
69 | self.target_fraction = nil | |
70 | self.target_for_sorting = nil | |
71 | else | |
72 | amount = Currency.simplify( | |
73 | self.target_integer, | |
74 | self.target_fraction | |
75 | ).to_f | |
76 | ||
77 | self.target_for_sorting = amount | |
78 | end | |
end | ||
56 | | |
81 | before_save :update_sorting_amounts | |
# How many entries to list per index page? See the Will Paginate plugin: | ||
# | ||
... | ... | |
array.sort! { | x, y | x.title <=> y.title } | ||
end | ||
363 | # Return an approximate percentage to guide target as an integer percent | |
364 | # amount (e.g. "45%"), or an empty string if there's no target present. Uses | |
365 | # the float amount caching columns for simplicity. | |
366 | # | |
367 | # If optional parameter "parentheses" is 'true', the returned string is | |
368 | # wrapped in parentheses in the non-empty case. | |
369 | # | |
370 | def percentage_complete(parentheses = false) | |
371 | if self.target_for_sorting.nil? || self.target_for_sorting == 0 | |
372 | return '' | |
373 | else | |
374 | percentage = ((100.0 / self.target_for_sorting) * self.total_for_sorting).floor() | |
375 | percentage = 0 if percentage < 0 | |
376 | ||
377 | # We currently allow > 100% for 'overpaid' bounties to indicate that | |
378 | # they're "doing well" for funding, but maybe need more donations to | |
379 | # attract a developer. | |
380 | # | |
381 | # percentage = 100 if percentage > 100 | |
382 | ||
383 | if parentheses | |
384 | return "(#{ percentage }%)" | |
385 | else | |
386 | return "#{ percentage }%" | |
387 | end | |
388 | end | |
389 | end | |
390 | ||
def thingytest | ||
I18n.t("Hello") | ||
end |
rool/rails/canvass/trunk/app/views/layouts/application.html.erb:
prev. | current | |
<% if using_quiet_prototype? -%> | ||
<%= javascript_include_tag( 'canvass/redcloth_preview.js' ) %> | ||
<% end -%> | ||
17 | | |
17 | <link type="text/css" rel="stylesheet" href="/css/risc_os_open_2024.css" media="all" /> | |
<%= stylesheet_link_tag( 'default_general', :media => :all ) %> | ||
<%= stylesheet_link_tag( 'default_print', :media => :print ) %> | ||
</head> |
rool/rails/canvass/trunk/app/views/polls/_edit.html.erb:
prev. | current | |
<% end -%> | ||
</td> | ||
</tr> | ||
40 | <tr> | |
41 | <th><%= f.label :target_for_sorting %></th> | |
42 | <td><%= currencyhelp_edit( poll.currency, f, :target, poll ) %></td> | |
43 | </tr> | |
<% if ( action == :update ) -%> | ||
<tr> | ||
<th><%= f.label :workflow_state %></th> | ||
... | ... | |
<p /> | ||
<%= f.label :description %> | ||
59 | | |
63 | (<a target="_blank" href="http://textile-lang.com">modern Textile</a>) | |
<br /> | ||
<div class="redcloth_preview_outer"> |
rool/rails/canvass/trunk/app/views/polls/_list.html.erb:
prev. | current | |
Poll.human_attribute_name( Poll.untranslated_column( header ) ) | ||
end | ||
%> | ||
22 | <th><%= Poll.human_attribute_name( 'percentage_complete' ) %></th> | |
<th class="spinner corner"><div id="spinner" style="display: none"></div></th> | ||
</tr> | ||
</thead> | ||
... | ... | |
</td> | ||
<td align="center"><%=h poll.votes %></td> | ||
<td align="right"><%= currencyhelp_compose( poll.currency, poll.total_integer, poll.total_fraction ) %></td> | ||
37 | <td align="center"><%= poll.percentage_complete() %></td> | |
<td class="actions"><%= apphelp_index_actions( 'poll', poll ) %></td> | ||
</tr> | ||
<% end -%> | ||
... | ... | |
<tfoot> | ||
<tr> | ||
43 | | |
45 | <td colspan="6"> | |
<div class="buttons"> | ||
<%= apphelp_protected_button_to( :new, { :method => :new_poll_path } ) %> | ||
<%= render :partial => 'shared/leightbox_button' %> |
rool/rails/canvass/trunk/app/views/polls/show.html.erb:
prev. | current | |
<h1><%= h @poll.title %></h1> | ||
3 | <% if @poll.target_for_sorting.present? && @poll.target_for_sorting > 0 -%> | |
4 | <h3> | |
5 | <%= Poll.human_attribute_name( 'target_for_sorting') %> | |
6 | <%= currencyhelp_compose( @poll.currency, @poll.target_integer, @poll.target_fraction, false, true ) %> | |
7 | </h3> | |
8 | <% end -%> | |
9 | ||
<p /> | ||
<%= RedCloth.new( @poll.description || '' ).to_html %> | ||
... | ... | |
</th> | ||
<td><%= @poll.votes %></td> | ||
</tr> | ||
20 | <% if @poll.target_for_sorting.present? && @poll.target_for_sorting > 0 %> | |
<tr> | ||
14 | | |
15 | | |
22 | <th><%= Poll.human_attribute_name( 'target_for_sorting' ) %></th> | |
23 | <td><%= currencyhelp_compose( @poll.currency, @poll.target_integer, @poll.target_fraction ) %></td> | |
</tr> | ||
25 | <% end %> | |
<tr> | ||
27 | <th><%= Poll.human_attribute_name( 'total_for_sorting' ) %></th> | |
28 | <td> | |
29 | <%= currencyhelp_compose( @poll.currency, @poll.total_integer, @poll.total_fraction ) %> | |
30 | <%= @poll.percentage_complete( true ) %> | |
31 | </td> | |
32 | </tr> | |
33 | ||
34 | <tr> | |
<th><%= Poll.human_attribute_name( 'workflow_state' ) %></th> | ||
<td class="poll_state"> | ||
<%= apphelp_state( @poll.workflow_state ) %> | ||
<%= help_link( "poll_states" ) %> | ||
</td> | ||
</tr> | ||
41 | ||
<tr valign="top"> | ||
<th>Help</th> | ||
<td> |
rool/rails/canvass/trunk/config/locales/en.yml:
prev. | current | |
workflow_state: "State" | ||
votes: "Donations" | ||
total_for_sorting: "Total" | ||
69 | target_for_sorting: "Guide target" | |
70 | percentage_complete: "Funding" | |
currency: "Supported donation currency" | ||
user: |
rool/rails/canvass/trunk/db/schema.rb:
prev. | current | |
1 | ||
1 | # This file is auto-generated from the current state of the database. Instead of editing this file, | |
# please use the migrations feature of Active Record to incrementally modify your database, and | ||
# then regenerate this schema definition. | ||
# | ||
... | ... | |
# | ||
# It's strongly recommended to check this file into your version control system. | ||
12 | ||
12 | ActiveRecord::Schema.define(:version => 20240126085900) do | |
create_table "audits", :force => true do |t| | ||
t.integer "auditable_id" | ||
... | ... | |
t.float "total_for_sorting" | ||
t.datetime "created_at" | ||
t.datetime "updated_at" | ||
84 | t.string "target_integer" | |
85 | t.string "target_fraction" | |
86 | t.float "target_for_sorting" | |
end | ||
add_index "polls", ["currency_id"], :name => "index_polls_on_currency_id" |