Compare commits

...

3 commits

18 changed files with 328 additions and 248 deletions

View file

@ -5,7 +5,8 @@ set -e # Abort on failure
git push
ssh debian@hickory.sfconservancy.org 'bash -s' << EOF
set -x # Show output
set -e
set -e # Abort on errors
cd /var/www/website
sudo -u www-data git pull
sudo chown www-data:www-data .

View file

@ -31,7 +31,7 @@ $ ./extract-code-added-in-commits.plx --repository=`pwd`/linux-historical --outp
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux-current
$ ./commit-id-list-matching-regex.plx `pwd`/linux-current/.git Hellwig &#39;(Submitted\s+by|original\s+patch|patch\s+(from|by)|originally\s+(from|by)).*&#39; &gt; ./hellwig-current.ids
$ ./extract-code-added-in-commits.plx --progress --repository=`pwd`/linux-current --output-dir=`pwd`/hellwig-through-2.6.34 --fork-limit=14 --blame-opts=-M --blame-opts=-M --blame-opts=-C --blame-opts=-C --central-commit e40152ee1e1c7a63f4777791863215e3faa37a86 &lt; hellwig-current.ids </code></pre>
<p>Note: e40152ee1e1c7a63f4777791863215e3faa37a86 is the 2.6.34 version created by Linus Torvalds <script type="text/javascript">
<p>Note: e40152ee1e1c7a63f4777791863215e3faa37a86 is the 2.6.34 version created by Linus Torvalds <script>
<!--
h='&#108;&#x69;&#110;&#x75;&#120;&#x2d;&#102;&#x6f;&#x75;&#110;&#100;&#x61;&#116;&#x69;&#x6f;&#110;&#46;&#x6f;&#114;&#x67;';a='&#64;';n='&#116;&#x6f;&#114;&#118;&#x61;&#108;&#100;&#x73;';e=n+a+h;
document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+e+'<\/'+'a'+'>');

View file

