Merge branch 'ben/copyright-assignment'
This commit is contained in:
commit
1ce6f36532
17 changed files with 519 additions and 0 deletions
10
requirements.txt
Normal file
10
requirements.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
backports.functools-lru-cache==1.6.4
|
||||
beautifulsoup4==4.9.3
|
||||
bs4==0.0.1
|
||||
Django==1.11.29
|
||||
pytz==2021.3
|
||||
soupsieve==1.9.6
|
||||
html5lib==0.999999999
|
||||
future
|
||||
|
||||
django_countries==5.5 # Supports both Python 2 and 3.
|
0
www/conservancy/apps/assignment/__init__.py
Normal file
0
www/conservancy/apps/assignment/__init__.py
Normal file
7
www/conservancy/apps/assignment/apps.py
Normal file
7
www/conservancy/apps/assignment/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AssignmentConfig(AppConfig):
|
||||
name = 'assignment'
|
72
www/conservancy/apps/assignment/forms.py
Normal file
72
www/conservancy/apps/assignment/forms.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
import datetime
|
||||
|
||||
from django import forms
|
||||
from django.core.validators import ValidationError
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import Assignment
|
||||
from .terms import TERMS
|
||||
|
||||
|
||||
def validate_in_past(value):
|
||||
# Adding a day to allow the current date anywhere on earth, regardless of
|
||||
# the server timezone.
|
||||
if value > timezone.now().date() + datetime.timedelta(days=1):
|
||||
raise ValidationError('Enter a date in the past')
|
||||
|
||||
|
||||
class AssignmentForm(forms.ModelForm):
|
||||
period_begins = forms.DateField(
|
||||
label='Assign the copyright in my above contributions starting on',
|
||||
help_text='You can use the day you first started contributing (or, equivalently, your date of birth), or any later date.',
|
||||
required=True,
|
||||
widget=forms.DateInput(attrs={'type': 'date'}),
|
||||
validators=[validate_in_past],
|
||||
)
|
||||
period_end_type = forms.ChoiceField(
|
||||
label='and ending on',
|
||||
choices=[
|
||||
('all future contributions', 'all future contributions (no end date)'),
|
||||
('a specific past date', 'a specific past date (specify below)'),
|
||||
],
|
||||
widget=forms.RadioSelect(),
|
||||
initial='all future contributions',
|
||||
)
|
||||
period_ends = forms.DateField(
|
||||
label='Specific past date (if applicable)',
|
||||
required=False,
|
||||
widget=forms.DateInput(attrs={'type': 'date'}),
|
||||
validators=[validate_in_past],
|
||||
)
|
||||
agreement_terms = forms.CharField(
|
||||
widget=forms.Textarea(attrs={'readonly': 'readonly'}),
|
||||
initial=TERMS,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['attestation_of_copyright'].required = True
|
||||
|
||||
class Meta:
|
||||
model = Assignment
|
||||
fields = [
|
||||
'full_name',
|
||||
'email',
|
||||
'country_of_residence',
|
||||
'repositories',
|
||||
'all_emails',
|
||||
'period_begins',
|
||||
'period_end_type',
|
||||
'period_ends',
|
||||
'agreement_terms',
|
||||
'attestation_of_copyright',
|
||||
]
|
||||
|
||||
def clean_period_ends(self):
|
||||
if 'period_begins' in self.cleaned_data and 'period_ends' in self.cleaned_data and self.cleaned_data['period_begins'] and self.cleaned_data['period_ends'] and self.cleaned_data['period_begins'] > self.cleaned_data['period_ends']:
|
||||
raise ValidationError('End of period is before start')
|
||||
|
||||
if self.cleaned_data['period_end_type'] == 'a specific past date' and not self.cleaned_data['period_ends']:
|
||||
raise ValidationError('This field is required')
|
||||
|
||||
return self.cleaned_data['period_ends']
|
28
www/conservancy/apps/assignment/migrations/0001_initial.py
Normal file
28
www/conservancy/apps/assignment/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2021-11-30 00:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Assignment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('full_name', models.CharField(max_length=255)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('place_of_residence', models.TextField(blank=True, verbose_name='Country of citizenship or residential address')),
|
||||
('repository', models.URLField(blank=True, verbose_name='Code repository')),
|
||||
('coverage', models.CharField(choices=[('up to this year', 'One-off up to and including this year'), ('ongoing', 'All existing and new contributions')], default='up to this year', max_length=50, verbose_name='Time period to assign')),
|
||||
('attestation_of_copyright', models.BooleanField(verbose_name='I attest that I own the copyright on these works')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2021-12-06 22:37
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
import django_countries.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assignment', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='assignment',
|
||||
name='coverage',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='assignment',
|
||||
name='place_of_residence',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='assignment',
|
||||
name='repository',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='all_emails',
|
||||
field=models.TextField(default='', verbose_name='All email addresses and/or names used by you to contribute to the above'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='country_of_residence',
|
||||
field=django_countries.fields.CountryField(default='', max_length=2),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='period_begins',
|
||||
field=models.DateField(default=datetime.date(2021, 1, 1), verbose_name='Assignment period begins'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='period_end_type',
|
||||
field=models.CharField(choices=[('all future contributions', 'all future contributions'), ('a specific past date', 'a specific past date')], default=datetime.date(2021, 1, 1), max_length=50, verbose_name='Time period to assign'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='period_ends',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='Assignment period ends (if applicable)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='repositories',
|
||||
field=models.TextField(default='', help_text='List of URLs, one per line', verbose_name="Code repositories contributed to that you'd like to assign"),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='assignment',
|
||||
name='attestation_of_copyright',
|
||||
field=models.BooleanField(verbose_name='I agree to be bound by the terms of the Copyright Assignment Agreement above, and that I own the copyright in the works defined above'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='assignment',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, verbose_name='Email address (to contact you if we have questions)'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2021-12-06 22:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assignment', '0002_auto_20211206_2237'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='assignment',
|
||||
name='id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignment',
|
||||
name='uuid',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
0
www/conservancy/apps/assignment/migrations/__init__.py
Normal file
0
www/conservancy/apps/assignment/migrations/__init__.py
Normal file
59
www/conservancy/apps/assignment/models.py
Normal file
59
www/conservancy/apps/assignment/models.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import uuid
|
||||
|
||||
from django.core.validators import URLValidator, ValidationError
|
||||
from django.db import models
|
||||
from django_countries.fields import CountryField
|
||||
|
||||
|
||||
def validate_mutiple_urls(value):
|
||||
"""Map the URLValidator() over text containing multiple URLs."""
|
||||
candidate_urls = [c.strip() for c in value.split()]
|
||||
invalid_urls = []
|
||||
# TODO: Improve this https://docs.djangoproject.com/en/3.2/ref/forms/validation/#raising-multiple-errors
|
||||
validator = URLValidator()
|
||||
for url in candidate_urls:
|
||||
try:
|
||||
validator(url)
|
||||
except ValidationError:
|
||||
invalid_urls.append(url)
|
||||
print(invalid_urls)
|
||||
if invalid_urls:
|
||||
raise ValidationError('These don\'t seem to be complete URLs:\n{}'.format('\n'.join(invalid_urls)))
|
||||
|
||||
|
||||
class Assignment(models.Model):
|
||||
"""A copyright assignment to Conservancy."""
|
||||
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
full_name = models.CharField(max_length=255)
|
||||
email = models.EmailField('Email address (to contact you if we have questions)')
|
||||
country_of_residence = CountryField()
|
||||
repositories = models.TextField(
|
||||
'Code repositories containing contributions of yours whose copyright you are assigning',
|
||||
help_text='List of URLs, one per line',
|
||||
validators=[validate_mutiple_urls],
|
||||
)
|
||||
all_emails = models.TextField(
|
||||
'All email addresses or other unique user identities, such as nicknames or handles, used by you to contribute to the above (i.e. in the commit logs)',
|
||||
)
|
||||
period_begins = models.DateField(
|
||||
'Assignment period begins',
|
||||
)
|
||||
period_end_type = models.CharField(
|
||||
'Time period to assign',
|
||||
max_length=50,
|
||||
choices=[
|
||||
('all future contributions', 'all future contributions'),
|
||||
('a specific past date', 'a specific past date'),
|
||||
],
|
||||
)
|
||||
period_ends = models.DateField(
|
||||
'Assignment period ends (if applicable)',
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
attestation_of_copyright = models.BooleanField(
|
||||
'By checking the box below, I am confirming that I agree to be bound by the terms of the Copyright Assignment Agreement above.',
|
||||
)
|
55
www/conservancy/apps/assignment/terms.py
Normal file
55
www/conservancy/apps/assignment/terms.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
import textwrap
|
||||
|
||||
TERMS = textwrap.dedent("""\
|
||||
Copyright Assignment Agreement
|
||||
|
||||
By checking the box below and submitting this form, you (``Assignor'') enter
|
||||
into this Agreement between Assignor and the Software Freedom Conservancy,
|
||||
Inc., a New York nonprofit corporation located in Brooklyn, New York, which
|
||||
has received recognition of exemption from federal income tax under Section
|
||||
501(c)(3) of the Internal Revenue Code and classification as a public
|
||||
charity (the ``Conservancy'').
|
||||
|
||||
For good and valuable consideration, receipt of which is hereby
|
||||
acknowledged, Assignor hereby transfers to the Conservancy their entire
|
||||
right, title, and interest in the copyright in the works identified by the
|
||||
repositories, email addresses, names, and time periods listed above (the
|
||||
“Works”). To the extent that the Assignor is assigning future rights, and
|
||||
the rights in future works do not vest in Conservancy upon the Works’
|
||||
creation, Assignor agrees to assign, immediately following the creation, all
|
||||
rights to Conservancy and irrevocably appoint Conservancy as their
|
||||
attorney-in-fact to take any necessary steps to perfect Conservancy’s rights
|
||||
under this Agreement.
|
||||
|
||||
The Conservancy will use its discretion for any relicensing of the Works
|
||||
under other free and open source software licenses. Decisions about
|
||||
relicensing made by Conservancy will apply to its assignees and successors.
|
||||
If you have any questions about Conservancy's relicensing philosophy, and
|
||||
what limitations it may have because it is a public charity, please contact
|
||||
us at copyright-assignment@sfconservancy.org .
|
||||
|
||||
The Conservancy hereby grants Assignor a royalty-free non-exclusive license
|
||||
to use the interests assigned hereunder for any purpose. The Conservancy's
|
||||
rights shall otherwise continue unchanged.
|
||||
|
||||
Assignor hereby grants to the Conservancy and to recipients of software
|
||||
distributed by the Conservancy a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Works, where
|
||||
such license applies only to those patent claims licensable by Assignor
|
||||
that are necessarily infringed by the Works alone or by combination of
|
||||
Assignor's contributions with the Works to which such contributions were
|
||||
submitted.
|
||||
|
||||
Assignor hereby represents and warrants that the email addresses and user
|
||||
identities identified above are unique to the Assignor, that no one but the
|
||||
Assignor has made contributions using the email addresses and user
|
||||
identities identified above, that the Assignor is the sole copyright holder
|
||||
for the Works assigned hereunder, and that the Assignor has the right and
|
||||
power to enter into this contract. Assignor hereby indemnifies and holds
|
||||
harmless the Conservancy, its officers, employees, agents, successors, and
|
||||
assigns against any and all claims, actions or damages (including reasonable
|
||||
attorney's fees) asserted by or paid to any party on account of a breach or
|
||||
alleged breach of the foregoing warranty. Assignor makes no other express or
|
||||
implied warranty (including without limitation any warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE).""")
|
9
www/conservancy/apps/assignment/urls.py
Normal file
9
www/conservancy/apps/assignment/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import AssignmentCreateView, AssignmentThanksView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', AssignmentCreateView.as_view(), name='assignement-add'),
|
||||
url(r'^(?P<pk>[\w-]+)/$', AssignmentThanksView.as_view(), name='assignment-thanks'),
|
||||
]
|
39
www/conservancy/apps/assignment/views.py
Normal file
39
www/conservancy/apps/assignment/views.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
from django.core.mail import send_mail
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic.edit import CreateView
|
||||
|
||||
from .forms import AssignmentForm
|
||||
from .models import Assignment
|
||||
|
||||
class AssignmentCreateView(CreateView):
|
||||
"""Show a form for the initial copyright assignment."""
|
||||
|
||||
form_class = AssignmentForm
|
||||
template_name = 'assignment/assignment_form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
intro = 'The following copyright assignment has been submitted:\n\n'
|
||||
body = intro + '\n'.join(['{}: {}'.format(k, v) for k, v in form.cleaned_data.items() if k != 'agreement_terms'])
|
||||
send_mail(
|
||||
'Copyright assignment form: {}'.format(form.cleaned_data['full_name']),
|
||||
body,
|
||||
'ben@sturm.com.au',
|
||||
['denver@sfconservancy.org', 'bsturmfels@sfconservancy.org'],
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self, *args, **kwargs):
|
||||
return reverse_lazy('assignment-thanks', kwargs={'pk': str(self.object.uuid)})
|
||||
|
||||
|
||||
class AssignmentThanksView(DetailView):
|
||||
model = Assignment
|
||||
template_name = 'assignment/thanks.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['form'] = AssignmentForm(instance=self.object)
|
||||
for _, field in context['form'].fields.items():
|
||||
field.widget.attrs['disabled'] = 'disabled'
|
||||
return context
|
|
@ -84,3 +84,25 @@ LOGGING = {
|
|||
'level': 'INFO',
|
||||
},
|
||||
}
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.humanize',
|
||||
# 'django.contrib.staticfiles',
|
||||
'conservancy.apps.blog',
|
||||
'conservancy.apps.contacts',
|
||||
'conservancy.apps.contractpatch',
|
||||
'conservancy.apps.events',
|
||||
'conservancy.apps.news',
|
||||
'conservancy.apps.staff',
|
||||
# 'conservancy.apps.summit_registration',
|
||||
'conservancy.apps.worldmap',
|
||||
'conservancy.apps.supporters',
|
||||
'conservancy.apps.fundgoal',
|
||||
'conservancy.apps.assignment',
|
||||
]
|
||||
|
|
54
www/conservancy/templates/assignment/assignment_form.html
Normal file
54
www/conservancy/templates/assignment/assignment_form.html
Normal file
|
@ -0,0 +1,54 @@
|
|||
{% extends "assignment/base_assignment.html" %}
|
||||
{% block category %}Copyright Assignment{% endblock %}
|
||||
{% block outercontent %}
|
||||
<h1>Copyright Assignment</h1>
|
||||
|
||||
<div class="mw7 mb5">
|
||||
<p>Thank you for considering assigning your copyright to the Software Freedom Conservancy. Your assignment helps us enforce free and open source software licenses.</p>
|
||||
|
||||
<p>By filling in and submitting the below form, you agree to assign your copyrights in the specified projects to Software Freedom Conservancy, which means that Conservancy can enforce the licenses for your code in court, minimizing the need for you to be involved. Conservancy agrees to keep your code under a free and open source license.</p>
|
||||
|
||||
<p>If you have any questions about assigning your copyright to Conservancy, please don't hesitate to email us at <a href="mailto:copyright-assignment@sfconservancy.org">copyright-assignment@sfconservancy.org</a>.</p>
|
||||
|
||||
<form id="assignment-form" action="." method="post" class="mw7">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form.errors %}
|
||||
<p class="dark-red bg-washed-red pa2 ba b--red br1">Please review the errors below.</p>
|
||||
{% endif %}
|
||||
|
||||
{{ form.as_p }}
|
||||
|
||||
<p><em>Please be aware that your employer or a contractor may own the rights in your work by virtue of their employment of you or by explicit transfer of ownership in an agreement. We recommend you review any relevant agreements or consult with a lawyer if you are not sure.</em></p>
|
||||
|
||||
<p><em>After submitting this agreement, if you would like to make any changes, you must let us know within 7 days by emailing <a href="mailto:copyright-assignment@sfconservancy.org">copyright-assignment@sfconservancy.org</a>, which is also where you can reach us if you have any questions.</em></p>
|
||||
<p><button type="submit" class="ph3 pv2">Submit</button></p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// End date field should be shown only when "a specific past date" is selected.
|
||||
const form = document.querySelector('#assignment-form');
|
||||
const past_date_label = document.querySelector('label[for=id_period_ends]');
|
||||
const past_date_field = document.querySelector('#id_period_ends');
|
||||
const past_date_container = past_date_field.parentElement;
|
||||
form.addEventListener('change', togglePastDate);
|
||||
togglePastDate(); // Run change handler once to initialise form.
|
||||
|
||||
// Text "(if applicable)" isn't relevant with JS enabled.
|
||||
past_date_label.innerHTML = past_date_label.innerHTML.replace(' (if applicable)', '');
|
||||
|
||||
function togglePastDate() {
|
||||
if (form['period_end_type'].value === 'all future contributions') {
|
||||
past_date_container.style.display = 'none';
|
||||
past_date_field.required = false;
|
||||
}
|
||||
else {
|
||||
past_date_container.style.display = '';
|
||||
past_date_field.required = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
49
www/conservancy/templates/assignment/base_assignment.html
Normal file
49
www/conservancy/templates/assignment/base_assignment.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
{% extends "base_conservancy.html" %}
|
||||
{% block category %}Copyright Assignment{% endblock %}
|
||||
{% block head %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
label { display: block; }
|
||||
input[type=text], input[type=email], input[type=date], select {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
input[type=checkbox] { width: auto; }
|
||||
span[class=helptext] {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
#id_place_of_residence {
|
||||
height: 5rem;
|
||||
}
|
||||
form ul {
|
||||
margin: -1em 0 0;
|
||||
}
|
||||
form li {
|
||||
list-style: none;
|
||||
line-height: 1.5;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
max-width: 45rem;
|
||||
height: 8rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
#id_full_name, #id_email {
|
||||
width: 100%;
|
||||
max-width: 25rem;
|
||||
}
|
||||
#id_agreement_terms {
|
||||
height: 20rem;
|
||||
}
|
||||
.helptext {
|
||||
max-width: 35rem;
|
||||
}
|
||||
.errorlist {
|
||||
margin: 1rem 0 0.25rem;
|
||||
color: #e7040f;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
15
www/conservancy/templates/assignment/thanks.html
Normal file
15
www/conservancy/templates/assignment/thanks.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% extends "assignment/base_assignment.html" %}
|
||||
{% load static %}
|
||||
{% block category %}Copyright Assignment{% endblock %}
|
||||
{% block outercontent %}
|
||||
<h1>Thanks! <svg style="color: #ff41b4; width: 30px; height: 30px; vertical-align: middle;"><use href="{% static 'img/font_awesome.svg' %}#heart"></use></svg></h1>
|
||||
|
||||
<div class="mw7 mb5">
|
||||
<p>Thank you for assigning your copyright to Software Freedom Conservancy! We have recorded the below information regarding the assignment and the works.</p>
|
||||
<p>We will be sending out verification emails to the email addresses you used to contribute, as specified below, in the coming weeks. Please follow the instructions there to complete the verification at that time.</p>
|
||||
<p>If you would like to make any changes, you must let us know within 7 days by emailing <a href="mailto:copyright-assignment@sfconservancy.org">copyright-assignment@sfconservancy.org</a>, which is also where you can reach us if you have any questions.</p>
|
||||
<form>
|
||||
{{ form.as_p }}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -59,4 +59,5 @@ urlpatterns = [
|
|||
url(r'^coming-soon.html', static_views.index),
|
||||
url(r'^fundraiser_data', fundgoal_views.view),
|
||||
url(r'^ccs-upload/', include('conservancy.apps.ccs_upload.urls', namespace='ccs_upload')),
|
||||
url(r'^assignment/', include('conservancy.apps.assignment.urls')),
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue