Add prototype Stripe sustainer payment workflow
This commit is contained in:
parent
a2f38653fb
commit
26a6928a20
12 changed files with 4053 additions and 3 deletions
3386
conservancy/static/js/vendor/alpine-3.14.1.js
vendored
Normal file
3386
conservancy/static/js/vendor/alpine-3.14.1.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,31 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Supporter
|
from .models import Supporter, SustainerOrder
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Supporter)
|
@admin.register(Supporter)
|
||||||
class SupporterAdmin(admin.ModelAdmin):
|
class SupporterAdmin(admin.ModelAdmin):
|
||||||
list_display = ('display_name', 'display_until_date')
|
list_display = ('display_name', 'display_until_date')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(SustainerOrder)
|
||||||
|
class SustainerOrderAdmin(admin.ModelAdmin):
|
||||||
|
fields = [
|
||||||
|
'created_time',
|
||||||
|
'paid_time',
|
||||||
|
'name',
|
||||||
|
'email',
|
||||||
|
'amount',
|
||||||
|
'acknowledge_publicly',
|
||||||
|
'add_to_mailing_list',
|
||||||
|
'tshirt_size',
|
||||||
|
'street',
|
||||||
|
'city',
|
||||||
|
'state',
|
||||||
|
'zip_code',
|
||||||
|
'country',
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly_fields = ['created_time', 'paid_time']
|
||||||
|
list_display = ['created_time', 'name', 'email', 'amount', 'paid']
|
||||||
|
list_filter = ['paid_time']
|
||||||
|
|
25
conservancy/supporters/forms.py
Normal file
25
conservancy/supporters/forms.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from .models import SustainerOrder
|
||||||
|
|
||||||
|
class SustainerForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = SustainerOrder
|
||||||
|
fields = [
|
||||||
|
'name',
|
||||||
|
'email',
|
||||||
|
'amount',
|
||||||
|
'acknowledge_publicly',
|
||||||
|
'add_to_mailing_list',
|
||||||
|
'tshirt_size',
|
||||||
|
'street',
|
||||||
|
'city',
|
||||||
|
'state',
|
||||||
|
'zip_code',
|
||||||
|
'country',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['amount'].widget.attrs['style'] = 'width: 5rem'
|
||||||
|
self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
|
65
conservancy/supporters/migrations/0001_initial.py
Normal file
65
conservancy/supporters/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# Generated by Django 4.2.11 on 2024-07-22 05:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Supporter',
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
'id',
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name='ID',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
('display_name', models.CharField(max_length=200)),
|
||||||
|
(
|
||||||
|
'display_until_date',
|
||||||
|
models.DateTimeField(
|
||||||
|
verbose_name='date until which this supporter name is displayed'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
('ledger_entity_id', models.CharField(max_length=200)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ('ledger_entity_id',),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SustainerOrder',
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
'id',
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name='ID',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
('created_time', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('email', models.EmailField(max_length=254)),
|
||||||
|
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
|
||||||
|
('paid_time', models.DateTimeField(null=True)),
|
||||||
|
('acknowledge_publicly', models.BooleanField(default=False)),
|
||||||
|
('add_to_mailing_list', models.BooleanField(default=False)),
|
||||||
|
('tshirt_size', models.CharField(max_length=50)),
|
||||||
|
('street', models.CharField(blank=True, max_length=255)),
|
||||||
|
('city', models.CharField(blank=True, max_length=255)),
|
||||||
|
('state', models.CharField(blank=True, max_length=255)),
|
||||||
|
('zip_code', models.CharField(blank=True, max_length=255)),
|
||||||
|
('country', models.CharField(blank=True, max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
conservancy/supporters/migrations/__init__.py
Normal file
0
conservancy/supporters/migrations/__init__.py
Normal file
|
@ -1,3 +1,4 @@
|
||||||
|
from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,3 +16,66 @@ class Supporter(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('ledger_entity_id',)
|
ordering = ('ledger_entity_id',)
|
||||||
|
|
||||||
|
|
||||||
|
class SustainerOrder(models.Model):
|
||||||
|
TSHIRT_CHOICES = [
|
||||||
|
(
|
||||||
|
'',
|
||||||
|
(("None", "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"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
created_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
email = models.EmailField()
|
||||||
|
amount = models.IntegerField(
|
||||||
|
default=128,
|
||||||
|
validators=[
|
||||||
|
validators.MinValueValidator(100),
|
||||||
|
])
|
||||||
|
paid_time = models.DateTimeField(null=True, blank=True)
|
||||||
|
acknowledge_publicly = models.BooleanField(default=False)
|
||||||
|
add_to_mailing_list = models.BooleanField(default=False)
|
||||||
|
tshirt_size = models.CharField(max_length=50, choices=TSHIRT_CHOICES)
|
||||||
|
street = models.CharField(max_length=255, blank=True)
|
||||||
|
city = models.CharField(max_length=255, blank=True)
|
||||||
|
state = models.CharField(max_length=255, blank=True)
|
||||||
|
zip_code = models.CharField(max_length=255, blank=True)
|
||||||
|
country = models.CharField(max_length=255, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'Sustainer order {self.id}: {self.email}'
|
||||||
|
|
||||||
|
def paid(self):
|
||||||
|
return self.paid_time is not None
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends "base_conservancy.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block subtitle %}Support Conservancy - {% endblock %}
|
||||||
|
{% block category %}sustainer{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1 class="lh-title tc mt4 mb4">Thanks!</h1>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,290 @@
|
||||||
|
{% extends "base_conservancy.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block subtitle %}Support Conservancy - {% endblock %}
|
||||||
|
{% block category %}sustainer{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
{{ block.super }}
|
||||||
|
<style>
|
||||||
|
@media screen and (min-width: 40em) {
|
||||||
|
#sustainer-grid {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
</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 id="sustainer-grid" class="mv4">
|
||||||
|
<div 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">
|
||||||
|
<a href="https://sfconservancy.org/videos/sfc-introduction_1080p.mp4"><img src="https://sfconservancy.org/videos/sfc-introduction-video_poster.jpg" alt="Software Freedom Conservancy introduction video"></a><br>
|
||||||
|
<a href="https://youtu.be/yCCxMfW0LTM">(watch on Youtube)</a>
|
||||||
|
</video>
|
||||||
|
|
||||||
|
<h3>The wide range of work we engage in is supported by people like you.</h3>
|
||||||
|
|
||||||
|
<p>We are so proud that we're funded by individuals and stay unbeholden to corporate interests and pressures. We stand up for developers, consumers and those who have been historically excluded. We work to make technology truly fair for all. </p>
|
||||||
|
|
||||||
|
<p>Thank you for helping making this work possible:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Standing up for consumer rights in <a href="/copyleft-compliance/">copyleft compliance</a></li>
|
||||||
|
<li>Supporting <a href="https://outreachy.org">Outreachy</a> with its increasing number of interns</li>
|
||||||
|
<li>Bringing <a href="/vizio">legal action against prolific license violators</a></li>
|
||||||
|
<li>Hiring team members to get projects <a href="https://reproducible-builds.org/news/">like Reproducible Builds</a> to continue pushing the forefront of software reproducibility</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 id="HelpUs">Help us Continue this Work</h3>
|
||||||
|
|
||||||
|
<p>We are beyond thankful for the ability to continue our work — which
|
||||||
|
only continues due to <strong>your financial contributions</strong>. We
|
||||||
|
recognize that not everyone has the same financial leeway to donate as they
|
||||||
|
have in the past. But please consider giving what you can so that our
|
||||||
|
organization can continue to advocate and support the rights of all
|
||||||
|
software users. We work hard and efficiently, and accomplish so much with
|
||||||
|
our small staff. We hope — through our hard work, creativity, and
|
||||||
|
passionate dedication — that we've demonstrated over the years how
|
||||||
|
Software Freedom Conservancy continues to be the beacon of change for
|
||||||
|
software freedom that the world needs. <a href="/donate/">Please consider
|
||||||
|
donating now!</a></p>
|
||||||
|
|
||||||
|
|
||||||
|
<details id="YearInReview">
|
||||||
|
<summary>Our Year in Review</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
|
||||||
|
FOSS, continuing to fight for your rights with <a
|
||||||
|
href="https://sfconservancy.org/copyleft-compliance/">copyleft compliance</a>,
|
||||||
|
and support our incredible <a href="https://sfconservancy.org/members/current/">member projects</a>.
|
||||||
|
Our staff engaged in many invited speaking opportunities, we grew our staff,
|
||||||
|
and we continue to build community around important issues like the software
|
||||||
|
Right to Repair movement. We hosted our first large conference, <a href="https://fossy.us">FOSSY</a>,
|
||||||
|
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>
|
||||||
|
<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
|
||||||
|
lawsuit of its kind to seek <b>third party beneficiary rights to the
|
||||||
|
complete, corresponding source code under the GPL</b> is progressing!
|
||||||
|
World-changing, public policy litigation requires resources and time, but
|
||||||
|
it's worth every penny so we can build a future where <b>every consumer</b> has
|
||||||
|
access to the source code they need to modify and repair their devices. At a
|
||||||
|
<a href="https://sfconservancy.org/blog/2023/oct/12/how-i-watched-motion-summary-judgment-hearing/">recent hearing in the case</a>,
|
||||||
|
we presented
|
||||||
|
<a href="https://sfconservancy.org/news/2023/oct/12/transcript-msj-hearing/">our opposition to Vizio's Summary Judgement Motion</a>,
|
||||||
|
our lawyers
|
||||||
|
<a href="https://sfconservancy.org/docs/Transcript_Full_Vizios_MSJ_HearingDeptC-33.231005.pdf">presented the eloquent case</a>
|
||||||
|
that recipients of software under the GPL Agreements <b>can</b> enforce their
|
||||||
|
rights to the corresponding source code themselves, and that right should not
|
||||||
|
be preempted by copyright law. We are the only organization currently taking
|
||||||
|
legal action to protect the GPL Agreements in the USA; your support is
|
||||||
|
critical for us to continue these kinds of efforts.</p>
|
||||||
|
|
||||||
|
<p>We have also <a href="https://sfconservancy.org/news/2020/jul/30/refile2020/">once again</a>
|
||||||
|
filed key DMCA exemptions for the following categories:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Computer programs for purposes of good-faith security research</li>
|
||||||
|
<li>"jailbreaking" smart TVs</li>
|
||||||
|
<li>"jailbreaking" routers</li>
|
||||||
|
<li>Literary works consisting of compilations of data generated by medical devices or their personal corresponding monitoring systems, to access personal data</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>These allow people access (that should be granted in the first place!) to
|
||||||
|
investigate their own devices for issues of license compliance, security,
|
||||||
|
and replacing the firmware on their TVs and routers. It's a key ability to
|
||||||
|
vet and replace software on devices we own and operate, so renewing this
|
||||||
|
exemption each time it comes up is very important for us all. The medical
|
||||||
|
device exemption was submitted by our Executive Director Karen Sandler in
|
||||||
|
partnership with others who are personally affected by proprietary medical
|
||||||
|
technology.</p>
|
||||||
|
|
||||||
|
<p>As software based technology becomes more pervasive in our lives, it's vital
|
||||||
|
that we communicate the importance of software freedom to the wider population.
|
||||||
|
In that vein <a href="https://sfconservancy.org/news/2022/may/12/introduction/">we've created a video</a>
|
||||||
|
(narrated by our Executive Director Karen Sandler) that introduces
|
||||||
|
the ideas of software freedom, and specifically what Software Freedom
|
||||||
|
Conservancy does. We also did a lot of public writings about some of the
|
||||||
|
important issues this year. From <a href="https://sfconservancy.org/news/2023/feb/09/kuhn-neo4j-purethink-expert-report/">our perspective on the Neo4j suit</a>
|
||||||
|
to <a href="https://sfconservancy.org/news/2023/aug/15/exit-zoom/">highlighting problematic behavior from proprietary software companies</a> and calling for folks to Exit Zoom. We'd also like to
|
||||||
|
remind you that as a Sustainer, we will provide you with your own <a href="https://bbb.sfconservancy.org">BigBlueButton</a> account so that you can host your own video calls on a FOSS platform. Once
|
||||||
|
you have donated to us, you can fill our the new account signup and your
|
||||||
|
account will be approved.</p>
|
||||||
|
|
||||||
|
<div class="picture-small right">
|
||||||
|
<a href="https://sfconservancy.org/videos/sfc-introduction_1080p.mp4"><img src="https://sfconservancy.org/videos/sfc-introduction-video_poster.jpg" alt="Thumbnail of video showing a tree and Software Freedom" /></a>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="NewStaff">
|
||||||
|
<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
|
||||||
|
critical in helping our license compliance efforts and helping our organization
|
||||||
|
take on the increasing needs from projects and new initiatives. SFC's new
|
||||||
|
systems administrator is Paul Visscher. With over 20 years experience with
|
||||||
|
Linux and free software, Paul's belief in the power of free software to help
|
||||||
|
people engage with technology in non-exploitative ways fits in perfectly to
|
||||||
|
support our growing organizational needs and mission. Helping make sure we
|
||||||
|
can provide solid FOSS replacements to proprietary technologies for all of us.</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="WritingAndSpeaking">
|
||||||
|
<summary>Writing and Speaking</summary>
|
||||||
|
<p>Our staff has been presenting and speaking about software freedom all year.
|
||||||
|
Our Executive Director Karen Sandler received an honorary doctorate from
|
||||||
|
Katholieke Universiteit Leuven for her incredible work in FOSS leadership, and
|
||||||
|
her advocacy and pursuit of software freedom and rights for all.
|
||||||
|
In November she spoke at <a href="https://www.sfscon.it/talks/the-history-of-and-path-forward-for-copyleft-and-the-gpl/">SFSCON about "The History of, and Path forward for, Copyleft and the GPL"</a>.</p>
|
||||||
|
|
||||||
|
<div class="picture-small right">
|
||||||
|
<video class="small-right" controls="" poster="/videos/2023-02-02_Sandler-Karen_KU-Leuven_Honorary-Doctorate_still.png" id="doctorate_vid">
|
||||||
|
<source src="/videos/2023-02-02_Sandler-Karen_KU-Leuven_Honorary-Doctorate.mp4">
|
||||||
|
<track src="/docs/2023-02-02_Sandler-Karen_KU-Leuven_Honorary-Doctorate.en.txt" kind="subtitles" srclang="en" label="English" />
|
||||||
|
<track src="/docs/2023-02-02_Sandler-Karen_KU-Leuven_Honorary-Doctorate.nl.txt" kind="subtitles" srclang="nl" label="Dutch (NL)" />
|
||||||
|
</video>
|
||||||
|
<p><a href="/videos/2023-02-02_Sandler-Karen_KU-Leuven_Honorary-Doctorate.mp4">Download Karen's talk</a>
|
||||||
|
or <a href="https://youtu.be/zca7dOU7jfs">watch on YouTube</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Policy Fellow Bradley M. Kuhn gave many conference talks and also represented SFC at many
|
||||||
|
government hearings and inquiries. Beginning the year at <a href="https://archive.fosdem.org/2023/schedule/speaker/bradley_m_kuhn/">FOSDEM</a>,
|
||||||
|
Bradley (and Karen) led the Legal and Policy DevRoom. He then spoke at SCaLE
|
||||||
|
20x giving a talk titled <a href="https://www.socallinuxexpo.org/scale/20x/presentations/learning-big-failures-improve-foss-advocacy-and-adoption">Learning From the Big Failures To Improve FOSS Advocacy and Adoption</a>.
|
||||||
|
As the host of a keynote discussion at FOSSY, Bradley facilitated a
|
||||||
|
<a href="https://sfconservancy.org/blog/2023/jul/19/rhel-panel-fossy-2023/">
|
||||||
|
community discussion about the RHEL policy change</a>.
|
||||||
|
As a panel member, he was the only representative for the FOSS community on
|
||||||
|
the FTC's discussion <a href="https://sfconservancy.org/news/2023/oct/04/ftc-ai-panel/">“Creative Economy and Generative AI“</a></p>
|
||||||
|
|
||||||
|
<p>Our Director of Compliance, Denver Gingerich, spoke at SFSCON talking about what it's really like to <a href="https://www.sfscon.it/talks/how-do-you-really-do-gpl-enforcement/">enforce the GPL</a>. His talk at FOSSY titled <a href="https://2023.fossy.us/schedule/presentation/84/">You don't carry a phone?! Improving societal acceptance of abnormal people</a>
|
||||||
|
was one of the most talked about from our own conference.
|
||||||
|
Pono represented SFC at <a href="https://www.socallinuxexpo.org/scale/19x">SCaLE
|
||||||
|
19x</a> and <a href="https://seagl.org">SeaGL</a>, which was great to be back
|
||||||
|
at community centered conferences.
|
||||||
|
SFC staffers also participate in key meetings to represent community interests
|
||||||
|
in a variety of FOSS related discussions concerning security, governmental use
|
||||||
|
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>
|
||||||
|
<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
|
||||||
|
software projects. Part of the unique position of our organization is the
|
||||||
|
expertise necessary to do this kind of work.</p>
|
||||||
|
|
||||||
|
<div class="picture-small right">
|
||||||
|
<img class="small-right" src="{% static 'img/outreachy-nigeria-1000-celebration.png' %}"
|
||||||
|
alt="Group Picture: Outreachy interns, mentors, and community coordinators gathered to celebrate the 1,000 interns milestone in Lagos, Nigeria." />
|
||||||
|
<p>Group Picture: Outreachy interns, mentors, and community coordinators gathered to celebrate the 1,000 interns milestone in Lagos, Nigeria.</p>
|
||||||
|
<p>You can view a <a href="https://diode.zone/w/p/25ifUPw9Lx42nrb14h41Ru">video of the celebration here.</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p><b>Outreachy</b> accepted 63 interns in the December 2022 cohort, and 64
|
||||||
|
interns in the May 2023 cohort with over 30 Free and Open Source software
|
||||||
|
communities. Bringing in new communities in the Open Science and Humanitarian
|
||||||
|
spheres, Outreachy continues to lead the way in providing opportunities to
|
||||||
|
people subject to systemic bias and impacted by underrepresentation in
|
||||||
|
technology. Celebrating our 1000th intern (!!!), there were celebrations
|
||||||
|
featuring alumni and current interns all over the world. </p>
|
||||||
|
|
||||||
|
<p><b>OpenWrt</b> released <a href="https://openwrt.org/releases/23.05/notes-23.05.0">version 23.05.0</a>
|
||||||
|
which continued adding hardware support for myriad devices, now supporting over
|
||||||
|
1790 devices, over 200 of which were added since last year! There is also now
|
||||||
|
support for various Rust packages, and major improvements to the core
|
||||||
|
components. OpenWrt remains one of the most important alternative firmware
|
||||||
|
projects, ensuring user rights in the ability to install (free) software on
|
||||||
|
your own devices.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p><a href="https://computinginresearch.org/">The Institute for Computing in
|
||||||
|
Research</a> completed it's fifth year, supporting 32 students in 3 cities this
|
||||||
|
summer. Now providing training, education and real world software experience to
|
||||||
|
high school students in 3 cities and exploring additional cities that may join
|
||||||
|
next summer. These research internships are a great way for high school
|
||||||
|
students to get involved in real academic research while also being exposed to
|
||||||
|
the ideas and principles of software freedom.</p>
|
||||||
|
|
||||||
|
<p><a href="https://inkscape.org">Inkscape</a> just celebrated their 20th
|
||||||
|
anniversary! Coinciding with their new <a href="https://inkscape.org/news/2023/11/18/big-small-release-inkscape-131-is-out/">1.3.1 release</a>, Inkscape is as active as ever and proving itself one
|
||||||
|
of the most vital free software projects, full of longevity and an ever
|
||||||
|
growing community. This year has been a big year of development, marking some
|
||||||
|
very exciting developments for the project. Creation of a new <a href="https://inkscape.org/*membership/blog/october-bug-accelerator-2023/">bug accelerator program</a>, a migration to <a href="https://inkscape.org/*membership/blog/august-gtk4-migration-tavmjong/">GTK4</a>, another year of <a href="https://inkscape.org/*membership/blog/customizable-canvas-controls/">GSOC participation</a> and new support for <a href="https://inkscape.org/*membership/blog/may-ai-extension-2023/">Adobe Illustrator import</a>!</p>
|
||||||
|
|
||||||
|
<p>Our member projects had a range of in person and online events this year.
|
||||||
|
<b>Inkscape</b> hosted an in-person meeting in Bensberg, Germany. A great meeting
|
||||||
|
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
|
||||||
|
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
|
||||||
|
code forges and what the new contributor experience is like. A great mix of
|
||||||
|
code related and process related talks. The <a href="https://reproducible-builds.org/">Reproducible Builds</a>
|
||||||
|
annual summit was hosted in Hamburg featuring incredible
|
||||||
|
technical talks, project planning and continues to build the momentum and
|
||||||
|
reach for reproducibility. </p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Our sustainers</h3>
|
||||||
|
|
||||||
|
<p>Anonymous (100 people), Aurimas Fišeras, Kat Walsh, Richard Wheeler, Karl Ove Hufthammer, Mark Wielaard, Karl Fogel, Richard Fontana, Richard L. Schmeidler, Karen Sandler, Russ Allbery, Christine Webber, Jeremy Allison, J.B. Nicholson, Michael Dexter, Tom Marble, Johannes Krampf, Michael Linksvayer, Jack Hill, Stefano Zacchiroli, Daniel Callahan, Ben Cotton, in memory of Marina Zhurakhinskaya, Jim Radford, Tyng-Ruey Chuang, Francois Marier, Henri Sivonen, Keith Packard, Monica Neumann, Michal Nazarewicz, Bdale Garbee, David Neary, Alexander Bokovoy, Andrew Isaacson, Brian Hart, James Pannacciulli, Sasa Poznanovic, David Batley, David Crossland, Steve Sprang, Bob Murphy, Mark Galassi, James R. Garrison, Bluebird Interactive, David Quist, Patrick Masson, Neil McGovern, Lenore Ramm Hill, Paul Logston, David Arnold, Benjamin Pfaff, Timothy Howes, Britta Gustafson, Wookey, Michael Gulick, Tanu Kaskinen, Jeffrey Layton, Raphaël Hertzog, Will Thompson, Matteo Settenvini, Kevin Krammer, Elana Hashman, Richard Schultz, Charkov Alexey, Donald Craig, Michael Catanzaro, Olav Reinert, Stephen Kitt, Barry Fishman, Luigi Toscano, Steve McIntyre, Cornelia Huck, Jonathan McDowell, Emmanuel Seyman, Mike Crowe, Alexandre Julliard, Ross Vandegrift, Ian Jackson, Alexander Reichle-Schmehl, Sang Engineering, Preston Maness, John Hagemeister, Julien Cristau, Rebecca Sobol, John Hughes, Peter Link, Solomon Peachy, Riccardo Ghetta, Stefano Rivera, Julian Gilbey, Srivats P, JRS System Solutions, Eric Dorland, Matija Nalis, Brett Smith, Dmitry Borodaenko, Johannes Berg, Howard Shih, Nigel Whillier, Peter Maydell, Lars Wirzenius, Stephanie Feingold, Kevin Adler, Matthew Vernon, Stefan Seefeld, scrye.com, Robert Horn, Andreas Bombe, Michael Kuhn, Stephen Waite, Philip Cohn-Cort, Stuart Smith, Michel Machado, Joseph Thompson, Joan Sandler, Sage Ross, Peter Levy, Daniel Gillmor, James Carter, Wilson E. Alvarez, Michael Andreen, Aaron Puchert, Andrew Eikum, Vladimir Michl, Gregory Grossmeier, Josh Triplett, James Blair, Felix Gruber, Claire Connelly, Antoine Amarilli, Kenneth J. Pronovici, Igalia S. L., Karl-Johan Karlsson, David Gibson, Tom Callaway, Steven A. Ovadia, Gerard Ryan, James Garrett, William Norris, Luke Faraone, Christian Gelinek, Chris Neugebauer, David Potter, Paul Fenwick, George Bojanov, Jondale Stratton, Kiatikun Luangkesorn, hmk, Yu Tomita, Jure Varlec, Antonin Houska, Chad Henderson, Adam Batkin, Marc Jeanmougin, Mike Dowling, Nicholas George, Leif Lindholm, Diane Trout, Daniel Walls, Donald Anderson, Darrick Wong, Greg Price, Martin Krafft, Tony Sebro, Matthew Treinish, Jason Baker, Kathy Giori, Brennen Bearnes, Olly Betts, Steven Adger, John Maloney, Gargi Sharma, Andrew Janke, Andy Kittner, Holger Levsen, Jacopo Corbetta, Andy Balaam, Justin W. Flory, Albert Chae, Elias Rudberg, Gene Hightower, Asumu Takikawa, John-Isaac Greant, Ulrich Czekalla, Bob Proulx, Nick Alcock, Geoffrey Knauth, Luke Shumaker, Stephen Hinton, Philip McGrath, Anjandev Momi, Meisam Tabriz, Alex Dryden, Thomas Schwinge, Julia Kreger, nicholas Bishop, Rachel Wonnacott, Benjamin Kraus, David Witten, Pontus Ullgren, Brendan Horan, Alex Karle, Michael Pennisi, Dave Jansen, Kit Aultman, Jason Prince, Frank Eigler, Keyhan Vakil, Daniel Whiting, tam phan, Jon Stumpf, Anna Philips, Anthony Symkowick, Drew Fustini, Anthony Mirabella, Eric Perko, Simon Michael, Rod Nayfield, Joerg Jaspert, Lieven Govaerts, David Harris, BRUST, Alexander Couzens, Amisha Singla, Athul Iddya, kyle Davis, Trace Pearson, Paul Williams, Peter Murray, anne fonteyn</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<progress min="0" max="100" value="84.5" class="w-100">84.5%</progress>
|
||||||
|
<div class="mv3">
|
||||||
|
<div class="f2 b">$15,558</div>
|
||||||
|
<div class="f5 b black-50">Remaining of the $100,000 goal</div>
|
||||||
|
</div>
|
||||||
|
<div class="mv3">
|
||||||
|
<div class="f2 b">15 days</div>
|
||||||
|
<div class="f5 b black-50">Remaining</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt4">
|
||||||
|
<a href="{% url "stripe2" %}">
|
||||||
|
<button type="submit" class="pointer" 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);">Become a Sustainer!</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt3">
|
||||||
|
<figure>
|
||||||
|
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt">
|
||||||
|
<figcaption class="tc black-70" style="margin-top: -20px">Our new Sustainer T-shirt</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,69 @@
|
||||||
|
{% 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>
|
||||||
|
{% 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' },
|
||||||
|
}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<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>Amount
|
||||||
|
<span class="db mt1">$ {{ form.amount }}</span>
|
||||||
|
</label></div>
|
||||||
|
<div class="mv3"><label class="lh-title"><input type="checkbox"> Acknowledge me on the public <a href="">list of sustainers</a></label></div>
|
||||||
|
<div class="mv3"><label class="lh-title"><input type="checkbox"> 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" 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>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -7,4 +7,8 @@ urlpatterns = [
|
||||||
path('', views.sustainers),
|
path('', views.sustainers),
|
||||||
path('banner/', TemplateView.as_view(template_name='supporters/banners.html')),
|
path('banner/', TemplateView.as_view(template_name='supporters/banners.html')),
|
||||||
path('banners/', TemplateView.as_view(template_name='supporters/banners.html')),
|
path('banners/', TemplateView.as_view(template_name='supporters/banners.html')),
|
||||||
|
path('success/', views.success),
|
||||||
|
path('webhook/', views.webhook),
|
||||||
|
path('stripe/', views.sustainers_stripe),
|
||||||
|
path('stripe2/', views.sustainers_stripe2, name='stripe2'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.utils import timezone
|
||||||
|
import stripe
|
||||||
|
|
||||||
from .. import ParameterValidator
|
from .. import ParameterValidator
|
||||||
from .models import Supporter
|
from . import forms
|
||||||
|
from .models import Supporter, SustainerOrder
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def sustainers(request):
|
def sustainers(request):
|
||||||
with ParameterValidator(request.GET, 'upgrade_id') as validator:
|
with ParameterValidator(request.GET, 'upgrade_id') as validator:
|
||||||
|
@ -37,3 +43,112 @@ def sponsors(request):
|
||||||
'anonymous_count' : anonymous_count
|
'anonymous_count' : anonymous_count
|
||||||
}
|
}
|
||||||
return render(request, "supporters/sponsors.html", c)
|
return render(request, "supporters/sponsors.html", c)
|
||||||
|
|
||||||
|
|
||||||
|
def create_checkout_session(reference_id, email, amount, base_url):
|
||||||
|
YOUR_DOMAIN = base_url
|
||||||
|
try:
|
||||||
|
checkout_session = stripe.checkout.Session.create(
|
||||||
|
client_reference_id=str(reference_id),
|
||||||
|
line_items=[
|
||||||
|
{
|
||||||
|
'price_data': {
|
||||||
|
'currency': 'usd',
|
||||||
|
'product_data': {'name': 'Contribution'},
|
||||||
|
'unit_amount': amount * 100,
|
||||||
|
},
|
||||||
|
'quantity': 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
customer_email=email,
|
||||||
|
mode='payment',
|
||||||
|
success_url=YOUR_DOMAIN + '/sustainer/success/?session_id={CHECKOUT_SESSION_ID}',
|
||||||
|
cancel_url=YOUR_DOMAIN + '/sustainer/stripe/',
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
return checkout_session.url
|
||||||
|
|
||||||
|
|
||||||
|
def sustainers_stripe(request):
|
||||||
|
return render(request, 'supporters/sustainers_stripe.html', {})
|
||||||
|
|
||||||
|
|
||||||
|
def sustainers_stripe2(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = forms.SustainerForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
order = form.save()
|
||||||
|
base_url = f'{request.scheme}://{request.get_host()}'
|
||||||
|
stripe_checkout_url = create_checkout_session(order.id, order.email, order.amount, base_url)
|
||||||
|
return redirect(stripe_checkout_url)
|
||||||
|
else:
|
||||||
|
form = forms.SustainerForm()
|
||||||
|
return render(request, 'supporters/sustainers_stripe2.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
stripe.api_key = 'sk_test_zaAqrpHmpkXnHQfAs4UWkE3d'
|
||||||
|
|
||||||
|
def fulfill_checkout(session_id):
|
||||||
|
print("Fulfilling Checkout Session", session_id)
|
||||||
|
|
||||||
|
# TODO: Make this function safe to run multiple times,
|
||||||
|
# even concurrently, with the same session ID
|
||||||
|
|
||||||
|
# TODO: Make sure fulfillment hasn't already been
|
||||||
|
# peformed for this Checkout Session
|
||||||
|
|
||||||
|
# Retrieve the Checkout Session from the API with line_items expanded
|
||||||
|
checkout_session = stripe.checkout.Session.retrieve(
|
||||||
|
session_id,
|
||||||
|
expand=['line_items'],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check the Checkout Session's payment_status property
|
||||||
|
# to determine if fulfillment should be peformed
|
||||||
|
if checkout_session.payment_status != 'unpaid':
|
||||||
|
# TODO: Perform fulfillment of the line items
|
||||||
|
|
||||||
|
# TODO: Record/save fulfillment status for this
|
||||||
|
# Checkout Session
|
||||||
|
logger.info(f'Session ID {session_id} PAID!')
|
||||||
|
try:
|
||||||
|
order = SustainerOrder.objects.get(id=checkout_session['client_reference_id'], paid_time=None)
|
||||||
|
order.paid_time=timezone.now()
|
||||||
|
order.save()
|
||||||
|
logger.info(f'Marked sustainer order {order.id} (order.email) as paid')
|
||||||
|
except SustainerOrder.DoesNotExist:
|
||||||
|
logger.info('No action')
|
||||||
|
|
||||||
|
|
||||||
|
def success(request):
|
||||||
|
fulfill_checkout(request.GET['session_id'])
|
||||||
|
return render(request, 'supporters/stripe_success.html', {})
|
||||||
|
|
||||||
|
|
||||||
|
def webhook(request):
|
||||||
|
payload = request.body
|
||||||
|
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
|
||||||
|
event = None
|
||||||
|
|
||||||
|
# From webhook dashboard
|
||||||
|
endpoint_secret = 'whsec_lLy9pqxAAHdl4fwiC0cFg1KwR6y4CvOH'
|
||||||
|
|
||||||
|
try:
|
||||||
|
event = stripe.Webhook.construct_event(
|
||||||
|
payload, sig_header, endpoint_secret
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
# Invalid payload
|
||||||
|
return HttpResponse(status=400)
|
||||||
|
except stripe.error.SignatureVerificationError:
|
||||||
|
# Invalid signature
|
||||||
|
return HttpResponse(status=400)
|
||||||
|
|
||||||
|
if (
|
||||||
|
event['type'] == 'checkout.session.completed'
|
||||||
|
or event['type'] == 'checkout.session.async_payment_succeeded'
|
||||||
|
):
|
||||||
|
fulfill_checkout(event['data']['object']['id'])
|
||||||
|
|
||||||
|
return HttpResponse(status=200)
|
||||||
|
|
|
@ -5,3 +5,4 @@ beautifulsoup4==4.11.2
|
||||||
html5lib==1.1
|
html5lib==1.1
|
||||||
django-countries==7.3.2
|
django-countries==7.3.2
|
||||||
Pillow==9.4.0
|
Pillow==9.4.0
|
||||||
|
stripe
|
Loading…
Reference in a new issue