Support two fundraising bars for a stretch match

Long ago, when the bar was all Javascript, we had the ability to have
sub-targets in a single bar.  I was not able to invest the time
necessary to figure out how to do that again using the newer setup
for the fundraising bar, so instead I've added the ability to have a
stretch match bar that appears above the other one once the first
match period ends.

While it handles most of the weird cases with some grace, it will
probably look weird unless you set up a `SITE_FUNDGOAL_0` as a match
that finishes at least a week or two before `SITE_FUNDGOAL_1`.

The two aren't really aware of each other, either, so you have to
make sure the objects are updated properly (i.e., it *will* display
the stretch when `SITE_FUNDGOAL_0` ends even if there are match funds
remaining in `SITE_FUNDGOAL_0`).

A better solution should be found and implemented before 2026-11-22.
If you're actually reading this commit message for a reason other
than historical interest and that date has past, you're probably in
big trouble right now. 😬
This commit is contained in:
Bradley Kuhn 2026-01-14 14:16:44 -08:00
parent 67b344d2f4
commit cf5bd77c56
4 changed files with 111 additions and 47 deletions

View file

@ -4,10 +4,14 @@ from .fundgoal.models import FundraisingGoal
def sitefundraiser(request): def sitefundraiser(request):
try: try:
fundgoal = FundraisingGoal.objects.get(fundraiser_code_name=settings.SITE_FUNDGOAL) fundgoal0 = FundraisingGoal.objects.get(fundraiser_code_name=settings.SITE_FUNDGOAL_0)
except FundraisingGoal.DoesNotExist: except FundraisingGoal.DoesNotExist:
fundgoal = None fundgoal0 = None
return {'sitefundgoal': fundgoal} try:
fundgoal1 = FundraisingGoal.objects.get(fundraiser_code_name=settings.SITE_FUNDGOAL_1)
except FundraisingGoal.DoesNotExist:
fundgoal1 = None
return {'sitefundgoal0': fundgoal0, 'sitefundgoal1': fundgoal1 }
def host_url(request): def host_url(request):

View file

