diff --git a/scipost/forms.py b/scipost/forms.py
index f50a5f97b69235e452ee3cc925c08c0fa7080609..3dac8846cc2988ef958cb3a4628e3cc47af9d5fd 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -1,3 +1,6 @@
+import string
+import random
+
 from django import forms
 
 from django.contrib.auth.models import User, Group
@@ -29,14 +32,20 @@ reg_ref_dict = dict(REGISTRATION_REFUSAL_CHOICES)
 
 
 class RegistrationForm(forms.Form):
+    """
+    Use this form to process the registration of new accounts.
+    Due to the construction of a separate Contributor from the User,
+    it is difficult to create a 'combined ModelForm'. All fields
+    are thus separately handled here.
+    """
     title = forms.ChoiceField(choices=TITLE_CHOICES, label='* Title')
     first_name = forms.CharField(label='* First name', max_length=100)
     last_name = forms.CharField(label='* Last name', max_length=100)
     email = forms.EmailField(label='* Email address')
-    orcid_id = forms.CharField(
-        label="  ORCID id", max_length=20,
-        widget=forms.TextInput({'placeholder': 'Recommended. Get one at orcid.org'}),
-        required=False)
+    invitation_key = forms.CharField(max_length=40, widget=forms.HiddenInput(), required=False)
+    orcid_id = forms.CharField(label="ORCID id", max_length=20, required=False,
+                               widget=forms.TextInput(
+                                    {'placeholder': 'Recommended. Get one at orcid.org'}))
     discipline = forms.ChoiceField(choices=SCIPOST_DISCIPLINES, label='* Main discipline')
     country_of_employment = LazyTypedChoiceField(
         choices=countries, label='* Country of employment', initial='NL',
@@ -54,9 +63,48 @@ class RegistrationForm(forms.Form):
         required=False)
     username = forms.CharField(label='* Username', max_length=100)
     password = forms.CharField(label='* Password', widget=forms.PasswordInput())
-    password_verif = forms.CharField(label='* Verify pwd', widget=forms.PasswordInput())
-    captcha = ReCaptchaField(attrs={'theme': 'clean'},
-                             label='* Answer this simple maths question:')
+    password_verif = forms.CharField(label='* Verify password', widget=forms.PasswordInput())
+    captcha = ReCaptchaField(attrs={'theme': 'clean'}, label='*Please verify to continue:')
+
+    def clean_password_verif(self):
+        if self.cleaned_data['password'] != self.cleaned_data['password_verif']:
+            self.add_error('password', 'Your passwords must match')
+            self.add_error('password_verif', 'Your passwords must match')
+
+    def clean_username(self):
+        if User.objects.filter(username=self.cleaned_data['username']).exists():
+            self.add_error('username', 'This username is already in use')
+        return self.cleaned_data.get('username', '')
+
+    def clean_email(self):
+        if User.objects.filter(email=self.cleaned_data['email']).exists():
+            self.add_error('email', 'This email address is already in use')
+        return self.cleaned_data.get('email', '')
+
+    def create_and_save_contributor(self, invitation_key=''):
+        user = User.objects.create_user(**{
+            'first_name': self.cleaned_data['first_name'],
+            'last_name': self.cleaned_data['last_name'],
+            'email': self.cleaned_data['email'],
+            'username': self.cleaned_data['username'],
+            'password': self.cleaned_data['password'],
+            'is_active': False
+        })
+        contributor, new = Contributor.objects.get_or_create(**{
+            'user': user,
+            'invitation_key': invitation_key,
+            'title': self.cleaned_data['title'],
+            'orcid_id': self.cleaned_data['orcid_id'],
+            'country_of_employment': self.cleaned_data['country_of_employment'],
+            'address': self.cleaned_data['address'],
+            'affiliation': self.cleaned_data['affiliation'],
+            'personalwebpage': self.cleaned_data['personalwebpage'],
+        })
+
+        if contributor.invitation_key == '':
+            contributor.generate_key()
+        contributor.save()
+        return contributor
 
 
 class DraftInvitationForm(forms.ModelForm):
diff --git a/scipost/migrations/0050_auto_20170416_2152.py b/scipost/migrations/0050_auto_20170416_2152.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2eb21d5cd61d7582c9d0af0d8a604a5a89420f6
--- /dev/null
+++ b/scipost/migrations/0050_auto_20170416_2152.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-04-16 19:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0049_editorialcollegefellowship_affiliation'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='contributor',
+            name='activation_key',
+            field=models.CharField(blank=True, max_length=40),
+        ),
+        migrations.AlterField(
+            model_name='contributor',
+            name='invitation_key',
+            field=models.CharField(blank=True, default='', max_length=40),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='registrationinvitation',
+            name='cited_in_submission',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='registration_invitations', to='submissions.Submission'),
+        ),
+        migrations.AlterField(
+            model_name='registrationinvitation',
+            name='invitation_key',
+            field=models.CharField(max_length=40, unique=True),
+        ),
+        migrations.AlterField(
+            model_name='registrationinvitation',
+            name='personal_message',
+            field=models.TextField(blank=True, default=''),
+            preserve_default=False,
+        ),
+    ]
diff --git a/scipost/models.py b/scipost/models.py
index 397181addf5bc8c1015706fc8855fa06f22cad00..ceb2aa42f7646fed59d693fb2d30847429bafc88 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -1,4 +1,7 @@
 import datetime
+import hashlib
+import random
+import string
 
 from django.contrib.auth.models import User
 from django.contrib.postgres.fields import ArrayField