@ -5,7 +5,7 @@
{% block category %}supporter{% endblock %}
{% block head %}
<script type="text/javascript" src="{% static 'js/\supporter-page.js' %}" defer></script>
<script src="{% static 'js/supporter-page.js' %}" defer></script>
<link href="{% static 'css/forms.css' %}" rel="stylesheet" type="text/css"/>
{% endblock %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,16 +1,48 @@
from django import forms
from django.utils.safestring import mark_safe
from django.urls import reverse
from .models import SustainerOrder
MONTH_MINIMUM = 10
YEAR_MINIMUM = 120
class SustainerFormRenderer(forms.renderers.DjangoTemplates):
# Customised layout with labels on own row
field_template_name = 'supporters/field.html'
class ButtonRadioSelect(forms.widgets.RadioSelect):
"""Radio button styled like a button. BYO CSS."""
# Extra <span> wrappers to support CSS
option_template_name = 'supporters/buttonradio_option.html'
use_fieldset = False
class Media:
css = {
'all': ['css/buttonradio.css'],
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attrs['class'] = 'button-select'
class SustainerForm(forms.ModelForm):
amount_monthly = forms.IntegerField(initial=12, required=False)
# Used to pre-fill the price selector
amount_option = forms.CharField(required=False)
template_name = 'supporters/sustainer_form.html'
MONTH_OPTIONS = [12, 25, 50, 100]
YEAR_OPTIONS = [128, 250, 500, 1000]
class Meta:
model = SustainerOrder
fields = [
'recurring',
'amount',
'name',
'email',
'amount',
'acknowledge_publicly',
'add_to_mailing_list',
'tshirt_size',
@ -20,10 +52,46 @@ class SustainerForm(forms.ModelForm):
'zip_code',
'country',
]
widgets = {
'recurring': ButtonRadioSelect(attrs={
'x-model': 'recurring',
# Reset the amount field and option when changing monthly/annually.
'x-on:change': '$refs.amount.value = null; amount_option = null',
}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.renderer = SustainerFormRenderer()
self.fields['recurring'].label = ''
self.fields['amount'].initial = self.YEAR_OPTIONS[0]
# So we can write to this field easily from Alpine JS.
self.fields['amount'].widget.attrs['x-ref'] = 'amount'
self.fields['amount'].widget.attrs['style'] = 'width: 5rem'
self.fields['amount'].initial = 128
self.fields['amount_monthly'].widget.attrs['style'] = 'width: 5rem'
self.fields['email'].help_text = 'For your payment receipt'
self.fields['tshirt_size'].help_text = mark_safe("""Sizing chart: <a href="/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>, <a href="/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a>""")
self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
def clean(self):
super().clean()
recurring = self.cleaned_data.get('recurring', '')
amount = self.cleaned_data.get('amount', 0)
minimum = MONTH_MINIMUM if recurring == 'month' else YEAR_MINIMUM
donate_url = reverse('donate')
if amount < minimum:
self.add_error(
'',
mark_safe(f'${minimum:d} is a minimum for Conservancy Sustainers. <a href="{donate_url}">Donate smaller amounts here</a>.')
)
tshirt_size = self.cleaned_data.get('tshirt_size')
if tshirt_size and not all([
self.cleaned_data.get('street'),
self.cleaned_data.get('city'),
self.cleaned_data.get('country')
]):
self.add_error(
'street',
'No address provided'
)

View file

@ -0,0 +1,71 @@
# Generated by Django 5.1.2 on 2024-10-22 04:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supporters', '0004_sustainerorder_payment_id_and_more'),
]
operations = [
migrations.AlterField(
model_name='sustainerorder',
name='amount',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='sustainerorder',
name='recurring',
field=models.CharField(
blank=True,
choices=[('', 'Once'), ('month', 'Monthly'), ('year', 'Annually')],
default='',
max_length=10,
),
),
migrations.AlterField(
model_name='sustainerorder',
name='tshirt_size',
field=models.CharField(
blank=True,
choices=[
('', [('', 'None')]),
(
"Men's",
[
("Men's S", "Men's S"),
("Men's M", "Men's M"),
("Men's L", "Men's L"),
("Men's XL", "Men's XL"),
("Men's 2XL", "Men's 2XL"),
],
),
(
"Standard women's",
[
("Standard women's S", "Standard women's S"),
("Standard women's M", "Standard women's M"),
("Standard women's L", "Standard women's L"),
("Standard women's XL", "Standard women's XL"),
("Standard women's 2XL", "Standard women's 2XL"),
],
),
(
"Fitted women's",
[
("Fitted women's S", "Fitted women's S"),
("Fitted women's M", "Fitted women's M"),
("Fitted women's L", "Fitted women's L"),
("Fitted women's XL", "Fitted women's XL"),
("Fitted women's 2XL", "Fitted women's 2XL"),
],
),
],
default='',
max_length=50,
verbose_name='T-shirt size',
),
),
]

View file

@ -1,4 +1,3 @@
from django.core import validators
from django.db import models
@ -11,6 +10,7 @@ class Supporter(models.Model):
def test(self):
return "TESTING"
def __str__(self):
return self.display_name
@ -20,9 +20,9 @@ class Supporter(models.Model):
class SustainerOrder(models.Model):
RENEW_CHOICES = [
('', 'None'),
('', 'Once'),
('month', 'Monthly'),
('year', 'Annual'),
('year', 'Annually'),
]
TSHIRT_CHOICES = [
(
@ -64,17 +64,14 @@ class SustainerOrder(models.Model):
created_time = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=255)
email = models.EmailField()
amount = models.IntegerField(
validators=[
validators.MinValueValidator(100),
])
recurring = models.CharField(max_length=10)
amount = models.PositiveIntegerField()
recurring = models.CharField(max_length=10, choices=RENEW_CHOICES, blank=True, default='')
payment_method = models.CharField(max_length=10, default='Stripe')
payment_id = models.CharField(max_length=255, blank=True)
paid_time = models.DateTimeField(null=True, blank=True)
acknowledge_publicly = models.BooleanField(default=True)
add_to_mailing_list = models.BooleanField(default=True)
tshirt_size = models.CharField(max_length=50, choices=TSHIRT_CHOICES, blank=True)
tshirt_size = models.CharField('T-shirt size', max_length=50, choices=TSHIRT_CHOICES, blank=True, default='')
street = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=255, blank=True)
state = models.CharField(max_length=255, blank=True)

View file

@ -0,0 +1,31 @@
.button-select {
display: grid;
gap: 0.5rem;
grid-template-columns: repeat(auto-fill, minmax(5rem, 1fr));
}
.button-select label > span {
text-align: center;
display: inline-block;
padding: 0.5rem 0;
width: 100%;
background: #ddd;
border-radius: 4px;
border: 1px solid transparent;
}
.button-select label input {
opacity: 0;
position: absolute;
}
/* Wish we could use :has reliably. */
.button-select label input:focus + span {
border-color: #eee;
outline: 2px solid #666;
}
.button-select label input:checked + span {
color: white;
background: #666;
}

View file

@ -0,0 +1,2 @@
<!-- Custom <span> wrapper around the label to enable radio fields to be styled like buttons. -->
{% if widget.wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} <span>{{ widget.label }}</span></label>{% endif %}

View file

@ -0,0 +1,11 @@
<!-- Labels on a separate line, custom help text layout -->
{% if field.use_fieldset %}
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
{% if field.label %}{{ field.legend_tag }}{% endif %}
{% else %}
{% if field.label %}{{ field.label_tag }}{% endif %}
{% endif %}
{{ field.errors }}
<div class="mt1">{{ field }}</div>
<div class="f7 black-60 mt1">{{ field.help_text }}</div>
{% if field.use_fieldset %}</fieldset>{% endif %}

View file

@ -5,33 +5,22 @@
{% block head %}
{{ block.super }}
{% include "opengraph_partial.html" with url="/sustainer/" title="Support Conservancy!" description="Software freedom is critical to many of today&rsquo;s most pressing social issues, but it&rsquo;s only effective when FOSS is for everyone. Support Conservancy today to help make that happen!" %}
{% include "opengraph_urllist_partial.html" with property='image' urls='' fallback='/static/img/conservancy-logo.png' %}
{{ form.media }}
<style>
@media screen and (min-width: 40em) {
#sustainer-grid {
#sustainer-grid-wrapper {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: min-content 1fr;
gap: 1.5rem;
}
}
progress {
background: #ddd;
border: none;
}
progress::-moz-progress-bar {
background: #224c57;
}
progress::-webkit-progress-bar {
background: #ddd;
}
progress::-webkit-progress-value {
background: #224c57;
}
.btn:active {
transform: scale(1.05);
}
.errorlist li {
color: #ff4136;
}
</style>
<script defer src="{% static "js/vendor/alpine-3.14.1.js" %}"></script>
{% endblock %}
@ -49,98 +38,115 @@
<p class="tr"><strong>Made Up Person</strong></p>
</div>
<div id="sustainer-grid" class="mv4">
<div>
<noscript>
<p><strong>Hey there!</strong> We greatly respect you visiting our site <strong>without JavaScript</strong>. We make significant efforts to ensure our site works without JavaScript and where necessary to use only free software JavaScript.</p>
<p><strong>Unfortunately, our payment providers Stripe and PayPal do not work without JavaScript enabled</strong>. We also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a> or otherwise please <a href="#">get in touch</a> and we'll try to work something out. Thanks for your support!</p>
</noscript>
<div id="sustainer-grid-wrapper" class="mv4" style="grid-template-columns: 2fr 1fr; gap: 1.5rem">
<section>
<noscript>
<div style="padding: 1rem; border: 2px solid #0f0; margin-bottom: 1.5rem">
<p><marquee><strong>Hey there!</strong></marquee> Thanks for visiting our site <strong>without JavaScript</strong>! We respect your choice and make a significant effort to ensure our site works without JavaScript or, where necessary, to use only free software JavaScript.</p>
<p>The bad news is that our credit card/ACH payment services <strong>Stripe and PayPal don't work without JavaScript</strong>. We also don't currently have the resources to handle PCI compliant credit-card processing directly.</p>
<form id="sustainer" method="post" action="."
x-data="{
tshirt_size: '',
tshirt_required: function () { return this.tshirt_size !== '' },
recurring: '',
}">
{% csrf_token %}
<fieldset class="bg-black-05 pa3 br3 center" style="max-width: 24rem; border: 1px solid #ccc">
<legend class="b f5">Become a Sustainer</legend>
{{ form.errors }}
<div class="mb2"><label>
<label class="mr1"><input type="radio" name="recurring" value="" checked x-model="recurring"> Annual</label>
<label class="mr1"><input type="radio" name="recurring" value="month" x-model="recurring"> Monthly</label>
<label><input type="radio" name="recurring" value="year" x-model="recurring"> Annual Renew</label>
</label></div>
<div class="mb2">
<label>Amount<br>
<span class="mt1" x-show="recurring === ''">$ {{ form.amount }}</span>
<span class="mt1" x-show="recurring === 'month'">$ {{ form.amount_monthly }}</span>
<span class="mt1" x-show="recurring === 'year'">$ {{ form.amount }}</span>
</label>
</div>
<div class="mb2"><label>Name
<span class="db mt1">{{ form.name }}</span>
</label></div>
<div class="mb2"><label>Email
<span class="db mt1">{{ form.email }}</span>
</label>
<p class="f7 black-60 mt1">To send your receipt</p>
</div>
<div class="mv3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the public <a href="">list of sustainers</a></label></div>
<div class="mv3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the low-traffic <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div>
<div class="mv3">
<label>T-shirt:
<!-- Form field has an x-model attribute in forms.py. -->
<span class="db mt1">{{ form.tshirt_size }}</span>
</label>
<p class="f7 black-60 mt1">Sizing:
<a href="https://sfconservancy.org/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>,
<a href="https://sfconservancy.org/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a></p>
<p>We'd still love to have you as a Sustainer though, and will gladly accept your payment by <a href="#wire-transfer">wire transfer</a> or <a href="#paper-check">paper check</a> . If those aren't feasible, please <a href="mailto:donate@sfconservancy.org">get in touch</a> and we'll try to work something out. Thanks for your support!</p>
<figure>
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200px">
</figure>
</div>
<!-- Using Alpine.js to show/hide the address based on T-shirt choice. -->
<template x-if="tshirt_required">
<div id="address">
<div class="mb2">
<label>Postal address</label>
</div>
<div class="mb2"><label>Street
<span class="db mt1">{{ form.street }}</span>
</label></div>
<div class="mb2"><label>City
<span class="db mt1">{{ form.city }}</span>
</label></div>
<div class="mb2"><label>State/Region
<span class="db mt1">{{ form.state }}</span>
</label></div>
<div class="mb2"><label>Zip/Postal
<span class="db mt1">{{ form.zip_code }}</span>
</label></div>
<div class="mb2"><label>Country
<span class="db mt1">{{ form.country }}</span>
</label></div>
<img src="{% static 'img/dancing-banana.gif' %}" alt="Dancing Banana">
</div>
</template>
</noscript>
<div class="mt3"><button type="submit" class="pointer btn f5 pv2" style="width: 100%; font-weight: bold; color: white; background-color: var(--orange); border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer by<br>Credit Card or <abbr title="US Bank Direct Debit">ACH</button></div>
</fieldset>
</form>
<!-- Alpine JS is used to show different payments amounts for monthly/annual, write the selected payment amount into the "amount" field, reset the seleted amount when you change monthly/annual and pop out the address when you select a T-shirt. -->
<form method="post" action="."
x-data="{
recurring: '{{ form.recurring.value|escapejs }}',
amount: parseInt('{{ form.amount.value|escapejs }}'),
amount_option: '{{ form.amount_option.value|escapejs }}',
amount_options: function() {
let month_options = {{ form.MONTH_OPTIONS|escapejs }};
let year_options = {{ form.YEAR_OPTIONS|escapejs }};
return this.recurring === 'month' ? month_options : year_options;
},
tshirt_size: '{{ form.tshirt_size.value|escapejs }}',
}">
{% csrf_token %}
<fieldset class="bg-black-05 pa3 br3 center" style="border: 1px solid #ccc">
<legend class="b f5">Become a Sustainer</legend>
{{ form.non_field_errors }}
<div>{{ form.recurring.as_field_group }}</div>
<p class="f7 mv3">Credit card and ACH payments are processed with Stripe. We also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a> and PayPal. Our sustainer program has a minimum of $120 USD per year, but we also accept <a href="#">donations of smaller amounts</a>.</p>
<div class="mt3">
<div id="amount_options" class="button-select">
<template x-for="m in amount_options">
<label>
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = $event.currentTarget.value" x-model="amount_option" required>
<span>$<span x-text="m.toLocaleString()"></span></span>
</label>
</template>
<!-- Hide if no JS -->
<template x-if="true">
<label>
<input type="radio" name="amount_option" value="other" x-on:change="$refs.amount.value = ''" x-model="amount_option" required>
<span>Other</span>
</label>
</template>
</div>
<div class="mt2" x-show="amount_option === 'other'">
{{ form.amount.as_field_group }}
</div>
</div>
<div>
<a href="{% url "sustainers" %}">
<button type="submit" class="pointer btn f5" style="height: 40px; width: 100%; font-weight: bold; color: white; background-color: var(--orange); opacity: 75%; border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer by PayPal</button>
</a>
</div>
</div>
<div style="grid-row: 1 / span 2">
<div class="mt3">{{ form.name.as_field_group }}</div>
<div class="mt2">{{ form.email.as_field_group }}</div>
<div class="mt3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the <a href="/sponsors#sustainers" target="_blank">list of sustainers</a></label></div>
<div class="mt3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div>
<div class="mt3">
{{ form.tshirt_size.as_field_group }}
<figure class="mt2">
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200">
</figure>
</div>
<div id="address" x-show="tshirt_size !== ''">
<fieldset class="mt3">
<legend>Postal address</legend>
<div>{{ form.street.as_field_group }}</div>
<div class="mt2">{{ form.city.as_field_group }}</div>
<div class="mt2">{{ form.state.as_field_group }}</div>
<div class="mt2">{{ form.zip_code.as_field_group }}</div>
<div class="mt2">{{ form.country.as_field_group }}</div>
</fieldset>
</div>
<div class="mt3"><button type="submit" class="pointer btn f5 pv2" style="width: 100%; font-weight: bold; color: white; background-color: var(--orange); border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Become a Sustainer by<br>Credit Card or <abbr title="US Bank Direct Debit">ACH</abbr></button></div>
</fieldset>
</form>
<p class="f7 mt3">Credit card and ACH payments are processed with Stripe. We also accept payment by paper check and wire transfer and PayPal (see below). Our sustainer program has a minimum of $120 USD per year, but we also accept <a href="/donate/">donations of smaller amounts</a>.</p>
<details id="wire-transfer">
<summary class="f6">Wire Transfer</summary>
<p>Contact <a href="mailto:donate@sfconservancy.org">Conservancy
by email</a> for wire transfer instructions. Include currency &amp; country.</p>
</details>
<details id="paper-check">
<summary class="f6">Paper Check</summary>
<p>Send a paper check to:</p>
<p>Software Freedom Conservancy, Inc.<br>
137 MONTAGUE ST STE 380<br>
BROOKLYN, NY 11201-3548 &nbsp; USA</p>
<p>Please write <q>SUSTAINER</q>, T-shirt size, if you are renewing, and if
you want public acknowledgment in memo line.</p>
</details>
<details id="paypal">
<summary class="f6">PayPal</summary>
<p>Please visit our <a href="{% url 'sustainer_paypal' %}">Become a Sustainer by PayPal</a> page.</p>
</details>
</section>
<section style="grid-row: 1 / span 2">
<video controls poster="https://sfconservancy.org/videos/sfc-introduction-video_poster.jpg" class="mb3">
<source src="https://sfconservancy.org/videos/sfc-introduction_1080p.mp4">
<track src="/docs/sfc-introduction-vtt-captions.txt" kind="subtitles" srclang="en" label="English">
@ -175,9 +181,10 @@
software freedom that the world needs. <a href="/donate/">Please consider
donating now!</a></p>
<h2 class="">2024 in Review</h2>
<details id="YearInReview">
<summary>Our Year in Review</summary>
<summary>Overview</summary>
<p>This has been a big year for Software Freedom Conservancy in our tireless
efforts to promote ethical technology, increase diversity and inclusion in
@ -190,7 +197,7 @@ Right to Repair movement. We hosted our first large conference, <a href="https:/
and while we finalize details for next year, we hope to see you there to join
us in community!</p>
<div class="picture-small right"> <img src="https://nextcloud.sfconservancy.org/apps/files_sharing/publicpreview/pnZYsi2CkjscLwc?file=/&fileId=24825&x=1366&y=768&a=true&etag=f4341a40f90786b0356201c21278ee23" alt="SFC lawyers posing outside at the courthouse“ " /></a>
<div class="picture-small right"> <img src="https://nextcloud.sfconservancy.org/apps/files_sharing/publicpreview/pnZYsi2CkjscLwc?file=/&fileId=24825&x=1366&y=768&a=true&etag=f4341a40f90786b0356201c21278ee23" alt="SFC lawyers posing outside at the courthouse“ " />
<p>SFC lawyers after recent Vizio case- CC BY-SA 4.0</p></div>
<p>Our <a href="https://vizio.sfconservancy.org">lawsuit against Vizio</a>— the first
@ -247,7 +254,7 @@ account will be approved.</p>
</details>
<details id="NewStaff">
<summary>New staff!</summary>
<summary>New Staff!</summary>
<p>SFC hired two additional employees this year! General Counsel Rick Sanders
joins the team to help with our continued legal needs. Rick has over 20 years
experience as a intellectual-property litigator. His expertise has been
@ -299,9 +306,8 @@ of FOSS and in critical infrastructure discussions and also presented in
classroom to educate students about software freedom.</p>
</details>
<details id="Highlights">
<summary>Highlights from some of our projects</summary>
<summary>Highlights From Our Member Projects</summary>
<p>We've raised, administered and/or facilitated $1.8 million to improve
software freedom directly! This includes contractors, interns and students,
administrators, and grants for creation, distribution and maintenance of free
@ -351,7 +357,7 @@ very exciting developments for the project. Creation of a new <a href="https://i
for the PLC and contributors to get together to plan and work on technical
challenges. The first back in-person <b>Selenium</b> <a
href="https://seleniumconf.com/">conference</a> was in Chicago this past
may</a>. Attendance from over 10 countries, it was an incredible reunion for
may. Attendance from over 10 countries, it was an incredible reunion for
the project contributors and users to get together. The <b>Git</b>
contributor summit was held online this year in September. Topics ranged from
ideas of new library support to how to better support for scaling with large
@ -361,6 +367,6 @@ annual summit was hosted in Hamburg featuring incredible
technical talks, project planning and continues to build the momentum and
reach for reproducibility. </p>
</details>
</div>
</section>
</div>
{% endblock %}

