diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py
index 0f3ea5de4bd041519094eb14dc7baf163ca6a74f..d5d7a8f606ab5cd27ab52f6036260623c1fe7cac 100644
--- a/SciPost_v1/settings/base.py
+++ b/SciPost_v1/settings/base.py
@@ -100,6 +100,7 @@ INSTALLED_APPS = (
     'partners',
     'funders',
     'stats',
+    'petitions',
     'webpack_loader',
 )
 
diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py
index ab3ea6f377dc701332e17b0f0a9f4152416c6126..cd3462f1bd2cd18e7e145be4bda23e1cc41d080b 100644
--- a/SciPost_v1/urls.py
+++ b/SciPost_v1/urls.py
@@ -43,6 +43,7 @@ urlpatterns = [
     url(r'^meetings/', include('virtualmeetings.urls', namespace="virtualmeetings")),
     url(r'^news/', include('news.urls', namespace="news")),
     url(r'^notifications/', include('notifications.urls', namespace="notifications")),
+    url(r'^petitions/', include('petitions.urls', namespace="petitions")),
     url(r'^production/', include('production.urls', namespace="production")),
     url(r'^partners/', include('partners.urls', namespace="partners")),
     url(r'^stats/', include('stats.urls', namespace="stats")),
diff --git a/news/templates/news/news_card_content_for_api.html b/news/templates/news/news_card_content_for_api.html
index d5f80ce81649c01dd762df05107f45cbacdbb3c1..5815a30d32e69e9235ba758878f0e155072f3468 100644
--- a/news/templates/news/news_card_content_for_api.html
+++ b/news/templates/news/news_card_content_for_api.html
@@ -3,7 +3,7 @@
     <div>
         <h5 class="text-muted mb-2">{{date}}</h5>
         <div>
-            {{blurb|truncatechars_html:90|safe}}
+            {{blurb|truncatechars_html:180|safe}}
 
             <br>
             <a href="{% url 'news:news' %}#news_{{id}}" class="my-1 d-inline-block">Read more</a>
diff --git a/news/templates/news/news_card_content_short.html b/news/templates/news/news_card_content_short.html
index 09f352dba28e09c03d01c1b99b85dee15a43dfb1..2f796a59052dbe0c60c8aea137a5fdd250c72036 100644
--- a/news/templates/news/news_card_content_short.html
+++ b/news/templates/news/news_card_content_short.html
@@ -3,7 +3,7 @@
     <div>
         <h5 class="text-muted mb-2">{{news.date|date:'j F Y'}}</h5>
         <div>
-            {{news.blurb|truncatechars_html:90|safe}}
+            {{news.blurb|truncatechars_html:180|safe}}
 
             <br>
             <a href="{% url 'news:news' %}#news_{{news.id}}" class="my-1 d-inline-block">Read more</a>
diff --git a/partners/admin.py b/partners/admin.py
index ccb1ffbd115c8115883e9213827520e3ddb2d89c..057cd39487465a435361cfafbb2488b833cb0e8b 100644
--- a/partners/admin.py
+++ b/partners/admin.py
@@ -5,6 +5,7 @@ from .models import Contact, Partner, Consortium, Institution,\
                     MembershipAgreement, ContactRequest, PartnersAttachment
 
 
+
 class AttachmentInline(admin.TabularInline):
     model = PartnersAttachment
 
diff --git a/partners/migrations/0033_auto_20171003_2058.py b/partners/migrations/0033_auto_20171003_2058.py
new file mode 100644
index 0000000000000000000000000000000000000000..be7d386c6842293568a7ad79c85abd5e49eea787
--- /dev/null
+++ b/partners/migrations/0033_auto_20171003_2058.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-03 18:58
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0065_authorshipclaim_publication'),
+        ('partners', '0032_auto_20170829_0727'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Petition',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=256)),
+                ('slug', models.SlugField()),
+                ('headline', models.CharField(max_length=256)),
+                ('statement', models.TextField()),
+                ('signatories', models.ManyToManyField(related_name='petitions', to='scipost.Contributor')),
+            ],
+        ),
+    ]
diff --git a/partners/migrations/0034_auto_20171003_2109.py b/partners/migrations/0034_auto_20171003_2109.py
new file mode 100644
index 0000000000000000000000000000000000000000..706955dfa76a33492164a1cbe974084c797e12e2
--- /dev/null
+++ b/partners/migrations/0034_auto_20171003_2109.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-03 19:09
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0033_auto_20171003_2058'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='petition',
+            name='signatories',
+            field=models.ManyToManyField(blank=True, related_name='petitions', to='scipost.Contributor'),
+        ),
+    ]
diff --git a/partners/migrations/0035_merge_20171004_0848.py b/partners/migrations/0035_merge_20171004_0848.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef6aa5fd82ce08519fde22d085e1e76014231cab
--- /dev/null
+++ b/partners/migrations/0035_merge_20171004_0848.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-04 06:48
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0033_auto_20171003_1512'),
+        ('partners', '0034_auto_20171003_2109'),
+    ]
+
+    operations = [
+    ]
diff --git a/partners/migrations/0036_auto_20171004_2014.py b/partners/migrations/0036_auto_20171004_2014.py
new file mode 100644
index 0000000000000000000000000000000000000000..256d70d53b58b1923693cb7a5db4a7f3e25772d9
--- /dev/null
+++ b/partners/migrations/0036_auto_20171004_2014.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-04 18:14
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0035_merge_20171004_0848'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='petition',
+            name='signatories',
+        ),
+        migrations.DeleteModel(
+            name='Petition',
+        ),
+    ]
diff --git a/partners/models.py b/partners/models.py
index ff95d07990170d04286329cc4d70a0c625203715..6f8df9d06a6d512633687bedf36bab611b32d1b7 100644
--- a/partners/models.py
+++ b/partners/models.py
@@ -33,6 +33,7 @@ from scipost.fields import ChoiceArrayField
 from scipost.models import get_sentinel_user, Contributor
 
 