@@ -37,10 +40,9 @@ class Contributor(models.Model):
     Permissions determine the sub-types.
     username, password, email, first_name and last_name are inherited from User.
     """
-    user = models.OneToOneField(User, on_delete=models.CASCADE)
-    invitation_key = models.CharField(max_length=40, default='',
-                                      blank=True, null=True)
-    activation_key = models.CharField(max_length=40, default='')
+    user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
+    invitation_key = models.CharField(max_length=40, blank=True)
+    activation_key = models.CharField(max_length=40, blank=True)
     key_expires = models.DateTimeField(default=timezone.now)
     status = models.SmallIntegerField(default=0, choices=CONTRIBUTOR_STATUS)
     title = models.CharField(max_length=4, choices=TITLE_CHOICES)
@@ -74,6 +76,15 @@ class Contributor(models.Model):
         # Please use get_title_display(). To be removed in future
         return self.get_title_display()
 
+    def is_SP_Admin(self):
+        return self.user.groups.filter(name='SciPost Administrators').exists()
+
+    def is_MEC(self):
+        return self.user.groups.filter(name='Editorial College').exists()
+
+    def is_VE(self):
+        return self.user.groups.filter(name='Vetting Editors').exists()
+
     def is_currently_available(self):
         unav_periods = UnavailabilityPeriod.objects.filter(contributor=self)
 
@@ -83,6 +94,18 @@ class Contributor(models.Model):
                 return False
         return True
 
+    def generate_key(self, feed=''):
+        """
+        Generate and save a new activation_key for the contributor, given a certain feed.
+        """
+        for i in range(5):
+            feed += random.choice(string.ascii_letters)
+        feed = feed.encode('utf8')
+        salt = self.user.username.encode('utf8')
+        self.activation_key = hashlib.sha1(salt+salt).hexdigest()
+        self.key_expires = datetime.datetime.now() + datetime.timedelta(days=2)
+        self.save()
+
     def private_info_as_table(self):
         template = Template('''
             <table>
@@ -289,8 +312,8 @@ class RegistrationInvitation(models.Model):
                                              blank=True, null=True)
     message_style = models.CharField(max_length=1, choices=INVITATION_STYLE,
                                      default=INVITATION_FORMAL)
-    personal_message = models.TextField(blank=True, null=True)
-    invitation_key = models.CharField(max_length=40, default='')
+    personal_message = models.TextField(blank=True)
+    invitation_key = models.CharField(max_length=40, unique=True)
     key_expires = models.DateTimeField(default=timezone.now)
     date_sent = models.DateTimeField(default=timezone.now)
     invited_by = models.ForeignKey(Contributor,
@@ -301,7 +324,6 @@ class RegistrationInvitation(models.Model):
     responded = models.BooleanField(default=False)
     declined = models.BooleanField(default=False)
 
-
     def __str__(self):
         return (self.first_name + ' ' + self.last_name
                 + ' on ' + self.date_sent.strftime("%Y-%m-%d"))
diff --git a/scipost/templates/scipost/accept_invitation_error.html b/scipost/templates/scipost/accept_invitation_error.html
index f1ab6a7fdba1bfc0ef6dcc27000a942774917a27..e9098a92acbc102891c2391b8e425cbac60d2b0c 100644
--- a/scipost/templates/scipost/accept_invitation_error.html
+++ b/scipost/templates/scipost/accept_invitation_error.html
@@ -2,16 +2,17 @@
 
 {% block pagetitle %}: accept invitation: error{% endblock pagetitle %}
 
-{% block bodysup %}
+{% block content %}
 
-<section>
-  <h1>Registration Invitation: error</h1>
+<div class="row">
+    <div class="col-12">
+      <h1>Registration Invitation</h1>
 
-  <p>Error message: {{ errormessage }}</p>
+  <p>Error message: <span class="text-danger">{{ errormessage }}</span></p>
 
   <p>You can in any case simply fill the <a href="{% url 'scipost:register' %}">
       registration form</a> to get access to the site's facilities.</p>
+    </div>
+</div>
 
-</section>
-
-{% endblock bodysup %}
+{% endblock content %}
diff --git a/scipost/templates/scipost/acknowledgement.html b/scipost/templates/scipost/acknowledgement.html
index eda6a45b789071e2b11523612dacac6358c75678..4749b08daf08778e9038de656467b00239ef98d0 100644
--- a/scipost/templates/scipost/acknowledgement.html
+++ b/scipost/templates/scipost/acknowledgement.html
@@ -2,16 +2,18 @@
 
 {% block pagetitle %}: acknowledgement {% endblock pagetitle %}
 
-{% block bodysup %}
+{% block content %}
 
-<section>
-  <h3>{{ ack_header }}</h3>
-  {% if ack_message %}
-    <p>{{ ack_message }}</p>
-  {% endif %}
-  {% if followup_message %}
-    <p>{{ followup_message }} <a href="{{ followup_link }}">{{ followup_link_label }}</a>.</p>
-  {% endif %}
-</section>
+<div class="row">
+    <div class="col-12">
+      <h2>{{ ack_header }}</h2>
+      {% if ack_message %}
+        <p>{{ ack_message }}</p>
+      {% endif %}
+      {% if followup_message %}
+        <p>{{ followup_message }} <a href="{{ followup_link }}">{{ followup_link_label }}</a>.</p>
+      {% endif %}
+    </div>
+</div>
 
-{% endblock bodysup %}
+{% endblock %}
diff --git a/scipost/templates/scipost/register.html b/scipost/templates/scipost/register.html
index 707c560f9955346fa3129b473d28e529bd43e482..bc4f4e0b9dea28bb24b570520728ab4dc42e1a96 100644
--- a/scipost/templates/scipost/register.html
+++ b/scipost/templates/scipost/register.html
@@ -10,8 +10,8 @@
     <div class="col-12">
         <div class="panel">
             <h1>Register to SciPost</h1>
-            {% if welcome_message %}
-            <h2>{{ welcome_message }}</h2>
+            {% if invitation %}
+                <h2>Welcome {{invitation.get_title_display}} {{invitation.last_name}} and thanks in advance for registering (by completing this form)</h2>
             {% endif %}
         </div>
     </div>
@@ -28,22 +28,14 @@
     </div>
     <div class="offset-md-1 col-md-7">
 
-      {% if invited %}
-          <form action="{% url 'scipost:invitation' key=key %}" method="post">
-            	{% csrf_token %}
-        	    {{ form|bootstrap }}
-        	<input class="btn btn-primary" type="submit" value="Submit" />
-          </form>
-      {% else %}
-          <form action="{% url 'scipost:register' %}" method="post">
-            {% csrf_token %}
-            {{ form|bootstrap }}
-            <input class="btn btn-primary" type="submit" value="Submit" />
-          </form>
-      {% endif %}
+      <form action="{% url 'scipost:register' %}" method="post">
+        {% csrf_token %}
+        {{ form|bootstrap }}
+        <input class="btn btn-primary" type="submit" value="Submit" />
+      </form>
 
       {% if errormessage %}
-          <p style="color:red;">{{ errormessage }}</p>
+          <p class="text-danger">{{ errormessage }}</p>
       {% endif %}
     </div>
 </div>
diff --git a/scipost/templates/scipost/request_new_activation_link.html b/scipost/templates/scipost/request_new_activation_link.html
index d17db11c8e44c45c80f553477a3c90481d03361e..7111b6dae7fc4101a3d97397ef860962ba901391 100644
--- a/scipost/templates/scipost/request_new_activation_link.html
+++ b/scipost/templates/scipost/request_new_activation_link.html
@@ -2,11 +2,13 @@
 
 {% block pagetitle %}: request new activation link{% endblock pagetitle %}
 
-{% block bodysup %}
+{% block content %}
 
-<section>
-  <h1>Request a new activation link</h1>
-  <p>Your previous activation link has expired. <a href="{% url 'scipost:request_new_activation_link' oldkey=oldkey %}">Click here</a> to have us email you a new one.</p>
-</section>
+<div class="row">
+    <div class="col-12">
+        <h2>Request a new activation link</h2>
+        <p>Your previous activation link has expired. <a href="{% url 'scipost:request_new_activation_link' contributor.id contributor.activation_key %}?confirm=1">Click here</a> to have us email you a new one.</p>
+    </div>
+</div>
 
-{% endblock bodysup %}
+{% endblock content %}
diff --git a/scipost/templates/scipost/unsubscribe.html b/scipost/templates/scipost/unsubscribe.html
index c32b358b0aae78a5d9510678719531e29e479708..39752b684e1d468f2232182050994f1ccc44ae1b 100644
--- a/scipost/templates/scipost/unsubscribe.html
+++ b/scipost/templates/scipost/unsubscribe.html
@@ -2,13 +2,14 @@
 
 {% block pagetitle %}: Unsubscribe{% endblock pagetitle %}
 
-{% block bodysup %}
-<section>
-  <h3>Unsubscribe</h3>
-  <p>To let us know that you do not want to receive any non-essential email
-    from SciPost (citation notifications, announcements etc),
-    <a href="{% url 'scipost:unsubscribe_confirm' key=contributor.activation_key %}">click here</a>.
-  </p>
-  <p>You can reset this preference at any time from your <a href="{% url 'scipost:personal_page' %}">personal page</a>.</p>
-</section>
-{% endblock bodysup %}
+{% block content %}
+<div class="row">
+    <div class="col-12">
+        <h2>Unsubscribe</h2>
+        <p>
+            <a href="{% url 'scipost:unsubscribe' contributor.id contributor.activation_key %}?confirm=1">Click here</a> to let us know that you do not want to receive any non-essential email from SciPost (citation notifications, announcements etc).
+        </p>
+        <p>You can reset this preference at any time from your <a href="{% url 'scipost:personal_page' %}">personal page</a>.</p>
+    </div>
+</div>
+{% endblock content %}
diff --git a/scipost/urls.py b/scipost/urls.py
index 03e228e4d5e87652999e203e366aaeb2aa9ca8db..7e2664d51028fab3c4f19b581bbca44ee2a74d57 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -13,19 +13,19 @@ JOURNAL_REGEX = '(?P<doi_string>%s)' % REGEX_CHOICES
 
 urlpatterns = [
     url(r'^$', views.index, name='index'),
-    url(r'^base$', views.base, name='base'),
 
     # General use pages
     url(r'^error$', TemplateView.as_view(template_name='scipost/error.html'), name='error'),
     url(r'^acknowledgement$', TemplateView.as_view(template_name='scipost/acknowledgement.html'),
         name='acknowledgement'),
 
-    ## Info
+    # Info
     url(r'^about$', views.AboutView.as_view(), name='about'),
     url(r'^call$', TemplateView.as_view(template_name='scipost/call.html'), name='call'),
     url(r'^foundation$', TemplateView.as_view(template_name='scipost/foundation.html'),
         name='foundation'),
-    url(r'^tour$', TemplateView.as_view(template_name='scipost/quick_tour.html'), name='quick_tour'),
+    url(r'^tour$', TemplateView.as_view(template_name='scipost/quick_tour.html'),
+        name='quick_tour'),
     url(r'^FAQ$', TemplateView.as_view(template_name='scipost/FAQ.html'), name='FAQ'),
     url(r'^terms_and_conditions$',
         TemplateView.as_view(template_name='scipost/terms_and_conditions.html'),
@@ -34,8 +34,7 @@ urlpatterns = [
         name='privacy_policy'),
 
     # Feeds
-    url(r'^feeds$', views.feeds, #TemplateView.as_view(template_name='scipost/feeds.html'),
-        name='feeds'),
+    url(r'^feeds$', views.feeds, name='feeds'),
     url(r'^rss/news/$', LatestNewsFeedRSS()),
     url(r'^atom/news/$', LatestNewsFeedAtom()),
     url(r'^rss/comments/$', LatestCommentsFeedRSS()),
@@ -69,21 +68,17 @@ urlpatterns = [
     # Contributors:
     ################
 
-    ## Registration
+    # Registration
     url(r'^register$', views.register, name='register'),
     url(r'^thanks_for_registering$',
         TemplateView.as_view(template_name='scipost/thanks_for_registering.html'),
         name='thanks_for_registering'),
-    url(r'^activation/(?P<key>.+)$', views.activation, name='activation'),
-    url(r'^request_new_activation_link/(?P<oldkey>.+)$',
-        views.request_new_activation_link,
-        name='request_new_activation_link'),
-    url(r'^already_activated$',
-        TemplateView.as_view(template_name='scipost/already_activated.html'),
-        name='already_activated'),
-    url(r'^unsubscribe/(?P<key>.+)$', views.unsubscribe, name='unsubscribe'),
-    url(r'^unsubscribe_confirm/(?P<key>.+)$',
-        views.unsubscribe_confirm, name='unsubscribe_confirm'),
+    url(r'^activation/(?P<contributor_id>[0-9]+)/(?P<key>.+)/$',
+        views.activation, name='activation'),
+    url(r'^activation/(?P<contributor_id>[0-9]+)/(?P<key>.+)/renew$',
+        views.request_new_activation_link, name='request_new_activation_link'),
+    url(r'^unsubscribe/(?P<contributor_id>[0-9]+)/(?P<key>.+)$', views.unsubscribe,
+        name='unsubscribe'),
     url(r'^vet_registration_requests$',
         views.vet_registration_requests, name='vet_registration_requests'),
     url(r'^vet_registration_request_ack/(?P<contributor_id>[0-9]+)$',
@@ -116,11 +111,9 @@ urlpatterns = [
     url(r'^registration_invitation_sent$',
         TemplateView.as_view(template_name='scipost/registration_invitation_sent.html'),
         name='registration_invitation_sent'),
-    #url(r'^invitation/(?P<key>.+)$', views.accept_invitation, name='accept_invitation'),
+
+    # Registration invitations
     url(r'^invitation/(?P<key>.+)$', views.invitation, name='invitation'),
-    url(r'^accept_invitation_error$',
-        TemplateView.as_view(template_name='scipost/accept_invitation_error.html'),
-        name='accept_invitation_error'),
     url(r'^mark_draft_inv_as_processed/(?P<draft_id>[0-9]+)$',
         views.mark_draft_inv_as_processed, name='mark_draft_inv_as_processed'),
     url(r'^citation_notifications$',
@@ -128,7 +121,7 @@ urlpatterns = [
     url(r'^process_citation_notification/(?P<cn_id>[0-9]+)$',
         views.process_citation_notification, name='process_citation_notification'),
 
-    ## Authentication
+    # Authentication
     url(r'^login/$', views.login_view, name='login'),
     url(r'^logout$', views.logout_view, name='logout'),
     url(r'^personal_page$', views.personal_page, name='personal_page'),
@@ -139,7 +132,8 @@ urlpatterns = [
     url(r'^update_personal_data$', views.update_personal_data, name='update_personal_data'),
 
     # Unavailabilities
-    url(r'^mark_unavailable_period$', views.mark_unavailable_period, name='mark_unavailable_period'),
+    url(r'^mark_unavailable_period$', views.mark_unavailable_period,
+        name='mark_unavailable_period'),
 
     # Contributor info
     url(r'^(?P<contributor_id>[0-9]+)$', views.contributor_info, name="contributor_info"),
diff --git a/scipost/utils.py b/scipost/utils.py
index dc4e597bb044ef3cdef80d70537332928661e5fd..f0f3ebcd37e23b5217169ab87e0c1b386b227c41 100644
--- a/scipost/utils.py
+++ b/scipost/utils.py
@@ -3,12 +3,14 @@ import hashlib
 import random
 import string
 
-from django.contrib.auth.models import User
 from django.core.mail import EmailMultiAlternatives
+from django.core.urlresolvers import reverse
 from django.template import Context, Template
 from django.utils import timezone
 
-from .models import Contributor, DraftInvitation, RegistrationInvitation
+from .models import DraftInvitation, RegistrationInvitation
+
+from common.utils import BaseMailUtil
 
 
 SCIPOST_SUMMARY_FOOTER = (
@@ -75,11 +77,9 @@ EMAIL_UNSUBSCRIBE_LINK_HTML = (
 )
 
 
-class Utils(object):
-    @classmethod
-    def load(cls, dict):
-        for var_name in dict:
-            setattr(cls, var_name, dict[var_name])
+class Utils(BaseMailUtil):
+    mail_sender = 'registration@scipost.org'
+    mail_sender_title = 'SciPost registration'
 
     @classmethod
     def password_mismatch(cls):
@@ -117,76 +117,28 @@ class Utils(object):
             return False
 
     @classmethod
-    def create_and_save_contributor(cls, invitation_key):
-        user = User.objects.create_user(
-            first_name=cls.form.cleaned_data['first_name'],
-            last_name=cls.form.cleaned_data['last_name'],
-            email=cls.form.cleaned_data['email'],
-            username=cls.form.cleaned_data['username'],
-            password=cls.form.cleaned_data['password']
-            )
-        # Set to inactive until activation via email link
-        user.is_active = False
-        user.save()
-        contributor = Contributor(
-            user=user,
-            invitation_key=invitation_key,
-            title=cls.form.cleaned_data['title'],
-            orcid_id=cls.form.cleaned_data['orcid_id'],
-            country_of_employment=cls.form.cleaned_data['country_of_employment'],
-            address=cls.form.cleaned_data['address'],
-            affiliation=cls.form.cleaned_data['affiliation'],
-            personalwebpage=cls.form.cleaned_data['personalwebpage'],
-            )
-        contributor.save()
-        Utils.load({'contributor': contributor})
+    def send_registration_email(cls):
+        """
+        Send mail after registration request has been recieved.
+
+        Requires loading:
+        contributor -- Contributor
+        """
+        cls._send_mail(cls, 'registration_request_received',
+                       [cls._context['contributor'].user.email],
+                       'request received')
 
     @classmethod
-    def send_registration_email(cls):
-        # Generate email activation key and link
-        salt = ""
-        for i in range(5):
-            salt = salt + random.choice(string.ascii_letters)
-        salt = salt.encode('utf8')
-        usernamesalt = cls.contributor.user.username
-        usernamesalt = usernamesalt.encode('utf8')
-        cls.contributor.activation_key = hashlib.sha1(salt+usernamesalt).hexdigest()
-        cls.contributor.key_expires = datetime.datetime.strftime(
-            datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
-        cls.contributor.save()
-        email_text = ('Dear ' + cls.contributor.get_title_display() + ' ' +
-                      cls.contributor.user.last_name +
-                      ', \n\nYour request for registration to the SciPost publication portal' +
-                      ' has been received. You now need to validate your email by visiting ' +
-                      'this link within the next 48 hours: \n\n' + 'https://scipost.org/activation/' +
-                      cls.contributor.activation_key +
-                      '\n\nYour registration will thereafter be vetted. Many thanks for your interest.'
-                      '\n\nThe SciPost Team.')
-        email_text_html = (
-            'Dear {{ title }} {{ last_name }},<br/>'
-            '\n<p>Your request for registration to the SciPost publication portal'
-            ' has been received. You now need to validate your email by visiting '
-            'this link within the next 48 hours:</p>'
-            '<p><a href="https://scipost.org/activation/{{ activation_key }}">'
-            'Activate your account</a></p>'
-            '\n<p>Your registration will thereafter be vetted. Many thanks for your interest.</p>'
-            '<p>The SciPost Team.</p>')
-        email_context = Context({
-            'title': cls.contributor.get_title_display(),
-            'last_name': cls.contributor.user.last_name,
-            'activation_key': cls.contributor.activation_key,
-        })
-        email_text_html += '<br/>' + EMAIL_FOOTER
-        html_template = Template(email_text_html)
-        html_version = html_template.render(email_context)
-        emailmessage = EmailMultiAlternatives(
-            'SciPost registration request received', email_text,
-            'SciPost registration <registration@scipost.org>',
-            [cls.contributor.user.email],
-            ['registration@scipost.org'],
-            reply_to=['registration@scipost.org'])
-        emailmessage.attach_alternative(html_version, 'text/html')
-        emailmessage.send(fail_silently=False)
+    def send_new_activation_link_email(cls):
+        """
+        Send mail after a new activation link on a Contributor has been generated.
+
+        Requires loading:
+        contributor -- Contributor
+        """
+        cls._send_mail(cls, 'new_activation_link',
+                       [cls._context['contributor'].user.email],
+                       'new email activation link')
 
     @classmethod
     def create_draft_invitation(cls):
@@ -653,6 +605,9 @@ class Utils(object):
         email_text += ',\n\n'
         email_text_html += ',<br/>'
         if cls.notification.cited_in_publication:
+            url_unsubscribe = reverse('scipost:unsubscribe',
+                                      args=[cls.notification.contributor.id,
+                                            cls.notification.contributor.activation_key])
             email_text += (
                 'We would like to notify you that '
                 'your work has been cited in a paper published by SciPost,'
@@ -662,8 +617,7 @@ class Utils(object):
                 ').\n\nWe hope you will find this paper of interest to your own research.'
                 '\n\nBest regards,\n\nThe SciPost Team'
                 '\n\nDon\'t want to receive such emails? Unsubscribe by visiting '
-                'https://scipost.org/unsubscribe/'
-                + cls.notification.contributor.activation_key + '.')
+                + url_unsubscribe + '.')
             email_text_html += (
                 '<p>We would like to notify you that '
                 'your work has been cited in a paper published by SciPost,</p>'
@@ -674,7 +628,7 @@ class Utils(object):
                 '<p>Best regards,</p><p>The SciPost Team</p><br/>'
                 + EMAIL_FOOTER + '<br/>'
                 '\n<p style="font-size: 10px;">Don\'t want to receive such emails? '
-                '<a href="https://scipost.org/unsubscribe/{{ key }}">Unsubscribe</a>.</p>')
+                '<a href="%s">Unsubscribe</a>.</p>' % url_unsubscribe)
             email_context['pub_title'] = cls.notification.cited_in_publication.title
             email_context['pub_author_list'] = cls.notification.cited_in_publication.author_list
             email_context['doi_label'] = cls.notification.cited_in_publication.doi_string
@@ -683,6 +637,9 @@ class Utils(object):
             html_template = Template(email_text_html)
             html_version = html_template.render(email_context)
         elif cls.notification.cited_in_submission:
+            url_unsubscribe = reverse('scipost:unsubscribe',
+                                      args=[cls.notification.contributor.id,
+                                            cls.notification.contributor.activation_key])
             email_text += (
                 'Your work has been cited in a manuscript submitted to SciPost,'
                 '\n\n' + cls.notification.cited_in_submission.title
@@ -691,8 +648,7 @@ class Utils(object):
                 'commenting on the above submission before the refereeing deadline.\n\n'
                 'Best regards,\n\nThe SciPost Team'
                 '\n\nDon\'t want to receive such emails? Unsubscribe by visiting '
-                'https://scipost.org/unsubscribe/'
-                + cls.notification.contributor.activation_key + '.')
+                + url_unsubscribe + '.')
             email_text_html += (
                 '<p>Your work has been cited in a manuscript submitted to SciPost,</p>'
                 '<p>{{ sub_title }} <br>by {{ sub_author_list }},</p>'
@@ -704,7 +660,7 @@ class Utils(object):
                 '<p>Best regards,</p><p>The SciPost Team</p><br/>'
                 + EMAIL_FOOTER + '<br/>'
                 '\n<p style="font-size: 10px;">Don\'t want to receive such emails? '
-                '<a href="https://scipost.org/unsubscribe/{{ key }}">Unsubscribe</a>.</p>')
+                '<a href="%s">Unsubscribe</a>.</p>' % url_unsubscribe)
             email_context['sub_title'] = cls.notification.cited_in_submission.title
             email_context['sub_author_list'] = cls.notification.cited_in_submission.author_list
             email_context['arxiv_identifier_w_vn_nr'] = cls.notification.cited_in_submission.arxiv_identifier_w_vn_nr
diff --git a/scipost/views.py b/scipost/views.py
index 51442d623dbb2f288fcbacf04f86cbeac2eca7e2..44d609e97024187ec54876a55de1daf3a0a8c827 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -57,20 +57,7 @@ def is_registered(user):
     return user.groups.filter(name='Registered Contributors').exists()
 
 
-def is_SP_Admin(user):
-    return user.groups.filter(name='SciPost Administrators').exists()
-
-
-def is_MEC(user):
-    return user.groups.filter(name='Editorial College').exists()
-
-
-def is_VE(user):
-    return user.groups.filter(name='Vetting Editors').exists()
-
-
 # Global search
-
 def normalize_query(query_string,
                     findterms=re.compile(r'"([^"]+)"|(\S+)').findall,
                     normspace=re.compile(r'\s{2,}').sub):
@@ -104,35 +91,23 @@ def documentsSearchResults(query):
     Naive implementation based on exact match of query.
     NEEDS UPDATING with e.g. Haystack.
     """