View file

@ -1,91 +0,0 @@
{% extends "base_conservancy.html" %}
{% load static %}
{% block subtitle %}Support Conservancy - {% endblock %}
{% block category %}sustainer{% endblock %}
{% block head %}
{{ block.super }}
<script defer src="{% static "js/vendor/alpine-3.14.1.js" %}"></script>
<style>
.btn:active {
transform: scale(1.05);
}
</style>
{% endblock %}
{% block content %}
<h1 class="lh-title tc mt4 mb0">Become a Sustainer Now</h1>
<p class="measure-wide center tc">Sustainers help us do our work in a strategic, long-term way.</p>
<div class="bg-black-05 pa3 br3 mb4 center" style="max-width: 24rem; border: 1px solid #ccc">
<form id="sustainer" method="post" action="."
x-data="{
tshirt_size: 'None',
tshirt_required: function () { return this.tshirt_size !== 'None' },
recurring: '',
}">
{% csrf_token %}
{{ form.errors }}
<div class="mb2"><label>Name
<span class="db mt1">{{ form.name }}</span>
</label></div>
<div class="mb2"><label>Email
<span class="db mt1">{{ form.email }}</span>
</label>
<p class="f7 black-60 mt1">To send your receipt</p>
</div>
<div class="mb2"><label>
<label class="mr1"><input type="radio" name="recurring" value="" checked x-model="recurring"> Once</label>
<label class="mr1"><input type="radio" name="recurring" value="month" x-model="recurring"> Monthly</label>
<label><input type="radio" name="recurring" value="year" x-model="recurring"> Annual</label>
</label></div>
<div class="mb2">
<label>Amount<br>
<span class="mt1" x-show="recurring === ''">$ {{ form.amount }}</span>
<span class="mt1" x-show="recurring === 'month'">$ {{ form.amount_monthly }}</span>
<span class="mt1" x-show="recurring === 'year'">$ {{ form.amount }}</span>
</label>
<button type="button" class="f7 pa1 lh-solid o-40" title="Multiply my impact">10x me</button>
</div>
<p class="f7 black-60 mt1">Giving is known to increase happiness, but are
you sure?
<a href="#">Undo</a></p>
<div class="mv3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the public <a href="">list of sustainers</a></label></div>
<div class="mv3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the low-traffic <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div>
<div class="mv3">
<label>T-shirt:
<!-- Form field has an x-model attribute in forms.py. -->
<span class="db mt1">{{ form.tshirt_size }}</span>
</label>
<p class="f7 black-60 mt1">Sizing:
<a href="https://sfconservancy.org/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>,
<a href="https://sfconservancy.org/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a></p>
</div>
<!-- Using Alpine.js to show/hide the address based on T-shirt choice. -->
<template x-if="tshirt_required">
<fieldset id="address">
<legend>Postal address</legend>
<div class="mb2"><label>Street
<span class="db mt1">{{ form.street }}</span>
</label></div>
<div class="mb2"><label>City
<span class="db mt1">{{ form.city }}</span>
</label></div>
<div class="mb2"><label>State/Region
<span class="db mt1">{{ form.state }}</span>
</label></div>
<div class="mb2"><label>Zip/Postal
<span class="db mt1">{{ form.zip_code }}</span>
</label></div>
<div class="mb2"><label>Country
<span class="db mt1">{{ form.country }}</span>
</label></div>
</fieldset>
</template>
<div class="mt3"><button type="submit" class="btn" style="height: 40px; width: 100%; font-size: 18px; font-weight: bold; color: white; background-color: var(--orange); border-radius: 0.5rem; border: none; border-bottom: 2px solid rgba(0,0,0,0.1);">Pay via Stripe</button></div>
<p class="f7 mt3">If you have concerns or issues paying with Stripe, we also accept payment by <a href="#">paper check</a> and <a href="#">wire transfer</a>.</p>
</form>
</div>
{% endblock %}

View file

@ -9,6 +9,7 @@ urlpatterns = [
path('banners/', TemplateView.as_view(template_name='supporters/banners.html')),
path('success/', views.success),
path('webhook/', views.webhook),
# TODO
path('stripe/', views.sustainers_stripe),
path('stripe2/', views.sustainers_stripe2, name='stripe2'),
path('paypal/', views.sustainers_paypal, name='sustainer_paypal'),
]

View file

@ -75,15 +75,15 @@ def create_checkout_session(reference_id, email: str, amount: int, recurring: st
return checkout_session.url
def sustainers_paypal(request):
return render(request, 'supporters/sustainers_paypal.html')
def sustainers_stripe(request):
if request.method == 'POST':
form = forms.SustainerForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.recurring = form.data['recurring']
if order.recurring == 'month':
order.amount = form.cleaned_data['amount_monthly']
order.save()
order = form.save()
base_url = f'{request.scheme}://{request.get_host()}'
stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, order.recurring, base_url)
return redirect(stripe_checkout_url)
@ -92,23 +92,6 @@ def sustainers_stripe(request):
return render(request, 'supporters/sustainers_stripe.html', {'form': form})
def sustainers_stripe2(request):
if request.method == 'POST':
form = forms.SustainerForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.recurring = form.data['recurring']
if order.recurring == 'month':
order.amount = form.cleaned_data['amount_monthly']
order.save()
base_url = f'{request.scheme}://{request.get_host()}'
stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, order.recurring, base_url)
return redirect(stripe_checkout_url)
else:
form = forms.SustainerForm()
return render(request, 'supporters/sustainers_stripe2.html', {'form': form})
stripe.api_key = settings.STRIPE_API_KEY
if stripe.api_key == '':
logger.warning('Missing STRIPE_API_KEY')