+
 ########################
 # Prospective Partners #
 ########################
diff --git a/partners/templates/partners/supporting_partners.html b/partners/templates/partners/supporting_partners.html
index 92d8bcb60a2d8c2176d65473ca3e0dd2638a497b..ab674d7406cb54b6be493d2c8a4ca15829f7a515 100644
--- a/partners/templates/partners/supporting_partners.html
+++ b/partners/templates/partners/supporting_partners.html
@@ -17,6 +17,29 @@
     </div>
 </div>
 
+<div class="row">
+  <div class="col-12">
+    <ul>
+      <li>
+	<p>
+	  <strong>Are you a scientist?</strong><br/>
+	  Is your institution or funding body not listed below as one of our current Supporting Partners?<br/>
+	  Please petition them to join by sending a librarian/director/... a personalized email, starting from this <a href="mailto:?subject=Petition to support SciPost&body={% autoescape on %}{% include 'petitions/petition_email.html' %}{% endautoescape %}&cc=partners@scipost.org">template</a>.<br/>
+	  You can also encourage them to join by <a href="{% url 'petitions:petition' slug='join-SPB' %}">signing our petition</a>.
+	</p>
+      </li>
+      <li>
+	<p>
+	  <strong>Are you a librarian, funding agency representative or other potential supporter?</strong><br/>
+	  Have a quick look at our <a href="{% static 'scipost/SPB/SciPost_Supporting_Partners_Board_Prospectus.pdf' %}">one-page Prospectus</a>.<br/>
+	  Read detailed information in the draft <a href="{% static 'scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf' %}">Partner Agreement</a>, and <a href="mailto:partners@scipost.org">contact us</a> to get further details.
+	</p>
+      </li>
+    </ul>
+  </div>
+</div>
+
+
 <div class="row">
   <div class="col-12">
       <h3>Openness at strictly minimal cost: the role of professional academics</h3>