-    publication_query = get_query(query,
-                                  ['title', 'author_list', 'abstract', 'doi_string'])
-    commentary_query = get_query(query,
-                                 ['pub_title', 'author_list', 'pub_abstract'])
-    submission_query = get_query(query,
-                                 ['title', 'author_list', 'abstract'])
-    thesislink_query = get_query(query,
-                                 ['title', 'author', 'abstract', 'supervisor'])
-    comment_query = get_query(query,
-                              ['comment_text'])
-
-    publication_search_queryset = Publication.objects.filter(
-        publication_query,
-        ).order_by('-publication_date')
-    commentary_search_queryset = Commentary.objects.filter(
-        commentary_query,
-        vetted=True,
-        ).order_by('-pub_date')
-    submission_search_queryset = Submission.objects.public().filter(
-        submission_query,
-        ).order_by('-submission_date')
-    thesislink_search_list = ThesisLink.objects.filter(
-        thesislink_query,
-        vetted=True,
-        ).order_by('-defense_date')
-    comment_search_list = Comment.objects.filter(
-        comment_query,
-        status__gte='1',
-        ).order_by('-date_submitted')
+    publication_query = get_query(query, ['title', 'author_list', 'abstract', 'doi_string'])
+    commentary_query = get_query(query, ['pub_title', 'author_list', 'pub_abstract'])
+    submission_query = get_query(query, ['title', 'author_list', 'abstract'])
+    thesislink_query = get_query(query, ['title', 'author', 'abstract', 'supervisor'])
+    comment_query = get_query(query, ['comment_text'])
+
+    publication_search_queryset = (Publication.objects.published()
+                                   .filter(publication_query).order_by('-publication_date'))
+    commentary_search_queryset = (Commentary.objects.vetted()
+                                  .filter(commentary_query).order_by('-pub_date'))
+    submission_search_queryset = (Submission.objects.public()
+                                  .filter(submission_query).order_by('-submission_date'))
+    thesislink_search_list = (ThesisLink.objects.vetted()
+                              .filter(thesislink_query).order_by('-defense_date'))
+    comment_search_list = (Comment.objects.vetted()
+                           .filter(comment_query).order_by('-date_submitted'))
+
     context = {'publication_search_queryset': publication_search_queryset,
                'commentary_search_queryset': commentary_search_queryset,
                'submission_search_queryset': submission_search_queryset,
@@ -200,12 +175,12 @@ def search(request):
 #############
 
 def index(request):
-    """ Main page """
-    context = {}
-    context['latest_newsitems'] = NewsItem.objects.all().order_by('-date')[:2]
-    context['submissions'] = Submission.objects.public().order_by('-submission_date')[:4]
-    context['publications'] = Publication.objects.published().order_by('-publication_date')[:4]
-
+    '''Main page.'''
+    context = {
+        'latest_newsitems': NewsItem.objects.all().order_by('-date')[:2],
+        'submissions': Submission.objects.public().order_by('-submission_date')[:4],
+        'publications': Publication.objects.published().order_by('-publication_date')[:4]
+    }
     return render(request, 'scipost/index.html', context)
 
 
@@ -213,11 +188,6 @@ def index(request):
 # Information
 ###############
 
-def base(request):
-    """ Skeleton for pages, used in template inheritance """
-    return render(request, 'scipost/base.html')
-
-
 def feeds(request):
     context = {'subject_areas_physics': SCIPOST_SUBJECT_AREAS[0][1]}
     return render(request, 'scipost/feeds.html', context)
@@ -228,193 +198,123 @@ def feeds(request):
 ################
 
 def register(request):
+    """
+    This public registration view shows and processes the form
+    that will create new user account requests. After registration
+    the Contributor will need to activate its account via the mail
+    sent. After activation the user needs to be vetted by the SciPost
+    admin.
+    """
     if request.user.is_authenticated():
-        return HttpResponseRedirect('personal_page')
-    if request.method == 'POST':
-        form = RegistrationForm(request.POST)
-        Utils.load({'form': form})
-        if form.is_valid():
-            if Utils.password_mismatch():
-                return render(request, 'scipost/register.html',
-                              {'form': form, 'errormessage': 'Your passwords must match'})
-            if Utils.username_already_taken():
-                return render(request, 'scipost/register.html',
-                              {'form': form, 'errormessage': 'This username is already in use'})
-            if Utils.email_already_taken():
-                return render(request, 'scipost/register.html',
-                              {'form': form,
-                               'errormessage': 'This email address is already in use'})
-            Utils.create_and_save_contributor('')
-            Utils.send_registration_email()
-            # If this email was associated to an invitation, mark it as responded to
-            try:
-                invitation = RegistrationInvitation.objects.get(
-                    email=form.cleaned_data['email'])
-                invitation.responded = True
-                invitation.save()
-            except ObjectDoesNotExist:
-                pass
-            except MultipleObjectsReturned:
-                # Delete the first invitation
-                invitation_to_delete = RegistrationInvitation.objects.filter(
-                    email=form.cleaned_data['email']).first()
-                invitation_to_delete.delete()
-            context = {'ack_header': 'Thanks for registering to SciPost.',
-                       'ack_message': ('You will receive an email with a link to verify '
-                                       'your email address. '
-                                       'Please visit this link within 48 hours. '
-                                       'Your credentials will thereafter be verified. '
-                                       'If your registration is vetted through by the '
-                                       'administrators, you will be enabled to contribute.'),
-                       }
-            return render(request, 'scipost/acknowledgement.html', context)
-    else:
-        form = RegistrationForm()
-    invited = False
-    context = {'form': form, 'invited': invited}
-    return render(request, 'scipost/register.html', context)
+        return redirect(reverse('scipost:personal_page'))
+
+    form = RegistrationForm(request.POST or None)
+    if form.is_valid():
+        contributor = form.create_and_save_contributor()
+        Utils.load({'contributor': contributor})
+        Utils.send_registration_email()
+
+        # Disable invitations related to the new Contributor
+        (RegistrationInvitation.objects.filter(email=form.cleaned_data['email'])
+         .update(responded=True))
+
+        context = {
+            'ack_header': 'Thanks for registering to SciPost.',
+            'ack_message': ('You will receive an email with a link to verify '
+                            'your email address. '
+                            'Please visit this link within 48 hours. '
+                            'Your credentials will thereafter be verified. '
+                            'If your registration is vetted through by the '
+                            'administrators, you will be enabled to contribute.'),
+        }
+        return render(request, 'scipost/acknowledgement.html', context)
+    return render(request, 'scipost/register.html', {'form': form, 'invited': False})
 
 
 def invitation(request, key):
-    """ Register, by invitation """
+    """
+    If a scientist has recieved an invitation (RegistrationInvitation)
+    he/she will finish it's invitation via still view which will prefill
+    the default registration form.
+    """
     invitation = get_object_or_404(RegistrationInvitation, invitation_key=key)
     if invitation.responded:
         errormessage = ('This invitation token has already been used, '
                         'or this email address is already associated to a registration.')
     elif timezone.now() > invitation.key_expires:
         errormessage = 'The invitation key has expired.'
-    elif request.method == 'POST':
-        form = RegistrationForm(request.POST)
-        Utils.load({'form': form})
-        if form.is_valid():
-            if Utils.password_mismatch():
-                return render(request, 'scipost/register.html', {
-                    'form': form, 'invited': True, 'key': key,
-                    'errormessage': 'Your passwords must match'})
-            if Utils.username_already_taken():
-                return render(request, 'scipost/register.html',
-                              {'form': form, 'invited': True, 'key': key,
-                               'errormessage': 'This username is already in use'})
-            if Utils.email_already_taken():
-                return render(request, 'scipost/register.html',
-                              {'form': form, 'invited': True, 'key': key,
-                               'errormessage': 'This email address is already in use'})
-            invitation.responded = True
-            invitation.save()
-            Utils.create_and_save_contributor(key)
-            Utils.send_registration_email()
-            context = {'ack_header': 'Thanks for registering to SciPost.',
-                       'ack_message': ('You will receive an email with a link to verify '
-                                       'your email address. '
-                                       'Please visit this link within 48 hours. '
-                                       'Your credentials will thereafter be verified. '
-                                       'If your registration is vetted through by the '
-                                       'administrators, you will be enabled to contribute.'),
-                       }
-            return render(request, 'scipost/acknowledgement.html', context)
-        else:
-            errormessage = 'form is invalidly filled'
-            return render(request, 'scipost/register.html',
-                          {'form': form, 'invited': True, 'key': key,
-                           'errormessage': errormessage})
     else:
-        form = RegistrationForm()
-        form.fields['title'].initial = invitation.title
-        form.fields['last_name'].initial = invitation.last_name
-        form.fields['first_name'].initial = invitation.first_name
-        form.fields['email'].initial = invitation.email
-        errormessage = ''
-        welcome_message = ('Welcome, ' + invitation.get_title_display() + ' '
-                           + invitation.last_name + ', and thanks in advance for '
-                           'registering (by completing this form)')
-        return render(request, 'scipost/register.html', {
-            'form': form, 'invited': True, 'key': key,
-            'errormessage': errormessage, 'welcome_message': welcome_message})
-
-    context = {'errormessage': errormessage}
-    return render(request, 'scipost/accept_invitation_error.html', context)
-
-
-def activation(request, key):
+        context = {
+            'invitation': invitation,
+            'form': RegistrationForm(initial=invitation.__dict__)
+        }
+        return render(request, 'scipost/register.html', context)
+    return render(request, 'scipost/accept_invitation_error.html', {'errormessage': errormessage})
+
+
+def activation(request, contributor_id, key):
     """
     After registration, an email verification link is sent.
     Once clicked, the account is activated.
     """
-    contributor = get_object_or_404(Contributor, activation_key=key)
+    contributor = get_object_or_404(Contributor, id=contributor_id, activation_key=key)
     if not contributor.user.is_active:
         if timezone.now() > contributor.key_expires:
-            context = {'oldkey': key}
-            return render(request, 'scipost/request_new_activation_link.html', context)
-        else:
-            contributor.user.is_active = True
-            contributor.user.save()
-            context = {'ack_header': 'Your email address has been confirmed.',
-                       'ack_message': ('Your SciPost account will soon be vetted. '
-                                       'You will soon receive an email from us.'),
-                       }
-            return render(request, 'scipost/acknowledgement.html', context)
-    else:
-        return render(request, 'scipost/already_activated.html')
-
-
-def request_new_activation_link(request, oldkey):
-    contributor = get_object_or_404(Contributor, activation_key=oldkey)
-    # Generate a new email activation key and link
-    salt = ""
-    for i in range(5):
-        salt = salt + random.choice(string.ascii_letters)
-
-    salt = salt.encode('utf8')
-    usernamesalt = contributor.user.username
-    usernamesalt = usernamesalt.encode('utf8')
-    contributor.activation_key = hashlib.sha1(salt+usernamesalt).hexdigest()
-    contributor.key_expires = datetime.datetime.strftime(
-        datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
-    contributor.save()
-    email_text = ('Dear ' + contributor.get_title_display() + ' ' + contributor.user.last_name +
-                  ', \n\n'
-                  'Your request for a new email activation link for registration to the SciPost '
-                  'publication portal has been received. You now need to visit this link within '
-                  'the next 48 hours: \n\n'
-                  'https://scipost.org/activation/' + contributor.activation_key +
-                  '\n\nYour registration will thereafter be vetted. Many thanks for your interest.'
-                  '\n\nThe SciPost Team.')
-    emailmessage = EmailMessage('SciPost registration: new email activation link',
-                                email_text, 'SciPost registration <registration@scipost.org>',
-                                [contributor.user.email, 'registration@scipost.org'],
-                                reply_to=['registration@scipost.org'])
-    emailmessage.send(fail_silently=False)
-    context = {
-        'ack_header': 'We have emailed you a new activation link.',
-        'ack_message': ('Please acknowledge it within its 48 hours validity '
-                        'window if you want us to proceed with vetting your registraion.'),
-    }
-    return render(request, 'scipost/acknowledgement.html', context)
+            return redirect(reverse('scipost:request_new_activation_link', kwargs={
+                'contributor_id': contributor_id,
+                'key': key
+            }))
+        contributor.user.is_active = True
+        contributor.user.save()
+        context = {'ack_header': 'Your email address has been confirmed.',
+                   'ack_message': ('Your SciPost account will soon be vetted. '
+                                   'You will soon receive an email from us.'),
+                   }
+        return render(request, 'scipost/acknowledgement.html', context)
+    messages.success(request, ('<h3>Your email has already been confirmed.</h3>'
+                               'Please wait for vetting of your registration.'
+                               ' We shall strive to send you an update by email within 24 hours.'))
+    return redirect(reverse('scipost:index'))
 
 
-def unsubscribe(request, key):
+def request_new_activation_link(request, contributor_id, key):
+    """
+    Once a user tries to activate its account using the email verification link sent
+    and the key has expired, the user redirected to possibly request a new token.
+    """
+    contributor = get_object_or_404(Contributor, id=contributor_id, activation_key=key)
+    if request.GET.get('confirm', False):
+        # Generate a new email activation key and link
+        contributor.generate_key()
+        Utils.load({'contributor': contributor})
+        Utils.send_new_activation_link_email()
+
+        context = {
+            'ack_header': 'We have emailed you a new activation link.',
+            'ack_message': ('Please acknowledge it within its 48 hours validity '
+                            'window if you want us to proceed with vetting your registraion.'),
+        }
+        return render(request, 'scipost/acknowledgement.html', context)
+    context = {'contributor': contributor}
+    return render(request, 'scipost/request_new_activation_link.html', context)
+
+
+def unsubscribe(request, contributor_id, key):
     """
     The link to this method is included in all email communications
     with a Contributor. The key used is the original activation key.
     At this link, the Contributor can confirm that he/she does not
     want to receive any non-essential email notifications from SciPost.
     """
-    contributor = get_object_or_404(Contributor, activation_key=key)
-    context = {'contributor': contributor, }
-    return render(request, 'scipost/unsubscribe.html', context)
-
-
-def unsubscribe_confirm(request, key):
-    contributor = get_object_or_404(Contributor, activation_key=key)
-    contributor.accepts_SciPost_emails = False
-    contributor.save()
-    context = {'ack_header': 'Unsubscribe',
-               'followup_message': ('We have recorded your preference: you will '
-                                    'no longer receive non-essential email '
-                                    'from SciPost. You can go back to your '),
-               'followup_link': reverse('scipost:personal_page'),
-               'followup_link_label': 'personal page'}
-    return render(request, 'scipost/acknowledgement.html', context)
+    contributor = get_object_or_404(Contributor, id=contributor_id, activation_key=key)
+    if request.GET.get('confirm', False):
+        contributor.accepts_SciPost_emails = False
+        contributor.save()
+        text = ('<h3>We have recorded your preference</h3>'
+                'You will no longer receive non-essential email from SciPost.')
+        messages.success(request, text)
+        return redirect(reverse('scipost:index'))
+    return render(request, 'scipost/unsubscribe.html', {'contributor': contributor})
 
 
 @permission_required('scipost.can_vet_registration_requests', return_403=True)
@@ -678,27 +578,17 @@ def registration_invitations(request, draft_id=None):
 
     sent_reg_inv = RegistrationInvitation.objects.filter(responded=False, declined=False)
     sent_reg_inv_fellows = sent_reg_inv.filter(invitation_type='F').order_by('last_name')
-    nr_sent_reg_inv_fellows = sent_reg_inv_fellows.count()
     sent_reg_inv_contrib = sent_reg_inv.filter(invitation_type='C').order_by('last_name')
-    nr_sent_reg_inv_contrib = sent_reg_inv_contrib.count()
     sent_reg_inv_ref = sent_reg_inv.filter(invitation_type='R').order_by('last_name')
-    nr_sent_reg_inv_ref = sent_reg_inv_ref.count()
     sent_reg_inv_cited_sub = sent_reg_inv.filter(invitation_type='ci').order_by('last_name')
-    nr_sent_reg_inv_cited_sub = sent_reg_inv_cited_sub.count()
     sent_reg_inv_cited_pub = sent_reg_inv.filter(invitation_type='cp').order_by('last_name')
-    nr_sent_reg_inv_cited_pub = sent_reg_inv_cited_pub.count()
 
     resp_reg_inv = RegistrationInvitation.objects.filter(responded=True, declined=False)
     resp_reg_inv_fellows = resp_reg_inv.filter(invitation_type='F').order_by('last_name')
-    nr_resp_reg_inv_fellows = resp_reg_inv_fellows.count()
     resp_reg_inv_contrib = resp_reg_inv.filter(invitation_type='C').order_by('last_name')
-    nr_resp_reg_inv_contrib = resp_reg_inv_contrib.count()
     resp_reg_inv_ref = resp_reg_inv.filter(invitation_type='R').order_by('last_name')
-    nr_resp_reg_inv_ref = resp_reg_inv_ref.count()
     resp_reg_inv_cited_sub = resp_reg_inv.filter(invitation_type='ci').order_by('last_name')
-    nr_resp_reg_inv_cited_sub = resp_reg_inv_cited_sub.count()
     resp_reg_inv_cited_pub = resp_reg_inv.filter(invitation_type='cp').order_by('last_name')
-    nr_resp_reg_inv_cited_pub = resp_reg_inv_cited_pub.count()
 
     decl_reg_inv = RegistrationInvitation.objects.filter(responded=True, declined=True)
 
@@ -710,25 +600,15 @@ def registration_invitations(request, draft_id=None):
     context = {
         'reg_inv_form': reg_inv_form, 'errormessage': errormessage,
         'sent_reg_inv_fellows': sent_reg_inv_fellows,
-        'nr_sent_reg_inv_fellows': nr_sent_reg_inv_fellows,
         'sent_reg_inv_contrib': sent_reg_inv_contrib,
-        'nr_sent_reg_inv_contrib': nr_sent_reg_inv_contrib,
         'sent_reg_inv_ref': sent_reg_inv_ref,
-        'nr_sent_reg_inv_ref': nr_sent_reg_inv_ref,
         'sent_reg_inv_cited_sub': sent_reg_inv_cited_sub,
-        'nr_sent_reg_inv_cited_sub': nr_sent_reg_inv_cited_sub,
         'sent_reg_inv_cited_pub': sent_reg_inv_cited_pub,
-        'nr_sent_reg_inv_cited_pub': nr_sent_reg_inv_cited_pub,
         'resp_reg_inv_fellows': resp_reg_inv_fellows,
-        'nr_resp_reg_inv_fellows': nr_resp_reg_inv_fellows,
         'resp_reg_inv_contrib': resp_reg_inv_contrib,
-        'nr_resp_reg_inv_contrib': nr_resp_reg_inv_contrib,
         'resp_reg_inv_ref': resp_reg_inv_ref,
-        'nr_resp_reg_inv_ref': nr_resp_reg_inv_ref,
         'resp_reg_inv_cited_sub': resp_reg_inv_cited_sub,
-        'nr_resp_reg_inv_cited_sub': nr_resp_reg_inv_cited_sub,
         'resp_reg_inv_cited_pub': resp_reg_inv_cited_pub,
-        'nr_resp_reg_inv_cited_pub': nr_resp_reg_inv_cited_pub,
         'decl_reg_inv': decl_reg_inv,
         'names_reg_contributors': names_reg_contributors,
         'existing_drafts': existing_drafts,
@@ -842,8 +722,7 @@ def mark_draft_inv_as_processed(request, draft_id):
 
 
 def login_view(request):
-    redirect_to = request.POST.get('next',
-                                   request.GET.get('next', reverse('scipost:personal_page')))
+    redirect_to = request.POST.get('next', reverse('scipost:personal_page'))
     redirect_to = (redirect_to
                    if is_safe_url(redirect_to, request.get_host())
                    else reverse('scipost:personal_page'))
@@ -914,7 +793,7 @@ def personal_page(request):
     nr_reg_awaiting_validation = 0
     nr_submissions_to_assign = 0
     nr_recommendations_to_prepare_for_voting = 0
-    if is_SP_Admin(contributor.user):
+    if contributor.is_SP_Admin():
         intwodays = now + timezone.timedelta(days=2)
 
         # count the number of pending registration requests
@@ -928,7 +807,7 @@ def personal_page(request):
     nr_assignments_to_consider = 0
     active_assignments = None
     nr_reports_to_vet = 0
-    if is_MEC(contributor.user):
+    if contributor.is_MEC():
         nr_assignments_to_consider = (EditorialAssignment.objects
                                       .filter(to=contributor, accepted=None, deprecated=False)
                                       .count())
@@ -940,7 +819,7 @@ def personal_page(request):
     nr_comments_to_vet = 0
     nr_thesislink_requests_to_vet = 0
     nr_authorship_claims_to_vet = 0
-    if is_VE(request.user):
+    if contributor.is_VE():
         nr_commentary_page_requests_to_vet = Commentary.objects.filter(vetted=False).count()
         nr_comments_to_vet = Comment.objects.filter(status=0).count()
         nr_thesislink_requests_to_vet = ThesisLink.objects.filter(vetted=False).count()
@@ -1056,19 +935,6 @@ def update_personal_data(request):
     if user_form.is_valid() and cont_form.is_valid():
         user_form.save()
         cont_form.save()
-        # request.user.email = user_form.cleaned_data['email']
-        # request.user.first_name = user_form.cleaned_data['first_name']
-        # request.user.contributor.title = cont_form.cleaned_data['title']
-        # request.user.contributor.discipline = cont_form.cleaned_data['discipline']
-        # request.user.contributor.expertises = cont_form.cleaned_data['expertises']
-        # request.user.contributor.orcid_id = cont_form.cleaned_data['orcid_id']
-        # request.user.contributor.country_of_employment = cont_form.cleaned_data['country_of_employment']
-        # request.user.contributor.address = cont_form.cleaned_data['address']
-        # request.user.contributor.affiliation = cont_form.cleaned_data['affiliation']
-        # request.user.contributor.personalwebpage = cont_form.cleaned_data['personalwebpage']
-        # request.user.contributor.accepts_SciPost_emails = cont_form.cleaned_data['accepts_SciPost_emails']
-        # request.user.save()
-        # request.user.contributor.save()
         messages.success(request, 'Your personal data has been updated.')
         return redirect(reverse('scipost:personal_page'))
     else:
@@ -1248,25 +1114,6 @@ def email_group_members(request):
     if request.method == 'POST':
         form = EmailGroupMembersForm(request.POST)
         if form.is_valid():
-            # recipient_emails = []
-            # for member in form.cleaned_data['group'].user_set.all():
-            #     recipient_emails.append(member.email)
-            # emailmessage = EmailMessage(
-            #     form.cleaned_data['email_subject'],
-            #     form.cleaned_data['email_text'],
-            #     'SciPost Admin <admin@scipost.org>',
-            #     ['admin@scipost.org'],
-            #     bcc=recipient_emails,
-            #     reply_to=['admin@scipost.org'])
-            # emailmessage.send(fail_silently=False)
-            # with mail.get_connection() as connection:
-            #     for member in form.cleaned_data['group'].user_set.all():
-            #         email_text = ('Dear ' + member.contributor.get_title_display() + ' ' +
-            #                       member.last_name + ', \n\n'
-            #                       + form.cleaned_data['email_text'])
-            #         mail.EmailMessage(form.cleaned_data['email_subject'],
-            #                           email_text, 'SciPost Admin <admin@scipost.org>',
-            #                           [member.email], connection=connection).send()
             group_members = form.cleaned_data['group'].user_set.all()
             p = Paginator(group_members, 32)
             for pagenr in p.page_range:
@@ -1286,14 +1133,14 @@ def email_group_members(request):
                                 email_text += SCIPOST_SUMMARY_FOOTER
                                 email_text_html += SCIPOST_SUMMARY_FOOTER_HTML
                             email_text_html += EMAIL_FOOTER
+                            url_unsubscribe = reverse('scipost:unsubscribe',
+                                                      args=[contributor.id,
+                                                            contributor.activation_key])
                             email_text += ('\n\nDon\'t want to receive such emails? '
-                                           'Unsubscribe by visiting '
-                                           'https://scipost.org/unsubscribe/'
-                                           + member.contributor.activation_key + '.')
+                                           'Unsubscribe by visiting %s.' % url_unsubscribe)
                             email_text_html += (
                                 '<br/>\n<p style="font-size: 10px;">Don\'t want to receive such '
-                                'emails? <a href="https://scipost.org/unsubscribe/{{ key }}">'
-                                'Unsubscribe</a>.</p>')
+                                'emails? <a href="%s">Unsubscribe</a>.</p>' % url_unsubscribe)
                             email_context = Context({
                                 'title': member.contributor.get_title_display(),
                                 'last_name': member.last_name,
diff --git a/submissions/constants.py b/submissions/constants.py
index 13b615f63e148dfc504e4b977fafef9713f655a6..d8d508b8d29c9a65d5f0b02057a9da1cd463fd2f 100644
--- a/submissions/constants.py
+++ b/submissions/constants.py
@@ -6,7 +6,7 @@ STATUS_AWAITING_ED_REC = 'awaiting_ed_rec'
 STATUS_REVIEW_CLOSED = 'review_closed'
 SUBMISSION_STATUS = (
     (STATUS_UNASSIGNED, 'Unassigned, undergoing pre-screening'),
-    (STATUS_RESUBMISSION_SCREENING, 'Resubmission incoming, undergoing pre-screening'),
+    (STATUS_RESUBMISSION_SCREENING, 'Resubmission incoming'),
     ('assignment_failed', 'Failed to assign Editor-in-charge; manuscript rejected'),
     (STATUS_EIC_ASSIGNED, 'Editor-in-charge assigned, manuscript under review'),
     (STATUS_REVIEW_CLOSED, 'Review period closed, editorial recommendation pending'),
@@ -75,6 +75,11 @@ SUBMISSION_TYPE = (
     ('Review', 'Review (candid snapshot of current research in a given area)'),
 )
 
+NO_REQUIRED_ACTION_STATUSES = SUBMISSION_STATUS_PUBLICLY_INVISIBLE + [
+    STATUS_UNASSIGNED,
+    STATUS_RESUBMISSION_SCREENING
+]
+
 ED_COMM_CHOICES = (
     ('EtoA', 'Editor-in-charge to Author'),
     ('EtoR', 'Editor-in-charge to Referee'),
diff --git a/submissions/migrations/0040_auto_20170416_2152.py b/submissions/migrations/0040_auto_20170416_2152.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8064dfabea16dc62fe3f69e35dd4f5d988a3f0e
--- /dev/null
+++ b/submissions/migrations/0040_auto_20170416_2152.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-04-16 19:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('submissions', '0039_auto_20170416_0948'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='submission',
+            name='status',
+            field=models.CharField(choices=[('unassigned', 'Unassigned, undergoing pre-screening'), ('resubmitted_incomin', 'Resubmission incoming'), ('assignment_failed', 'Failed to assign Editor-in-charge; manuscript rejected'), ('EICassigned', 'Editor-in-charge assigned, manuscript under review'), ('review_closed', 'Review period closed, editorial recommendation pending'), ('revision_requested', 'Editor-in-charge has requested revision'), ('resubmitted', 'Has been resubmitted'), ('resubmitted_and_rejected', 'Has been resubmitted and subsequently rejected'), ('resubmitted_and_rejected_visible', 'Has been resubmitted and subsequently rejected (still publicly visible)'), ('voting_in_preparation', 'Voting in preparation (eligible Fellows being selected)'), ('put_to_EC_voting', 'Undergoing voting at the Editorial College'), ('awaiting_ed_rec', 'Awaiting Editorial Recommendation'), ('EC_vote_completed', 'Editorial College voting rounded up'), ('accepted', 'Publication decision taken: accept'), ('rejected', 'Publication decision taken: reject'), ('rejected_visible', 'Publication decision taken: reject (still publicly visible)'), ('published', 'Published'), ('withdrawn', 'Withdrawn by the Authors')], default='unassigned', max_length=30),
+        ),
+    ]
diff --git a/templates/email/new_activation_link.html b/templates/email/new_activation_link.html
new file mode 100644
index 0000000000000000000000000000000000000000..3d8def3613888ad4377396c3c11972a053dd946f
--- /dev/null
+++ b/templates/email/new_activation_link.html
@@ -0,0 +1,9 @@
+Dear {{contributor.get_title_display}} {{contributor.user.last_name}},\n\n
+
+Your request for a new email activation link for registration to the SciPost publication portal has been received. You now need to visit this link within the next 48 hours: \n\n
+
+{% url 'scipost:activation' contributor.id contributor.activation_key %}
+\n\n
+
+Your registration will thereafter be vetted. Many thanks for your interest.\n
+The SciPost Team.
diff --git a/templates/email/new_activation_link_html.html b/templates/email/new_activation_link_html.html
new file mode 100644
index 0000000000000000000000000000000000000000..8ab56b93af64371f8dda5906ef741bedffa90ddb
--- /dev/null
+++ b/templates/email/new_activation_link_html.html
@@ -0,0 +1,15 @@
+<h3>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</h3>
+
+<p>
+    Your request for a new email activation link for registration to the SciPost publication portal has been received. You now need to visit this link within the next 48 hours:
+</p>
+<p>
+    <a href="{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a>
+</p>
+
+<p>
+    Your registration will thereafter be vetted. Many thanks for your interest.
+    The SciPost Team.
+</p>
+
+{% include 'email/_footer.html' %}
diff --git a/templates/email/registration_request_received.html b/templates/email/registration_request_received.html
new file mode 100644
index 0000000000000000000000000000000000000000..d18818fbfc929832f1e7815bf3771bb675e3a313
--- /dev/null
+++ b/templates/email/registration_request_received.html
@@ -0,0 +1,9 @@
+Dear {{contributor.get_title_display}} {{contributor.user.last_name}},\n\n
+
+Your request for registration to the SciPost publication portal has been received. You now need to validate your email by visiting this link within the next 48 hours: \n\n
+
+{% url 'scipost:activation' contributor.id contributor.activation_key %}
+\n\n
+
+Your registration will thereafter be vetted. Many thanks for your interest.\n
+The SciPost Team.
diff --git a/templates/email/registration_request_received_html.html b/templates/email/registration_request_received_html.html
new file mode 100644
index 0000000000000000000000000000000000000000..ca016a1a10a7d626ffa8646284a4e9e9c229fd65
--- /dev/null
+++ b/templates/email/registration_request_received_html.html
@@ -0,0 +1,15 @@
+<h3>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</h3>
+
+<p>
+    Your request for registration to the SciPost publication portal has been received. You now need to validate your email by visiting this link within the next 48 hours:
+</p>
+<p>
+    <a href="{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a>
+</p>
+
+<p>
+    Your registration will thereafter be vetted. Many thanks for your interest.
+    The SciPost Team.
+</p>
+
+{% include 'email/_footer.html' %}