View file

@ -12,7 +12,7 @@
<link rel="stylesheet" type="text/css" href="{% static 'css/tachyons.min.css' %}"/>
<link rel="stylesheet" type="text/css" media="screen" href="{% static 'css/conservancy.css' %}" />
<link rel="stylesheet" type="text/css" media="(min-width: 67em)" href="{% static 'css/conservancy-bigscreen.css' %}" />
<script type="text/javascript" src="{% static 'js/conservancy.js' %}" defer></script>
<script src="{% static 'js/conservancy.js' %}" defer></script>
{% block head %}{% endblock %}
</head>
@ -51,10 +51,10 @@
<li class="search dn-ns">
<form method="get" action="https://duckduckgo.com" class="ml2 flex mw6">
<input id="search-query" type="text" name="q" placeholder="Search with DuckDuckGo" class="pa2 ba b--gray br0" style="x-border-right: none; flex: 1 1 auto; width: 1%;" />
<input id="search-query" type="text" name="q" placeholder="Search with DuckDuckGo" class="pa2 ba b--gray br0" style="flex: 1 1 auto; width: 1%;" />
<input type="hidden" name="sites" value="sfconservancy.org" />
<button type="submit" class="bg-orange bn white pa2 pointer btn-orange" style="margin-left: -1px;">
<svg style="color: white; width: 20px; height: 20px;"><use href="{% static 'img/font_awesome.svg' %}#search"></use></svg></a>
<svg style="color: white; width: 20px; height: 20px;"><use href="{% static 'img/font_awesome.svg' %}#search"></use></svg>
</button>
</form>
</li>