@ -1,51 +1,106 @@
{% load humanize %} {% load humanize %}
{% if sitefundgoal.days_remaining >= -22 %}{# i.e. 7 days over completion #} {% if sitefundgoal1.days_remaining >= -22 or sitefundgoal0.days_remaining >= -22 %} {# IF LEVEL 0 (only show this at all for 7 days after fundraiser and stretch are over) #}
<div class="fundraiser-top-text ph3 pt2 pb3 mb2 mb3-ns"> <div class="fundraiser-top-text ph3 pt2 pb3 mb2 mb3-ns">
<div class="mw8 center ph2 ph4-ns"> <div class="mw8 center ph2 ph4-ns">
<div class="mt2 mb3 tc"> <div class="mt2 mb3 tc">
{% if sitefundgoal.days_remaining >= 0 %} {% if sitefundgoal0.match_remaining <= 0 %} {# IF LEVEL 0.1 #}
{% if sitefundgoal.match_remaining <= 0 %} {% if sitefundgoal1.days_remaining >= 0 %} {# IF LEVEL 0.1.0 #}
Thanks to so many donors, we earned our full match! {% if sitefundgoal1.match_remaining <= 0 %} {# IF LEVEL 0.1.0.0 #}
Help us go further to stand up for software freedom &mdash; <a href="/sustainer">sign up now</a>! Thanks to all our donors who participated in our historic donation match challenge!
{% else %} Thanks to all of you, we raised our goal of <b>$503,878</b> to support software freedom:
{% if sitefundgoal.days_remaining == 0 %} {% else %} {# ELSE LEVEL 0.1.0.0 when sitefundgoal1.match_remaining > 0 #}
For the next {{ sitefundgoal.hours_remaining }} hour{{ sitefundgoal.hours_remaining|pluralize }} <strong>only</strong>, the Thanks to so many donors, we met <em>our largest match donation ever of <strong>$211,939</strong></em>.
{% elif sitefundgoal.days_remaining == 1 %} Two generous anonymous <a href="/news/2026/jan/14/match-challenge-extended-by-40012/">donors have provided</a> <em>another ${{ sitefundgoal1.fundraiser_goal_amount|floatformat:0|intcomma }}</em> of <em>additional</em> matching funds.
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. Through tomorrow only, the <a href="/sustainer/">Give now to help us</a> reach <a href="/news/2026/jan/14/match-challenge-extended-by-40012/">this stretch goal</a>!<br/>
{% elif sitefundgoal.days_remaining < 14 %} {% if sitefundgoal1.days_remaining == 0 %} {# IF LEVEL 0.1.0.0.0 sitefundgoal1.days_remaining alternatives #}
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. For only {{ sitefundgoal.days_remaining }} more days, the For <strong>only</strong> the next {{ sitefundgoal1.hours_remaining }} hour{{ sitefundgoal1.hours_remaining|pluralize }}, the
{% else %} {% elif sitefundgoal1.days_remaining == 1 %} {# ELIF LEVEL 0.1.0.0.0 sitefundgoal1.days_remaining alternatives #}
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. Until January 15, the Through tomorrow only, the
{% endif %} {% elif sitefundgoal1.days_remaining < 14 %} {# ELIF LEVEL 0.1.0.0.0 sitefundgoal1.days_remaining alternatives #}
next <b>${{ sitefundgoal.match_remaining|floatformat:0|intcomma }}</b> of <a href="/sustainer/">support we receive</a> will be matched! For only {{ sitefundgoal1.days_remaining }} more days, the
{% endif %} {% else %} {# ELIF LEVEL 0.1.0.0.0 sitefundgoal1.days_remaining alternatives #}
{% else %} Until February 5, the
Thanks to all our donors who participated in our {% endif %} {# ELSE LEVEL 0.1.0.0.0 sitefundgoal1.days_remaining alternatives #}
historic donation match challenge! Thanks to you, <a href="https://sfconservancy.org/blog/2025/jan/16/end-of-year-2024-fundraiser-a-massive-success/">we next <b>${{ sitefundgoal1.match_remaining|floatformat:0|intcomma }}</b> of <a href="/sustainer/">financial support we receive</a> will be matched!
raised more than $480k to support software freedom</a>: {% endif %} {# ENDIF LEVEL 0.1.0.0 (testing whether or not sitefundgoal1.match_remaining has anything left in it) #}
{% endif %} {% else %} {# ELSE LEVEL 0.1.0 when (sitefundgoal1.days_remaining < 0) #}
</div> {# The problem with automating this is that it does not actually test here if we made the match or not; we are usually watching it so carefully that it should not matter? We also have made ever match we did since 2015, so no one has probably thought hard about this problem. -- bkuhn #}
Thanks to all our donors who participated in our historic donation match challenge!
Thanks to all of you, we raised our goal of <b>$503,878</b>.
Help us go further to stand up for software freedom &mdash; <a href="/sustainer">sign up now</a>!
{% endif %} {# ENDIF LEVEL 0.1.0 (whether or not there are any sitefundgoal1.days_remaining) #}
<a href="/sustainer/" style="text-decoration: none !important"> <a href="/sustainer/" style="text-decoration: none !important">
<div id="siteprogressbar" class="flex items-stretch w-100"> <div id="siteprogressbar" class="flex items-stretch w-100">
{% if sitefundgoal.match_remaining <= 0 %} {% if sitefundgoal1.match_remaining <= 0 %} {# IF LEVEL 0.1.1 #}
<div class="progress matched ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.fundraiser_so_far_amount }}px"> <div class="progress matched ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal1.fundraiser_so_far_amount }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100 ph1">${{ sitefundgoal.fundraiser_goal_amount|floatformat:0|intcomma }} fully matched!</span> <span id="site-fundraiser-match-count" class="soFarText tc w-100 ph1 done">${{ sitefundgoal1.fundraiser_goal_amount|floatformat:0|intcomma }} fully matched!</span>
</div> </div>
<div class="progress exceeded pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.match_exceeded_by }}px"> {% else %} {# ELSE LEVEL 0.1.1 when sitefundgoal1.match_remaining > 0 #}
<span id="site-fundraiser-match-count" class="soFarText tc w-100 exceeded ph1">${{sitefundgoal.match_exceeded_by|floatformat:0|intcomma }} additional<br> raised!<br></span> <div class="progress ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal1.fundraiser_so_far_amount }}px">
</div> <span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ sitefundgoal1.fundraiser_so_far_amount|floatformat:0|intcomma }} matched!</span>
{% else %} </div>
<div class="progress ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.fundraiser_so_far_amount }}px"> <div class="final-goal pv1 b flex items-center" style="flex-basis: {{ sitefundgoal1.match_remaining }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ sitefundgoal.fundraiser_so_far_amount|floatformat:0|intcomma }} matched!</span> <span id="site-fundraiser-final-goal" class="goalText tc w-100 ph1">${{ sitefundgoal1.match_remaining|floatformat:0|intcomma }} to go!</span>
</div> </div>
<div class="final-goal pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.match_remaining }}px"> {% endif %} {# ENDIF LEVEL 0.1.1 testing whether or not sitefundgoal1.match_remaining has anything in it #}
<span id="site-fundraiser-final-goal" class="goalText tc w-100 ph1">${{ sitefundgoal.match_remaining|floatformat:0|intcomma }} to go!</span>
</div>
{% endif %}
</div> </div>
</a> </a>
<a href="/sustainer/" style="text-decoration: none !important">
<div id="siteprogressbar" class="flex items-stretch w-100">
{% if sitefundgoal0.match_remaining <= 0 %} {# IF LEVEL 0.1.2, pointless test of sitefundgoal0.match_remaining (if we get here, else block should never be used!) #}
<div class="progress matched ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.fundraiser_so_far_amount }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100 ph1 done">${{ sitefundgoal0.fundraiser_goal_amount|floatformat:0|intcomma }} fully matched!</span>
</div>
{% else %} {# ELSE LEVEL 0.1.2, sitefundgoal0.match_remaining > 0 , but NOTE! We are here inside IF LEVEL 0.1, so this *should* never appear, included just for consistency #}
<div class="progress ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.fundraiser_so_far_amount }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ sitefundgoal0.fundraiser_so_far_amount|floatformat:0|intcomma }} matched!</span>
</div>
<div class="final-goal pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.match_remaining }}px">
<span id="site-fundraiser-final-goal" class="goalText tc w-100 ph1">${{ sitefundgoal0.match_remaining|floatformat:0|intcomma }} to go!</span>
</div>
{% endif %} {# ENDIF LEVEL 0.1.2, this ends the pointless block here for consistency, see comment above. #}
</div>
</a>
{% else %} {# ELSE LEVEL 0.1 when (sitefundgoal0.match_remaining > 0) #}
{% if sitefundgoal0.days_remaining >= 0 %} {# IF LEVEL 0.1.3 #}
{% if sitefundgoal0.match_remaining <= 0 %} {# IF LEVEL 0.1.3.0 #}
Thanks to so many donors, we earned our full match!
{% else %} {# ELSE LEVEL 0.1.3.0 when sitefundgoal0.match_remaining > 0 #}
{% if sitefundgoal0.days_remaining == 0 %} {# IF LEVEL 0.1.3.0.0 testing options for sitefundgoal0.days_remaining #}
For <strong>only</strong> the next {{ sitefundgoal0.hours_remaining }} hour{{ sitefundgoal0.hours_remaining|pluralize }}, the
{% elif sitefundgoal0.days_remaining == 1 %} {# ELSIF LEVEL 0.1.3.0.0 testing options for sitefundgoal0.days_remaining #}
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. Through tomorrow only, the
{% elif sitefundgoal0.days_remaining < 14 %} {# ELSIF LEVEL 0.1.3.0.0 testing options for sitefundgoal0.days_remaining #}
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. For only {{ sitefundgoal0.days_remaining }} more days, the
{% else %} {# ELSE LEVEL 0.1.3.0.0 testing options for sitefundgoal0.days_remaining #}
Help us reach our goal of <b>$503,878</b> this season to keep SFC going. Until January 15, the
{% endif %} {# ENDIF LEVEL 0.1.3.0.0 testing options for sitefundgoal0.days_remaining #}
next <b>${{ sitefundgoal0.match_remaining|floatformat:0|intcomma }}</b> of <a href="/sustainer/">support we receive</a> will be matched!
{% endif %} {# ENDIF LEVEL 0.1.3.0 testing if sitefundgoal0.match_remaining has anything left #}
{% else %} {# ENDIF LEVEL 0.1.3, when sitefundgoal0.days_remaining < 0 #}
{# The problem with automating this is that it does not actually test here if we made the match or not; we are usually watching it so carefully that it should not matter? We also have made ever match we did since 2015, so no one has probably thought hard about this problem. -- bkuhn #}
Thanks to all our donors who participated in our historic donation match challenge!
Help us go further to stand up for software freedom &mdash; <a href="/sustainer">sign up now</a>!
{% endif %} {# ENDIF LEVEL 0.1.3, testing if there are any sitefundgoal0.days_remaining left #}
<a href="/sustainer/" style="text-decoration: none !important">
<div id="siteprogressbar" class="flex items-stretch w-100">
{% if sitefundgoal0.match_remaining <= 0 %} {# IF LEVEL 0.1.3.1 #}
<div class="progress matched ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.fundraiser_so_far_amount }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100 ph1 done">${{ sitefundgoal0.fundraiser_goal_amount|floatformat:0|intcomma }} fully matched!</span>
</div>
{% else %} {# ELSE LEVEL 0.1.3.1 sitefundgoal0.match_remaining > 0 #}
<div class="progress ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.fundraiser_so_far_amount }}px">
<span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ sitefundgoal0.fundraiser_so_far_amount|floatformat:0|intcomma }} matched!</span>
</div>
<div class="final-goal pv1 b flex items-center" style="flex-basis: {{ sitefundgoal0.match_remaining }}px">
<span id="site-fundraiser-final-goal" class="goalText tc w-100 ph1">${{ sitefundgoal0.match_remaining|floatformat:0|intcomma }} to go!</span>
</div>
{% endif %} {# ENDIF LEVEL 0.1.3.1 sitefundgoal0.match_remaining #}
</div>
</a>
{% endif %} {# ENDIF LEVEL 0.1, two choices for based on sitefundgoal0.match_remaining #}
</div> </div>
</div> </div>
{% endif %} </div>
{% endif %} {# ENDIF LEVEL 0 (the fundraiser has been over for > 7 days) #}

View file

@ -162,4 +162,6 @@ USETHESOURCE = {
'LIST_RECIPIENT': 'ccs-review@lists.sfconservancy.org', 'LIST_RECIPIENT': 'ccs-review@lists.sfconservancy.org',
} }
SITE_FUNDGOAL = 'cy2025-end-year-match' SITE_FUNDGOAL_0 = 'cy2025-end-year-match'
SITE_FUNDGOAL_1 = 'fy2025-26-extend-match'

View file

@ -1168,6 +1168,9 @@ body > header {
#siteprogressbar .soFarText { #siteprogressbar .soFarText {
color: white; color: white;
} }
#siteprogressbar .done {
font-size: 130%;
}
#siteprogressbar .exceeded { #siteprogressbar .exceeded {
color: var(--khaki-green); color: var(--khaki-green);
} }