diff --git a/SciPost_v1/settings/production.py b/SciPost_v1/settings/production.py
index ac0546cab12d7d38e10f38d9249ddfe02512a164..b95b188543eec3c02a750b111877015627119244 100644
--- a/SciPost_v1/settings/production.py
+++ b/SciPost_v1/settings/production.py
@@ -1,3 +1,6 @@
+import sentry_sdk
+from sentry_sdk.integrations.django import DjangoIntegration
+
 from .base import *
 
 # THE MAIN THING HERE
@@ -18,7 +21,8 @@ WEBPACK_LOADER['DEFAULT']['CACHE'] = True
 WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] = '/home/scipost/webapps/scipost_static/bundles/'
 
 # Error reporting
-ADMINS = MANAGERS = (('J.S.Caux', 'J.S.Caux@uva.nl'), ('J.de Wit', 'jorrandewit@outlook.com'))
+ADMINS = []
+MANAGERS = (('J.S.Caux', 'J.S.Caux@uva.nl'), ('J.de Wit', 'jorrandewit@outlook.com'))
 
 # Cookies
 SESSION_COOKIE_SECURE = True
@@ -57,3 +61,10 @@ LOGGING['handlers']['scipost_file_doi']['filename'] = '/home/scipost/webapps/sci
 
 # API
 REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = ('rest_framework.renderers.JSONRenderer',)
+
+
+# Sentry
+sentry_sdk.init(
+    dsn=get_secret('SENTRY_DSN'),
+    integrations=[DjangoIntegration()]
+)
diff --git a/affiliations/admin.py b/affiliations/admin.py
index fc4aa88c963abfdd8e52f67fe6c44d7f7ba9740b..f398cbde6a702263d3a606e9073ed45bd77ebb18 100644
--- a/affiliations/admin.py
+++ b/affiliations/admin.py
@@ -7,5 +7,14 @@ from django.contrib import admin
 from .models import Affiliation, Institution
 
 
-admin.site.register(Affiliation)
-admin.site.register(Institution)
+class AffiliationAdmin(admin.ModelAdmin):
+    search_fields = ['institution__name', 'institution__acronym',
+                     'contributor__user__last_name']
+
+admin.site.register(Affiliation, AffiliationAdmin)
+
+
+class InstitutionAdmin(admin.ModelAdmin):
+    search_fields =['name', 'acronym']
+
+admin.site.register(Institution, InstitutionAdmin)
diff --git a/affiliations/forms.py b/affiliations/forms.py
index 41b281de326282a01d7c56c6ac7318af868402a0..05dc8c902e3192f72a16a1d2992c4119471bf644 100644
--- a/affiliations/forms.py
+++ b/affiliations/forms.py
@@ -9,6 +9,8 @@ from django_countries import countries
 from django_countries.fields import LazyTypedChoiceField
 from django_countries.widgets import CountrySelectWidget
 
+from ajax_select.fields import AutoCompleteSelectField
+
 from common.widgets import DateWidget
 
 from .models import Affiliation, Institution
@@ -117,3 +119,11 @@ class InstitutionMergeForm(forms.ModelForm):
                 institution=old_institution).update(institution=self.instance)
             old_institution.delete()
         return self.instance
+
+
+class InstitutionOrganizationSelectForm(forms.ModelForm):
+    organization = AutoCompleteSelectField('organization_lookup')
+
+    class Meta:
+        model = Institution
+        fields = []
diff --git a/affiliations/migrations/0003_institution_organization.py b/affiliations/migrations/0003_institution_organization.py
new file mode 100644
index 0000000000000000000000000000000000000000..169975618df4f7454d6c1c52bf9a8cfbc488171e
--- /dev/null
+++ b/affiliations/migrations/0003_institution_organization.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-29 06:44
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('organizations', '0010_auto_20190223_1406'),
+        ('affiliations', '0002_auto_20171229_1435'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='institution',
+            name='organization',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='institutions', to='organizations.Organization'),
+        ),
+    ]
diff --git a/affiliations/models.py b/affiliations/models.py
index 5bb05f92ecc0971936f30f21818613315aa7372c..ed9e2d280e00ddef8f6fe5a089849e6fd18ef92d 100644
--- a/affiliations/models.py
+++ b/affiliations/models.py
@@ -22,6 +22,8 @@ class Institution(models.Model):
     acronym = models.CharField(max_length=16, blank=True)
     country = CountryField()
     type = models.CharField(max_length=16, choices=INSTITUTION_TYPES, default=TYPE_UNIVERSITY)
+    organization = models.ForeignKey('organizations.Organization', on_delete=models.CASCADE,
+                                     blank=True, null=True)
 
     objects = InstitutionQuerySet.as_manager()
 
diff --git a/affiliations/templates/affiliations/institution_confirm_delete.html b/affiliations/templates/affiliations/institution_confirm_delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..bc40d6ef792d923965578137ada5341e5dfa3cfc
--- /dev/null
+++ b/affiliations/templates/affiliations/institution_confirm_delete.html
@@ -0,0 +1,46 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: delete Institution{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block breadcrumb_items %}
+{{ block.super }}
+<span class="breadcrumb-item">{{ institution.name }}</span>
+{% endblock %}
+
+{% block content %}
+
+<h1>Institution: confirm delete</h1>
+<div class="row">
+  <div class="col-4">
+    <table class="table">
+      <tbody>
+	<tr><td>Name:</td><td>{{ institution.name }}</td></tr>
+	<tr><td>Acronym:</td><td>{{ institution.acronym }}</td></tr>
+	<tr><td>Country:</td><td>{{ institution.get_country_display }}</td></tr>
+	<tr><td>Type:</td><td>{{ institution.get_type_display }}</td></tr>
+	<tr><td>Organization:</td><td>{{ institution.organization }}</td></tr>
+	<tr>
+	  <td>(aff)Affiliations:</td>
+	  <td>
+	    <ul class="list-unstyled">
+	      {% for aff in institution.affiliations.all %}
+	      <li>{{ aff }}</li>
+	      {% endfor %}
+	    </ul>
+	  </td>
+	</tr>
+      </tbody>
+    </table>
+  </div>
+  <div class="col-12">
+    <form method="post">
+      {% csrf_token %}
+      <h3 class="mb-2">Are you sure you want to delete this Institution?</h3>
+      <input type="submit" class="btn btn-danger" value="Yes, delete it" />
+    </form>
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/affiliations/templates/affiliations/institution_link_organization.html b/affiliations/templates/affiliations/institution_link_organization.html
new file mode 100644
index 0000000000000000000000000000000000000000..9bd1517aed943bdfeed68558ec5d54161da0e4d6
--- /dev/null
+++ b/affiliations/templates/affiliations/institution_link_organization.html
@@ -0,0 +1,45 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: link Institution to Organization{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block breadcrumb_items %}
+{{ block.super }}
+<span class="breadcrumb-item">{{ institution.name }}</span>
+{% endblock %}
+
+{% block content %}
+
+<h1>Institution: link to Organization</h1>
+<div class="row">
+  <div class="col-4">
+    <table class="table">
+      <tbody>
+	<tr><td>Name:</td><td>{{ institution.name }}</td></tr>
+	<tr><td>Acronym:</td><td>{{ institution.acronym }}</td></tr>
+	<tr><td>Country:</td><td>{{ institution.get_country_display }}</td></tr>
+	<tr><td>Type:</td><td>{{ institution.get_type_display }}</td></tr>
+	<tr><td>Organization:</td><td>{{ institution.organization }}</td></tr>
+      </tbody>
+    </table>
+  </div>
+  <div class="col-6">
+    <h3>Link to:</h3>
+    <form action="{% url 'affiliations:link_to_organization' pk=institution.pk %}" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+      <input type="submit" value="Link" class="btn btn-primary">
+    </form>
+  </div>
+  <div class="col-2">
+    <p>Can't find it in the selector? <a href="{% url 'organizations:organization_create' %}" target="_blank">Add a new organization to our database</a> (opens in new window)</p>
+  </div>
+</div>
+
+{% endblock content %}
+
+{% block footer_script %}
+{{ block.super }}
+{{ form.media }}
+{% endblock footer_script %}
diff --git a/affiliations/templates/affiliations/institutions_without_organization_list.html b/affiliations/templates/affiliations/institutions_without_organization_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..76be007587d5853abc746ad625c4633b132f29e9
--- /dev/null
+++ b/affiliations/templates/affiliations/institutions_without_organization_list.html
@@ -0,0 +1,39 @@
+{% extends 'affiliations/base.html' %}
+
+
+{% block pagetitle %}: Institutions{% endblock pagetitle %}
+
+
+{% block breadcrumb_items %}
+    <span class="breadcrumb-item">Institutions without Organization</span>
+{% endblock %}
+
+{% block content %}
+
+<h1 class="highlight">Institutions without Organization</h1>
+
+{% if is_paginated %}
+    {% include 'partials/pagination.html' with page_obj=page_obj %}
+{% endif %}
+
+<ul>
+    {% for institution in object_list %}
+        <li>
+            {% if perms.scipost.can_manage_affiliations %}
+	    <a href="{% url 'affiliations:link_to_organization' pk=institution.pk %}">Link to Org</a>
+	    &emsp;
+	    <a href="{% url 'affiliations:institution_delete' institution_id=institution.pk %}" class="text-danger">Delete</a>
+	    &emsp;
+	    {% endif %}
+            <a href="{{ institution.get_absolute_url }}">{{ institution }}</a>
+        </li>
+    {% empty %}
+        <li><em>There are no Institutions without an Organization.</em><li>
+    {% endfor %}
+</ul>
+{% if is_paginated %}
+    {% include 'partials/pagination.html' with page_obj=page_obj %}
+{% endif %}
+
+
+{% endblock content %}
diff --git a/affiliations/urls.py b/affiliations/urls.py
index 5221f5d8ea7ab48e491885a2f2cacac743ff0bf4..db9ce2d7f92548a0168c9f5b61f01d8e95c31d32 100644
--- a/affiliations/urls.py
+++ b/affiliations/urls.py
@@ -12,6 +12,14 @@ urlpatterns = [
         name='institution_details'),
     url(r'^(?P<institution_id>[0-9]+)/edit', views.InstitutionUpdateView.as_view(),
         name='institution_edit'),
+    url(r'^(?P<institution_id>[0-9]+)/delete/', views.InstitutionDeleteView.as_view(),
+        name='institution_delete'),
     url(r'^(?P<institution_id>[0-9]+)/merge$', views.merge_institutions,
         name='merge_institutions'),
+    url(r'^institutions_without_organization/$',
+        views.InstitutionWithoutOrganizationListView.as_view(),
+        name='institutions_without_organization'),
+    url(r'^(?P<pk>[0-9]+)/link_to_organization/$',
+        views.LinkInstitutionToOrganizationView.as_view(),
+        name='link_to_organization'),
 ]
diff --git a/affiliations/views.py b/affiliations/views.py
index 3107fdf2cfda6f972a654697dafaac8ff7db4d97..2f7d98203a8a6c69a90970db4fed049203f57efd 100644
--- a/affiliations/views.py
+++ b/affiliations/views.py
@@ -5,14 +5,16 @@ __license__ = "AGPL v3"
 from django.shortcuts import redirect
 from django.contrib import messages
 from django.contrib.auth.decorators import permission_required
-from django.urls import reverse
+from django.urls import reverse, reverse_lazy
 from django.utils.decorators import method_decorator
 from django.views.generic.detail import DetailView
-from django.views.generic.edit import UpdateView
+from django.views.generic.edit import UpdateView, DeleteView
 from django.views.generic.list import ListView
 from django.shortcuts import get_object_or_404
 
-from .forms import InstitutionMergeForm
+from scipost.mixins import PermissionsMixin
+
+from .forms import InstitutionMergeForm, InstitutionOrganizationSelectForm
 from .models import Institution
 
 
@@ -26,6 +28,13 @@ class InstitutionDetailView(DetailView):
     pk_url_kwarg = 'institution_id'
 
 
+class InstitutionDeleteView(PermissionsMixin, DeleteView):
+    model = Institution
+    permission_required = 'scipost.can_manage_affiliations'
+    pk_url_kwarg = 'institution_id'
+    success_url = reverse_lazy('affiliations:institutions_without_organization')
+
+
 @method_decorator(permission_required('scipost.can_manage_affiliations'), name='dispatch')
 class InstitutionUpdateView(UpdateView):
     model = Institution
@@ -59,3 +68,24 @@ def merge_institutions(request, institution_id):
             a=form.cleaned_data.get('institution', '?'), b=institution))
 
     return redirect(reverse('affiliations:institution_edit', args=(institution.id,)))
+
+
+class InstitutionWithoutOrganizationListView(ListView):
+    queryset = Institution.objects.filter(organization=None)
+    paginate_by = 20
+    template_name = 'affiliations/institutions_without_organization_list.html'
+
+
+class LinkInstitutionToOrganizationView(PermissionsMixin, UpdateView):
+    """
+    For an existing Institution instance, specify the link to an Organization.
+    """
+    permission_required = 'scipost.can_manage_affiliations'
+    model = Institution
+    form_class = InstitutionOrganizationSelectForm
+    template_name = 'affiliations/institution_link_organization.html'
+    success_url = reverse_lazy('affiliations:institutions_without_organization')
+
+    def form_valid(self, form):
+        form.instance.organization = form.cleaned_data['organization']
+        return super().form_valid(form)
diff --git a/colleges/managers.py b/colleges/managers.py
index 67e450e0cd057ac2ac532955a241317339b4e59a..e07b7f0fc0ced30b4262a66f3ef1309bdc1934b3 100644
--- a/colleges/managers.py
+++ b/colleges/managers.py
@@ -25,8 +25,32 @@ class FellowQuerySet(models.QuerySet):
             Q(start_date__isnull=True, until_date__isnull=True)
             ).ordered()
 