diff --git a/petitions/__init__.py b/petitions/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/petitions/admin.py b/petitions/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dfc9a12a52b0499c25a0d85f0fc1b588520ed48
--- /dev/null
+++ b/petitions/admin.py
@@ -0,0 +1,17 @@
+from django.contrib import admin
+
+from .models import Petition, PetitionSignatory
+
+
+class PetitionAdmin(admin.ModelAdmin):
+    prepopulated_fields = {'slug': ('title',)}
+
+
+admin.site.register(Petition, PetitionAdmin)
+
+
+class PetitionSignatoryAdmin(admin.ModelAdmin):
+    search_fields = ['last_name', 'country', 'institution']
+
+
+admin.site.register(PetitionSignatory, PetitionSignatoryAdmin)
diff --git a/petitions/apps.py b/petitions/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..aef02c09b06a79f8743cfb740c788fecf73c1d01
--- /dev/null
+++ b/petitions/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class PetitionsConfig(AppConfig):
+    name = 'petitions'
diff --git a/petitions/forms.py b/petitions/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ce10ab92e4cb0b333aa3e6256de8d2a66db7d33
--- /dev/null
+++ b/petitions/forms.py
@@ -0,0 +1,10 @@
+from django import forms
+
+from .models import PetitionSignatory
+
+
+class SignPetitionForm(forms.ModelForm):
+    class Meta:
+        model = PetitionSignatory
+        fields = ['title', 'first_name', 'last_name',
+                  'email', 'country_of_employment', 'affiliation']
diff --git a/petitions/managers.py b/petitions/managers.py
new file mode 100644
index 0000000000000000000000000000000000000000..e21badb0810bd0be41432dc5b49eabdd007f4704
--- /dev/null
+++ b/petitions/managers.py
@@ -0,0 +1,9 @@
+from django.db import models
+
+
+class PetitionSignatoryQuerySet(models.QuerySet):
+    def verified(self):
+        return self.filter(verified=True)
+
+    def unverified(self):
+        return self.filter(verified=False)
diff --git a/petitions/migrations/0001_initial.py b/petitions/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..801a4fb65d08f94ce7495cf87502cf1a53e63993
--- /dev/null
+++ b/petitions/migrations/0001_initial.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-04 18:14
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django_countries.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('scipost', '0065_authorshipclaim_publication'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Petition',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=256)),
+                ('slug', models.SlugField()),
+                ('headline', models.CharField(max_length=256)),
+                ('statement', models.TextField()),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='PetitionSignatory',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(choices=[('PR', 'Prof.'), ('DR', 'Dr'), ('MR', 'Mr'), ('MRS', 'Mrs'), ('MS', 'Ms')], max_length=4)),
+                ('first_name', models.CharField(max_length=128)),
+                ('last_name', models.CharField(max_length=128)),
+                ('email', models.EmailField(max_length=254)),
+                ('country_of_employment', django_countries.fields.CountryField(max_length=2)),
+                ('affiliation', models.CharField(max_length=300, verbose_name='affiliation')),
+                ('signed_on', models.DateTimeField(auto_now_add=True)),
+                ('verification_key', models.CharField(blank=True, max_length=40)),
+                ('verified', models.BooleanField(default=False)),
+                ('petition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='petitions.Petition')),
+                ('signatory', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor')),
+            ],
+        ),
+    ]
diff --git a/petitions/migrations/0002_auto_20171004_2107.py b/petitions/migrations/0002_auto_20171004_2107.py
new file mode 100644
index 0000000000000000000000000000000000000000..6803454eae6f9ad46aadf5f5a4582fae31b4e262
--- /dev/null
+++ b/petitions/migrations/0002_auto_20171004_2107.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-04 19:07
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('petitions', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='petitionsignatory',
+            options={'verbose_name_plural': 'petition signatories'},
+        ),
+    ]
diff --git a/petitions/migrations/0003_auto_20171004_2139.py b/petitions/migrations/0003_auto_20171004_2139.py
new file mode 100644
index 0000000000000000000000000000000000000000..47b2fe956c0698ca553a356ad8b4f15ebfe40a62
--- /dev/null
+++ b/petitions/migrations/0003_auto_20171004_2139.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-04 19:39
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('petitions', '0002_auto_20171004_2107'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='petitionsignatory',
+            options={'ordering': ['last_name', 'country_of_employment', 'affiliation'], 'verbose_name_plural': 'petition signatories'},
+        ),
+        migrations.AlterField(
+            model_name='petitionsignatory',
+            name='petition',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='petition_signatories', to='petitions.Petition'),
+        ),
+        migrations.AlterField(
+            model_name='petitionsignatory',
+            name='signatory',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='petition_signatories', to='scipost.Contributor'),
+        ),
+    ]
diff --git a/petitions/migrations/0004_petition_preamble.py b/petitions/migrations/0004_petition_preamble.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f6007370c4e5051f55ec4ea5fd424624ae02a0b
--- /dev/null
+++ b/petitions/migrations/0004_petition_preamble.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-05 10:53
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('petitions', '0003_auto_20171004_2139'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='petition',
+            name='preamble',
+            field=models.TextField(blank=True, null=True),
+        ),
+    ]
diff --git a/petitions/migrations/__init__.py b/petitions/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/petitions/models.py b/petitions/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..fec56dbd666f2f6bfc7dfec414c436132da14f03
--- /dev/null
+++ b/petitions/models.py
@@ -0,0 +1,51 @@
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.db import models
+
+from django_countries.fields import CountryField
+
+from .managers import PetitionSignatoryQuerySet
+
+from scipost.constants import TITLE_CHOICES
+
+
+class Petition(models.Model):
+    title = models.CharField(max_length=256)
+    slug = models.SlugField()
+    headline = models.CharField(max_length=256)
+    preamble = models.TextField(blank=True, null=True)
+    statement = models.TextField()
+    creator = models.ForeignKey(User, on_delete=models.CASCADE)
+    created = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return self.title
+
+    def get_absolute_url(self):
+        return reverse('petitions:petition', kwargs={'slug': self.slug})
+
+
+class PetitionSignatory(models.Model):
+    petition = models.ForeignKey('petitions.Petition', on_delete=models.CASCADE)
+    signatory = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
+                                  blank=True, null=True)
+    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
+    first_name = models.CharField(max_length=128)
+    last_name = models.CharField(max_length=128)
+    email = models.EmailField()
+    country_of_employment = CountryField()
+    affiliation = models.CharField(max_length=300, verbose_name='affiliation')
+    signed_on = models.DateTimeField(auto_now_add=True)
+    verification_key = models.CharField(max_length=40, blank=True)
+    verified = models.BooleanField(default=False)
+
+    objects = PetitionSignatoryQuerySet.as_manager()
+
+    class Meta:
+        default_related_name = 'petition_signatories'
+        ordering = ['last_name', 'country_of_employment', 'affiliation']
+        verbose_name_plural = 'petition signatories'
+
+    def __str__(self):
+        return '%s, %s %s (%s)' % (self.last_name, self.get_title_display(),
+                                   self.first_name, self.petition.slug)
diff --git a/petitions/templates/petitions/petition.html b/petitions/templates/petitions/petition.html
new file mode 100644
index 0000000000000000000000000000000000000000..f04d85ff3325a5f130fc0a374740ddf32f424e41
--- /dev/null
+++ b/petitions/templates/petitions/petition.html
@@ -0,0 +1,53 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: Petition{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-12">
+    <h1 class="highlight">{{ petition.headline }}</h1>
+    {% if petition.preamble %}
+    <h3>Preamble:</h3>
+    <p>{{ petition.preamble|safe|escape|linebreaksbr }}</p>
+    {% endif %}
+    <h3>Petition statement:</h3>
+    <p>{{ petition.statement|safe|escape|linebreaksbr }}</p>
+      {% if not is_signed %}
+      <h3>Please consider signing this petition</h3>
+      <form action="{{ petition.get_absolute_url }}" method="post">
+        {% csrf_token %}
+        {{ form|bootstrap:'3,9' }}
+        <input class="btn btn-primary" type="submit" value="Sign this petition" />
+      </form>
+      {% else %}
+      <h3>Thank you for signing this petition!</h3>
+      <p>Please invite your colleagues to also sign.</p>
+      {% endif %}
+
+    </div>
+</div>
+
+<div class="row">
+  <div class="col-12">
+      <h2>Signatories</h2>
+      <table class="table">
+    	{% for signatory in petition.petition_signatories.verified %}
+        	<tr>
+        	  <td>{{ signatory.last_name }}</td>
+        	  <td>{{ signatory.get_title_display }}</td>
+		  <td>{{ signatory.first_name }}</td>
+        	  <td>{{ signatory.affiliation }}</td>
+        	  <td>{{ signatory.get_country_of_employment_display }}</td>
+        	</tr>
+    	{% empty %}
+        	<tr><td>No signatories found</td></tr>
+    	{% endfor %}
+      </table>
+  </div>
+</div>
+
+
+{% endblock content %}
diff --git a/petitions/templates/petitions/petition_email.html b/petitions/templates/petitions/petition_email.html
new file mode 100644
index 0000000000000000000000000000000000000000..eb4b4543014eccc45b54335eab833b576e94fbfb
--- /dev/null
+++ b/petitions/templates/petitions/petition_email.html
@@ -0,0 +1,18 @@
+[PLEASE FILL IN THE TO FIELD ABOVE (keeping partners@scipost.org in cc)]%0D%0A
+%0D%0A
+Dear ...%0D%0A
+%0D%0A
+[PLEASE WRITE A PERSONALIZED MESSAGE]%0D%0A
+%0D%0A
+Here under, you will find basic information about SciPost and how you can support it.%0D%0A
+%0D%0A
+Sincerely,%0D%0A
+[YOUR SIGNATURE]%0D%0A
+%0D%0A
+%0D%0A
+%0D%0A
+SciPost (https://scipost.org) is a top-quality next-generation Open Access publication portal managed by professional scientists. Its principles, ideals and implementation can be found at https://scipost.org/about and https://scipost.org/FAQ.%0D%0A
+%0D%0A
+SciPost follows a different funding model than most traditional publishers. It operates on an entirely not-for-profit basis, and charges neither subscription fees nor article processing charges; instead, its activities are financed through a cost-slashing consortial model.%0D%0A
+%0D%0A
+By making a small financial commitment, the institutions and organizations that benefit from SciPost’s activities can become Supporting Partners. This enables SciPost to perform all of its publication-related activities, maintain its online portal and implement its long-term development plan. Details of the consortial funding scheme and how to join can be found at https://scipost.org/partners or by emailing partners@scipost.org.%0D%0A
diff --git a/petitions/tests.py b/petitions/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/petitions/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/petitions/urls.py b/petitions/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..7175ce752a716d15049fcfbcbb7608bd0f510601
--- /dev/null
+++ b/petitions/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+    url(r'^(?P<slug>[-\w]+)/verify_signature/(?P<key>.+)$',
+        views.verify_signature, name='verify_signature'),
+    url(r'^(?P<slug>[-\w]+)$', views.petition, name='petition'),
+]
diff --git a/petitions/views.py b/petitions/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a7a9c7026f058617eeae1f072abbf47104d8cdf
--- /dev/null
+++ b/petitions/views.py
@@ -0,0 +1,97 @@
+import hashlib
+import random
+import string
+
+from django.contrib import messages
+from django.core.mail import EmailMultiAlternatives
+from django.shortcuts import get_object_or_404, render, redirect
+from django.template import Context, Template
+
+from .models import Petition, PetitionSignatory
+from .forms import SignPetitionForm
+
+
+def petition(request, slug):
+    petition = get_object_or_404(Petition, slug=slug)
+
+    is_signed = False
+    initial = {}
+    if request.user.is_authenticated:
+        is_signed = petition.petition_signatories.verified().filter(
+            signatory=request.user.contributor).exists()
+        initial = {
+            'title': request.user.contributor.title,
+            'first_name': request.user.first_name,
+            'last_name': request.user.last_name,
+            'email': request.user.email,
+            'country_of_employment': request.user.contributor.country_of_employment,
+            'affiliation': request.user.contributor.affiliation,
+        }
+
+    form = SignPetitionForm(request.POST or None, initial=initial)
+    if form.is_valid():
+        signature = form.save(commit=False)
+        signature.petition = petition
+        message = ('<h3>Many thanks for signing!</h3>'
+                   '<p>Please invite your colleagues to also sign.</p>')
+        if request.user.is_authenticated:
+            signature.signatory = request.user.contributor
+            signature.verified = True
+        else:
+            # Generate verification key and link
+            salt = ""
+            for i in range(5):
+                salt = salt + random.choice(string.ascii_letters)
+            salt = salt.encode('utf8')
+            verificationsalt = form.cleaned_data['last_name']
+            verificationsalt = verificationsalt.encode('utf8')
+            verification_key = hashlib.sha1(salt+verificationsalt).hexdigest()
+            signature.verification_key = verification_key
+            message += '\n<p>You will receive an email with a verification link.</p>'
+            email_text = ('Please click on the link below to confirm '
+                          'your signature of the petition: \n'
+                          'https://scipost.org/petitions/'
+                          + petition.slug + '/verify_signature/' + verification_key + '.\n')
+            email_text_html = (
+                '<p>Please click on the link below to confirm '
+                'your signature of the petition:</p>'
+                '<p><a href="https://scipost.org/petitions/{{ slug }}/verify_signature'
+                '/{{ key }}">confirm your signature</a></p>')
+            html_template = Template(email_text_html)
+            html_version = html_template.render(Context({'slug': petition.slug,
+                                                         'key': verification_key}))
+            emailmessage = EmailMultiAlternatives(
+                'Petition signature verification',
+                email_text,
+                'SciPost petitions<petitions@scipost.org>',
+                [form.cleaned_data['email']],
+                bcc=['petitions@scipost.org'])
+            emailmessage.attach_alternative(html_version, 'text/html')
+            emailmessage.send()
+        signature.save()
+        messages.success(request, message)
+        return redirect(petition.get_absolute_url())
+
+    context = {
+        'petition': petition,
+        'is_signed': is_signed,
+        'form': form,
+    }
+    return render(request, 'petitions/petition.html', context)
+
+
+def verify_signature(request, slug, key):
+    petition = get_object_or_404(Petition, slug=slug)
+    try:
+        signature = petition.petition_signatories.get(verification_key=key)
+    except PetitionSignatory.DoesNotExist:
+        messages.warning(request, ('Unknown signature key.'))
+        return redirect(petition.get_absolute_url())
+
+    if not signature.verified:
+        # Slight reduction of db write-use
+        signature.verified = True
+        signature.save()
+    messages.success(request, ('<h3>Many thanks for confirming your signature.</h3>'
+                               '<p>Please invite your colleagues to also sign.</p>'))
+    return redirect(petition.get_absolute_url())
diff --git a/production/signals.py b/production/signals.py
index 2d17c3a331cd97ce5d2547cd909b5188a472ee6e..31327d531302210210e7c4d53557592a7305a655 100644
--- a/production/signals.py
+++ b/production/signals.py
@@ -46,9 +46,8 @@ def notify_stream_completed(sender, instance, **kwargs):
     """
     Notify the production team about a Production Stream being completed.
     """
-    stream = instance.stream
-    notify.send(sender=sender, recipient=stream.officer.user,
+    notify.send(sender=sender, recipient=instance.officer.user,
                 actor=sender, verb=' marked Production Stream as completed.', target=instance)
 
-    notify.send(sender=sender, recipient=stream.supervisor.user,
+    notify.send(sender=sender, recipient=instance.supervisor.user,
                 actor=sender, verb=' marked Production Stream as completed.', target=instance)
diff --git a/production/views.py b/production/views.py
index 9c86ca0c9f0473030373e175da29151e893829a6..64b7d4eb282aadac5ca3586c880efeecbc89afa1 100644
--- a/production/views.py
+++ b/production/views.py
@@ -217,6 +217,7 @@ class DeleteEventView(DeleteView):
 
 @is_production_user()
 @permission_required('scipost.can_publish_accepted_submission', raise_exception=True)
+@transaction.atomic
 def mark_as_completed(request, stream_id):
     stream = get_object_or_404(ProductionStream.objects.ongoing(), pk=stream_id)
     stream.status = PRODUCTION_STREAM_COMPLETED
diff --git a/scipost/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf b/scipost/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf
index db013469658f2b128320a15750ff5ef25d2f71b3..9ceeb075f17d3d3297638dbc93eff559d7fdb1c5 100644
Binary files a/scipost/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf and b/scipost/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf differ
diff --git a/scipost/static/scipost/SPB/SciPost_Supporting_Partners_Board_Prospectus.pdf b/scipost/static/scipost/SPB/SciPost_Supporting_Partners_Board_Prospectus.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a601150698bfcb5a3d4070d6176752e451f3c1fe
Binary files /dev/null and b/scipost/static/scipost/SPB/SciPost_Supporting_Partners_Board_Prospectus.pdf differ
diff --git a/scipost/static/scipost/assets/js/notifications.js b/scipost/static/scipost/assets/js/notifications.js
index 6f85c449bd19b4f917a6aee7affc17bdf1c267fc..5900e9eddb03d2e24297916bd10a3a260402d966 100644
--- a/scipost/static/scipost/assets/js/notifications.js
+++ b/scipost/static/scipost/assets/js/notifications.js
@@ -5,7 +5,7 @@ var notify_api_url_list = "/notifications/api/list/";
 var notify_api_url_toggle_read = "/notifications/mark-toggle/";
 var notify_api_url_mark_all_read = "/notifications/mark-all-as-read/";
 var notify_fetch_count = "5";
-var notify_refresh_period = 15000;
+var notify_refresh_period = 60000;
 var consecutive_misfires = 0;
 var registered_functions = [fill_notification_badge];
 
@@ -91,7 +91,7 @@ function fill_notification_badge(data) {
 }
 
 function get_notification_list() {
-    var data = fetch_api_data(notify_api_url_list, function(data) {
+    fetch_api_data(notify_api_url_list, true, function(data) {
 
         var messages = data.list.map(function (item) {
             var message = '';
@@ -128,10 +128,13 @@ function get_notification_list() {
     });
 }
 
-function fetch_api_data(url, callback) {
+function fetch_api_data(url, once, callback) {
     if (!url) {
         var url = notify_api_url_count;
     }
+    if (!once) {
+        var once = false;
+    }
 
     if (registered_functions.length > 0) {
         //only fetch data if a function is setup
@@ -153,17 +156,28 @@ function fetch_api_data(url, callback) {
         r.open("GET", url + '?max=' + notify_fetch_count, true);
         r.send();
     }
-    if (consecutive_misfires < 10) {
-        setTimeout(fetch_api_data,notify_refresh_period);
-    } else {
-        var badges = document.getElementsByClassName(notify_badge_class);
-        if (badges) {
-            for (var i=0; i < badges.length; i++){
-                badges[i].innerHTML = "!";
-                badges[i].title = "Connection lost!"
+    var timer = null;
+    if (!once) {
+        if (consecutive_misfires < 10 && !once) {
+            timer = setTimeout(fetch_api_data, notify_refresh_period);
+        } else {
+            var badges = document.getElementsByClassName(notify_badge_class);
+            if (badges) {
+                for (var i=0; i < badges.length; i++){
+                    badges[i].innerHTML = "!";
+                    badges[i].title = "Connection lost!"
+                }
             }
         }
     }
+
+    return stop;
+    function stop() {
+        if (timer) {
+            clearTimeout(timer);
+            timer = 0;
+        }
+    }
 }
 
 setTimeout(fetch_api_data, 1000);
diff --git a/scipost/templates/scipost/index.html b/scipost/templates/scipost/index.html
index f3ac59a3f7e27a01cabc8d02b0552bba89113761..c5cc4d3b4c455618213d1fcdd9b1ac2e854e049e 100644
--- a/scipost/templates/scipost/index.html
+++ b/scipost/templates/scipost/index.html
@@ -120,11 +120,14 @@
 
             <div>
                 <p>
-                    SciPost guarantees free online access to all publications in all its Journals and does not charge any article processing fees for publishing. Supporting Partners provide operating funds to SciPost.
+                    SciPost guarantees free online access to all publications in all its Journals and does not charge any article processing fees for publishing. Supporting Partners provide operating funds to SciPost through a cost-slashing consortial model.
                 </p>
                 <p>
-                    Institutions: consider joining our <a href="{% url 'partners:partners' %}">Supporting Partners Board</a>. SciPost cannot exist without your support.
+                    Institutions: consider joining our <a href="{% url 'partners:partners' %}">Supporting Partners Board</a>. SciPost cannot exist without your support. Look at our <a href="{% static 'scipost/SPB/SciPost_Supporting_Partners_Board_Prospectus.pdf' %}">one-page Prospectus</a> and at our full <a href="{% static 'scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf' %}">Partner Agreement</a>.
                 </p>
+		<p>
+		  <span style="color: red;">Scientists, please help us out:</span> if it is not listed on our <a href="{% url 'partners:partners' %}">Partners page</a>, please encourage your institution (through a librarian, director, ...) to join by <a href="{% url 'petitions:petition' slug='join-SPB' %}">signing our petition</a>, and by personally emailing them directly using this <a href="mailto:?subject=Petition to support SciPost&body={% autoescape on %}{% include 'petitions/petition_email.html' %}{% endautoescape %}&cc=partners@scipost.org">email template</a>.
+		</p>
             </div>
         </div>
     </div><!-- End Partners -->