View file

@ -3,8 +3,8 @@
<li class="Sustainers"><a href="/sustainer/">Sustainers</a></li>
<li class="Directors"><a href="/about/board/">Board of Directors</a></li>
<li class="Staff"><a href="/about/staff/">Staff</a></li>
<li clas="Eval"><a href="/about/eval-committee/">Evaluation Committee</a></li>
<li clas="Outside"><a href="/about/outside/">Outside Counsel, et alia</a></li>
<li class="Eval"><a href="/about/eval-committee/">Evaluation Committee</a></li>
<li class="Outside"><a href="/about/outside/">Outside Counsel, et alia</a></li>
<li class="Transparency"><a href="/about/transparency">Transparency</a></li>
<li class="Contact"><a href="/about/contact/">Contact</a></li>
</ul>

View file

@ -4,7 +4,7 @@
{% block category %}sustainer{% endblock %}
{% block head %}
<script type="text/javascript" src="{% static 'js/supporter-page.js' %}" defer></script>
<script src="{% static 'js/supporter-page.js' %}" defer></script>
<link href="{% static 'css/forms.css' %}" rel="stylesheet" type="text/css"/>
<style>
.hidden { display: none; }

View file

@ -51,7 +51,7 @@ urlpatterns = [
re_path(r'^about/', views.content),
re_path(r'^activities/', views.content),
re_path(r'^copyleft-compliance/', views.content, {'fundraiser_sought': 'vmware-match-0'}),
re_path(r'^donate/', views.content),
path('donate/', views.content, name='donate'),
path('fossy/', views.content),
re_path(r'^GiveUpGitHub/', views.content),
re_path(r'^learn/', views.content),