+    def specialties_overlap(self, discipline, expertises=[]):
+        """
+        Returns all Fellows specialized in the given discipline
+        and any of the (optional) expertises.
+
+        This method is also separately implemented for Contributor and Profile objects.
+        """
+        qs = self.filter(contributor__profile__discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(contributor__profile__expertises__overlap=expertises)
+        return qs
+
+    def specialties_contain(self, discipline, expertises=[]):
+        """
+        Returns all Fellows specialized in the given discipline
+        and all of the (optional) expertises.
+
+        This method is also separately implemented for Contributor and Profile objects.
+        """
+        qs = self.filter(contributor__profile__discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(contributor__profile__expertises__contains=expertises)
+        return qs
+
     def ordered(self):
-        """Return ordered queryset explicitly, since this may have big affect on performance."""
+        """Return ordered queryset explicitly, since this may have big effect on performance."""
         return self.order_by('contributor__user__last_name')
 
     def return_active_for_submission(self, submission):
@@ -56,7 +80,7 @@ class PotentialFellowshipQuerySet(models.QuerySet):
         return self.filter(
             profile__discipline=contributor.profile.discipline,
             status=POTENTIAL_FELLOWSHIP_ELECTION_VOTE_ONGOING
-        ).order_by('profile__last_name')
+        ).distinct().order_by('profile__last_name')
 
     def to_vote_on(self, contributor):
         return self.vote_needed(contributor).exclude(
diff --git a/colleges/templates/colleges/potentialfellowship_list.html b/colleges/templates/colleges/potentialfellowship_list.html
index 6cc4b4e90102462f1bb52f3dfbea6e246360a355..7131ac1ef2e61b584a557c93e3a23e9ef742ad47 100644
--- a/colleges/templates/colleges/potentialfellowship_list.html
+++ b/colleges/templates/colleges/potentialfellowship_list.html
@@ -117,7 +117,7 @@ $(document).ready(function($) {
             <div class="single d-inline" data-specialization="{{expertise|lower}}" data-toggle="tooltip" data-placement="bottom" title="{{expertise|get_specialization_display}}">{{expertise|get_specialization_code}}</div>
 	    {% endfor %}
 	  </td>
-	  <td style="color: #ffffff; background-color:{{ potfel.status|potfelstatuscolor }};">{{ potfel.get_status_display }}&nbsp;<small>{% voting_results_display potfel %}</small></td>
+	  <td style="color: #ffffff; background-color:{{ potfel.status|potfelstatuscolor }};">{{ potfel.get_status_display }}<br/><small>{% voting_results_display potfel %}</small></td>
 	  <td>{{ potfel.latest_event_details }}</td>
 	</tr>
 	{% empty %}
diff --git a/colleges/templatetags/colleges_extras.py b/colleges/templatetags/colleges_extras.py
index 13a510c612de262bff017503d69c40950cb34f5d..3a9b26c02527abc5020090a14ffb1c3fcc5b3935 100644
--- a/colleges/templatetags/colleges_extras.py
+++ b/colleges/templatetags/colleges_extras.py
@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
 
 
 from django import template
+from django.utils.html import format_html, mark_safe
 
 from ..constants import (
     POTENTIAL_FELLOWSHIP_IDENTIFIED, POTENTIAL_FELLOWSHIP_NOMINATED,
@@ -14,6 +15,7 @@ from ..constants import (
     POTENTIAL_FELLOWSHIP_INTERESTED, POTENTIAL_FELLOWSHIP_REGISTERED,
     POTENTIAL_FELLOWSHIP_ACTIVE_IN_COLLEGE, POTENTIAL_FELLOWSHIP_SCIPOST_EMERITUS
     )
+from ..models import Fellowship
 
 from common.utils import hslColorWheel
 
@@ -62,8 +64,33 @@ def potfelstatuscolor(status):
 @register.simple_tag
 def voting_results_display(potfel):
     if potfel.status == POTENTIAL_FELLOWSHIP_ELECTION_VOTE_ONGOING:
-        return ' Agree: %s, Abstain: %s, Disagree: %s' % (
-            potfel.in_agreement.count(),
-            potfel.in_abstain.count(),
-            potfel.in_disagreement.count())
+        nr_agree = potfel.in_agreement.count()
+        nr_abstain = potfel.in_abstain.count()
+        nr_disagree = potfel.in_disagreement.count()
+        nr_spec_agree = potfel.in_agreement.all().specialties_overlap(
+            potfel.profile.discipline, potfel.profile.expertises).count()
+        nr_spec_abstain = potfel.in_abstain.all().specialties_overlap(
+            potfel.profile.discipline, potfel.profile.expertises).count()
+        nr_spec_disagree = potfel.in_disagreement.all().specialties_overlap(
+            potfel.profile.discipline, potfel.profile.expertises).count()
+        nr_specialists = Fellowship.objects.active().specialties_overlap(
+            potfel.profile.discipline, potfel.profile.expertises).count()
+        nr_Fellows = Fellowship.objects.active().specialties_overlap(
+            potfel.profile.discipline).count()
+        # Establish whether election criterion has been met.
+        # Rule is: spec Agree must be >= 3/4 of (total nr of spec - nr abstain)
+        election_agree_percentage = int(
+            100 * nr_spec_agree/(max(1, nr_specialists - nr_spec_abstain)))
+        election_criterion_met = nr_spec_agree > 0 and election_agree_percentage >= 75
+        if election_criterion_met:
+            election_text = ('&emsp;<strong class="bg-success p-1 text-white">'
+                             'Elected (%s&#37; in favour)</strong>' % str(election_agree_percentage))
+        else:
+            election_text = ('&emsp;<strong class="bg-warning p-1 text-white">'
+                             '%s&#37; in favour</strong>') % str(election_agree_percentage)
+        return format_html('Specialists ({}):<br/>Agree: {}, Abstain: {}, Disagree: {}&nbsp;{}<br/>'
+                           'All: ({} Fellows)<br/>Agree: {}, Abstain: {}, Disagree: {}',
+                           nr_specialists, nr_spec_agree, nr_spec_abstain, nr_spec_disagree,
+                           mark_safe(election_text),
+                           nr_Fellows, nr_agree, nr_abstain, nr_disagree)
     return ''
diff --git a/conflicts/management/commands/check_submission_metadata.py b/conflicts/management/commands/check_submission_metadata.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc7bbbbd49cd36d43a7cc7199061e47c6f7ff01d
--- /dev/null
+++ b/conflicts/management/commands/check_submission_metadata.py
@@ -0,0 +1,24 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import traceback
+
+from django.core.management.base import BaseCommand
+
+from submissions.models import Submission
+
+
+class Command(BaseCommand):
+    """Verify the metadata formatting and flag errors."""
+
+    def handle(self, *args, **kwargs):
+        for sub in Submission.objects.all():
+            # Check that the author list is properly formatted
+            try:
+                if 'entries' in sub.metadata:
+                    author_str_list = [
+                        a['name'].split()[-1] for a in sub.metadata['entries'][0]['authors']]
+            except:
+                print('Error for %s' % sub.preprint)
+                traceback.print_exc()
diff --git a/conflicts/management/commands/update_coi_via_arxiv.py b/conflicts/management/commands/update_coi_via_arxiv.py
index 288a63e4ad5d9ee8f810d3bf96e28f371ca7a2b4..9c49692a6fb5d010e89ffab5045871e6630c8f0b 100644
--- a/conflicts/management/commands/update_coi_via_arxiv.py
+++ b/conflicts/management/commands/update_coi_via_arxiv.py
@@ -35,15 +35,13 @@ class Command(BaseCommand):
             # Get all possibly relevant Profiles
             author_str_list = [a.split()[-1] for a in sub.author_list.split(',')]
             if 'entries' in sub.metadata:
-                sub.metadata['entries'][0]['authors']
-                # last_names = []
                 author_str_list += [
                     a['name'].split()[-1] for a in sub.metadata['entries'][0]['authors']]
-                author_str_list = set(author_str_list)  # Distinct operator
+            author_str_list = set(author_str_list)  # Distinct operator
             author_profiles = Profile.objects.filter(
                 Q(contributor__in=sub.authors.all()) |
                 Q(last_name__in=author_str_list)).distinct()
 
             n_new_conflicts += caller.compare(author_profiles, fellow_profiles, submission=sub)
             Submission.objects.filter(id=sub.id).update(needs_conflicts_update=False)
-        return n_new_conflicts
+        return str(n_new_conflicts)
diff --git a/forums/models.py b/forums/models.py
index 47b56585aee6dec7b173de21acf4f2c1d8efb798..39f9ff08ae684e832ab6455f74ff109d1e4e7a2f 100644
--- a/forums/models.py
+++ b/forums/models.py
@@ -208,7 +208,7 @@ class Post(models.Model):
                               self.posted_by.last_name, self.subject[:32])
 
     def get_absolute_url(self):
-        return '%s#post%s' % (self.get_forum().get_absolute_url(), self.id)
+        return '%s#post%s' % (self.get_anchor_forum_or_meeting().get_absolute_url(), self.id)
 
     @property
     def nr_followups(self):
@@ -226,16 +226,17 @@ class Post(models.Model):
         print ('post %s id_list: %s' % (self.id, id_list))
         return id_list
 
-    def get_forum(self):
+    def get_anchor_forum_or_meeting(self):
         """
         Climb back the hierarchy up to the original Forum.
         If no Forum is found, return None.
         """
         type_forum = ContentType.objects.get_by_natural_key('forums', 'forum')
-        if self.parent_content_type == type_forum:
+        type_meeting = ContentType.objects.get_by_natural_key('forums', 'meeting')
+        if self.parent_content_type == type_forum or self.parent_content_type == type_meeting:
             return self.parent
         else:
-            return self.parent.get_forum()
+            return self.parent.get_anchor_forum_or_meeting()
 
 
 class Motion(Post):
diff --git a/forums/templates/forums/forum_detail.html b/forums/templates/forums/forum_detail.html
index 6b1be878e9c83d273d1a00e606d6eb62c6f83a0b..2d3d0626f9e85dd704bed50b123960a334167464 100644
--- a/forums/templates/forums/forum_detail.html
+++ b/forums/templates/forums/forum_detail.html
@@ -19,7 +19,7 @@
 
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight">
+    <h2 class="highlight">
       {% if forum.meeting %}
       {% with context_colors=forum.meeting.context_colors %}
       <span class="badge badge-{{ context_colors.bg }} mx-0 mb-2 p-2 text-{{ context_colors.text }}">
@@ -34,7 +34,7 @@
 	<a href="{{ forum.get_absolute_url }}">{{ forum }}</a>
 	<span class="badge badge-primary badge-pill">{% with nr_posts=forum.nr_posts %}{{ nr_posts }} post{{ nr_posts|pluralize }}{% endwith %}</span>
       </span>
-    </h3>
+    </h2>
 
     {% if forum.parent %}
     <p>Parent: <a href="{{ forum.parent.get_absolute_url }}">{{ forum.parent }}</a></p>
@@ -92,39 +92,45 @@
     {% endif %}
 
 
-    <h3>Table of Contents</h3>
-    <ul>
-      <li><a href="#Description">Description</a></li>
-      {% if forum.meeting %}
-      <li><a href="#Preamble">Preamble</a></li>
-      <li><a href="#Motions">Motions</a></li>
-      {% endif %}
-      <li><a href="#Posts">Posts</a></li>
-      {% if forum.meeting %}
-      <li><a href="#Minutes">Minutes</a></li>
-      {% endif %}
-    </ul>
+    <h2>Table of Contents</h2>
+    <div class="m-2">
+      <ul>
+	<li><a href="#Description">Description</a></li>
+	{% if forum.meeting %}
+	<li><a href="#Preamble">Preamble</a></li>
+	<li><a href="#Motions">Motions</a></li>
+	{% endif %}
+	<li><a href="#Posts">Posts</a></li>
+	{% if forum.meeting %}
+	<li><a href="#Minutes">Minutes</a></li>
+	{% endif %}
+      </ul>
+    </div>
   </div>
 </div>
 
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight" id="Description">Description</h3>
-    {{ forum.description|restructuredtext }}
+    <h2 class="highlight" id="Description">Description</h2>
+    <div class="m-2">
+      {{ forum.descripteion|restructuredtext }}
+    </div>
   </div>
 </div>
 
 {% if forum.meeting %}
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight" id="Preamble">Preamble</h3>
-    {{ forum.meeting.preamble|restructuredtext }}
+    <h2 class="highlight" id="Preamble">Preamble</h2>
+    <div class="m-2">
+      {{ forum.meeting.preamble|restructuredtext }}
+    </div>
   </div>
 </div>
 
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight" id="Motions">Motions</h3>
+    <h2 class="highlight" id="Motions">Motions</h2>
     <ul>
       {% if forum.meeting.future %}
       <li>Adding Motions will be activated once the meeting starts</li>
@@ -143,7 +149,7 @@
 
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight" id="Posts">Posts</h3>
+    <h2 class="highlight" id="Posts">Posts</h2>
     <ul>
       <li><a href="{% url 'forums:post_create' slug=forum.slug parent_model='forum' parent_id=forum.id %}">Add a new Post</a></li>
     </ul>
@@ -158,8 +164,10 @@
 {% if forum.meeting %}
 <div class="row">
   <div class="col-12">
-    <h3 class="highlight" id="Minutes">Minutes</h3>
-    {{ forum.meeting.minutes|restructuredtext }}
+    <h2 class="highlight" id="Minutes">Minutes</h2>
+    <div class="m-2">
+      {{ forum.meeting.minutes|restructuredtext }}
+    </div>
   </div>
 </div>
 {% endif %}
diff --git a/forums/templates/forums/motion_confirm_create.html b/forums/templates/forums/motion_confirm_create.html
new file mode 100644
index 0000000000000000000000000000000000000000..3d626c7063acf74c3642f169f781058a5d746042
--- /dev/null
+++ b/forums/templates/forums/motion_confirm_create.html
@@ -0,0 +1,58 @@
+{% extends 'forums/base.html' %}
+
+{% load bootstrap %}
+{% load restructuredtext %}
+
+
+{% block breadcrumb_items %}
+{{ block.super }}
+<span class="breadcrumb-item">Confirm Motion creation</span>
+{% endblock %}
+
+{% block pagetitle %}: Motion: confirm creation{% endblock pagetitle %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-12">
+    <h3 class="highlight">Preview</h3>
+
+    <div class="card">
+      <div class="card-header">
+	{{ form.initial.subject }}
+      </div>
+      <div class="card-body">
+	{% if form.initial.text %}
+	{{ form.initial.text|restructuredtext }}
+	{% else %}
+	<span class="text-danger">No text given</span>
+	{% endif %}
+      </div>
+    </div>
+
+    {% if form.errors %}
+    {% for field in form %}
+    {% for error in field.errors %}
+    <div class="alert alert-danger">
+      <strong>{{ field.name }} - {{ error|escape }}</strong>
+    </div>
+    {% endfor %}
+    {% endfor %}
+    {% for error in form.non_field_errors %}
+    <div class="alert alert-danger">
+      <strong>{{ error|escape }}</strong>
+    </div>
+    {% endfor %}
+    {% endif %}
+
+    <form action="" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+      <input type="submit" value="Confirm Motion creation" class="btn btn-primary">
+      <span class="text-danger">&nbsp;<strong>Not satisfied?</strong> Hit your browser's back button and redraft your Motion</span>
+    </form>
+
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/forums/templates/forums/motion_form.html b/forums/templates/forums/motion_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..ce4f3e20e1d9dee884a9beed919c0ac70660e26c
--- /dev/null
+++ b/forums/templates/forums/motion_form.html
@@ -0,0 +1,27 @@
+{% extends 'forums/base.html' %}
+
+{% load bootstrap %}
+{% load restructuredtext %}
+
+
+{% block breadcrumb_items %}
+{{ block.super }}
+<span class="breadcrumb-item">{% if form.instance.id %}Update {{ form.instance }}{% else %}New Motion{% endif %}</span>
+{% endblock %}
+
+{% block pagetitle %}: Motion{% endblock pagetitle %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-12">
+    <h3 class="highlight">Create a new Motion</h3>
+    <form action="" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+      <input type="submit" value="Run preview" class="btn btn-primary">
+    </form>
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/forums/templates/forums/post_card.html b/forums/templates/forums/post_card.html
index 78da1449f3b3de3c22d50cd9f098583781fc83f5..6abd15a0b6c62b46c98390a7636a313a4c27c3a6 100644
--- a/forums/templates/forums/post_card.html
+++ b/forums/templates/forums/post_card.html
@@ -1,6 +1,6 @@
 {% load restructuredtext %}
 
-<div class="card {% if post.motion %}text-white bg-dark{% else %}text-body{% endif %}" id="post{{ post.id }}">
+<div class="card m-2 {% if post.motion %}text-white bg-dark{% else %}text-body{% endif %}" id="post{{ post.id }}">
   <div class="card-header">
     {{ post.subject }}
     <div class="postInfo">
diff --git a/forums/views.py b/forums/views.py
index dc9eb86728d7b1d623d1779db96f6280ec8aa782..6f9df6f59780bd3d1f8e3e37385d247b3e5e93bd 100644
--- a/forums/views.py
+++ b/forums/views.py
@@ -236,7 +236,7 @@ class MotionCreateView(PostCreateView):
     """
     model = Motion
     form_class = MotionForm
-    template_name = 'forums/post_form.html'
+    template_name = 'forums/motion_form.html'
 
     def get_initial(self, *args, **kwargs):
         initial = super().get_initial(*args, **kwargs)
@@ -310,6 +310,7 @@ class MotionConfirmCreateView(PostConfirmCreateView):
     Specialization of PostConfirmCreateView to Motion-class objects.
     """
     form_class = MotionForm
+    template_name = 'forums/motion_confirm_create.html'
 
     def get_initial(self, *args, **kwargs):
         initial = super().get_initial(*args, **kwargs)
diff --git a/journals/services.py b/journals/services.py
index 4cd4bb77ab7941818a5bfc36c720b25bdeb25fd1..5d8ea0d78b70bfca1b5c6917017fc492482623d8 100644
--- a/journals/services.py
+++ b/journals/services.py
@@ -61,7 +61,12 @@ def update_citedby(doi_label):
                     'Please contact the SciPost Admin.')
         return
 
-    response_deserialized = ET.fromstring(r.text)
+    try:
+        response_deserialized = ET.fromstring(r.text)
+    except ET.ParseError: # something went wrong, abort
+        logger.info('Response parsing failed for doi: %s', publication.doi_string)
+        return
+
     prefix = '{http://www.crossref.org/qrschema/2.0}'
     citations = []
     for link in response_deserialized.iter(prefix + 'forward_link'):
diff --git a/journals/templates/journals/base.html b/journals/templates/journals/base.html
index 48517580e9c8cd51edadc640d79f12fa546c2631..5e3cc60aefa15fff3ac6e39f5d97002fb6b88573 100644
--- a/journals/templates/journals/base.html
+++ b/journals/templates/journals/base.html
@@ -40,11 +40,20 @@
         <div class="row my-1">
             <div class="col-12">
                 {% if journal.active %}
-                    <p>{{journal}} is published by the SciPost Foundation under the journal doi: 10.21468/{{journal.name}}{% if journal.issn %} and ISSN {{journal.issn}}{% endif %}.</p>
+                <p>{{journal}} is published by the SciPost Foundation under the journal doi: 10.21468/{{journal.name}}{% if journal.issn %} and ISSN {{journal.issn}}{% endif %}.</p>
                 {% endif %}
 		{% if journal.doi_label == 'SciPostPhys' %}
-		    <p>SciPost Physics has been awarded the DOAJ Seal <img src="{% static 'scipost/images/DOAJ_Seal_logo_big.png' %}" alt="DOAJ Seal" width="40"> from the <a href="https://doaj.org">Directory of Open Access Journals</a></p>
-    		<p class="mb-0">All content in {{ journal }} is deposited and permanently preserved in the CLOCKSS archive <a href="https://www.clockss.org/clockss/Home" target="_blank"><img src="{% static 'scipost/images/clockss_original_logo_boxed_ai-cropped-90.png' %}" alt="CLOCKSS logo" width="40"></a></p>
+		<p class="mb-1">
+		  SciPost Physics has been awarded the DOAJ Seal <img src="{% static 'scipost/images/DOAJ_Seal_logo_big.png' %}" alt="DOAJ Seal" width="40"> from the <a href="https://doaj.org">Directory of Open Access Journals</a>
+		</p>
+		<p>
+		  All content in {{ journal }} is deposited and permanently preserved in the CLOCKSS archive <a href="https://www.clockss.org/clockss/Home" target="_blank"><img src="{% static 'scipost/images/clockss_original_logo_boxed_ai-cropped-90.png' %}" alt="CLOCKSS logo" width="40"></a>
+		</p>
+    		<p class="mb-1">
+		  Self-computed impact factor
+		  <small><sup><i class="fa fa-question-circle" data-toggle="tooltip" data-html="true" title="Number of citations in year N for papers published in years N-1 and N-2,<br/>divided by the number of papers in those years.<br/>Data obtained from Crossref's Cited-by service"></i></sup></small> for 2018		  (using <a href="https://www.crossref.org/services/cited-by/" target="_blank">Crossref Cited-by</a> data)
+: <strong>5.25</strong>
+		</p>
 		{% endif %}
 	    </div>
         </div>
diff --git a/journals/templatetags/lookup.py b/journals/templatetags/lookup.py
index 0cdcd41b33540a993e5f23bbdedf3931f4ae882b..cdb5e7fd3ca81cd803bc0b19bfde70239e1d32d9 100644
--- a/journals/templatetags/lookup.py
+++ b/journals/templatetags/lookup.py
@@ -10,7 +10,6 @@ from ajax_select import register, LookupChannel
 from ..models import Publication
 
 from funders.models import Funder, Grant
-from organizations.models import Organization
 
 
 @register('publication_lookup')
@@ -43,30 +42,6 @@ class PublicationLookup(LookupChannel):
             raise PermissionDenied
 
 
-@register('organization_lookup')
-class OrganizationLookup(LookupChannel):
-    model = Organization
-
-    def get_query(self, q, request):
-        return (self.model.objects.order_by('name')
-                .filter(Q(name__icontains=q) |
-                        Q(acronym__icontains=q) |
-                        Q(name_original__icontains=q))[:10])
-
-    def format_item_display(self, item):
-        """(HTML) format item for displaying item in the selected deck area."""
-        return u"<span class='auto_lookup_display'>%s</span>" % item.full_name_with_acronym
-
-    def format_match(self, item):
-        """(HTML) Format item for displaying in the dropdown."""
-        return item.full_name_with_acronym
-
-    def check_auth(self, request):
-        """Check if has organization administrative permissions."""
-        if not request.user.has_perm('scipost.can_manage_organizations'):
-            raise PermissionDenied
-
-
 @register('funder_lookup')
 class FunderLookup(LookupChannel):
     model = Funder
diff --git a/notifications/views.py b/notifications/views.py
index 955e23ab384be6c2759f35f6241ce61c6f9d8d55..a98a37183f284f429ea851bfbe2898c1248c7ca4 100644
--- a/notifications/views.py
+++ b/notifications/views.py
@@ -65,57 +65,15 @@ def live_unread_notification_count(request):
 
 def live_notification_list(request):
     """Return JSON of unread count and content of messages."""
-    if not request.user.is_authenticated():
-        data = {
-            'unread_count': 0,
-            'list': []
-        }
-        return JsonResponse(data)
-
-    try:
-        # Default to 5 as a max number of notifications
-        num_to_fetch = max(int(request.GET.get('max', 10)), 1)
-        num_to_fetch = min(num_to_fetch, 100)
-    except ValueError:
-        num_to_fetch = 5
-
-    try:
-        offset = int(request.GET.get('offset', 0))
-    except ValueError:
-        offset = 0
-
-    list = []
-
-    #for n in request.user.notifications.all()[offset:offset + num_to_fetch]:
-    # Kill notifications for now
-    for n in None:
-        struct = model_to_dict(n)
-        # struct['unread'] = struct['pseudo_unread']
-        struct['slug'] = id2slug(n.id)
-        if n.actor:
-            if isinstance(n.actor, User):
-                # Humanize if possible
-                struct['actor'] = '{f} {l}'.format(f=n.actor.first_name, l=n.actor.last_name)
-            else:
-                struct['actor'] = str(n.actor)
-        if n.target:
-            if hasattr(n.target, 'notification_name'):
-                struct['target'] = n.target.notification_name
-            else:
-                struct['target'] = str(n.target)
-            struct['forward_link'] = n.get_absolute_url()
-        if n.action_object:
-            struct['action_object'] = str(n.action_object)
-        struct['timesince'] = n.timesince()
-
-        list.append(struct)
-
-    if request.GET.get('mark_as_read'):
-        # Mark all as read
-        request.user.notifications.mark_all_as_read()
+    # if not request.user.is_authenticated():
+    #     data = {
+    #         'unread_count': 0,
+    #         'list': []
+    #     }
+    #     return JsonResponse(data)
 
     data = {
-        'unread_count': request.user.notifications.unread().count(),
-        'list': list
+        'unread_count': 0,
+        'list': []
     }
     return JsonResponse(data)
diff --git a/organizations/management/commands/gobble_institutions.py b/organizations/management/commands/gobble_institutions.py
new file mode 100644
index 0000000000000000000000000000000000000000..45a21fc8a7f36ae80ecfac4f87e34b59efb36f1e
--- /dev/null
+++ b/organizations/management/commands/gobble_institutions.py
@@ -0,0 +1,30 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.core.management.base import BaseCommand
+
+from affiliations.models import Affiliation as deprec_Affiliation
+from affiliations.models import Institution
+from organizations.models import Organization
+from profiles.models import Affiliation
+
+
+class Command(BaseCommand):
+    help = ('For affiliations.Institution objects with a defined organization '
+            'field, update the user Profiles to the new profiles.Affiliation objects '
+            'and delete the deprecated Institution and Affiliation objects.')
+
+    def handle(self, *args, **kwargs):
+        for inst in Institution.objects.exclude(organization=None):
+            print('Handling institution %s' % str(inst))
+            for deprec_aff in deprec_Affiliation.objects.filter(institution=inst):
+                Affiliation.objects.create(
+                    profile=deprec_aff.contributor.profile,
+                    organization=deprec_aff.institution.organization,
+                    date_from=deprec_aff.begin_date,
+                    date_until=deprec_aff.end_date)
+                print('\t\tDeleting affiliation %s' % str(deprec_aff))
+                deprec_aff.delete()
+            print('\tDeleting institution %s' % str(inst))
+            inst.delete()
diff --git a/organizations/templates/organizations/organization_list.html b/organizations/templates/organizations/organization_list.html
index 4cce72c17bbb585be1092f2a5172fbaf4ce6ab1d..eca989ccb45d1f9a0f2c485fbccfebce1757b9d2 100644
--- a/organizations/templates/organizations/organization_list.html
+++ b/organizations/templates/organizations/organization_list.html
@@ -36,6 +36,7 @@ $(document).ready(function($) {
       <li><a href="{% url 'organizations:dashboard' %}">Go to the dashboard</a></li>
       <li><a href="{% url 'organizations:organization_create' %}">Create a new Organization instance</a></li>
       <li><a href="{% url 'funders:funders_dashboard' %}">Link Funders to Organizations</a> ({{ nr_funders_wo_organization }} found in need of linking)</li>
+      <li><a href="{% url 'affiliations:institutions_without_organization' %}">Link (deprecated) affiliations.Institutions to Organizations</a> ({{ nr_institutions_wo_organization }} found in need of linking)</li>
     </ul>
     {% endif %}
   </div>
diff --git a/organizations/templatetags/lookup.py b/organizations/templatetags/lookup.py
new file mode 100644
index 0000000000000000000000000000000000000000..a57ad364a16de5531dce5487b85fd13bdbef24d5
--- /dev/null
+++ b/organizations/templatetags/lookup.py
@@ -0,0 +1,33 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.core.exceptions import PermissionDenied
+from django.db.models import Q
+
+from ajax_select import register, LookupChannel
+
+from ..models import Organization
+
+
+@register('organization_lookup')
+class OrganizationLookup(LookupChannel):
+    model = Organization
+
+    def get_query(self, q, request):
+        return (self.model.objects.order_by('name')
+                .filter(Q(name__icontains=q) |
+                        Q(acronym__icontains=q) |
+                        Q(name_original__icontains=q))[:10])
+
+    def format_item_display(self, item):
+        """(HTML) format item for displaying item in the selected deck area."""
+        return u"<span class='auto_lookup_display'>%s</span>" % item.full_name_with_acronym
+
+    def format_match(self, item):
+        """(HTML) Format item for displaying in the dropdown."""
+        return item.full_name_with_acronym
+
+    def check_auth(self, request):
+        """Allow use by everybody (this is used in the registration form)."""
+        pass
diff --git a/organizations/views.py b/organizations/views.py
index 78d73153fd2b314985a98ebc5db0f60f57751cb8..ee7e856be60608d4865329f549d61d8672ff3a0c 100644
--- a/organizations/views.py
+++ b/organizations/views.py
@@ -23,6 +23,7 @@ from .forms import OrganizationEventForm, ContactPersonForm,\
     NewContactForm, ContactActivationForm, ContactRoleForm
 from .models import Organization, OrganizationEvent, ContactPerson, Contact, ContactRole
 
+from affiliations.models import Institution
 from funders.models import Funder
 from mails.utils import DirectMailUtil
 from mails.views import MailEditorSubview
@@ -70,6 +71,9 @@ class OrganizationListView(PaginationMixin, ListView):
         context = super().get_context_data(*args, **kwargs)
         if self.request.user.has_perm('scipost.can_manage_organizations'):
             context['nr_funders_wo_organization'] = Funder.objects.filter(organization=None).count()
+        if self.request.user.has_perm('scipost.can_manage_organizations'):
+            context['nr_institutions_wo_organization'] = Institution.objects.filter(
+                organization=None).count()
         context['pubyears'] = range(int(timezone.now().strftime('%Y')), 2015, -1)
         context['countrycodes'] = [code['country'] for code in list(
             Organization.objects.all().distinct('country').values('country'))]
diff --git a/profiles/admin.py b/profiles/admin.py
index 1b54b2c2a72c5d9253e0c89c62ec59d0c5cce768..d0e860a7fbdcafc4a33b4e80d029d04ef8a3dd3a 100644
--- a/profiles/admin.py
+++ b/profiles/admin.py
@@ -4,7 +4,7 @@ __license__ = "AGPL v3"
 
 from django.contrib import admin
 
-from .models import Profile, ProfileEmail, ProfileNonDuplicates
+from .models import Profile, ProfileEmail, ProfileNonDuplicates, Affiliation
 
 
 class ProfileEmailInline(admin.TabularInline):
@@ -12,10 +12,15 @@ class ProfileEmailInline(admin.TabularInline):
     extra = 0
 
 
+class AffiliationInline(admin.TabularInline):
+    model = Affiliation
+    extra = 0
+
+
 class ProfileAdmin(admin.ModelAdmin):
     list_display = ['__str__', 'email', 'discipline', 'expertises', 'has_active_contributor']
     search_fields = ['first_name', 'last_name', 'emails__email', 'orcid_id']
-    inlines = [ProfileEmailInline]
+    inlines = [ProfileEmailInline, AffiliationInline]
 
 admin.site.register(Profile, ProfileAdmin)
 
diff --git a/profiles/constants.py b/profiles/constants.py
index fb7c120de3db2c927f5fbab193a24b4adffff12c..f318b40b24f8914b7b9f17bf88a0470712fc8442 100644
--- a/profiles/constants.py
+++ b/profiles/constants.py
@@ -9,3 +9,34 @@ PROFILE_NON_DUPLICATE_REASONS = (
     (DIFFERENT_PEOPLE, 'These are different people'),
     (MULTIPLE_ALLOWED, 'Multiple Profiles allowed for this person'),
 )
+
+
+AFFILIATION_CATEGORY_EMPLOYED_PROF_FULL = 'employed_prof_full'
+AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSOCIATE = 'employed_prof_associate'
+AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSISTANT = 'employed_prof_assistant'
+AFFILIATION_CATEGORY_EMPLOYED_PROF_EMERITUS = 'employed_prof_emeritus'
+AFFILIATION_CATEGORY_EMPLOYED_PERMANENT_STAFF = 'employed_permanent_staff'
+AFFILIATION_CATEGORY_EMPLOYED_FIXED_TERM_STAFF = 'employed_fixed_term_staff'
+AFFILIATION_CATEGORY_EMPLOYED_TENURE_TRACK = 'employed_tenure_track'
+AFFILIATION_CATEGORY_EMPLOYED_POSTDOC = 'employed_postdoc'
+AFFILIATION_CATEGORY_EMPLOYED_PhD = 'employed_phd'
+AFFILIATION_CATEGORY_ASSOCIATE_SCIENTIST = 'associate_scientist'
+AFFILIATION_CATEGORY_CONSULTANT = 'consultant'
+AFFILIATION_CATEGORY_VISITOR = 'visitor'
+AFFILIATION_CATEGORY_UNSPECIFIED = 'unspecified'
+
+AFFILIATION_CATEGORIES = (
+    (AFFILIATION_CATEGORY_EMPLOYED_PROF_FULL, 'Full Professor'),
+    (AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSOCIATE, 'Associate Professor'),
+    (AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSISTANT, 'Assistant Professor'),
+    (AFFILIATION_CATEGORY_EMPLOYED_PROF_EMERITUS, 'Emeritus Professor'),
+    (AFFILIATION_CATEGORY_EMPLOYED_PERMANENT_STAFF, 'Permanent Staff'),
+    (AFFILIATION_CATEGORY_EMPLOYED_FIXED_TERM_STAFF, 'Fixed Term Staff'),
+    (AFFILIATION_CATEGORY_EMPLOYED_TENURE_TRACK, 'Tenure Tracker'),
+    (AFFILIATION_CATEGORY_EMPLOYED_POSTDOC, 'Postdoctoral Researcher'),
+    (AFFILIATION_CATEGORY_EMPLOYED_PhD, 'PhD candidate'),
+    (AFFILIATION_CATEGORY_ASSOCIATE_SCIENTIST, 'Associate Scientist'),
+    (AFFILIATION_CATEGORY_CONSULTANT, 'Consultant'),
+    (AFFILIATION_CATEGORY_VISITOR, 'Visitor'),
+    (AFFILIATION_CATEGORY_UNSPECIFIED, 'Unspecified'),
+)
diff --git a/profiles/forms.py b/profiles/forms.py
index dfba810721e71273c78feab0bfdf397948b161e2..59dd7b319ed49297de9138a3f2e9c9586e52239b 100644
--- a/profiles/forms.py
+++ b/profiles/forms.py
@@ -5,6 +5,8 @@ __license__ = "AGPL v3"
 from django import forms
 from django.shortcuts import get_object_or_404
 
+from ajax_select.fields import AutoCompleteSelectField
+
 from common.forms import ModelChoiceFieldwithid
 from invitations.models import RegistrationInvitation
 from journals.models import UnregisteredAuthor
@@ -12,7 +14,7 @@ from ontology.models import Topic
 from scipost.models import Contributor
 from submissions.models import RefereeInvitation
 
-from .models import Profile, ProfileEmail
+from .models import Profile, ProfileEmail, Affiliation
 
 
 class ProfileForm(forms.ModelForm):
@@ -136,7 +138,10 @@ class ProfileMergeForm(forms.Form):
             email__in=profile.emails.values_list('email', flat=True)).update(
             primary=False, profile=profile)
 
-        # Move all invitations to the "new" profile.
+        # Move all affiliations to the "new" profile
+        profile_old.affiliations.all().update(profile=profile)
+
+        # Move all invitations to the "new" profile
         profile_old.refereeinvitation_set.all().update(profile=profile)
         profile_old.registrationinvitation_set.all().update(profile=profile)
 
@@ -165,3 +170,16 @@ class ProfileEmailForm(forms.ModelForm):
         """Save to a profile."""
         self.instance.profile = self.profile
         return super().save()
+
+
+class AffiliationForm(forms.ModelForm):
+    organization = AutoCompleteSelectField('organization_lookup')
+
+    class Meta:
+        model = Affiliation
+        fields = ['profile', 'organization', 'category',
+                  'description', 'date_from', 'date_until']
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['profile'].widget = forms.HiddenInput()
diff --git a/profiles/managers.py b/profiles/managers.py
index 556d1af12dd01dd665df986926f745aab885043f..25212f24ab3d5163ba8467926f572967114997cc 100644
--- a/profiles/managers.py
+++ b/profiles/managers.py
@@ -31,3 +31,27 @@ class ProfileQuerySet(models.QuerySet):
         ).filter(nr_count__gt=1).exclude(full_name__in=nonduplicate_full_names)
         return profiles.filter(full_name__in=[item['full_name'] for item in duplicates]
                               ).order_by('last_name', 'first_name', '-id')
+
+    def specialties_overlap(self, discipline, expertises=[]):
+        """
+        Returns all Profiles specialized in the given discipline
+        and any of the (optional) expertises.
+
+        This method is also separately implemented for Contributor and Fellowship objects.
+        """
+        qs = self.filter(discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(expertises__overlap=expertises)
+        return qs
+
+    def specialties_contain(self, discipline, expertises=[]):
+        """
+        Returns all Profiles specialized in the given discipline
+        and all of the (optional) expertises.
+
+        This method is also separately implemented for Contributor and Fellowship objects.
+        """
+        qs = self.filter(discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(expertises__contains=expertises)
+        return qs
diff --git a/profiles/migrations/0018_affiliation.py b/profiles/migrations/0018_affiliation.py
new file mode 100644
index 0000000000000000000000000000000000000000..83b3d0301775c1b37145ac5e706c477daba79e23
--- /dev/null
+++ b/profiles/migrations/0018_affiliation.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-27 14:14
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('organizations', '0010_auto_20190223_1406'),
+        ('profiles', '0017_auto_20190126_2058'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Affiliation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('category', models.CharField(choices=[('employed_prof_full', 'Full Professor'), ('employed_prof_associate', 'Associate Professor'), ('employed_prof_assistant', 'Assistant Professor'), ('employed_prof_emeritus', 'Emeritus Professor'), ('employed_permanent_staff', 'Permanent Staff'), ('employed_fixed_term_staff', 'Fixed Term Staff'), ('employed_tenure_track', 'Tenure Tracker'), ('employed_postdoc', 'Postdoctoral Researcher'), ('employed_phd', 'PhD candidate'), ('associate_scientist', 'Associate Scientist'), ('consultant', 'Consultant'), ('visitor', 'Visotor')], help_text='Select the most suitable category', max_length=64)),
+                ('description', models.CharField(max_length=256)),
+                ('date_from', models.DateField(blank=True, null=True)),
+                ('date_until', models.DateField(blank=True, null=True)),
+                ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='affiliations', to='organizations.Organization')),
+                ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='affiliations', to='profiles.Profile')),
+            ],
+            options={
+                'ordering': ['profile__user__last_name', 'profile__user__first_name', 'date_until'],
+                'default_related_name': 'affiliations',
+            },
+        ),
+    ]
diff --git a/profiles/migrations/0019_auto_20190327_1520.py b/profiles/migrations/0019_auto_20190327_1520.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee2a8810c796494d4e7f5922013c2019a266a264
--- /dev/null
+++ b/profiles/migrations/0019_auto_20190327_1520.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-27 14:20
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0018_affiliation'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='affiliation',
+            options={'ordering': ['profile__last_name', 'profile__first_name', 'date_until']},
+        ),
+        migrations.AlterField(
+            model_name='affiliation',
+            name='category',
+            field=models.CharField(choices=[('employed_prof_full', 'Full Professor'), ('employed_prof_associate', 'Associate Professor'), ('employed_prof_assistant', 'Assistant Professor'), ('employed_prof_emeritus', 'Emeritus Professor'), ('employed_permanent_staff', 'Permanent Staff'), ('employed_fixed_term_staff', 'Fixed Term Staff'), ('employed_tenure_track', 'Tenure Tracker'), ('employed_postdoc', 'Postdoctoral Researcher'), ('employed_phd', 'PhD candidate'), ('associate_scientist', 'Associate Scientist'), ('consultant', 'Consultant'), ('visitor', 'Visitor')], help_text='Select the most suitable category', max_length=64),
+        ),
+    ]
diff --git a/profiles/migrations/0020_auto_20190327_1713.py b/profiles/migrations/0020_auto_20190327_1713.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9d9b127c257ee4098f097bc1ac9dcb57d979ca3
--- /dev/null
+++ b/profiles/migrations/0020_auto_20190327_1713.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-27 16:13
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0019_auto_20190327_1520'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='affiliation',
+            options={'ordering': ['profile__last_name', 'profile__first_name', '-date_until']},
+        ),
+    ]
diff --git a/profiles/migrations/0021_auto_20190329_0744.py b/profiles/migrations/0021_auto_20190329_0744.py
new file mode 100644
index 0000000000000000000000000000000000000000..28fd2593584769f68a491869f264b9dbcfb70ed7
--- /dev/null
+++ b/profiles/migrations/0021_auto_20190329_0744.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-29 06:44
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0020_auto_20190327_1713'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='affiliation',
+            name='category',
+            field=models.CharField(choices=[('employed_prof_full', 'Full Professor'), ('employed_prof_associate', 'Associate Professor'), ('employed_prof_assistant', 'Assistant Professor'), ('employed_prof_emeritus', 'Emeritus Professor'), ('employed_permanent_staff', 'Permanent Staff'), ('employed_fixed_term_staff', 'Fixed Term Staff'), ('employed_tenure_track', 'Tenure Tracker'), ('employed_postdoc', 'Postdoctoral Researcher'), ('employed_phd', 'PhD candidate'), ('associate_scientist', 'Associate Scientist'), ('consultant', 'Consultant'), ('visitor', 'Visitor'), ('unspecified', 'Unspecified')], default='unspecified', help_text='Select the most suitable category', max_length=64),
+        ),
+    ]
diff --git a/profiles/migrations/0022_auto_20190331_1926.py b/profiles/migrations/0022_auto_20190331_1926.py
new file mode 100644
index 0000000000000000000000000000000000000000..aaed115c4ba9240c0f6ac932133b795da4d385a2
--- /dev/null
+++ b/profiles/migrations/0022_auto_20190331_1926.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-31 17:26
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0021_auto_20190329_0744'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='affiliation',
+            name='description',
+            field=models.CharField(blank=True, max_length=256, null=True),
+        ),
+    ]
diff --git a/profiles/models.py b/profiles/models.py
index 2a7e9b5af27a07ebd2069a4c7ec0265d2082896c..0a4fafa693d19d57fe0b6f6eafa0f44bd0059b95 100644
--- a/profiles/models.py
+++ b/profiles/models.py
@@ -18,7 +18,8 @@ from journals.models import Publication, PublicationAuthorsTable
 from ontology.models import Topic
 from theses.models import ThesisLink
 
-from .constants import PROFILE_NON_DUPLICATE_REASONS
+from .constants import (PROFILE_NON_DUPLICATE_REASONS,
+                        AFFILIATION_CATEGORIES, AFFILIATION_CATEGORY_UNSPECIFIED)
 from .managers import ProfileQuerySet
 
 
@@ -164,3 +165,48 @@ class ProfileNonDuplicates(models.Model):
     @property
     def full_name(self):
         return '%s%s' % (self.profiles.first().last_name, self.profiles.first().first_name)
+
+
+
+################
+# Affiliations #
+################
+
+class Affiliation(models.Model):
+    """
+    Link between a Profile and an Organization, for a specified time interval.
+
+    Fields:
+    * profile
+    * organization
+    * description
+    * date_from
+    * date_until
+
+    Affiliations can overlap in time.
+
+    Ideally, each Profile would have at least one valid Affiliation at each moment
+    of time during the whole duration of that person's career.
+    """
+    profile = models.ForeignKey('profiles.Profile', on_delete=models.CASCADE,
+                                related_name='affiliations')
+    organization = models.ForeignKey('organizations.Organization', on_delete=models.CASCADE,
+                                     related_name='affiliations')
+    category = models.CharField(max_length=64, choices=AFFILIATION_CATEGORIES,
+                                default=AFFILIATION_CATEGORY_UNSPECIFIED,
+                                help_text='Select the most suitable category')
+    description = models.CharField(max_length=256, blank=True, null=True)
+    date_from = models.DateField(blank=True, null=True)
+    date_until = models.DateField(blank=True, null=True)
+
+    class Meta:
+        default_related_name = 'affiliations'
+        ordering = ['profile__last_name', 'profile__first_name',
+                    '-date_until']
+
+    def __str__(self):
+        return '%s, %s [%s to %s]' % (
+            str(self.profile),
+            str(self.organization),
+            self.date_from.strftime('%Y-%m-%d') if self.date_from else 'Undefined',
+            self.date_until.strftime('%Y-%m-%d') if self.date_until else 'Undefined')
diff --git a/profiles/templates/profiles/_affiliations_table.html b/profiles/templates/profiles/_affiliations_table.html
new file mode 100644
index 0000000000000000000000000000000000000000..9c3303f2b0ec6fc319eb20f8f93810ccfad5a9b0
--- /dev/null
+++ b/profiles/templates/profiles/_affiliations_table.html
@@ -0,0 +1,36 @@
+<table class="table">
+  <thead class="thead-default">
+    <tr>
+      <th>Organization</th>
+      <th>Category</th>
+      <th>From</th>
+      <th>Until</th>
+      {% if actions %}
+      <td>Actions</td>
+      {% endif %}
+    </tr>
+  </thead>
+  <tbody>
+    {% for aff in profile.affiliations.all %}
+    <tr>
+      <td>
+	{{ aff.organization }}
+	{% if aff.description %}<br/>&nbsp;<em>{{ aff.description }}</em>{% endif %}
+      </td>
+      <td>{{ aff.get_category_display }}</td>
+      <td>{% if aff.date_from %}{{ aff.date_from|date:'Y-m-d' }}{% else %}Undefined{% endif %}</td>
+      <td>{% if aff.date_until %}{{ aff.date_until|date:'Y-m-d' }}{% else %}Undefined{% endif %}</td>
+      {% if actions %}
+      <td>
+	<ul class="list-unstyled">
+	  <li><a href="{% url 'profiles:affiliation_update' profile_id=profile.id pk=aff.id %}" class="text-warning">Update</a></li>
+	  <li><a href="{% url 'profiles:affiliation_delete' profile_id=profile.id pk=aff.id %}" class="text-danger">Delete</a></li>
+	</ul>
+      </td>
+      {% endif %}
+    </tr>
+    {% empty %}
+    <tr><td colspan="4">No Affiliation has been defined</td></tr>
+    {% endfor %}
+  </tbody>
+</table>
diff --git a/profiles/templates/profiles/_profile_card.html b/profiles/templates/profiles/_profile_card.html
index 503bd04d65b0f3df805f52d734073582574cbae0..448842decfcb031d27ef066efda59fad491b1287 100644
--- a/profiles/templates/profiles/_profile_card.html
+++ b/profiles/templates/profiles/_profile_card.html
@@ -14,35 +14,44 @@
       <table class="table">
 	<tr>
 	  <td>Name:</td>
-      <td>{{ profile }}</td>
+	  <td>{{ profile }}</td>
 	</tr>
+	<tr>
+	  <td>Affiliations
+	    <ul>
+	      <li><a href="{% url 'profiles:affiliation_create' profile_id=profile.id %}">Add a new Affiliation</a></li>
+	    </ul>
+	  </td>
+	  <td>
+	    {% include 'profiles/_affiliations_table.html' with profile=profile actions=True %}
+	  </td>
 	<tr>
 	  <td>Email(s)</td>
 	  <td>
 	    <table class="table table-sm">
-            <thead>
+              <thead>
+		<tr>
+                  <th colspan="2">Email</th>
+                  <th>Still valid</th>
+                  <th></th>
+		</tr>
+              </thead>
+    	      {% for profile_mail in profile.emails.all %}
               <tr>
-                <th colspan="2">Email</th>
-                <th>Still valid</th>
-                <th></th>
+		<td>{% if profile_mail.primary %}<strong>{% endif %}{{ profile_mail.email }}{% if profile_mail.primary %}</strong>{% endif %}</td>
+		<td>{{ profile_mail.primary|yesno:'Primary,Alternative' }}</td>
+		<td>
+                  <i class="fa {{ profile_mail.still_valid|yesno:'fa-check-circle text-success,fa-times-circle text-danger' }}"></i>
+
+		</td>
+		<td class="d-flex">
+                  <form method="post" action="{% url 'profiles:toggle_email_status' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link">{{ profile_mail.still_valid|yesno:'Deprecate,Mark valid' }}</button></form>
+		  <form method="post" action="{% url 'profiles:email_make_primary' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link">Make primary</button></form>
+                  <form method="post" action="{% url 'profiles:delete_profile_email' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link text-danger ml-2" onclick="return confirm('Sure you want to delete {{ profile_mail.email }}?')"><i class="fa fa-trash"></i></button></form>
+		</td>
               </tr>
-            </thead>
-    	      {% for profile_mail in profile.emails.all %}
-        	      <tr>
-                      <td>{% if profile_mail.primary %}<strong>{% endif %}{{ profile_mail.email }}{% if profile_mail.primary %}</strong>{% endif %}</td>
-                      <td>{{ profile_mail.primary|yesno:'Primary,Alternative' }}</td>
-                      <td>
-                          <i class="fa {{ profile_mail.still_valid|yesno:'fa-check-circle text-success,fa-times-circle text-danger' }}"></i>
-
-                      </td>
-                      <td class="d-flex">
-                        <form method="post" action="{% url 'profiles:toggle_email_status' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link">{{ profile_mail.still_valid|yesno:'Deprecate,Mark valid' }}</button></form>
-			<form method="post" action="{% url 'profiles:email_make_primary' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link">Make primary</button></form>
-                        <form method="post" action="{% url 'profiles:delete_profile_email' profile_mail.id %}">{% csrf_token %}<button type="submit" class="btn btn-link text-danger ml-2" onclick="return confirm('Sure you want to delete {{ profile_mail.email }}?')"><i class="fa fa-trash"></i></button></form>
-                      </td>
-                  </tr>
     	      {% endfor %}
-          </table>
+            </table>
 	  </td>
 	</tr>
 	<tr>
diff --git a/profiles/templates/profiles/affiliation_confirm_delete.html b/profiles/templates/profiles/affiliation_confirm_delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..b7eeec911df8a8ef14a9eb0b0270c024d97be404
--- /dev/null
+++ b/profiles/templates/profiles/affiliation_confirm_delete.html
@@ -0,0 +1,29 @@
+{% extends 'profiles/base.html' %}
+
+{% load bootstrap %}
+
+{% block breadcrumb_items %}
+    {{ block.super }}
+    <span class="breadcrumb-item">Delete {{ affiliation }}</span>
+{% endblock %}
+
+{% block pagetitle %}: Delete Affiliation{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+    <div class="col-12">
+        <h1 class="highlight">Delete Affiliation</h1>
+	{% include 'profiles/_affiliations_table.html' with contributor=affiliation.profile.contributor actions=False %}
+    </div>
+</div>
+<div class="row">
+  <div class="col-12">
+    <form method="post">
+      {% csrf_token %}
+      <h3 class="mb-2">Are you sure you want to delete this Affiliation?</h3>
+      <input type="submit" class="btn btn-danger" value="Yes, delete it" />
+    </form>
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/profiles/templates/profiles/affiliation_form.html b/profiles/templates/profiles/affiliation_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..aefa75e6b83bba23ec9df9ebe3f3920dc0fcd408
--- /dev/null
+++ b/profiles/templates/profiles/affiliation_form.html
@@ -0,0 +1,46 @@
+{% extends 'profiles/base.html' %}
+
+{% load bootstrap %}
+
+{% load scipost_extras %}
+
+{% block breadcrumb_items %}
+    {{ block.super }}
+    <span class="breadcrumb-item">{% if form.instance.id %}Update{% else %}Add new{% endif %} Affiliation</span>
+{% endblock %}
+
+{% block pagetitle %}: Affiliation{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+  <div class="col-12">
+    <h3 class="highlight">Add a new Affiliation to your Profile</h3>
+    <p class="text-danger">Don't find the organization you need in our list? Please <a href="{% url 'helpdesk:ticket_create' %}">create a Ticket</a> providing us with the details, we'll get back to you!</p>
+    <form action="" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+
+      {% if form.errors %}
+      {% for field in form %}
+      {% for error in field.errors %}
+      <div class="alert alert-danger">
+	<strong>{{ field.name }} - {{ error|escape }}</strong>
+      </div>
+      {% endfor %}
+      {% endfor %}
+      {% for error in form.non_field_errors %}
+      <div class="alert alert-danger">
+	<strong>{{ error|escape }}</strong>
+      </div>
+      {% endfor %}
+      {% endif %}
+      <input type="submit" value="Submit" class="btn btn-primary">
+    </form>
+  </div>
+</div>
+{% endblock content %}
+
+{% block footer_script %}
+    {{ block.super }}
+    {{ form.media }}
+{% endblock footer_script %}
diff --git a/profiles/urls.py b/profiles/urls.py
index 5fc21faf8468a389094722ae9bd8478528b61237..9e248b56d0dc569406301b812873db07a3723f7e 100644
--- a/profiles/urls.py
+++ b/profiles/urls.py
@@ -72,4 +72,19 @@ urlpatterns = [
         views.delete_profile_email,
         name='delete_profile_email'
     ),
+    url(
+        r'^(?P<profile_id>[0-9]+)/affiliation/add/$',
+        views.AffiliationCreateView.as_view(),
+        name='affiliation_create'
+    ),
+    url(
+        r'^(?P<profile_id>[0-9]+)/affiliation/(?P<pk>[0-9]+)/update/$',
+        views.AffiliationUpdateView.as_view(),
+        name='affiliation_update'
+    ),
+    url(
+        r'^(?P<profile_id>[0-9]+)/affiliation/(?P<pk>[0-9]+)/delete/$',
+        views.AffiliationDeleteView.as_view(),
+        name='affiliation_delete'
+    ),
 ]
diff --git a/profiles/views.py b/profiles/views.py
index 33aee843b4fb8fe2244c4c8ca20c079d7654b5e4..096725e392741ae85c02d11ddddc6441221d136a 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
 
 
 from django.contrib import messages
+from django.contrib.auth.mixins import UserPassesTestMixin
 from django.core.urlresolvers import reverse, reverse_lazy
 from django.db import transaction
 from django.db.models import Q
@@ -24,8 +25,8 @@ from invitations.models import RegistrationInvitation
 from journals.models import UnregisteredAuthor
 from submissions.models import RefereeInvitation
 
-from .models import Profile, ProfileEmail
-from .forms import ProfileForm, ProfileMergeForm, ProfileEmailForm
+from .models import Profile, ProfileEmail, Affiliation
+from .forms import ProfileForm, ProfileMergeForm, ProfileEmailForm, AffiliationForm
 
 
 
@@ -240,7 +241,7 @@ class ProfileListView(PermissionsMixin, PaginationMixin, ListView):
         context = super().get_context_data(**kwargs)
         contributors_w_duplicate_email = Contributor.objects.with_duplicate_email()
         contributors_w_duplicate_names = Contributor.objects.with_duplicate_names()
-        contributors_wo_profile = Contributor.objects.active().filter(profile__isnull=True)
+        contributors_wo_profile = Contributor.objects.nonduplicates().filter(profile__isnull=True)
         nr_potential_duplicate_profiles = Profile.objects.potential_duplicates().count()
         unreg_auth_wo_profile = UnregisteredAuthor.objects.filter(profile__isnull=True)
         refinv_wo_profile = RefereeInvitation.objects.filter(profile__isnull=True)
@@ -377,3 +378,84 @@ def delete_profile_email(request, email_id):
     profile_email.delete()
     messages.success(request, 'Email deleted')
     return redirect(profile_email.profile.get_absolute_url())
+
+
+class AffiliationCreateView(UserPassesTestMixin, CreateView):
+    model = Affiliation
+    form_class = AffiliationForm
+    template_name = 'profiles/affiliation_form.html'
+
+    def test_func(self):
+        """
+        Allow creating an Affiliation if user is Admin, EdAdmin or is
+        the Contributor to which this Profile is related.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return True
+        return self.request.user.contributor.profile.id == int(self.kwargs.get('profile_id'))
+
+    def get_initial(self, *args, **kwargs):
+        initial = super().get_initial(*args, **kwargs)
+        profile = get_object_or_404(Profile, pk=self.kwargs.get('profile_id'))
+        initial.update({
+            'profile': profile
+        })
+        return initial
+
+    def get_success_url(self):
+        """
+        If request.user is Admin or EdAdmin, redirect to profile detail view.
+        Otherwise if request.user is Profile owner, return to personal page.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return reverse_lazy('profiles:profile_detail',
+                                kwargs={'pk': self.object.profile.id})
+        return reverse_lazy('scipost:personal_page')
+
+
+class AffiliationUpdateView(UserPassesTestMixin, UpdateView):
+    model = Affiliation
+    form_class = AffiliationForm
+    template_name = 'profiles/affiliation_form.html'
+
+    def test_func(self):
+        """
+        Allow updating an Affiliation if user is Admin, EdAdmin or is
+        the Contributor to which this Profile is related.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return True
+        return self.request.user.contributor.profile.id == int(self.kwargs.get('profile_id'))
+
+    def get_success_url(self):
+        """
+        If request.user is Admin or EdAdmin, redirect to profile detail view.
+        Otherwise if request.user is Profile owner, return to personal page.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return reverse_lazy('profiles:profile_detail',
+                                kwargs={'pk': self.object.profile.id})
+        return reverse_lazy('scipost:personal_page')
+
+
+class AffiliationDeleteView(UserPassesTestMixin, DeleteView):
+    model = Affiliation
+
+    def test_func(self):
+        """
+        Allow deleting an Affiliation if user is Admin, EdAdmin or is
+        the Contributor to which this Profile is related.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return True
+        return self.request.user.contributor.profile.id == int(self.kwargs.get('profile_id'))
+
+    def get_success_url(self):
+        """
+        If request.user is Admin or EdAdmin, redirect to profile detail view.
+        Otherwise if request.user is Profile owner, return to personal page.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return reverse_lazy('profiles:profile_detail',
+                                kwargs={'pk': self.object.profile.id})
+        return reverse_lazy('scipost:personal_page')
diff --git a/requirements.txt b/requirements.txt
index 5f8150f36acdde695738326547f04b47cbd0be9e..d7e634573575b5f14057c1bcae8036a706af36ec 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -32,6 +32,8 @@ Pygments==2.2.0  # Syntax highlighter
 Sphinx==1.4.9
 sphinx-rtd-theme==0.1.9  # Sphinx theme
 
+# Sentry
+sentry-sdk==0.7.8
 
 # Testing
 factory-boy==2.10.0
diff --git a/scipost/forms.py b/scipost/forms.py
index 23094e18e6c17dbdf9c423f3231f214f9a41ebf3..d490ba10fc88291e20f11a8bd2890b390eeee209 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -6,6 +6,7 @@ import datetime
 import pyotp
 
 from django import forms
+from django.contrib.auth import authenticate
 from django.contrib.auth.models import User, Group
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.password_validation import validate_password
@@ -16,7 +17,6 @@ from django.utils.dates import MONTHS
 
 from django_countries import countries
 from django_countries.widgets import CountrySelectWidget
-from django_countries.fields import LazyTypedChoiceField
 
 from ajax_select.fields import AutoCompleteSelectField
 from haystack.forms import ModelSearchForm as HayStackSearchForm
@@ -31,7 +31,7 @@ from .models import Contributor, DraftInvitation, UnavailabilityPeriod, \
     Remark, AuthorshipClaim, PrecookedEmail, TOTPDevice
 from .totp import TOTPVerification
 
-from affiliations.models import Affiliation, Institution
+from affiliations.models import Affiliation as deprec_Affiliation
 from common.forms import MonthYearWidget, ModelChoiceFieldwithid
 from organizations.decorators import has_contact
 
@@ -42,6 +42,7 @@ from funders.models import Grant
 from invitations.models import CitationNotification
 from journals.models import PublicationAuthorsTable, Publication
 from mails.utils import DirectMailUtil
+from profiles.models import Profile, ProfileEmail, Affiliation
 from submissions.models import Submission, EditorialAssignment, RefereeInvitation, Report, \
     EditorialCommunication, EICRecommendation
 from theses.models import ThesisLink
@@ -96,15 +97,18 @@ class RegistrationForm(forms.Form):
         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',
-        widget=CountrySelectWidget(layout=(
-            '{widget}<img class="country-select-flag" id="{flag_id}"'
-            ' style="margin: 6px 4px 0" src="{country.flag}">')))
-    affiliation = forms.CharField(label='* Affiliation', max_length=300)
+    current_affiliation = AutoCompleteSelectField(
+        'organization_lookup',
+        help_text=('Start typing, then select in the popup; '
+                   'if you do not find the organization you seek, '
+                   'please fill in your institution name and address instead.'),
+        show_help_text=False,
+        required=False,
+        label='* Current affiliation')
     address = forms.CharField(
-        label='Address', max_length=1000,
-        widget=forms.TextInput({'placeholder': 'For postal correspondence'}), required=False)
+        label='Institution name and address', max_length=1000,
+        widget=forms.TextInput({'placeholder': '[only if you did not find your affiliation above]'}),
+        required=False)
     personalwebpage = forms.URLField(
         label='Personal web page', required=False,
         widget=forms.TextInput({'placeholder': 'full URL, e.g. https://www.[yourpage].com'}))
@@ -113,10 +117,24 @@ class RegistrationForm(forms.Form):
     password = forms.CharField(label='* Password', widget=forms.PasswordInput())
     password_verif = forms.CharField(label='* Verify password', widget=forms.PasswordInput(),
                                      help_text='Your password must contain at least 8 characters')
-    captcha = ReCaptchaField(label='*Please verify to continue:')
+    captcha = ReCaptchaField(label='* Please verify to continue:')
     subscribe = forms.BooleanField(
         required=False, initial=False, label='Stay informed, subscribe to the SciPost newsletter.')
 
+    def clean(self):
+        """
+        Check that either an organization or an address are provided.
+        """
+        cleaned_data = super(RegistrationForm, self).clean()
+        current_affiliation = cleaned_data.get('current_affiliation', None)
+        address = cleaned_data.get('address', '')
+
+        if current_affiliation is None and address == '':
+            raise forms.ValidationError(
+                'You must either specify a Current Affiliation, or '
+                'fill in the institution name and address field'
+            )
+
     def clean_password(self):
         password = self.cleaned_data.get('password', '')
         user = User(
@@ -155,11 +173,34 @@ class RegistrationForm(forms.Form):
             'password': self.cleaned_data['password'],
             'is_active': False
         })
-        institution, __ = Institution.objects.get_or_create(
-            country=self.cleaned_data['country_of_employment'],
-            name=self.cleaned_data['affiliation'],
-        )
+        # Get or create a Profile
+        profile = Profile.objects.filter(
+            title=self.cleaned_data['title'],
+            first_name=self.cleaned_data['first_name'],
+            last_name=self.cleaned_data['last_name'],
+            discipline=self.cleaned_data['discipline']).first()
+        if profile is None:
+            profile = Profile.objects.create(
+                title=self.cleaned_data['title'],
+                first_name=self.cleaned_data['first_name'],
+                last_name=self.cleaned_data['last_name'],
+                discipline=self.cleaned_data['discipline'],
+                orcid_id=self.cleaned_data['orcid_id'],
+                webpage=self.cleaned_data['personalwebpage'])
+        # Add a ProfileEmail to this Profile
+        profile_email = ProfileEmail.objects.get_or_create(
+            profile=profile, email=self.cleaned_data['email'])
+        profile.emails.update(primary=False)
+        profile.emails.filter(id=profile_email.id).update(primary=True, still_valid=True)
+        # Create an Affiliation for this Profile
+        current_affiliation = self.cleaned_data.get('current_affiliation', None)
+        if current_affiliation:
+            Affiliation.objects.create(
+                profile=profile,
+                organization=self.cleaned_data['current_affiliation'])
+        # Create the Contributor object
         contributor, __ = Contributor.objects.get_or_create(**{
+            'profile': profile,
             'user': user,
             'invitation_key': self.cleaned_data.get('invitation_key', ''),
             'title': self.cleaned_data['title'],
@@ -168,10 +209,6 @@ class RegistrationForm(forms.Form):
             'personalwebpage': self.cleaned_data['personalwebpage'],
             'accepts_SciPost_emails': self.cleaned_data['subscribe'],
         })
-        affiliation, __ = Affiliation.objects.get_or_create(
-            contributor=contributor,
-            institution=institution,
-        )
         contributor.save()
         return contributor
 
@@ -297,30 +334,58 @@ class SciPostAuthenticationForm(AuthenticationForm):
     Inherits from django.contrib.auth.forms:AuthenticationForm.
 
     Extra fields:
-    - next: url for the next page, obtainable via POST
+    * next: url for the next page, obtainable via POST
 
     Overriden methods:
