Context variables
Global context
Available in every template (layout, sections, pages).
| Variable | Type | Notes |
|---|---|---|
current_url | string | absolute URL of the request |
organization | organization | see Objects |
supporter | supporter or None | logged-in supporter |
supporter_urls.login | string | login URL |
supporter_urls.logout | string | logout URL |
supporter_urls.dashboard | string | supporter private area |
supporter_urls.profile | string | supporter profile |
supporter_urls.donations | string | supporter donations |
supporter_urls.campaigns | string | supporter campaigns |
pages | list of pages | all published pages |
campaigns | list of campaigns | all campaigns |
menus | list of menus | |
menuitems | list of menu items | |
blogs | list of blogs | |
articles | list of articles |
The global lists (campaigns, pages, blogs, articles) expose only the basic
attributes. The current page variable (campaign, page, blog, article,
project) exposes all the ones described in Objects and attributes.
Per-page variables
| Page | Template | Additional variables |
|---|---|---|
| Campaign | campaign | campaign; for a Lead campaign also form, custom_field_definitions, lead_form_success, captcha |
| Thank you | thankyou | checkout_campaign, checkout_campaign_url, checkout_supporter, checkout_donation, message_published, form |
| Peer campaign | peer_campaign | campaign, peer_campaign, stats |
| Peer campaign form | peer_campaign_form | form, supporter, campaign, peer_campaign (None on creation) |
| Peer campaign management (v2) | peer_campaign_form | as above, plus stats, peer_campaign_updated, peer_campaign_public_url, peer_campaign_update_url_v2 |
| Page | page | page |
| Blog | blog | blog |
| Article | article | article |
| Project | project | project |
| Project list | project_list | projects |
The template used is the one for the type (e.g. campaign) or the one set on the
individual content (campaign.template).
Thank you
checkout_supporter.id and checkout_donation.id are the numeric ids of the
supporter and of the donation just recorded, for conversion pixels and dataLayers.
<script>
dataLayer.push({
event: "donation",
donation_id: "{{ checkout_donation.id }}",
supporter_id: "{{ checkout_supporter.id }}",
value: "{{ checkout_donation.amount }}",
});
</script>
In the theme preview (.../thankyou/__preview__) both ids are 0, like the rest of
the example data.
Peer campaign
The form has two fields, peer_supporter_name and custom_message. The slug is
derived from the name (slugify plus a numeric suffix if taken) and cannot be changed
later, because it is the address the promoter has already shared.
The peer_campaign_form template renders three pages, told apart by
peer_campaign:
campaigns/<slug>/fundraise/: creation,peer_campaignisNone. It returns 404, before asking for login, if the campaign does not allow personal fundraisers or is a Lead campaign;campaigns/<slug>/<peer_slug>/update/v2/: detail and management of the fundraiser, with statistics and a prefilled form. The form is sent via POST topeer_campaign_update_url_v2; after a successful save the view returns here withpeer_campaign_updatedset toTrue;campaigns/<slug>/<peer_slug>/update/: v1, still active. After saving it redirects to the public page and does not pass the additional variables.
Themes link to /my/campaigns/<id>/ instead, in the supporter
area. v2 stays active because it is the form action in themes
released earlier.
peer_campaign_public_url is absolute and without __preview: it is the address to
share. peer_campaign_update_url_v2 is relative and keeps __preview, because it is
a form target.
Disabling personal fundraisers on the parent campaign prevents new ones from being created; existing ones stay public and editable.
Supporter area
These pages are static theme templates and do not use the sections engine.
| Page | Template | Additional variables |
|---|---|---|
| Login | templates/supporters/login.html | form, email_sent, email_sent_to, captcha, error |
| Dashboard | templates/supporters/dashboard.html | profile, donations, peer_campaigns |
| Profile | templates/supporters/profile.html | form, profile, saved |
| Donations | templates/supporters/donations.html | profile, donations |
| Campaigns | templates/supporters/peer_campaigns.html | profile, peer_campaigns |
| Campaign detail | templates/supporters/peer_campaign.html | form, profile, campaign, peer_campaign, stats, peer_campaign_updated, peer_campaign_public_url, peer_campaign_manage_url |
profile is a supporter, donations a list of
donation and peer_campaigns a list of
peer_campaign.
The donations and campaigns pages used to receive querysets: a theme that calls
donations.all() must be updated, donations is now already a list.
The campaign detail lives at /my/campaigns/<id>/ and is reached from
peer_campaign.manage_url. It uses the id rather than the slug, because a personal
fundraiser's slug is unique only within its campaign. It shows statistics, the link to
share and a form prefilled with name and message. The form is sent via POST to
peer_campaign_manage_url (the page itself, with __preview kept); after a
successful save the view returns here with peer_campaign_updated set to True. A
fundraiser belonging to another supporter returns 404.
error is token when the login link has expired or was already used: a link works
only once and for one hour. The session does not expire.
Who can log in
Anyone. The link is sent to any valid address, even one the organization does not
know: the supporter record is created when the link is opened. The exception is
an archived address, which gets error=token like an invalid link.
The theme must not promise conditions that do not exist ("the address you donated with") nor say whether the address was known: the response is always the same, otherwise the page reveals who donated to the organization.
The captcha on templates/supporters/login.html is mandatory: the form sends an email
from the organization's domain to an address chosen by whoever fills it in. The
theme must render the widget and the honeypot.
In the area forms, disable the button on submit and show a waiting state, so the
form is not sent several times. The reference theme uses a data-submit on the
button and a submit listener that sets form.dataset.submitted.
Bot protection (captcha)
Views with a public form add the captcha variable to the context. If
captcha.enabled is false the theme must render nothing.
Three forms are protected: lead box, checkout and supporter login. The private area forms have no captcha, because whoever uses them has already logged in.
| Attribute | Description |
|---|---|
captcha.enabled | false when protection is disabled |
captcha.challenge_url | endpoint the widget gets the challenge from |
captcha.script_url | the only script to load, served by the platform |
captcha.algorithm | challenge algorithm |
captcha.i18n_url | widget language file, null if not available |
captcha.language | two-letter language code |
captcha.field_name | name of the field the widget fills in |
captcha.honeypot_field | name of the decoy field to hide |
{% if captcha.enabled %}
<script type="module" async defer src="{{ captcha.script_url }}"></script>
<altcha-widget challenge="{{ captcha.challenge_url }}"
name="{{ captcha.field_name }}"
language="{{ captcha.language }}"
auto="onfocus"></altcha-widget>
{% endif %}
Load only captcha.script_url, not the widget bundle: it also registers the
challenge algorithm, which the bundle does not know. Pointing to altcha.min.js by
hand makes every verification fail with "unsupported algorithm". worker_url no
longer exists.
The decoy field goes off screen (not display:none), with autocomplete="off" and
tabindex="-1": otherwise the browser fills it in and blocks a real person.
Always read the endpoint and asset paths from captcha, never write them by hand.
Also render form.non_field_errors(): a filled decoy field produces a form error,
not a field error.
Notes
- SEO variables (
seo_title,seo_description,google_tag, ...) are used only in the header and are not accessible in theme templates. - The
donatepage uses a system template, not the sections engine. - Always use
supporter_urlsinstead of writing paths by hand: they keep__preview, so the first click does not leave the theme preview.