-    - confirm_login_allowed: disallow inactive or unvetted accounts.
+    * clean: allow either username, or email as substitute for username
+    * confirm_login_allowed: disallow inactive or unvetted accounts.
     """
     next = forms.CharField(widget=forms.HiddenInput(), required=False)
     code = forms.CharField(
         required=False, widget=forms.TextInput(attrs={'autocomplete': 'off'}),
         help_text="Please type in the code displayed on your authenticator app from your device")
 
+    def clean(self):
+        """Allow either username, or email as substitute for username."""
+        username = self.cleaned_data.get('username')
+        password = self.cleaned_data.get('password')
+
+        if username is not None and password:
+            self.user_cache = authenticate(self.request, username=username, password=password)
+            if self.user_cache is None:
+                try:
+                    _user = User.objects.get(email=username)
+                    self.user_cache = authenticate(
+                        self.request, username=_user.username, password=password)
+                except:
+                    pass
+            if self.user_cache is None:
+                raise forms.ValidationError(
+                    ("Please enter a correct %(username)s and password. "
+                     "Note that both fields may be case-sensitive. "
+                     "Your can use your email instead of your username."),
+                    code='invalid_login',
+                    params={'username': self.username_field.verbose_name},
+                )
+            else:
+                self.confirm_login_allowed(self.user_cache)
+
+        return self.cleaned_data
+
     def confirm_login_allowed(self, user):
         if not user.is_active:
             raise forms.ValidationError(
                 ('Your account is not yet activated. '
-                  'Please first activate your account by clicking on the '
-                  'activation link we emailed you.'),
+                 'Please first activate your account by clicking on the '
+                 'activation link we emailed you.'),
                 code='inactive',
-                )
+            )
         if not user.groups.exists():
             raise forms.ValidationError(
                 ('Your account has not yet been vetted.\n'
-                  'Our admins will verify your credentials very soon, '
-                  'and if vetted (your will receive an information email) '
-                  'you will then be able to login.'),
+                 'Our admins will verify your credentials very soon, '
+                 'and if vetted (your will receive an information email) '
+                 'you will then be able to login.'),
                 code='unvetted',
                 )
         if user.devices.exists():
@@ -467,7 +532,7 @@ class ContributorMergeForm(forms.Form):
         contrib_from_qs.update(duplicate_of=contrib_into)
 
         # Step 2: update all ForeignKey relations
-        Affiliation.objects.filter(contributor=contrib_from).update(contributor=contrib_into)
+        deprec_Affiliation.objects.filter(contributor=contrib_from).update(contributor=contrib_into)
         Fellowship.objects.filter(contributor=contrib_from).update(contributor=contrib_into)
         PotentialFellowshipEvent.objects.filter(
             noted_by=contrib_from).update(noted_by=contrib_into)
diff --git a/scipost/managers.py b/scipost/managers.py
index 8fe31e821eacc7ed6574fad3811bafdfbdb38ba6..d3844a2a288b370c6f1498f6ff6518f1c02ff479 100644
--- a/scipost/managers.py
+++ b/scipost/managers.py
@@ -57,6 +57,30 @@ class ContributorQuerySet(models.QuerySet):
         """TODO: NEEDS UPDATE TO NEW FELLOWSHIP RELATIONS."""
         return self.filter(fellowships__isnull=False).distinct()
 
+    def specialties_overlap(self, discipline, expertises=[]):
+        """
+        Returns all Contributors specialized in the given discipline
+        and any of the (optional) expertises.
+
+        This method is also separately implemented for Profile and Fellowship objects.
+        """
+        qs = self.filter(profile__discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(profile__expertises__overlap=expertises)
+        return qs
+
+    def specialties_contain(self, discipline, expertises=[]):
+        """
+        Returns all Contributors specialized in the given discipline
+        and all of the (optional) expertises.
+
+        This method is also separately implemented for Profile and Fellowship objects.
+        """
+        qs = self.filter(profile__discipline=discipline)
+        if expertises and len(expertises) > 0:
+            qs = qs.filter(profile__expertises__contains=expertises)
+        return qs
+
     def with_duplicate_names(self):
         """
         Returns only potential duplicate Contributors (as identified by first and
diff --git a/scipost/templates/partials/scipost/personal_page/account.html b/scipost/templates/partials/scipost/personal_page/account.html
index 39af7836ac87e1909d63e586b37bc88a92218460..a5d6d423a459c7b4c4e7e9701904f588ad0d2512 100644
--- a/scipost/templates/partials/scipost/personal_page/account.html
+++ b/scipost/templates/partials/scipost/personal_page/account.html
@@ -57,7 +57,7 @@
 
         {% if recommend_totp %}
             <div class="border border-danger p-2 mb-3">
-                  <h3 class="text-warningx">
+                  <h3>
                       <i class="fa fa-exclamation-triangle text-danger"></i>
                       Please increase your account's security</h3>
                   <div>
@@ -162,6 +162,16 @@
     </div>
 </div>
 
+<div class="row">
+  <div class="col-12">
+    <h3>Your Affiliations:</h3>
+    <ul>
+      <li><a href="{% url 'profiles:affiliation_create' profile_id=contributor.profile.id %}">Add a new Affiliation</a></li>
+    </ul>
+    {% include 'profiles/_affiliations_table.html' with profile=contributor.profile actions=True %}
+  </div>
+</div>
+
 {% if unavailability_form %}
     <hr>
     <div class="row">
diff --git a/scipost/templates/scipost/_private_info_as_table.html b/scipost/templates/scipost/_private_info_as_table.html
index 5e2450b5f83d399961cc4a7f0714c1ecfa3be515..ea9cdd7b69cbb1e4c2301dca6d268d1d8ed23a6f 100644
--- a/scipost/templates/scipost/_private_info_as_table.html
+++ b/scipost/templates/scipost/_private_info_as_table.html
@@ -5,15 +5,10 @@
     <tr><td>Email: </td><td>{{ contributor.user.email }}</td></tr>
     <tr><td>ORCID id: </td><td>{{ contributor.orcid_id }}</td></tr>
     <tr>
-        <td>Affiliation(s):</td>
-        <td>
-            {% for affiliation in contributor.affiliations.active %}
-                {% if not forloop.first %}
-                    <br>
-                {% endif %}
-                {{ affiliation.institution }}
-            {% endfor %}
-        </td>
+      <td>Affiliation(s):</td>
+      <td>
+	{% include 'profiles/_affiliations_table.html' with contributor=contributor actions=False %}
+      </td>
     </tr>
     <tr><td>Address: </td><td>{{ contributor.address }}</td></tr>
     <tr><td>Personal web page: </td><td>{{ contributor.personalwebpage }}</td></tr>
diff --git a/scipost/templates/scipost/_public_info_as_table.html b/scipost/templates/scipost/_public_info_as_table.html
index a3b73a41a3193ec35862d22f53d11d96c0e412dc..af335e2362dea207721c17f08091e9e8b2894972 100644
--- a/scipost/templates/scipost/_public_info_as_table.html
+++ b/scipost/templates/scipost/_public_info_as_table.html
@@ -3,17 +3,6 @@
     <tr><td>First name: </td><td>{{ contributor.user.first_name }}</td></tr>
     <tr><td>Last name: </td><td>{{ contributor.user.last_name }}</td></tr>
     <tr><td>ORCID id: </td><td>{{ contributor.orcid_id|default:'-' }}</td></tr>
-    <tr>
-        <td>Affiliation(s):</td>
-        <td>
-            {% for affiliation in contributor.affiliations.active %}
-                {% if not forloop.first %}
-                    <br>
-                {% endif %}
-                {{ affiliation.institution }}
-            {% endfor %}
-        </td>
-    </tr>
     <tr><td>Personal web page: </td><td>{{ contributor.personalwebpage|default:'-' }}</td></tr>
 
     {% if perms.scipost.can_vet_registration_requests %}
diff --git a/scipost/templates/scipost/navbar.html b/scipost/templates/scipost/navbar.html
index aa54892400d8988b7bb2ee49fc929a9b4c512250..aec258392e028312046eae70fc67cd0c3492d0ff 100644
--- a/scipost/templates/scipost/navbar.html
+++ b/scipost/templates/scipost/navbar.html
@@ -64,6 +64,8 @@
                         </div>
                         <div class="col-md-6">
                           <div class="dropdown-item"><a href="{% url 'submissions:submit_manuscript' %}">Submit a manuscript</a>&nbsp;<i class="fa fa-angle-right" aria-hidden="true"></i></div>
+                          <div class="dropdown-item"><a href="{% url 'submissions:author_guidelines' %}">Author guidelines</a>&nbsp;<i class="fa fa-angle-right" aria-hidden="true"></i></div>
+                          <div class="dropdown-item"><a href="{% url 'submissions:referee_guidelines' %}">Referee guidelines</a>&nbsp;<i class="fa fa-angle-right" aria-hidden="true"></i></div>
                         </div>
                     </div>
                 <div class="dropdown-divider"></div>
diff --git a/scipost/templates/scipost/register.html b/scipost/templates/scipost/register.html
index 1a77c273be8194416e99a555dd65adeaba6229ff..50dd299da035e04838313ca5a7350fcc81bf9d75 100644
--- a/scipost/templates/scipost/register.html
+++ b/scipost/templates/scipost/register.html
@@ -44,3 +44,8 @@
 
 
 {% endblock content %}
+
+{% block footer_script %}
+    {{ block.super }}
+    {{ form.media }}
+{% endblock footer_script %}
diff --git a/scipost/views.py b/scipost/views.py
index 72400cedb28c51630ae0d15187b9a4502b7ea7e0..c1c0f843c9d054d34b75b6b88b5c1506f35fe73d 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -402,12 +402,12 @@ class SciPostLoginView(LoginView):
     Inherits from django.contrib.auth.views:LoginView.
 
     Overriden fields:
-    - template_name
-    - authentication_form
+    * template_name
+    * authentication_form
 
     Overriden methods:
-    - get initial: allow prefilling with GET data, for 'next'
-    - get redirect url
+    * get initial: allow prefilling with GET data, for 'next'
+    * get redirect url
     """
 
     template_name = 'scipost/login.html'
@@ -466,10 +466,10 @@ class SciPostPasswordChangeView(PasswordChangeView):
     Inherits from django.contrib.auth.views:PasswordChangeView.
 
     Overriden fields:
-    - template_name
+    * template_name
 
     Overriden methods:
-    - get_success_url
+    * get_success_url
     """
     template_name = 'scipost/password_change.html'
 
@@ -491,12 +491,12 @@ class SciPostPasswordResetView(PasswordResetView):
     Derived from django.contrib.auth.views:PasswordResetView.
 
     Overriden fields:
-    - template_name
-    - email_template_name
-    - subject_template_name
+    * template_name
+    * email_template_name
+    * subject_template_name
 
     Overriden methods:
-    - get_success_url
+    * get_success_url
     """
 
     template_name = 'scipost/password_reset.html'
@@ -520,10 +520,10 @@ class SciPostPasswordResetConfirmView(PasswordResetConfirmView):
     Derived from django.contrib.auth.views:PasswordResetConfirmView.
 
     Overriden fields:
-    - template_name
+    * template_name
 
     Overriden methods:
-    - get_success_url
+    * get_success_url
     """
     template_name = 'scipost/password_reset_confirm.html'
 
diff --git a/submissions/admin.py b/submissions/admin.py
index 3d27be9bce38695553eadfe5f0e549f27b2eaf2a..ddf6b838a5e32fa1101a8ea86780d8625bca0841 100644
--- a/submissions/admin.py
+++ b/submissions/admin.py
@@ -48,7 +48,7 @@ class SubmissionAdminForm(forms.ModelForm):
 class SubmissionAdmin(GuardedModelAdmin):
     date_hierarchy = 'submission_date'
     form = SubmissionAdminForm
-    list_display = ('title', 'author_list', 'submitted_to',
+    list_display = ('title', 'author_list', 'preprint', 'submitted_to',
                     'status', 'visible_public', 'visible_pool', 'refereeing_cycle',
                     'submission_date', 'publication')
     list_filter = ('status', 'discipline', 'submission_type', 'submitted_to')
diff --git a/submissions/management/commands/email_fellows_tasklist.py b/submissions/management/commands/email_fellows_tasklist.py
index 738473672d0b4a7b3ddad6426d6556ed4bd04f74..7425635d5187a336d97fa9832f2b8cd2261f6e10 100644
--- a/submissions/management/commands/email_fellows_tasklist.py
+++ b/submissions/management/commands/email_fellows_tasklist.py
@@ -28,13 +28,16 @@ class Command(BaseCommand):
             assignments_upcoming_deadline = assignments_ongoing.refereeing_deadline_within(days=7)
             if recs_to_vote_on or assignments_ongoing or assignments_to_consider or assignments_upcoming_deadline:
                 mail_sender = DirectMailUtil(
-                    'fellows/email_fellow_tasklist', delayed_processing=False,  # Render immediately, because m2m/querysets cannot be saved for later rendering.
+                    'fellows/email_fellow_tasklist',
+                    # Render immediately, because m2m/querysets cannot be saved for later rendering:
+                    delayed_processing=False,
+                    object=fellow,
                     fellow=fellow,
                     nr_potfels_to_vote_on=nr_potfels_to_vote_on,
                     recs_to_vote_on=recs_to_vote_on,
                     assignments_ongoing=assignments_ongoing,
                     assignments_to_consider=assignments_to_consider,
                     assignments_upcoming_deadline=assignments_upcoming_deadline)
-                mail_sender.send()
+                mail_sender.send_mail()
                 count += 1
         self.stdout.write(self.style.SUCCESS('Emailed {} fellows.'.format(count)))
diff --git a/submissions/refereeing_cycles.py b/submissions/refereeing_cycles.py
index 21e264835c5d845773a31cfaeda67110c2e7a142..d89ee510bc42789af338f52e958c857459d29d53 100644
--- a/submissions/refereeing_cycles.py
+++ b/submissions/refereeing_cycles.py
@@ -239,7 +239,7 @@ class BaseCycle:
                 self.add_action(action)
 
         # Submission is a resubmission: EIC has to determine which cycle to proceed with.
-        comments_to_vet = self._submission.comments.awaiting_vetting().values_list('id')
+        comments_to_vet = self._submission.comments.awaiting_vetting()
         for comment in comments_to_vet:
             self.add_action(VettingAction(comment))
 
diff --git a/submissions/templates/submissions/_guidelines_dl.html b/submissions/templates/submissions/_guidelines_dl.html
new file mode 100644
index 0000000000000000000000000000000000000000..237f8856a230b01905fd2058e79d2bde1ba63a13
--- /dev/null
+++ b/submissions/templates/submissions/_guidelines_dl.html
@@ -0,0 +1,41 @@
+<div class="card">
+  <div class="card-header">
+    Useful further links
+  </div>
+  <div class="card-body pb-0">
+    <div class="small">
+      <dl class="row">
+	<dt class="col-sm-6 col-md-5">Page</dt>
+	<dd class="col-sm-6 col-md-7"><em>... where you will find info on:</em></dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if 'by-laws' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'scipost:EdCol_by-laws' %}">Editorial College by-laws</a></dt>
+	<dd class="col-sm-6 col-md-7"><em>Official rules for all our editorial workflows</em></dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if 'journals_terms_and_conditions' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'journals:journals_terms_and_conditions' %}">Journals Terms and Conditions</a></dt>
+	<dd class="col-sm-6 col-md-7"><em>Expectations, Open Access policy, license and copyright, author obligations, referee code of conduct, corrections and retractions</em></dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if '/terms_and_conditions' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'scipost:terms_and_conditions' %}">SciPost Terms and Conditions</a></dt>
+	<dd class="col-sm-6 col-md-7"><em>General terms and conditions pertaining to ownership, license to use, contributions, impermissible uses, etc.</em></dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if 'author_guidelines' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'submissions:author_guidelines' %}">Author guidelines</a>
+	</dt>
+	<dd class="col-sm-6 col-md-7"><em>A simple guide on how to proceed as an author</em>
+	</dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if 'sub_and_ref_procedure' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'submissions:sub_and_ref_procedure' %}">Submission and refereeing procedure</a>
+	</dt>
+	<dd class="col-sm-6 col-md-7"><em>More details about submission procedure and refereeing protocols</em>
+	</dd>
+	<dt class="col-sm-6 col-md-5">
+	  {% if 'referee_guidelines' in request.path %}<i class="fa fa-arrow-circle-right text-success"></i>{% endif %}
+	  <a href="{% url 'submissions:referee_guidelines' %}">Refereeing guidelines</a></dt>
+	<dd class="col-sm-6 col-md-7"><em>A simple guide on how (if you are a referee) to act professionally, and (if you are an author) react constructively</em></dd>
+      </dl>
+    </div>
+  </div>
+</div>
diff --git a/submissions/templates/submissions/author_guidelines.html b/submissions/templates/submissions/author_guidelines.html
index 2c452d8b28bef015dfcd382cf901080b410c4e09..10fa8b389a8bbc768d58cd592f9d149277e824d2 100644
--- a/submissions/templates/submissions/author_guidelines.html
+++ b/submissions/templates/submissions/author_guidelines.html
@@ -18,30 +18,21 @@
 </div>
 
 <div class="row">
-  <div class="col-lg-6">
-
-<h4>On this page:</h4>
-<ul>
-  <li><a href="#manuprep">Manuscript preparation</a></li>
-  <li><a href="#manusub">Manuscript submission</a></li>
-  <li><a href="#refphase">The refereeing phase</a></li>
-  <li><a href="#resub">Resubmission</a></li>
-  <li><a href="#postacceptance">Post-acceptance</a></li>
-  <li><a href="#FAQ">Frequently asked questions</a></li>
-</ul>
-
+  <div class="col-lg-5">
+    <h4>The following is a quick summary guide to authoring at SciPost.</h4>
+    <p>It is not meant to replace our official rules and other helpful pages, which you will find in the Useful links box next to this menu.</p>
+    <h4>On this page:</h4>
+    <ul>
+      <li><a href="#manuprep">Manuscript preparation</a></li>
+      <li><a href="#manusub">Manuscript submission</a></li>
+      <li><a href="#refphase">The refereeing phase</a></li>
+      <li><a href="#resub">Resubmission</a></li>
+      <li><a href="#postacceptance">Post-acceptance</a></li>
+      <li><a href="#FAQ">Frequently asked questions</a></li>
+    </ul>
   </div>
-  <div class="col-lg-6">
-    <p>
-      The following is a general guide to authoring at SciPost.
-      It does not replace
-      our Journals' <a href="{% url 'journals:journals_terms_and_conditions' %}">Terms and Conditions</a>
-      (which extend the general <a href="{% url 'scipost:terms_and_conditions' %}">SciPost Terms and Conditions</a>), and <a href="{% url 'scipost:EdCol_by-laws' %}">Editorial College by-laws</a> which remain in force as official rules.
-    </p>
-    <p>
-      Before submitting to SciPost, you should familiarize yourself with the
-      <a href="{% url 'journals:journals_terms_and_conditions' %}">SciPost Journals Terms and Conditions</a>.
-    </p>
+  <div class="col-lg-7">
+    {% include 'submissions/_guidelines_dl.html' %}
   </div>
 </div>
 
diff --git a/submissions/templates/submissions/referee_guidelines.html b/submissions/templates/submissions/referee_guidelines.html
index 712cd750cc112a5c35aea38e27c3d27d5839a469..1b918c46a879ecdbf6c0e61dcd0e4af2cddb0709 100644
--- a/submissions/templates/submissions/referee_guidelines.html
+++ b/submissions/templates/submissions/referee_guidelines.html
@@ -16,49 +16,48 @@
 <h2 class="highlight-x">A guide for referees and authors</h2>
 
 <div class="row">
-  <div class="col-lg-6">
+  <div class="col-lg-5">
+    <p>
+      The following is a general guide to refereeing at SciPost.
+      Slightly informal in style, it is meant to introduce you to how our peer review system works.
+    </p>
+    <p>It is not meant to replace our official rules and other helpful pages, which you will find in the Useful links box next to this menu.</p>
 
-<h4>On this page:</h4>
-<ul>
-  <li><a href="#forReferees">For Referees</a>
-    <ul>
-      <li><a href="#refereeInvited">Have you been invited to write a Report?</a></li>
-      <li><a href="#refereeContributor">Do you wish to contribute a Report?</a></li>
-      <li><a href="#refereeWhatIsAsked">What do we ask you in our Report form?</a></li>
-      <li><a href="#refereeBeforeWritingReport">Before you write your Report</a></li>
-      <li><a href="#refereeWritingReport">Writing your Report</a></li>
-      <li><a href="#refereeGoodReport">The characteristics of a good Report</a></li>
-      <li><a href="#refereeFAQ">FAQ</a></li>
-    </ul>
-  </li>
+    <p>
+      You should already be somewhat familiar with our <a href="{% url 'submissions:sub_and_ref_procedure' %}">submission and refereeing procedure</a>.
+    </p>
+    <p>
+      Referees should in particular be familiar with
+      our <a href="{% url 'journals:journals_terms_and_conditions' %}#referee_code_of_conduct">referee code of conduct</a>.
+      Authors should in particular be familiar with
+      our <a href="{% url 'journals:journals_terms_and_conditions' %}#author_obligations">author obligations</a>.
+    </p>
 
-  <li><a href="#forAuthors">For Authors</a>
+    <h4>On this page:</h4>
     <ul>
-      <li><a href="#authorsReact">How to react to a Report?</a></li>
-    </ul>
-  </li>
-</ul>
+      <li><a href="#forReferees">For Referees</a>
+	<ul>
+	  <li><a href="#refereeInvited">Have you been invited to write a Report?</a></li>
+	  <li><a href="#refereeContributor">Do you wish to contribute a Report?</a></li>
+	  <li><a href="#refereeWhatIsAsked">What do we ask you in our Report form?</a></li>
+	  <li><a href="#refereeBeforeWritingReport">Before you write your Report</a></li>
+	  <li><a href="#refereeWritingReport">Writing your Report</a></li>
+	  <li><a href="#refereeGoodReport">The characteristics of a good Report</a></li>
+	  <li><a href="#refereeFAQ">FAQ</a></li>
+	</ul>
+      </li>
 
-  </div>
-  <div class="col-lg-6">
-<p>
-  The following is a general guide to refereeing at SciPost.
-  Slightly informal in style, it is meant to introduce you to how our peer review system works.
-  It does not replace
-  our Journals' <a href="{% url 'journals:journals_terms_and_conditions' %}">Terms and Conditions</a>
-  (which extend the general <a href="{% url 'scipost:terms_and_conditions' %}">SciPost Terms and Conditions</a>), and <a href="{% url 'scipost:EdCol_by-laws' %}">Editorial College by-laws</a> which remain in force as official rules.
-</p>
-<p>
-  You should already be somewhat familiar with our <a href="{% url 'submissions:sub_and_ref_procedure' %}">submission and refereeing procedure</a>.
-</p>
-<p>
-  Referees should in particular be familiar with
-  our <a href="{% url 'journals:journals_terms_and_conditions' %}#referee_code_of_conduct">referee code of conduct</a>.
-  Authors should in particular be familiar with
-  our <a href="{% url 'journals:journals_terms_and_conditions' %}#author_obligations">author obligations</a>.
-</p>
+      <li><a href="#forAuthors">For Authors</a>
+	<ul>
+	  <li><a href="#authorsReact">How to react to a Report?</a></li>
+	</ul>
+      </li>
+    </ul>
   </div>
 
+  <div class="col-lg-7">
+    {% include 'submissions/_guidelines_dl.html' %}
+  </div>
 </div>
 
 <hr>
diff --git a/submissions/templates/submissions/sub_and_ref_procedure.html b/submissions/templates/submissions/sub_and_ref_procedure.html
index a57a79d4ae5cd9a842eb1de9d5e73f94e86ce2b4..a9e67fdf4b734500a86c6fb224c22c148c407896 100644
--- a/submissions/templates/submissions/sub_and_ref_procedure.html
+++ b/submissions/templates/submissions/sub_and_ref_procedure.html
@@ -15,6 +15,12 @@
     </div>
 </div>
 
+<div class="row">
+  <div class="col-lg-7">
+    {% include 'submissions/_guidelines_dl.html' %}
+  </div>
+</div>
+
 {% if perms.scipost.can_attend_VGMs %}
 <div class="container border border-danger">
   <span class="text-danger">DRAFT (POOLVIEW ONLY) - <a href="mailto:admin@scipost.org">COMMENTS WELCOME</a></span>
diff --git a/templates/email/fellows/email_fellow_tasklist.html b/templates/email/fellows/email_fellow_tasklist.html
index adb315f44f39f6a1142e3077195e38aae1e82031..6b10719dd889956f88f51c02d569868af74e86a0 100644
--- a/templates/email/fellows/email_fellow_tasklist.html
+++ b/templates/email/fellows/email_fellow_tasklist.html
@@ -45,12 +45,14 @@
             {% if assignment.submission.cycle.has_required_actions %}
                 <h3>Required actions (go to the <a href="https://scipost.org{% url 'submissions:editorial_page' assignment.submission.preprint.identifier_w_vn_nr %}">Editorial page</a> to carry them out):</h3>
                 <ul>
-                  {% for action in assignment.submission.cycle.get_required_actions %}
-                      <li>{{action.1}}</li>
+                  {% for code, action in assignment.submission.cycle.required_actions.items %}
+                      <li>{{ action|safe }}</li>
                   {% empty %}
                       <li>No action required. Great job!</li>
                   {% endfor %}
                 </ul>
+            {% else %}
+                <p>No action required. Great job!</p>
             {% endif %}
           </li>
       {% endfor %}
diff --git a/virtualmeetings/management/commands/transfer_old_motion_to_new.py b/virtualmeetings/management/commands/transfer_old_motion_to_new.py
new file mode 100644
index 0000000000000000000000000000000000000000..e65b27169a01876ae670e9a195cd2fa5be3698ea
--- /dev/null
+++ b/virtualmeetings/management/commands/transfer_old_motion_to_new.py
@@ -0,0 +1,40 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.core.management.base import BaseCommand
+from django.contrib.contenttypes.models import ContentType
+
+from virtualmeetings.models import Motion as deprec_Motion
+from forums.models import Post, Motion
+
+class Command(BaseCommand):
+    help = ('Temporary method to transfer old virtualmeetings.Motions '
+            'to new forums.Motion ones.')
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            '--old_pk', action='store', default=0, type=int, dest='old_pk', help='Old Motion id')
+        parser.add_argument(
+            '--new_pk', action='store', default=0, type=int, dest='new_pk', help='New Motion id')
+
+    def handle(self, *args, **kwargs):
+        old_motion = deprec_Motion.objects.get(pk=kwargs['old_pk'])
+        new_motion = Motion.objects.get(pk=kwargs['new_pk'])
+        # Transfer the votes
+        for voter in old_motion.in_agreement.all():
+            new_motion.in_agreement.add(voter.user)
+        for voter in old_motion.in_notsure.all():
+            new_motion.in_doubt.add(voter.user)
+        for voter in old_motion.in_disagreement.all():
+            new_motion.in_disagreement.add(voter.user)
+        # Transfer the old remarks to Post objects
+        type_motion = ContentType.objects.get_by_natural_key('forums', 'post')
+        for remark in old_motion.remarks.all():
+            Post.objects.get_or_create(
+                posted_by=remark.contributor.user,
+                posted_on=remark.date,
+                parent_content_type=type_motion,
+                parent_object_id=new_motion.id,
+                subject='Remark',
+                text=remark.remark)