diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py
index 0bc456e6fc11f3f29c79da458918f705935d5c44..30aa73cfcfdecf33260ffd7431f7e954c3f07d0e 100644
--- a/SciPost_v1/settings/base.py
+++ b/SciPost_v1/settings/base.py
@@ -138,9 +138,9 @@ SHELL_PLUS_POST_IMPORTS = (
     ('submissions.factories', ('SubmissionFactory', 'EICassignedSubmissionFactory')),
     ('commentaries.factories',
         ('EmptyCommentaryFactory',
-         'VettedCommentaryFactory',
+         'CommentaryFactory',
          'UnvettedCommentaryFactory',
-         'UnpublishedVettedCommentaryFactory',)),
+         'UnpublishedCommentaryFactory',)),
     ('scipost.factories', ('ContributorFactory')),
 )
 
diff --git a/SciPost_v1/settings/local_jorran.py b/SciPost_v1/settings/local_jorran.py
index 0ca80ebb49d60854a148e3619d6d79bae6e0d078..bb65bb29a62b13b3f42c541dd6a4906b01924e71 100644
--- a/SciPost_v1/settings/local_jorran.py
+++ b/SciPost_v1/settings/local_jorran.py
@@ -13,23 +13,23 @@ MIDDLEWARE += (
 INTERNAL_IPS = ['127.0.0.1', '::1']
 
 # Static and media
-STATIC_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/'
-MEDIA_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/media/'
+STATIC_ROOT = '/Users/jorrandewit/Documents/Develop/SciPost/scipost_v1/local_files/static/'
+MEDIA_ROOT = '/Users/jorrandewit/Documents/Develop/SciPost/scipost_v1/local_files/media/'
 WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] =\
-    '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/bundles/'
+    '/Users/jorrandewit/Documents/Develop/SciPost/scipost_v1/local_files/static/bundles/'
 
 MAILCHIMP_API_USER = get_secret("MAILCHIMP_API_USER")
 MAILCHIMP_API_KEY = get_secret("MAILCHIMP_API_KEY")
 
-DATABASES['default']['PORT'] = '5433'
+DATABASES['default']['PORT'] = '5432'
 
 # iThenticate
 ITHENTICATE_USERNAME = get_secret('ITHENTICATE_USERNAME')
 ITHENTICATE_PASSWORD = get_secret('ITHENTICATE_PASSWORD')
 
 # Logging
-LOGGING['handlers']['scipost_file_arxiv']['filename'] = '/Users/jorranwit/Develop/SciPost/SciPost_v1/logs/arxiv.log'
-LOGGING['handlers']['scipost_file_doi']['filename'] = '/Users/jorranwit/Develop/SciPost/SciPost_v1/logs/doi.log'
+LOGGING['handlers']['scipost_file_arxiv']['filename'] = '/Users/jorrandewit/Documents/Develop/SciPost/SciPost_v1/logs/arxiv.log'
+LOGGING['handlers']['scipost_file_doi']['filename'] = '/Users/jorrandewit/Documents/Develop/SciPost/SciPost_v1/logs/doi.log'
 
 # Other
 CROSSREF_LOGIN_ID = get_secret("CROSSREF_LOGIN_ID")
diff --git a/affiliations/factories.py b/affiliations/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..c01e316b1598c30d0bd732341c65464dbd1a64f1
--- /dev/null
+++ b/affiliations/factories.py
@@ -0,0 +1,26 @@
+import factory
+
+from .constants import INSTITUTION_TYPES
+from .models import Institution, Affiliation
+
+
+class InstitutionFactory(factory.django.DjangoModelFactory):
+    name = factory.Faker('company')
+    acronym = factory.lazy_attribute(lambda o: o.name[:16])
+    country = factory.Faker('country_code')
+    type = factory.Iterator(INSTITUTION_TYPES, getter=lambda c: c[0])
+
+    class Meta:
+        model = Institution
+        django_get_or_create = ('name',)
+
+
+class AffiliationFactory(factory.django.DjangoModelFactory):
+    institution = factory.SubFactory('affiliations.factories.InstitutionFactory')
+    contributor = factory.SubFactory('scipost.factories.ContributorFactory')
+    begin_date = factory.Faker('date_this_decade')
+    end_date = factory.Faker('future_date', end_date="+2y")
+
+    class Meta:
+        model = Affiliation
+        django_get_or_create = ('institution', 'contributor')
diff --git a/affiliations/forms.py b/affiliations/forms.py
index 774a2e6a7895455701b1fbc7652c79cc6c4aa205..b46b37472ecbe4b35b7817c730bb02194d06b11d 100644
--- a/affiliations/forms.py
+++ b/affiliations/forms.py
@@ -1,6 +1,5 @@
 from django import forms
 from django.forms import BaseModelFormSet, modelformset_factory
-# from django.db.models import F
 
 from django_countries import countries
 from django_countries.fields import LazyTypedChoiceField
diff --git a/colleges/factories.py b/colleges/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc1a829b8427c6ffb2358776aabfb5e2bae15f67
--- /dev/null
+++ b/colleges/factories.py
@@ -0,0 +1,22 @@
+import factory
+
+from scipost.models import Contributor
+
+from .models import Fellowship
+
+
+class BaseFellowshipFactory(factory.django.DjangoModelFactory):
+    contributor = factory.Iterator(Contributor.objects.all())
+    start_date = factory.Faker('date_this_year')
+    until_date = factory.Faker('date_between', start_date="now", end_date="+2y")
+
+    guest = factory.Faker('boolean', chance_of_getting_true=10)
+
+    class Meta:
+        model = Fellowship
+        django_get_or_create = ('contributor', 'start_date')
+        abstract = True
+
+
+class FellowshipFactory(BaseFellowshipFactory):
+    pass
diff --git a/colleges/management/__init__.py b/colleges/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/colleges/management/commands/__init__.py b/colleges/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/colleges/management/commands/create_fellowships.py b/colleges/management/commands/create_fellowships.py
new file mode 100644
index 0000000000000000000000000000000000000000..b28ad5069950659c47e69e2794859309079bed2b
--- /dev/null
+++ b/colleges/management/commands/create_fellowships.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from colleges import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Fellowships objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Fellowships to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_fellowships(kwargs['number'])
+
+    def create_fellowships(self, n):
+        factories.FellowshipFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Fellowships.'.format(n=n)))
diff --git a/commentaries/admin.py b/commentaries/admin.py
index 93aee6375bdb1b36584042765d5e5220d7d1ca0e..f303ebcfefa5c47f18b939ac1f180aff8412aed4 100644
--- a/commentaries/admin.py
+++ b/commentaries/admin.py
@@ -29,4 +29,5 @@ class CommentaryAdmin(admin.ModelAdmin):
     date_hierarchy = 'latest_activity'
     form = CommentaryAdminForm
 
+
 admin.site.register(Commentary, CommentaryAdmin)
diff --git a/commentaries/factories.py b/commentaries/factories.py
index c908621441bff8715a7bbaf5cce7cd3833994445..0e0f2df751417dc915d3e88002a2cb8e085d4a0c 100644
--- a/commentaries/factories.py
+++ b/commentaries/factories.py
@@ -8,31 +8,31 @@ from common.helpers import random_arxiv_identifier_with_version_number, random_e
 from .constants import COMMENTARY_TYPES
 from .models import Commentary
 
-from faker import Faker
 
-
-class CommentaryFactory(factory.django.DjangoModelFactory):
+class BaseCommentaryFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Commentary
+        django_get_or_create = ('pub_DOI', 'arxiv_identifier')
+        abstract = True
 
     requested_by = factory.Iterator(Contributor.objects.all())
+    vetted = True
+    vetted_by = factory.Iterator(Contributor.objects.all())
     type = factory.Iterator(COMMENTARY_TYPES, getter=lambda c: c[0])
     discipline = factory.Iterator(SCIPOST_DISCIPLINES, getter=lambda c: c[0])
     domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
     subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
-    title = factory.Faker('text')
+    title = factory.Faker('sentence')
     pub_DOI = factory.Sequence(lambda n: random_external_doi())
-    arxiv_identifier = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
+    arxiv_identifier = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number('1'))
     author_list = factory.Faker('name')
     pub_abstract = factory.Faker('text')
-    pub_date = factory.Faker('date')
-    arxiv_link = factory.Faker('uri')
-    pub_abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
+    pub_date = factory.Faker('date_this_decade')
+    pub_abstract = factory.Faker('paragraph')
 
-    @factory.post_generation
-    def arxiv_link(self, create, extracted, **kwargs):
-        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier
-        self.arxiv_or_DOI_string = self.arxiv_identifier
+    arxiv_link = factory.lazy_attribute(lambda o: 'https://arxiv.org/abs/%s' % o.arxiv_identifier)
+    arxiv_or_DOI_string = factory.lazy_attribute(lambda o: (
+        o.arxiv_identifier if o.arxiv_identifier else o.pub_DOI))
 
     @factory.post_generation
     def create_urls(self, create, extracted, **kwargs):
@@ -40,27 +40,45 @@ class CommentaryFactory(factory.django.DjangoModelFactory):
 
     @factory.post_generation
     def add_authors(self, create, extracted, **kwargs):
-        contributors = list(Contributor.objects.order_by('?')
-                            .exclude(pk=self.requested_by.pk).all()[:4])
+        contributors = Contributor.objects.order_by('?').exclude(pk=self.requested_by.pk)[:4]
         self.author_list = ', '.join(
-            ['%s %s' % (contrib.user.first_name,
-                        contrib.user.last_name) for contrib in contributors])
-        self.authors.add(*contributors)
+            ['%s %s' % (contrib.user.first_name, contrib.user.last_name)
+                for contrib in contributors])
 
+        if create:
+            self.authors.add(*contributors)
 
-class VettedCommentaryFactory(CommentaryFactory):
-    vetted = True
-    vetted_by = factory.Iterator(Contributor.objects.all())
+    @factory.post_generation
+    def set_journal_data(self, create, extracted, **kwargs):
+        if not self.pub_DOI:
+            return
 
+        data = self.pub_DOI.split('/')[1].split('.')
+        self.journal = data[0]
+        self.volume = data[1]
+        self.pages = data[2]
 
-class UnpublishedVettedCommentaryFactory(VettedCommentaryFactory):
-    pub_DOI = ''
 
+class CommentaryFactory(BaseCommentaryFactory):
+    pass
 
-class UnvettedCommentaryFactory(CommentaryFactory):
+
+class UnvettedCommentaryFactory(BaseCommentaryFactory):
     vetted = False
+    vetted_by = None
 
 
-class UnvettedArxivPreprintCommentaryFactory(CommentaryFactory):
-    vetted = False
+class UnpublishedCommentaryFactory(BaseCommentaryFactory):
     pub_DOI = ''
+    pub_date = None
+
+
+class UnvettedUnpublishedCommentaryFactory(UnpublishedCommentaryFactory):
+    vetted = False
+    vetted_by = None
+
+
+class PublishedCommentaryFactory(BaseCommentaryFactory):
+    arxiv_identifier = ''
+    arxiv_link = ''
+    arxiv_or_DOI_string = factory.lazy_attribute(lambda o: o.pub_DOI)
diff --git a/commentaries/management/__init__.py b/commentaries/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/commentaries/management/commands/__init__.py b/commentaries/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/commentaries/management/commands/create_commentaries.py b/commentaries/management/commands/create_commentaries.py
new file mode 100644
index 0000000000000000000000000000000000000000..1353a86dd1794882a4587d0f916e98f7ddd9ad33
--- /dev/null
+++ b/commentaries/management/commands/create_commentaries.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from commentaries import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Commentaries objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Commentaries to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_commentaries(kwargs['number'])
+
+    def create_commentaries(self, n):
+        factories.CommentaryFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Commentaries.'.format(n=n)))
diff --git a/commentaries/templates/commentaries/_commentary_card_content.html b/commentaries/templates/commentaries/_commentary_card_content.html
index 7c5954005dbba4b8a85a7312f0684392da50c232..ff4e0df85e17167d59b0716583737f07df316d0a 100644
--- a/commentaries/templates/commentaries/_commentary_card_content.html
+++ b/commentaries/templates/commentaries/_commentary_card_content.html
@@ -1,6 +1,6 @@
 <div class="card-body">
     <h3 class="card-title">
-        <a href="{% url 'commentaries:commentary' commentary.arxiv_or_DOI_string %}">{{ commentary.title }}</a>
+        <a href="{{ commentary.get_absolute_url }}">{{ commentary.title }}</a>
     </h3>
     <p class="mt-0 mb-3">
         by {{ commentary.author_list }}{% if commentary.type == 'published' %}, {{ commentary.journal }} {{ commentary.volume }}, {{ commentary.pages }}{% elif commentary.type == 'preprint' %} &middot; <a href="{{ commentary.arxiv_link }}">{{ commentary.arxiv_link }}</a>{% endif %}
diff --git a/commentaries/test_forms.py b/commentaries/test_forms.py
index 35e65abb64b3b50ed5f3aca96a9a65dbefce65a5..a24e9785c060b0da78756622c57ccec9e15d02f5 100644
--- a/commentaries/test_forms.py
+++ b/commentaries/test_forms.py
@@ -5,8 +5,8 @@ from django.test import TestCase
 from common.helpers import model_form_data
 from scipost.factories import UserFactory, ContributorFactory
 
-from .factories import VettedCommentaryFactory, UnvettedCommentaryFactory,\
-                       UnvettedArxivPreprintCommentaryFactory
+from .factories import CommentaryFactory, UnvettedCommentaryFactory,\
+                       UnvettedUnpublishedCommentaryFactory
 from .forms import RequestPublishedArticleForm, VetCommentaryForm, DOIToQueryForm,\
                    ArxivQueryForm, RequestArxivPreprintForm
 from .models import Commentary
@@ -189,7 +189,7 @@ class TestRequestArxivPreprintForm(TestCase):
     def setUp(self):
         add_groups_and_permissions()
         ContributorFactory.create_batch(5)
-        factory_instance = UnvettedArxivPreprintCommentaryFactory.build()
+        factory_instance = UnvettedUnpublishedCommentaryFactory.build()
         self.user = UserFactory()
         self.valid_form_data = model_form_data(factory_instance, RequestPublishedArticleForm)
         self.valid_form_data['arxiv_identifier'] = factory_instance.arxiv_identifier
@@ -199,7 +199,7 @@ class TestRequestArxivPreprintForm(TestCase):
         self.assertTrue(form.is_valid())
 
     def test_identifier_that_already_has_commentary_page_is_invalid(self):
-        commentary = UnvettedArxivPreprintCommentaryFactory()
+        commentary = UnvettedUnpublishedCommentaryFactory()
         invalid_data = {**self.valid_form_data, **{'arxiv_identifier': commentary.arxiv_identifier}}
         form = RequestArxivPreprintForm(invalid_data)
         self.assertEqual(form.is_valid(), False)
diff --git a/commentaries/test_views.py b/commentaries/test_views.py
index 24f46518623509e53016c7939f564ceee26d53cd..bddad132390fe8ecc387899c3fa7c46f7da2621c 100644
--- a/commentaries/test_views.py
+++ b/commentaries/test_views.py
@@ -5,8 +5,8 @@ from django.test import TestCase, Client, RequestFactory
 from scipost.models import Contributor
 from scipost.factories import ContributorFactory, UserFactory
 
-from .factories import UnvettedCommentaryFactory, VettedCommentaryFactory, UnpublishedVettedCommentaryFactory, \
-    UnvettedArxivPreprintCommentaryFactory
+from .factories import UnvettedCommentaryFactory, CommentaryFactory, UnpublishedCommentaryFactory, \
+    UnvettedUnpublishedCommentaryFactory
 from .forms import CommentarySearchForm, RequestPublishedArticleForm
 from .models import Commentary
 from .views import RequestPublishedArticle, prefill_using_DOI, RequestArxivPreprint
@@ -57,7 +57,7 @@ class RequestArxivPreprintTest(TestCase):
         add_groups_and_permissions()
         self.target = reverse('commentaries:request_arxiv_preprint')
         self.contributor = ContributorFactory()
-        self.commentary_instance = UnvettedArxivPreprintCommentaryFactory.build(requested_by=self.contributor)
+        self.commentary_instance = UnvettedUnpublishedCommentaryFactory.build(requested_by=self.contributor)
         self.valid_form_data = model_form_data(self.commentary_instance, RequestPublishedArticleForm)
         # The form field is called 'identifier', while the model field is called 'arxiv_identifier',
         # so model_form_data doesn't include it.
@@ -76,6 +76,7 @@ class RequestArxivPreprintTest(TestCase):
         self.assertEqual(commentary.arxiv_or_DOI_string, "arXiv:" + self.commentary_instance.arxiv_identifier)
         self.assertEqual(commentary.requested_by, self.contributor)
 
+
 class VetCommentaryRequestsTest(TestCase):
     """Test cases for `vet_commentary_requests` view method"""
 
@@ -119,7 +120,7 @@ class VetCommentaryRequestsTest(TestCase):
 
         # Only vetted Commentaries exist!
         # ContributorFactory.create_batch(5)
-        VettedCommentaryFactory(requested_by=ContributorFactory(), vetted_by=ContributorFactory())
+        CommentaryFactory(requested_by=ContributorFactory(), vetted_by=ContributorFactory())
         response = self.client.get(self.view_url)
         self.assertEquals(response.context['commentary_to_vet'], None)
 
@@ -134,7 +135,7 @@ class BrowseCommentariesTest(TestCase):
 
     def setUp(self):
         add_groups_and_permissions()
-        VettedCommentaryFactory(discipline='physics', requested_by=ContributorFactory())
+        CommentaryFactory(discipline='physics', requested_by=ContributorFactory())
         self.view_url = reverse('commentaries:browse', kwargs={
             'discipline': 'physics',
             'nrweeksback': '1'
@@ -155,7 +156,7 @@ class CommentaryDetailTest(TestCase):
     def setUp(self):
         add_groups_and_permissions()
         self.client = Client()
-        self.commentary = UnpublishedVettedCommentaryFactory(
+        self.commentary = UnpublishedCommentaryFactory(
             requested_by=ContributorFactory(), vetted_by=ContributorFactory())
         self.target = reverse(
             'commentaries:commentary',
diff --git a/commentaries/views.py b/commentaries/views.py
index 015fb814d1a87c8739186b1c1001ecdad39df58c..532c8b2cfc14fbe56868959ca4c8f61a7b22f7de 100644
--- a/commentaries/views.py
+++ b/commentaries/views.py
@@ -233,13 +233,7 @@ def commentary_detail(request, arxiv_or_DOI_string):
                                    arxiv_or_DOI_string=arxiv_or_DOI_string)
 
     form = CommentForm()
-    try:
-        author_replies = Comment.objects.filter(
-            commentary=commentary, is_author_reply=True, status__gte=1)
-    except Comment.DoesNotExist:
-        author_replies = ()
-    context = {'commentary': commentary,
-               'author_replies': author_replies, 'form': form}
+    context = {'commentary': commentary, 'form': form}
     return render(request, 'commentaries/commentary_detail.html', context)
 
 
diff --git a/comments/factories.py b/comments/factories.py
index 1f76cf6a460001cb60077d1d5e73bdf075dd2c6a..bd6a3c784d52457cf1ae506921b27bb73176df37 100644
--- a/comments/factories.py
+++ b/comments/factories.py
@@ -1,29 +1,37 @@
+import random
 import factory
-import pytz
 
-from django.utils import timezone
-
-from commentaries.factories import VettedCommentaryFactory
+from commentaries.models import Commentary
 from scipost.models import Contributor
-from submissions.factories import EICassignedSubmissionFactory
-from theses.factories import VettedThesisLinkFactory
+from submissions.models import Submission, Report
+from theses.models import ThesisLink
 
 from .constants import STATUS_VETTED
 from .models import Comment
 
 from faker import Faker
 
-timezone.now()
-
 
 class CommentFactory(factory.django.DjangoModelFactory):
+    status = STATUS_VETTED
+    vetted_by = factory.Iterator(Contributor.objects.all())
+
     author = factory.Iterator(Contributor.objects.all())
-    comment_text = factory.lazy_attribute(lambda x: Faker().paragraph())
-    remarks_for_editors = factory.lazy_attribute(lambda x: Faker().paragraph())
+    comment_text = factory.Faker('paragraph')
+    remarks_for_editors = factory.Faker('paragraph')
     file_attachment = Faker().file_name(extension='pdf')
-    status = STATUS_VETTED  # All comments will have status vetted!
-    vetted_by = factory.Iterator(Contributor.objects.all())
-    date_submitted = Faker().date_time_between(start_date="-3y", end_date="now", tzinfo=pytz.UTC)
+    date_submitted = factory.Faker('date_this_decade')
+
+    # Categories
+    is_cor = factory.Faker('boolean', chance_of_getting_true=20)
+    is_rem = factory.Faker('boolean', chance_of_getting_true=20)
+    is_que = factory.Faker('boolean', chance_of_getting_true=20)
+    is_ans = factory.Faker('boolean', chance_of_getting_true=20)
+    is_obj = factory.Faker('boolean', chance_of_getting_true=20)
+    is_rep = factory.Faker('boolean', chance_of_getting_true=20)
+    is_val = factory.Faker('boolean', chance_of_getting_true=20)
+    is_lit = factory.Faker('boolean', chance_of_getting_true=20)
+    is_sug = factory.Faker('boolean', chance_of_getting_true=20)
 
     class Meta:
         model = Comment
@@ -31,16 +39,27 @@ class CommentFactory(factory.django.DjangoModelFactory):
 
 
 class CommentaryCommentFactory(CommentFactory):
-    content_object = factory.SubFactory(VettedCommentaryFactory)
+    content_object = factory.Iterator(Commentary.objects.all())
 
 
 class SubmissionCommentFactory(CommentFactory):
-    content_object = factory.SubFactory(EICassignedSubmissionFactory)
+    content_object = factory.Iterator(Submission.objects.all())
+
+    @factory.post_generation
+    def replies(self, create, extracted, **kwargs):
+        if create:
+            for i in range(random.randint(0, 2)):
+                ReplyCommentFactory(content_object=self)
+
+
+class ReplyCommentFactory(CommentFactory):
+    content_object = factory.SubFactory(SubmissionCommentFactory, replies=False)
+    is_author_reply = factory.Faker('boolean')
 
 
 class ThesislinkCommentFactory(CommentFactory):
-    content_object = factory.SubFactory(VettedThesisLinkFactory)
+    content_object = factory.Iterator(ThesisLink.objects.all())
 
 
-class ReplyCommentFactory(CommentFactory):
-    content_object = factory.SubFactory(SubmissionCommentFactory)
+class ReportCommentFactory(CommentFactory):
+    content_object = factory.Iterator(Report.objects.all())
diff --git a/comments/migrations/0003_auto_20180314_1502.py b/comments/migrations/0003_auto_20180314_1502.py
new file mode 100644
index 0000000000000000000000000000000000000000..585e4360923d274cd2c0204c18b1c5c9c4be9552
--- /dev/null
+++ b/comments/migrations/0003_auto_20180314_1502.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-14 14:02
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('comments', '0002_auto_20171229_1435'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='comment',
+            name='commentary',
+        ),
+        migrations.RemoveField(
+            model_name='comment',
+            name='in_reply_to_comment',
+        ),
+        migrations.RemoveField(
+            model_name='comment',
+            name='in_reply_to_report',
+        ),
+        migrations.RemoveField(
+            model_name='comment',
+            name='submission',
+        ),
+        migrations.RemoveField(
+            model_name='comment',
+            name='thesislink',
+        ),
+    ]
diff --git a/comments/models.py b/comments/models.py
index e138ca39ad2b9a7fefc8d3f7606207bdfc176df5..960419d6cb51ad72ad873c0ba903523e4df52025 100644
--- a/comments/models.py
+++ b/comments/models.py
@@ -17,7 +17,8 @@ from .constants import COMMENT_STATUS, STATUS_PENDING
 from .managers import CommentQuerySet
 
 
-WARNING_TEXT = 'Warning: Rather use/edit `content_object` instead or be 100% sure you know what you are doing!'
+WARNING_TEXT = ('Warning: Rather use/edit `content_object` instead or be 100% sure you'
+                ' know what you are doing!')
 US_NOTICE = 'Warning: This field is out of service and will be removed in the future.'
 
 
@@ -28,9 +29,9 @@ class Comment(TimeStampedModel):
     status = models.SmallIntegerField(default=STATUS_PENDING, choices=COMMENT_STATUS)
     vetted_by = models.ForeignKey('scipost.Contributor', blank=True, null=True,
                                   on_delete=models.CASCADE, related_name='comment_vetted_by')
-    file_attachment = models.FileField(upload_to='uploads/comments/%Y/%m/%d/', blank=True,
-                                       validators=[validate_file_extension, validate_max_file_size]
-                                       )
+    file_attachment = models.FileField(
+        upload_to='uploads/comments/%Y/%m/%d/', blank=True,
+        validators=[validate_file_extension, validate_max_file_size])
 
     # A Comment is always related to another model
     # This construction implicitly has property: `on_delete=models.CASCADE`
@@ -40,23 +41,6 @@ class Comment(TimeStampedModel):
 
     nested_comments = GenericRelation('comments.Comment', related_query_name='comments')
 
-    # -- U/S
-    # These fields will be removed in the future.
-    # They still exists only to prevent possible data loss.
-    commentary = models.ForeignKey('commentaries.Commentary', blank=True, null=True,
-                                   on_delete=models.CASCADE, help_text=US_NOTICE)
-    submission = models.ForeignKey('submissions.Submission', blank=True, null=True,
-                                   on_delete=models.CASCADE, related_name='comments_old',
-                                   help_text=US_NOTICE)
-    thesislink = models.ForeignKey('theses.ThesisLink', blank=True, null=True,
-                                   on_delete=models.CASCADE, help_text=US_NOTICE)
-    in_reply_to_comment = models.ForeignKey('self', blank=True, null=True,
-                                            related_name="nested_comments_old",
-                                            on_delete=models.CASCADE, help_text=US_NOTICE)
-    in_reply_to_report = models.ForeignKey('submissions.Report', blank=True, null=True,
-                                           on_delete=models.CASCADE, help_text=US_NOTICE)
-    # -- End U/S
-
     # Author info
     is_author_reply = models.BooleanField(default=False)
     author = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
@@ -77,6 +61,7 @@ class Comment(TimeStampedModel):
     remarks_for_editors = models.TextField(blank=True,
                                            verbose_name='optional remarks for the Editors only')
     date_submitted = models.DateTimeField('date submitted', default=timezone.now)
+
     # Opinions
     nr_A = models.PositiveIntegerField(default=0)
     in_agreement = models.ManyToManyField('scipost.Contributor', related_name='in_agreement',
diff --git a/comments/templates/comments/_comment_identifier.html b/comments/templates/comments/_comment_identifier.html
index 5eb681610a2fd2e657a5d71fcea0a9d1656e16f3..9b7ac2da98169b92f313d3ddd9fa041ce0bc3d40 100644
--- a/comments/templates/comments/_comment_identifier.html
+++ b/comments/templates/comments/_comment_identifier.html
@@ -7,9 +7,8 @@
 <div class="commentid" id="comment_id{{ comment.id }}">
     <h3>
         {% if request.user.contributor and request.user.contributor == comment.core_content_object.editor_in_charge or is_edcol_admin and request.user|is_not_author_of_submission:comment.core_content_object.arxiv_identifier_w_vn_nr %}
-            <h3>{% if comment.anonymous %}(chose public anonymity) {% endif %}<a href="{{ comment.author.get_absolute_url }}">{{ comment.author.user.first_name }} {{ comment.author.user.last_name }}</a>
+            {% if comment.anonymous %}(chose public anonymity) {% endif %}<a href="{{ comment.author.get_absolute_url }}">{{ comment.author.user.first_name }} {{ comment.author.user.last_name }}</a>
               on {{ comment.date_submitted|date:'Y-m-d' }}
-            </h3>
         {% elif comment.anonymous %}
             Anonymous on {{comment.date_submitted|date:'Y-m-d'}}
         {% else %}
@@ -17,7 +16,7 @@
             <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a>
             on {{comment.date_submitted|date:'Y-m-d'}}
         {% endif %}
-	{% if comment.doi_string %}&nbsp; <small>{{ comment|citation }}</small>{% endif %}
+    	{% if comment.doi_string %}&nbsp; <small>{{ comment|citation }}</small>{% endif %}
     </h3>
 
 
diff --git a/comments/templates/comments/_single_comment.html b/comments/templates/comments/_single_comment.html
index 665503b06852f4686845577dde1c1356898f831a..5c1137c2e4223106ef50268e61fc51e1a94b724d 100644
--- a/comments/templates/comments/_single_comment.html
+++ b/comments/templates/comments/_single_comment.html
@@ -15,24 +15,24 @@
 
     <p class="my-3 pb-2">
         {{ comment.comment_text|linebreaksbr }}
-
-        {% if comment.file_attachment %}
-            <h3>Attachment:</h3>
-            <p>
-                <a target="_blank" href="{{ comment.get_attachment_url }}">
-                    {% if comment.file_attachment|is_image %}
-                        <img class="attachment attachment-comment" src="{{ comment.get_attachment_url }}">
-                    {% else %}
-                        {{ comment.file_attachment|filename }}<br><small>{{ comment.file_attachment.size|filesizeformat }}</small>
-                    {% endif %}
-                </a>
-            </p>
-        {% endif %}
     </p>
+    {% if comment.file_attachment %}
+        <h3>Attachment:</h3>
+        <p>
+            <a target="_blank" href="{{ comment.get_attachment_url }}">
+                {% if comment.file_attachment|is_image %}
+                    <img class="attachment attachment-comment" src="{{ comment.get_attachment_url }}">
+                {% else %}
+                    {{ comment.file_attachment|filename }}
+                {% endif %}
+            </a>
+        </p>
+    {% endif %}
+
     {% if is_editorial_college or is_edcol_admin %}
         {% if comment.remarks_for_editors %}
             <h3>Remarks for editors:</h3>
-            <p>{{ comment.remarks_for_editors|linebreaks }}</p>
+            <p>{{ comment.remarks_for_editors|linebreaksbr }}</p>
         {% endif %}
     {% endif %}
 
diff --git a/comments/test_views.py b/comments/test_views.py
index c16e256953ec7be3ea87ce46c9e96f16763cb15c..16d91d5d54adf61e629689bf455898b6acbc3448 100644
--- a/comments/test_views.py
+++ b/comments/test_views.py
@@ -6,7 +6,7 @@ from django.http import Http404
 from scipost.factories import ContributorFactory
 from theses.factories import ThesisLinkFactory
 from submissions.factories import EICassignedSubmissionFactory
-from commentaries.factories import UnpublishedVettedCommentaryFactory
+from commentaries.factories import UnpublishedCommentaryFactory
 
 from .factories import CommentFactory
 from .forms import CommentForm
@@ -84,7 +84,7 @@ class TestNewComment(TestCase):
         """ Valid Comment gets saved """
 
         contributor = ContributorFactory()
-        commentary = UnpublishedVettedCommentaryFactory()
+        commentary = UnpublishedCommentaryFactory()
         valid_comment_data = model_form_data(CommentFactory, CommentForm)
         target = reverse('comments:new_comment', kwargs={'object_id': commentary.id, 'type_of_object': 'commentary'})
 
diff --git a/common/helpers/__init__.py b/common/helpers/__init__.py
index abe97df91f6222671cdcf4338f5ac1a39d0073ba..88f0fb9216d67f08929dc56e73245ed3da5fc8ee 100644
--- a/common/helpers/__init__.py
+++ b/common/helpers/__init__.py
@@ -32,8 +32,8 @@ def model_form_data(model, form_class, form_kwargs={}):
     return filter_keys(model_data, form_fields)
 
 
-def random_arxiv_identifier_with_version_number():
-    return random_arxiv_identifier_without_version_number() + "v0"
+def random_arxiv_identifier_with_version_number(version_nr='0'):
+    return random_arxiv_identifier_without_version_number() + 'v' + str(version_nr)
 
 
 def random_arxiv_identifier_without_version_number():
@@ -44,15 +44,25 @@ def random_scipost_journal():
     return random.choice(SCIPOST_JOURNALS_SUBMIT)[0]
 
 
-def random_external_journal():
+def random_external_journal_abbrev():
     return random.choice((
-        'PhysRevA.',
-        'PhysRevB.',
-        'PhysRevC.',
-        'nature.'
-        'S0550-3213(01)',
-        '1742-5468/',
-        '0550-3213(96)'
+        'Ann. Phys.',
+        'Phys. Rev. A',
+        'Phys. Rev. B',
+        'Phys. Rev. C',
+        'Phys. Rev. Lett.',
+        'Europhys. Lett.',
+        'J. Math. Anal. Appl.',
+        'Nat. Phys.'
+        'J. Phys. A',
+        'J. Stat. Phys.',
+        'J. Stat. Mech.',
+        'J. Math. Phys.',
+        'Lett. Math. Phys.',
+        'Sov. Phys. JETP',
+        'Sov. Phys. JETP',
+        'Nucl. Phys. B',
+        'Adv. Phys.'
     ))
 
 
@@ -64,14 +74,41 @@ def random_scipost_doi():
     return '10.21468/%s.%s' % (random_scipost_journal(), random_pub_number())
 
 
+def random_scipost_report_doi_label():
+    return 'SciPost.Report.%s' % random_digits(4)
+
+
 def random_external_doi():
-    return '10.%s/%s%s' % (random_digits(5), random_external_journal(), random_pub_number())
+    """
+    Return a fake/random doi as if all journal abbrev and pub_number are separated by `.`, which
+    can be helpfull for testing purposes.
+    """
+    journal = random.choice((
+        'PhysRevA',
+        'PhysRevB',
+        'PhysRevC',
+        'PhysRevLett',
+        'nature'
+        'S0550-3213(01)',
+        '1742-5468',
+        '0550-3213(96)'
+    ))
+    return '10.%s/%s.%s' % (random_digits(5), journal, random_pub_number())
 
 
 def random_digits(n):
     return "".join(random.choice(string.digits) for _ in range(n))
 
 
+def generate_orcid():
+    return '{}-{}-{}-{}'.format(
+        random_digits(4),
+        random_digits(4),
+        random_digits(4),
+        random_digits(4),
+    )
+
+
 def filter_keys(dictionary, keys_to_keep):
     # Field is empty if not on model.
     return {key: dictionary.get(key, "") for key in keys_to_keep}
diff --git a/journals/admin.py b/journals/admin.py
index 955fef5eddbb325a49a02e51f3796c06b4c4d0f8..956df25fea384d0aa0c737c6ab49740e2a2779f2 100644
--- a/journals/admin.py
+++ b/journals/admin.py
@@ -55,6 +55,7 @@ class PublicationAdminForm(forms.ModelForm):
 
 class ReferenceInline(admin.TabularInline):
     model = Reference
+    extra = 0
 
 
 class AuthorsInline(admin.TabularInline):
diff --git a/journals/factories.py b/journals/factories.py
index e6b9ef92328909e0a8826e93e4cd34113c8a0989..a49839f1e1b195f3e5822ba6f14ecd3a8b1553b6 100644
--- a/journals/factories.py
+++ b/journals/factories.py
@@ -1,18 +1,37 @@
 import factory
 import datetime
 import pytz
+import random
 
-from django.utils import timezone
-
-from common.helpers import random_digits
-from journals.constants import SCIPOST_JOURNALS
+from common.helpers import random_digits, random_external_doi, random_external_journal_abbrev
+from journals.constants import SCIPOST_JOURNALS, SCIPOST_JOURNAL_PHYSICS_LECTURE_NOTES,\
+    ISSUES_AND_VOLUMES, INDIVIDUAL_PUBLCATIONS, PUBLICATION_PUBLISHED
 from submissions.factories import PublishedSubmissionFactory
 
-from .models import Journal, Volume, Issue, Publication
+from .models import Journal, Volume, Issue, Publication, Reference
 
 from faker import Faker
 
 
+class ReferenceFactory(factory.django.DjangoModelFactory):
+    reference_number = factory.LazyAttribute(lambda o: o.publication.references.count() + 1)
+    identifier = factory.lazy_attribute(lambda n: random_external_doi())
+    link = factory.Faker('uri')
+
+    class Meta:
+        model = Reference
+
+    @factory.lazy_attribute
+    def citation(self):
+        faker = Faker()
+        return '<em>{}</em> {} <b>{}</b>, {} ({})'.format(
+            faker.sentence(),
+            random_external_journal_abbrev(),
+            random.randint(1, 100),
+            random.randint(1, 100),
+            faker.year())
+
+
 class JournalFactory(factory.django.DjangoModelFactory):
     name = factory.Iterator(SCIPOST_JOURNALS, getter=lambda c: c[0])
     doi_label = factory.Iterator(SCIPOST_JOURNALS, getter=lambda c: c[0])
@@ -20,25 +39,21 @@ class JournalFactory(factory.django.DjangoModelFactory):
 
     class Meta:
         model = Journal
-        django_get_or_create = ('name', 'doi_label',)
+        django_get_or_create = ('name',)
+
+    @factory.lazy_attribute
+    def structure(self):
+        if self.name == SCIPOST_JOURNAL_PHYSICS_LECTURE_NOTES:
+            return INDIVIDUAL_PUBLCATIONS
+        return ISSUES_AND_VOLUMES
 
 
 class VolumeFactory(factory.django.DjangoModelFactory):
     in_journal = factory.SubFactory(JournalFactory)
-    number = 9999
-    doi_label = factory.Faker('md5')
-
-    @factory.post_generation
-    def doi(self, create, extracted, **kwargs):
-        self.number = self.in_journal.volume_set.count()
-        self.doi_label = self.in_journal.doi_label + '.' + str(self.number)
-
-    @factory.post_generation
-    def dates(self, create, extracted, **kwargs):
-        timezone.now()
-        self.start_date = Faker().date_time_between(start_date="-3y", end_date="now",
-                                                    tzinfo=pytz.UTC)
-        self.until_date = self.start_date + datetime.timedelta(weeks=26)
+    doi_label = factory.lazy_attribute(lambda o: '%s.%i' % (o.in_journal.doi_label, o.number))
+    number = factory.lazy_attribute(lambda o: o.in_journal.volumes.count() + 1)
+    start_date = factory.Faker('date_time_this_decade')
+    until_date = factory.lazy_attribute(lambda o: o.start_date + datetime.timedelta(weeks=26))
 
     class Meta:
         model = Volume
@@ -47,21 +62,12 @@ class VolumeFactory(factory.django.DjangoModelFactory):
 
 class IssueFactory(factory.django.DjangoModelFactory):
     in_volume = factory.Iterator(Volume.objects.all())
-    number = 9999
-    doi_label = factory.Faker('md5')
+    number = factory.LazyAttribute(lambda o: o.in_volume.issues.count() + 1)
+    doi_label = factory.LazyAttribute(lambda o: '%s.%i' % (o.in_volume.doi_label, o.number))
 
-    @factory.post_generation
-    def doi(self, create, extracted, **kwargs):
-        self.number = self.in_volume.issue_set.count()
-        self.doi_label = self.in_volume.doi_label + '.' + str(self.number)
-
-    @factory.post_generation
-    def dates(self, create, extracted, **kwargs):
-        timezone.now()
-        self.start_date = Faker().date_time_between(start_date=self.in_volume.start_date,
-                                                    end_date=self.in_volume.until_date,
-                                                    tzinfo=pytz.UTC)
-        self.until_date = self.start_date + datetime.timedelta(weeks=4)
+    start_date = factory.LazyAttribute(lambda o: Faker().date_time_between(
+        start_date=o.in_volume.start_date, end_date=o.in_volume.until_date, tzinfo=pytz.UTC))
+    until_date = factory.LazyAttribute(lambda o: o.start_date + datetime.timedelta(weeks=4))
 
     class Meta:
         model = Issue
@@ -69,42 +75,99 @@ class IssueFactory(factory.django.DjangoModelFactory):
 
 
 class PublicationFactory(factory.django.DjangoModelFactory):
-    accepted_submission = factory.SubFactory(PublishedSubmissionFactory)
+    accepted_submission = factory.SubFactory(
+        PublishedSubmissionFactory, generate_publication=False)
     paper_nr = 9999
-    pdf_file = Faker().file_name(extension='pdf')
-    in_issue = factory.Iterator(Issue.objects.all())
-    submission_date = factory.Faker('date')
-    acceptance_date = factory.Faker('date')
-    publication_date = factory.Faker('date')
-    doi_label = factory.Faker('md5')
+    pdf_file = factory.Faker('file_name', extension='pdf')
+    status = PUBLICATION_PUBLISHED
+    submission_date = factory.Faker('date_this_year')
+    acceptance_date = factory.Faker('date_this_year')
+    publication_date = factory.Faker('date_this_year')
+
+    discipline = factory.LazyAttribute(lambda o: o.accepted_submission.discipline)
+    domain = factory.LazyAttribute(lambda o: o.accepted_submission.domain)
+    subject_area = factory.LazyAttribute(lambda o: o.accepted_submission.subject_area)
+    title = factory.LazyAttribute(lambda o: o.accepted_submission.title)
+    abstract = factory.LazyAttribute(lambda o: o.accepted_submission.abstract)
+
+    # Dates
+    submission_date = factory.LazyAttribute(lambda o: o.accepted_submission.submission_date)
+    acceptance_date = factory.LazyAttribute(lambda o: o.accepted_submission.latest_activity)
+    publication_date = factory.LazyAttribute(lambda o: o.accepted_submission.latest_activity)
+    latest_activity = factory.LazyAttribute(lambda o: o.accepted_submission.latest_activity)
+
+    # Authors
+    author_list = factory.LazyAttribute(lambda o: o.accepted_submission.author_list)
+
+    class Meta:
+        model = Publication
+        django_get_or_create = ('accepted_submission', )
+
+    class Params:
+        journal = None
+
+    @factory.lazy_attribute
+    def in_issue(self):
+        # Make sure Issues, Journals and doi are correct.
+        if self.journal:
+            journal = Journal.objects.get(name=self.journal)
+        else:
+            journal = Journal.objects.order_by('?').first()
+
+        if journal.has_issues:
+            return Issue.objects.for_journal(journal.name).order_by('?').first()
+        return None
+
+    @factory.lazy_attribute
+    def in_journal(self):
+        # Make sure Issues, Journals and doi are correct.
+        if self.journal:
+            journal = Journal.objects.get(name=self.journal)
+        elif not self.in_issue:
+            journal = Journal.objects.has_individual_publications().order_by('?').first()
+        else:
+            return None
+
+        if not journal.has_issues:
+            # Keep this logic in case self.journal is set.
+            return journal
+        return None
+
+    @factory.lazy_attribute
+    def paper_nr(self):
+        if self.in_issue:
+            return self.in_issue.publications.count() + 1
+        elif self.in_journal:
+            return self.in_journal.publications.count() + 1
+
+    @factory.lazy_attribute
+    def doi_label(self):
+        if self.in_issue:
+            return self.in_issue.doi_label + '.' + str(self.paper_nr).rjust(3, '0')
+        elif self.in_journal:
+            return '%s.%i' % (self.in_journal.doi_label, self.paper_nr)
 
     @factory.post_generation
-    def doi(self, create, extracted, **kwargs):
-        paper_nr = self.in_issue.publications.count()
-        self.paper_nr = paper_nr
-        self.doi_label = self.in_issue.doi_label + '.' + str(paper_nr).rjust(3, '0')
+    def generate_publication(self, create, extracted, **kwargs):
+        if create and extracted is not False:
+            return
+
+        from journals.factories import PublicationFactory
+        factory.RelatedFactory(
+            PublicationFactory, 'accepted_submission',
+            title=self.title, author_list=self.author_list)
 
     @factory.post_generation
-    def submission_data(self, create, extracted, **kwargs):
-        # Content
-        self.discipline = self.accepted_submission.discipline
-        self.domain = self.accepted_submission.domain
-        self.subject_area = self.accepted_submission.subject_area
-        self.title = self.accepted_submission.title
-        self.abstract = self.accepted_submission.abstract
-
-        # Authors
-        self.author_list = self.accepted_submission.author_list
-        # self.authors.add(*self.accepted_submission.authors.all())
-        self.authors_claims.add(*self.accepted_submission.authors_claims.all())
-        self.authors_false_claims.add(*self.accepted_submission.authors_false_claims.all())
+    def author_relations(self, create, extracted, **kwargs):
+        if not create:
+            return
 
-        # Dates
-        self.submission_date = self.accepted_submission.latest_activity
-        self.acceptance_date = self.accepted_submission.latest_activity
-        self.publication_date = self.accepted_submission.latest_activity
-        self.latest_activity = self.accepted_submission.latest_activity
+        # Append references
+        for i in range(5):
+            ReferenceFactory(publication=self)
 
-    class Meta:
-        model = Publication
-        django_get_or_create = ('accepted_submission', )
+        # Copy author data from Submission
+        for author in self.accepted_submission.authors.all():
+            self.authors.create(publication=self, contributor=author)
+        self.authors_claims.add(*self.accepted_submission.authors_claims.all())
+        self.authors_false_claims.add(*self.accepted_submission.authors_false_claims.all())
diff --git a/journals/forms.py b/journals/forms.py
index 4464d567a65e5ad49d7d0870b78f1f807c35e853..0c6ea15bcd423d55e652191e2e2041357cafd240 100644
--- a/journals/forms.py
+++ b/journals/forms.py
@@ -16,6 +16,7 @@ from .constants import STATUS_DRAFT, PUBLICATION_PREPUBLISHED, PUBLICATION_PUBLI
 from .exceptions import PaperNumberingError
 from .models import Issue, Publication, Reference, UnregisteredAuthor, PublicationAuthorsTable
 from .utils import JournalUtils
+from .signals import notify_manuscript_published
 
 
 from funders.models import Grant, Funder
@@ -83,6 +84,20 @@ class FundingInfoForm(forms.ModelForm):
         return super().save(*args, **kwargs)
 
 
+class BasePublicationAuthorsTableFormSet(BaseModelFormSet):
+    def save(self, *args, **kwargs):
+        objects = super().save(*args, **kwargs)
+        for form in self.ordered_forms:
+            form.instance.order = form.cleaned_data['ORDER']
+            form.instance.save()
+        return objects
+
+
+PublicationAuthorOrderingFormSet = modelformset_factory(
+    PublicationAuthorsTable, fields=(), can_order=True, extra=0,
+    formset=BasePublicationAuthorsTableFormSet)
+
+
 class CreateMetadataXMLForm(forms.ModelForm):
     class Meta:
         model = Publication
@@ -638,4 +653,6 @@ class PublicationPublishForm(RequestFormMixin, forms.ModelForm):
             # Email authors
             JournalUtils.load({'publication': self.instance})
             JournalUtils.send_authors_paper_published_email()
+            notify_manuscript_published(self.request.user, self.instance, False)
+
         return self.instance
diff --git a/journals/management/__init__.py b/journals/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/journals/management/commands/__init__.py b/journals/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/journals/management/commands/create_issues.py b/journals/management/commands/create_issues.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea07e6ac7ca831da3148b22110b9ca7e4a28576f
--- /dev/null
+++ b/journals/management/commands/create_issues.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from journals import factories
+
+
+class Command(BaseCommand):
+    help = 'Create Issue objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Issues to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_issues(kwargs['number'])
+
+    def create_issues(self, n):
+        factories.IssueFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Issues.'.format(n=n)))
diff --git a/journals/management/commands/create_journals.py b/journals/management/commands/create_journals.py
new file mode 100644
index 0000000000000000000000000000000000000000..eaaa425050f9016ea0ed2ca9dde902b553bad262
--- /dev/null
+++ b/journals/management/commands/create_journals.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from journals import factories
+
+
+class Command(BaseCommand):
+    help = 'Create Journal objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Journals to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_journals(kwargs['number'])
+
+    def create_journals(self, n):
+        factories.JournalFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Journals.'.format(n=n)))
diff --git a/journals/management/commands/create_publications.py b/journals/management/commands/create_publications.py
new file mode 100644
index 0000000000000000000000000000000000000000..24c2f0480db625b119939ba394fb73d7f11901ab
--- /dev/null
+++ b/journals/management/commands/create_publications.py
@@ -0,0 +1,30 @@
+from django.core.management.base import BaseCommand
+
+from journals.constants import SCIPOST_JOURNALS_SUBMIT
+from journals.factories import PublicationFactory
+
+
+class Command(BaseCommand):
+    help = 'Create random Publication objects by using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of publications to add',
+        )
+        parser.add_argument(
+            '--journal', choices=[i[0] for i in SCIPOST_JOURNALS_SUBMIT],
+            action='store', dest='journal',
+            help='The name of the specific Journal to add the Publications to',
+        )
+
+    def handle(self, *args, **kwargs):
+        if kwargs['number'] > 0:
+            journal = None
+            if kwargs.get('journal'):
+                journal = kwargs['journal']
+            self.create_publications(kwargs['number'], journal=journal)
+
+    def create_publications(self, n, journal=None):
+        PublicationFactory.create_batch(n, journal=journal)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Publications.'.format(n=n)))
diff --git a/journals/management/commands/create_volumes.py b/journals/management/commands/create_volumes.py
new file mode 100644
index 0000000000000000000000000000000000000000..29dce2f400ace4310f7996774503965260707425
--- /dev/null
+++ b/journals/management/commands/create_volumes.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from journals import factories
+
+
+class Command(BaseCommand):
+    help = 'Create Volume objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Volumes to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_volumes(kwargs['number'])
+
+    def create_volumes(self, n):
+        factories.VolumeFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Volumes.'.format(n=n)))
diff --git a/journals/migrations/0025_auto_20180314_1637.py b/journals/migrations/0025_auto_20180314_1637.py
new file mode 100644
index 0000000000000000000000000000000000000000..35b306e63723cd7b0b0ec6db28a0d0c5317b7c59
--- /dev/null
+++ b/journals/migrations/0025_auto_20180314_1637.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-14 15:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('journals', '0024_auto_20180310_1740'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='issue',
+            name='in_journal',
+            field=models.ForeignKey(blank=True, help_text='Assign either an Volume or Journal to the Issue', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='issues', to='journals.Journal'),
+        ),
+        migrations.AlterField(
+            model_name='issue',
+            name='in_volume',
+            field=models.ForeignKey(blank=True, help_text='Assign either an Volume or Journal to the Issue', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='issues', to='journals.Volume'),
+        ),
+        migrations.AlterField(
+            model_name='publication',
+            name='in_issue',
+            field=models.ForeignKey(blank=True, help_text='Assign either an Issue or Journal to the Publication', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='journals.Issue'),
+        ),
+        migrations.AlterField(
+            model_name='publication',
+            name='in_journal',
+            field=models.ForeignKey(blank=True, help_text='Assign either an Issue or Journal to the Publication', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='journals.Journal'),
+        ),
+    ]
diff --git a/journals/models.py b/journals/models.py
index 5d0ec94f68761b269f33a91843cc3a9474fad4e3..a7c846e42bba6ecf7c0a37b00cbbe046ece1eaae 100644
--- a/journals/models.py
+++ b/journals/models.py
@@ -224,10 +224,10 @@ class Issue(models.Model):
     An Issue may be used as a subgroup of Publications related to a specific Journal object.
     """
     in_journal = models.ForeignKey(
-        'journals.Journal', on_delete=models.PROTECT, null=True, blank=True,
+        'journals.Journal', on_delete=models.CASCADE, null=True, blank=True,
         help_text='Assign either an Volume or Journal to the Issue')
     in_volume = models.ForeignKey(
-        'journals.Volume', on_delete=models.PROTECT, null=True, blank=True,
+        'journals.Volume', on_delete=models.CASCADE, null=True, blank=True,
         help_text='Assign either an Volume or Journal to the Issue')
     number = models.PositiveSmallIntegerField()
     start_date = models.DateField(default=timezone.now)
@@ -341,10 +341,10 @@ class Publication(models.Model):
     accepted_submission = models.OneToOneField('submissions.Submission', on_delete=models.CASCADE,
                                                related_name='publication')
     in_issue = models.ForeignKey(
-        'journals.Issue', on_delete=models.PROTECT, null=True, blank=True,
+        'journals.Issue', on_delete=models.CASCADE, null=True, blank=True,
         help_text='Assign either an Issue or Journal to the Publication')
     in_journal = models.ForeignKey(
-        'journals.Journal', on_delete=models.PROTECT, null=True, blank=True,
+        'journals.Journal', on_delete=models.CASCADE, null=True, blank=True,
         help_text='Assign either an Issue or Journal to the Publication')
     paper_nr = models.PositiveSmallIntegerField()
     status = models.CharField(max_length=8,
diff --git a/journals/signals.py b/journals/signals.py
new file mode 100644
index 0000000000000000000000000000000000000000..01787e588d96919588b5c32ed3f9dbaf4256b46f
--- /dev/null
+++ b/journals/signals.py
@@ -0,0 +1,17 @@
+from django.contrib.auth.models import User, Group
+
+from notifications.signals import notify
+
+
+def notify_manuscript_published(sender, instance, created, **kwargs):
+    """
+    Notify the authors about their new Publication.
+
+    instance -- Publication instance
+    """
+    if instance.is_published:
+        authors = User.objects.filter(contributor__publications=instance)
+        editorial_administration = Group.objects.get(name='Editorial Administrators')
+        for user in authors:
+            notify.send(sender=sender, recipient=user, actor=editorial_administration,
+                        verb=' published your manuscript.', target=instance)
diff --git a/journals/templates/journals/manage_metadata.html b/journals/templates/journals/manage_metadata.html
index 08ae88c8079ddfdbc104961abab2b010b75adc7d..8b0554e06f17e37f8d007205bf55727f44c11bc2 100644
--- a/journals/templates/journals/manage_metadata.html
+++ b/journals/templates/journals/manage_metadata.html
@@ -103,16 +103,8 @@ event: "focusin"
           <div class="col-md-6">
               <h2 class="ml-3">Actions</h2>
             <ul>
-              <li>Mark the first author
-                <ul class="list-unstyled pl-4">
-                  {% for author in publication.authors.all %}
-                  <li>
-                    {{ author.order }}. <a href="{% url 'journals:mark_first_author' doi_label=publication.doi_label author_object_id=author.id %}">{{ author }}</a>
-                  </li>
-                  {% endfor %}
-                </ul>
-	      </li>
               <li><a href="{% url 'journals:add_author' doi_label=publication.doi_label %}">Add a missing author</a></li>
+              <li><a href="{% url 'journals:update_author_ordering' doi_label=publication.doi_label %}">Update Author ordering</a></li>
               <li><a href="{% url 'journals:create_citation_list_metadata' publication.doi_label %}">Create/update citation list metadata</a></li>
               <li><a href="{% url 'journals:create_funding_info_metadata' publication.doi_label %}">Create/update funding info metadata</a></li>
 
diff --git a/journals/templates/journals/publication_authors_form.html b/journals/templates/journals/publication_authors_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..861f712b9e0e4a275ba9b436757658c8352346ac
--- /dev/null
+++ b/journals/templates/journals/publication_authors_form.html
@@ -0,0 +1,47 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Publication Authors{% endblock pagetitle %}
+
+{% block breadcrumb %}
+    <div class="container-outside header">
+        <div class="container">
+            <nav class="breadcrumb hidden-sm-down">
+                <a href="{% url 'journals:journals' %}" class="breadcrumb-item">Journals</a>
+                <a href="{{publication.get_absolute_url}}" class="breadcrumb-item">{{publication.citation}}</a>
+                <span class="breadcrumb-item active">Author ordering</span>
+
+            </nav>
+        </div>
+    </div>
+{% endblock %}
+
+{% block content %}
+
+
+<h1 class="highlight">Author Ordering</h1>
+
+<div class="mb-4">
+    {% include 'partials/journals/publication_li_content.html' with publication=publication %}
+</div>
+<a href="{% url 'journals:add_author' publication.doi_label %}">Add missing author</a>
+<h3 class="highlight">Ordering</h3>
+
+<form method="post" enctype="multipart/form-data">
+    {% csrf_token %}
+    {{ formset.management_form }}
+    <ul class="fa-ul sortable-list d-inline-block">
+        {% for form in formset %}
+            <li>
+                <i class="fa fa-sort"></i>
+                {{ form.instance.first_name }} {{ form.instance.last_name }}
+                <div class="d-none">{{ form }}</div>
+            </li>
+        {% endfor %}
+    </ul>
+    <br>
+    <input type="submit" class="btn btn-primary" value="Save ordering">
+</form>
+
+{% endblock %}
diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html
index 20f08ba694ec77c6fc6d9d491eaf3333a274596e..c2f1e27c069cdc2a85fc35af99714f296fe7b0d3 100644
--- a/journals/templates/journals/publication_detail.html
+++ b/journals/templates/journals/publication_detail.html
@@ -172,17 +172,8 @@
             <div class="col-12">
                 <h3>Editorial Administration tools</h3>
                 <ul class="mb-0">
-                  <li>
-                      Mark the first author
-                      <ul class="list-unstyled pl-4">
-                        {% for author in publication.authors.all %}
-                          <li>
-                            {{ author.order }}. <a href="{% url 'journals:mark_first_author' doi_label=publication.doi_label author_object_id=author.id %}">{{ author }}</a>
-                          </li>
-                        {% endfor %}
-                      </ul>
-                  </li>
                   <li><a href="{% url 'journals:add_author' doi_label=publication.doi_label %}">Add a missing author</a></li>
+                  <li><a href="{% url 'journals:update_author_ordering' doi_label=publication.doi_label %}">Update Author ordering</a></li>
                   <li><a href="{% url 'journals:create_citation_list_metadata' publication.doi_label %}">Create/update citation list metadata</a></li>
                   <li><a href="{% url 'journals:create_funding_info_metadata' publication.doi_label %}">Create/update funding info metadata</a></li>
                   <li><a href="{% url 'journals:create_metadata_xml' publication.doi_label %}">Create/update the XML metadata</a></li>
diff --git a/journals/urls/general.py b/journals/urls/general.py
index 1e3442302505823c1c8aee16753532eb21cb75cf..7d4430e1b5def101440e9dc01e32a73490959222 100644
--- a/journals/urls/general.py
+++ b/journals/urls/general.py
@@ -32,6 +32,10 @@ urlpatterns = [
             regex=PUBLICATION_DOI_REGEX),
         journals_views.DraftPublicationApprovalView.as_view(),
         name='send_publication_for_approval'),
+    url(r'^admin/publications/(?P<doi_label>{regex})/authors$'.format(regex=PUBLICATION_DOI_REGEX),
+        # journals_views.PublicationAuthorOrderingView.as_view(),
+        journals_views.publication_authors_ordering,
+        name='update_author_ordering'),
     url(r'^admin/publications/(?P<doi_label>{regex})/grants$'.format(regex=PUBLICATION_DOI_REGEX),
         journals_views.PublicationGrantsView.as_view(),
         name='update_grants'),
@@ -48,10 +52,6 @@ urlpatterns = [
     url(r'^admin/(?P<doi_label>{regex})/authors/add$'.format(regex=PUBLICATION_DOI_REGEX),
         journals_views.add_author,
         name='add_author'),
-    url(r'^admin/(?P<doi_label>{regex})/authors/mark_first/(?P<author_object_id>[0-9]+)$'.format(
-            regex=PUBLICATION_DOI_REGEX),
-        journals_views.mark_first_author,
-        name='mark_first_author'),
     url(r'^admin/(?P<doi_label>{regex})/manage_metadata$'.format(regex=PUBLICATION_DOI_REGEX),
         journals_views.manage_metadata,
         name='manage_metadata'),
diff --git a/journals/views.py b/journals/views.py
index 465dedd616866150242c10137931f2ad1243d366..f4a338d5d836be9a6ae9f748dd209bb1b818c5fc 100644
--- a/journals/views.py
+++ b/journals/views.py
@@ -30,7 +30,8 @@ from .models import Journal, Issue, Publication, Deposit, DOAJDeposit,\
 from .forms import FundingInfoForm,\
                    UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm,\
                    ReferenceFormSet, CreateMetadataDOAJForm, DraftPublicationForm,\
-                   PublicationGrantsForm, DraftPublicationApprovalForm, PublicationPublishForm
+                   PublicationGrantsForm, DraftPublicationApprovalForm, PublicationPublishForm,\
+                   PublicationAuthorOrderingFormSet
 from .mixins import PublicationMixin, ProdSupervisorPublicationPermissionMixin
 from .utils import JournalUtils
 
@@ -212,6 +213,22 @@ class PublicationGrantsRemovalView(PermissionsMixin, DetailView):
         return redirect(reverse('journals:update_grants', args=(self.object.doi_label,)))
 
 
+@permission_required('scipost.can_publish_accepted_submission', raise_exception=True)
+def publication_authors_ordering(request, doi_label):
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    formset = PublicationAuthorOrderingFormSet(
+        request.POST or None, queryset=publication.authors.order_by('order'))
+    if formset.is_valid():
+        formset.save()
+        messages.success(request, 'Author ordering updated')
+        return redirect(publication.get_absolute_url())
+    context = {
+        'formset': formset,
+        'publication': publication,
+    }
+    return render(request, 'journals/publication_authors_form.html', context)
+
+
 class DraftPublicationUpdateView(PermissionsMixin, UpdateView):
     """
     Any Production Officer or Administrator can draft a new publication without publishing here.
@@ -306,25 +323,6 @@ def manage_metadata(request, doi_label=None, issue_doi_label=None, journal_doi_l
     return render(request, 'journals/manage_metadata.html', context)
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-def mark_first_author(request, publication_id, author_object_id):
-    publication = get_object_or_404(Publication, id=publication_id)
-    author_object = get_object_or_404(publication.authors, id=author_object_id)
-
-    # Redo ordering
-    author_object.order = 1
-    author_object.save()
-    author_objects = publication.authors.exclude(id=author_object.id)
-    count = 2
-    for author in author_objects:
-        author.order = count
-        author.save()
-        count += 1
-    messages.success(request, 'Marked {} first author'.format(author_object))
-    return redirect(reverse('journals:manage_metadata',
-                            kwargs={'doi_label': publication.doi_label}))
-
-
 @permission_required('scipost.can_draft_publication', return_403=True)
 @transaction.atomic
 def add_author(request, doi_label, contributor_id=None, unregistered_author_id=None):
diff --git a/news/factories.py b/news/factories.py
index 74dcad74481d0c0de0e9dfeaabba153673a29723..a17229133a154ccd27117e135fac715a522ab54d 100644
--- a/news/factories.py
+++ b/news/factories.py
@@ -7,8 +7,8 @@ class NewsItemFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = NewsItem
 
-    date = factory.Faker('date_time')
-    headline = factory.Faker('sentence', nb_words=6)
-    blurb = factory.Faker('text', max_nb_chars=200)
-    followup_link = factory.Faker('url')
+    date = factory.Faker('date_this_year')
+    headline = factory.Faker('sentence')
+    blurb = factory.Faker('paragraph', nb_sentences=8)
+    followup_link = factory.Faker('uri')
     followup_link_text = factory.Faker('sentence', nb_words=4)
diff --git a/news/management/__init__.py b/news/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/news/management/commands/__init__.py b/news/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/news/management/commands/create_news.py b/news/management/commands/create_news.py
new file mode 100644
index 0000000000000000000000000000000000000000..96e170762e48e6a2b57aa4e741786bff6b7247b2
--- /dev/null
+++ b/news/management/commands/create_news.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from news import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random News Item objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of News items to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_news_items(kwargs['number'])
+
+    def create_news_items(self, n):
+        factories.NewsItemFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} News Items.'.format(n=n)))
diff --git a/notifications/models.py b/notifications/models.py
index e05b53710ade32063ef292107119e6f9b0d4835c..f6dfb21ea25c0250531b17f3a074eb617904d1ac 100644
--- a/notifications/models.py
+++ b/notifications/models.py
@@ -3,7 +3,6 @@ from django.core.urlresolvers import reverse
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.fields import GenericForeignKey
-from django.utils import timezone
 
 from .constants import NOTIFICATION_TYPES
 from .managers import NotificationQuerySet
diff --git a/notifications/templates/notifications/partials/notification_list_popover.html b/notifications/templates/notifications/partials/notification_list_popover.html
new file mode 100644
index 0000000000000000000000000000000000000000..9ce9cd38d7f56fd8e789542e662ea487d19f10d3
--- /dev/null
+++ b/notifications/templates/notifications/partials/notification_list_popover.html
@@ -0,0 +1,52 @@
+{% load request_filters %}
+
+<div class="popover-template popover">
+    <div class="popover notifications" role="tooltip">
+        <div class="arrow"></div>
+        {% if user.contributor %}
+            <div class="header">
+                <h3>{{ user.contributor.get_title_display }} {{ user.first_name }} {{ user.last_name }}</h3>
+                <a class="item" href="{% url 'scipost:update_personal_data' %}"><i class="fa fa-gear"></i> Update personal data</a>
+            </div>
+            {% if not user.contributor.is_currently_available %}
+                <div class="unavailable">
+                    <div class="head">You are currently unavailable</div>
+                    <div class="text">Check your availability in your personal page if this should not be the case.</div>
+                </div>
+            {% endif %}
+        {% else %}
+            <div class="header">
+                <h3>{{ user.first_name }} {{ user.last_name }}</h3>
+            </div>
+        {% endif %}
+
+        <div class="links">
+            <a class="item {% active 'scipost:personal_page' %}" href="{% url 'scipost:personal_page' %}">Personal Page</a>
+            {% if user.partner_contact or perms.scipost.can_read_partner_page %}
+                  <a class="item {% active 'partners:dashboard' %}" href="{% url 'partners:dashboard' %}">Partner Page</a>
+            {% endif %}
+
+            {% if perms.scipost.can_view_timesheets %}
+                <a class="item {% active 'finances:finance' %}" href="{% url 'finances:finance' %}">Financial Administration</a>
+            {% endif %}
+
+            {% if perms.scipost.can_view_all_funding_info %}
+                <a class="item {% active 'funders:funders' %}" href="{% url 'funders:funders' %}">Funders</a>
+            {% endif %}
+
+            {% if perms.scipost.can_view_production %}
+                <a class="item {% active 'production:production' %}" href="{% url 'production:production' %}">Production</a>
+            {% endif %}
+
+            {% if perms.scipost.can_view_pool %}
+                <a class="item {% active 'submissions:pool' %}" href="{% url 'submissions:pool' %}">Submissions Pool</a>
+            {% endif %}
+
+            <a class="item" href="{% url 'scipost:logout' %}">Logout</a>
+        </div>
+
+        <h4 class="inbox-header">Inbox</h4>
+        <div class="live_notify_list"></div>
+    </div>
+    <div class="popover-body"></div>
+</div>
diff --git a/notifications/templatetags/notifications_tags.py b/notifications/templatetags/notifications_tags.py
index 8d8bccd2402cd1f213000b4597ba82bc67390833..a89cafee93cf6d8501328fe71fb7958e3557c623 100644
--- a/notifications/templatetags/notifications_tags.py
+++ b/notifications/templatetags/notifications_tags.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
-from django.core.urlresolvers import reverse
 from django.template import Library
+from django.template.loader import render_to_string
 from django.utils.html import format_html
 
 register = Library()
@@ -18,44 +18,12 @@ def live_notify_list(context):
     if not user:
         return ''
 
-    html = '<div class="popover-template popover">'
-    html += '<div class="popover notifications" role="tooltip">'
-
-    # User default links
-    html += '<h6 class="header">Welcome {first_name} {last_name}</h6>'.format(
-        first_name=user.first_name, last_name=user.last_name)
-
-    if hasattr(user, 'contributor'):
-        html += '<a class="item" href="{url}">Personal Page</a>'.format(
-            url=reverse('scipost:personal_page'))
-
-    # User specific links
-    if user.has_perm('scipost.can_read_partner_page'):
-        html += '<a class="item" href="{url}">Partner Page</a>'.format(
-            url=reverse('partners:dashboard'))
-    if user.has_perm('scipost.can_view_timesheets'):
-        html += '<a class="item" href="{url}">Financial Administration</a>'.format(
-            url=reverse('finances:finance'))
-    if user.has_perm('scipost.can_view_all_funding_info'):
-        html += '<a class="item" href="{url}">Funders</a>'.format(
-            url=reverse('funders:funders'))
-    if user.has_perm('scipost.can_view_production'):
-        html += '<a class="item" href="{url}">Production</a>'.format(
-            url=reverse('production:production'))
-    if user.has_perm('scipost.can_view_pool'):
-        html += '<a class="item" href="{url}">Submission Pool</a>'.format(
-            url=reverse('submissions:pool'))
-
-    # Logout links
-    html += '<div class="divider"></div>'
-    html += '<a class="item" href="{url}">Logout</a>'.format(
-        url=reverse('scipost:logout'))
-
-    # Notifications
-    html += '<div class="divider"></div><h6 class="header">Inbox</h6>'
-    html += '<div class="live_notify_list"></div></div>'
-    html += '<div class="popover-body"></div></div>'
-    return format_html(html)
+    request = context['request']
+    context = {
+        'user': user,
+    }
+    return render_to_string('notifications/partials/notification_list_popover.html',
+                            context, request=request)
 
 
 def user_context(context):
diff --git a/notifications/views.py b/notifications/views.py
index b36eaab4806253da91d5c18cfc1aa033983249b9..8728b683a0639b1230570b53d6139098809764f9 100644
--- a/notifications/views.py
+++ b/notifications/views.py
@@ -60,7 +60,7 @@ def live_notification_list(request):
 
     try:
         # Default to 5 as a max number of notifications
-        num_to_fetch = max(int(request.GET.get('max', 5)), 1)
+        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
diff --git a/package.json b/package.json
index 9e3d508dcb107b4bbfa981744c683f1089a08a11..37b40615f359601885e9a941ce88f79c1691d868 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
     "extract-text-webpack-plugin": "^3.0.0",
     "file-loader": "^0.11.2",
     "imports-loader": "^0.7.1",
-    "jquery": "^2.2.0",
+    "jquery": "^3.3.1",
+    "jquery-ui": "^1.12.1",
     "node-loader": "^0.6.0",
     "node-sass": "^4.4.0",
     "popper.js": "^1.11.1",
diff --git a/requirements.txt b/requirements.txt
index 21531ce520116fba8000b6c04f2201a7bb7ee953..0328daa6b14b1f1ef889a4db82427391e2cb817f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -32,9 +32,8 @@ sphinx-rtd-theme==0.1.9  # Sphinx theme
 
 
 # Testing
-factory-boy==2.9.2
-Faker==0.7.18
-fake-factory==0.7.2  # Old version of Faker package
+factory-boy==2.10.0
+Faker==0.8.12
 
 
 # Django Utils
diff --git a/scipost/factories.py b/scipost/factories.py
index 67cb4e2df31821db68e780bbf44fef72d83636c2..0e3f561745d184e402192cf4de0c5d5a1f88ad28 100644
--- a/scipost/factories.py
+++ b/scipost/factories.py
@@ -4,31 +4,36 @@ import random
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group
 
+from common.helpers import generate_orcid
 from submissions.models import Submission
 
 from .models import Contributor, EditorialCollege, EditorialCollegeFellowship, Remark
 from .constants import TITLE_CHOICES, SCIPOST_SUBJECT_AREAS
 
-from django_countries.data import COUNTRIES
-from faker import Faker
-
 
 class ContributorFactory(factory.django.DjangoModelFactory):
-    title = random.choice(list(dict(TITLE_CHOICES).keys()))
+    title = factory.Iterator(TITLE_CHOICES, getter=lambda c: c[0])
     user = factory.SubFactory('scipost.factories.UserFactory', contributor=None)
     status = 1  # normal user
-    vetted_by = factory.SubFactory('scipost.factories.ContributorFactory', vetted_by=None)
-    personalwebpage = factory.Faker('url')
-    country_of_employment = factory.Iterator(list(COUNTRIES))
-    affiliation = factory.Faker('company')
+    vetted_by = factory.Iterator(Contributor.objects.all())
+    personalwebpage = factory.Faker('uri')
     expertises = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: [c[0]])
-    personalwebpage = factory.Faker('domain_name')
+    orcid_id = factory.lazy_attribute(lambda n: generate_orcid())
     address = factory.Faker('address')
+    invitation_key = factory.Faker('md5')
+    activation_key = factory.Faker('md5')
+    key_expires = factory.Faker('future_datetime')
 
     class Meta:
         model = Contributor
         django_get_or_create = ('user',)
 
+    @factory.post_generation
+    def add_to_vetting_editors(self, create, extracted, **kwargs):
+        if create:
+            from affiliations.factories import AffiliationFactory
+            AffiliationFactory(contributor=self)
+
 
 class VettingEditorFactory(ContributorFactory):
     @factory.post_generation
@@ -69,7 +74,7 @@ class EditorialCollegeFactory(factory.django.DjangoModelFactory):
 
     class Meta:
         model = EditorialCollege
-        django_get_or_create = ('discipline', )
+        django_get_or_create = ('discipline',)
 
 
 class EditorialCollegeFellowshipFactory(factory.django.DjangoModelFactory):
@@ -85,7 +90,7 @@ class SubmissionRemarkFactory(factory.django.DjangoModelFactory):
     contributor = factory.Iterator(Contributor.objects.all())
     submission = factory.Iterator(Submission.objects.all())
     date = factory.Faker('date_time_this_decade')
-    remark = factory.lazy_attribute(lambda x: Faker().paragraph())
+    remark = factory.Faker('paragraph')
 
     class Meta:
         model = Remark
diff --git a/scipost/management/commands/create_contributors.py b/scipost/management/commands/create_contributors.py
new file mode 100644
index 0000000000000000000000000000000000000000..025513c74d49b6f82a77268706cf84d314a950bb
--- /dev/null
+++ b/scipost/management/commands/create_contributors.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from scipost import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Contributor objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Contributors to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_contributors(kwargs['number'])
+
+    def create_contributors(self, n):
+        factories.ContributorFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Contributors.'.format(n=n)))
diff --git a/scipost/management/commands/create_remarks.py b/scipost/management/commands/create_remarks.py
new file mode 100644
index 0000000000000000000000000000000000000000..19ebe3e92c5098192ae4880b149f5822018528f5
--- /dev/null
+++ b/scipost/management/commands/create_remarks.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from scipost import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Remark objects (related to a Submission) using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Remarks to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_remarks(kwargs['number'])
+
+    def create_remarks(self, n):
+        factories.SubmissionRemarkFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Remarks.'.format(n=n)))
diff --git a/scipost/management/commands/models.py b/scipost/management/commands/models.py
deleted file mode 100644
index 0447660e2a189556e389b610e0c5d6bc797218aa..0000000000000000000000000000000000000000
--- a/scipost/management/commands/models.py
+++ /dev/null
@@ -1,188 +0,0 @@
-import datetime
-import hashlib
-import random
-import string
-
-from django.db import models, IntegrityError
-from django.conf import settings
-from django.utils import timezone
-
-from . import constants
-from .managers import RegistrationInvitationQuerySet, CitationNotificationQuerySet
-
-from scipost.constants import TITLE_CHOICES
-
-
-class RegistrationInvitation(models.Model):
-    """
-    Invitation to particular persons for registration
-    """
-    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
-    first_name = models.CharField(max_length=30)
-    last_name = models.CharField(max_length=150)
-    email = models.EmailField()
-    status = models.CharField(max_length=8, choices=constants.REGISTATION_INVITATION_STATUSES,
-                              default=constants.STATUS_DRAFT)
-
-    # Text content
-    message_style = models.CharField(max_length=1, choices=constants.INVITATION_STYLE,
-                                     default=constants.INVITATION_FORMAL)
-    personal_message = models.TextField(blank=True)
-    invited_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
-                                   blank=True, null=True, related_name='invitations_sent')
-    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='invitations_created')
-
-    # Related to objects
-    invitation_type = models.CharField(max_length=2, choices=constants.INVITATION_TYPE,
-                                       default=constants.INVITATION_CONTRIBUTOR)
-
-    # Response keys
-    invitation_key = models.CharField(max_length=40, unique=True)
-    key_expires = models.DateTimeField(default=timezone.now)
-
-    # Timestamps
-    date_sent_first = models.DateTimeField(null=True, blank=True)
-    date_sent_last = models.DateTimeField(null=True, blank=True)
-    times_sent = models.PositiveSmallIntegerField(default=0)
-    created = models.DateTimeField(auto_now_add=True)
-    modified = models.DateTimeField(auto_now=True)
-
-    objects = RegistrationInvitationQuerySet.as_manager()
-
-    class Meta:
-        ordering = ['last_name']
-
-    def __str__(self):
-        return '{} {} on {}'.format(self.first_name, self.last_name,
-                                    self.created.strftime("%Y-%m-%d"))
-
-    def save(self, *args, **kwargs):
-        self.refresh_keys(commit=False)
-        return super().save(*args, **kwargs)
-
-    def refresh_keys(self, force_new_key=False, commit=True):
-        # Generate email activation key and link
-        if not self.invitation_key or force_new_key:
-            # TODO: Replace this all by the `secrets` package available from python 3.6(!)
-            salt = ''
-            for i in range(5):
-                salt += random.choice(string.ascii_letters)
-            salt = salt.encode('utf8')
-            invitationsalt = self.last_name.encode('utf8')
-            self.invitation_key = hashlib.sha1(salt + invitationsalt).hexdigest()
-            self.key_expires = timezone.now() + datetime.timedelta(days=365)
-        if commit:
-            self.save()
-
-    def mail_sent(self, user=None):
-        """
-        Update instance fields as if a new invitation mail has been sent out.
-        """
-        if self.status == constants.STATUS_DRAFT:
-            self.status = constants.STATUS_SENT
-        if not self.date_sent_first:
-            self.date_sent_first = timezone.now()
-        self.date_sent_last = timezone.now()
-        self.invited_by = user or self.created_by
-        self.times_sent += 1
-        self.citation_notifications.update(processed=True)
-        self.save()
-
-    @property
-    def has_responded(self):
-        return self.status in [constants.STATUS_DECLINED, constants.STATUS_REGISTERED]
-
-
-class CitationNotification(models.Model):
-    invitation = models.ForeignKey('invitations.RegistrationInvitation',
-                                   on_delete=models.SET_NULL,
-                                   null=True, blank=True)
-    contributor = models.ForeignKey('scipost.Contributor',
-                                    on_delete=models.CASCADE,
-                                    null=True, blank=True,
-                                    related_name='+')
-
-    # Content
-    submission = models.ForeignKey('submissions.Submission', null=True, blank=True,
-                                   related_name='+')
-    publication = models.ForeignKey('journals.Publication', null=True, blank=True,
-                                    related_name='+')
-    processed = models.BooleanField(default=False)
-
-    # Meta info
-    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notifications_created')
-    date_sent = models.DateTimeField(null=True, blank=True)
-    created = models.DateTimeField(auto_now_add=True)
-    modified = models.DateTimeField(auto_now=True)
-
-    objects = CitationNotificationQuerySet.as_manager()
-
-    class Meta:
-        default_related_name = 'citation_notifications'
-        unique_together = (
-            ('invitation', 'submission'),
-            ('invitation', 'publication'),
-            ('contributor', 'submission'),
-            ('contributor', 'publication'),
-        )
-
-    def __str__(self):
-        _str = 'Citation for '
-        if self.invitation:
-            _str += ' Invitation ({} {})'.format(
-                self.invitation.first_name,
-                self.invitation.last_name,
-            )
-        elif self.contributor:
-            _str += ' Contributor ({})'.format(self.contributor)
-
-        _str += ' on '
-        if self.submission:
-            _str += 'Submission ({})'.format(self.submission.arxiv_identifier_w_vn_nr)
-        elif self.publication:
-            _str += 'Publication ({})'.format(self.publication.doi_label)
-        return _str
-
-    def save(self, *args, **kwargs):
-        if not self.submission and not self.publication:
-            raise IntegrityError(('CitationNotification needs to be related to either a '
-                                  'Submission or Publication object.'))
-        return super().save(*args, **kwargs)
-
-    def mail_sent(self):
-        """
-        Update instance fields as if a new citation notification mail has been sent out.
-        """
-        self.processed = True
-        if not self.date_sent:
-            # Don't overwrite by accident...
-            self.date_sent = timezone.now()
-        self.save()
-
-    def related_notifications(self):
-        return CitationNotification.objects.unprocessed().filter(
-            models.Q(contributor=self.contributor) | models.Q(invitation=self.invitation))
-
-    def get_first_related_contributor(self):
-        return self.related_notifications().filter(contributor__isnull=False).first()
-
-    @property
-    def email(self):
-        if self.invitation:
-            return self.invitation.email
-        elif self.contributor:
-            return self.contributor.user.email
-
-    @property
-    def last_name(self):
-        if self.invitation:
-            return self.invitation.last_name
-        elif self.contributor:
-            return self.contributor.user.last_name
-
-    @property
-    def get_title(self):
-        if self.invitation:
-            return self.invitation.get_title_display()
-        elif self.contributor:
-            return self.contributor.get_title_display()
diff --git a/scipost/management/commands/populate_db.py b/scipost/management/commands/populate_db.py
index 4efe19d85887e5a79782a2b8defb1919286e049f..4812d71e6fa2958a98c7792ff5c97579aa94b83e 100644
--- a/scipost/management/commands/populate_db.py
+++ b/scipost/management/commands/populate_db.py
@@ -1,34 +1,11 @@
 from django.core.management.base import BaseCommand
 
-from commentaries.factories import VettedCommentaryFactory
-from comments.factories import CommentaryCommentFactory, SubmissionCommentFactory,\
-                               ThesislinkCommentFactory
-from scipost.factories import SubmissionRemarkFactory
-from journals.factories import JournalFactory, VolumeFactory, IssueFactory, PublicationFactory
-from news.factories import NewsItemFactory
-from submissions.factories import EICassignedSubmissionFactory
-from theses.factories import VettedThesisLinkFactory
-
-from ...factories import ContributorFactory, EditorialCollegeFactory,\
-                         EditorialCollegeFellowshipFactory
+from comments.factories import CommentaryCommentFactory,\
+                               ThesislinkCommentFactory, ReplyCommentFactory
 
 
 class Command(BaseCommand):
     def add_arguments(self, parser):
-        parser.add_argument(
-            '--news',
-            action='store_true',
-            dest='news',
-            default=False,
-            help='Add NewsItems',
-        )
-        parser.add_argument(
-            '--commentaries',
-            action='store_true',
-            dest='commentaries',
-            default=False,
-            help='Add 5 Commentaries',
-        )
         parser.add_argument(
             '--comments',
             action='store_true',
@@ -36,147 +13,13 @@ class Command(BaseCommand):
             default=False,
             help='Add 10 Comments',
         )
-        parser.add_argument(
-            '--contributor',
-            action='store_true',
-            dest='contributor',
-            default=False,
-            help='Add 5 Contributors',
-        )
-        parser.add_argument(
-            '--college',
-            action='store_true',
-            dest='editorial-college',
-            default=False,
-            help='Add 5 Editorial College and Fellows (Contributors required)',
-        )
-        parser.add_argument(
-            '--pubset',
-            action='store_true',
-            dest='pubset',
-            default=False,
-            help='Add 5 Issues, Volumes and Journals',
-        )
-        parser.add_argument(
-            '--issues',
-            action='store_true',
-            dest='issues',
-            default=False,
-            help='Add 5 Issues',
-        )
-        parser.add_argument(
-            '--submissions',
-            action='store_true',
-            dest='submissions',
-            default=False,
-            help='Add 5 new submissions status EIC assigned',
-        )
-        parser.add_argument(
-            '--publications',
-            action='store_true',
-            dest='publications',
-            default=False,
-            help='Add 5 Publications (includes --issues action)',
-        )
-        parser.add_argument(
-            '--remarks',
-            action='store_true',
-            dest='remarks',
-            default=False,
-            help='Add 5 new Remarks linked to Submissions',
-        )
-        parser.add_argument(
-            '--theses',
-            action='store_true',
-            dest='theses',
-            default=False,
-            help='Add 5 ThesisLinks',
-        )
-        parser.add_argument(
-            '--all',
-            action='store_true',
-            dest='all',
-            default=False,
-            help='Add all available',
-        )
 
     def handle(self, *args, **kwargs):
-        if kwargs['contributor'] or kwargs['all']:
-            n = 5
-            if kwargs['all']:
-                n += 10
-            self.create_contributors(n)
-        if kwargs['commentaries'] or kwargs['all']:
-            self.create_commentaries()
-        if kwargs['comments'] or kwargs['all']:
+        if kwargs['comments']:
             self.create_comments()
-        if kwargs['editorial-college'] or kwargs['all']:
-            self.create_editorial_college()
-            self.create_editorial_college_fellows()
-        if kwargs['news'] or kwargs['all']:
-            self.create_news_items()
-        if kwargs['submissions'] or kwargs['all']:
-            self.create_submissions()
-        if kwargs['pubset'] or kwargs['all']:
-            self.create_pubset()
-        if kwargs['issues'] or kwargs['all']:
-            self.create_issues()
-        if kwargs['publications'] or kwargs['all']:
-            self.create_publications()
-        if kwargs['remarks'] or kwargs['all']:
-            self.create_remarks()
-        if kwargs['theses'] or kwargs['all']:
-            self.create_theses()
-
-    def create_contributors(self, n=5):
-        ContributorFactory.create_batch(n)
-        self.stdout.write(self.style.SUCCESS('Successfully created %i Contributors.' % n))
-
-    def create_commentaries(self):
-        VettedCommentaryFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Commentaries.'))
 
     def create_comments(self):
         CommentaryCommentFactory.create_batch(3)
-        SubmissionCommentFactory.create_batch(4)
+        ReplyCommentFactory.create_batch(2)
         ThesislinkCommentFactory.create_batch(3)
         self.stdout.write(self.style.SUCCESS('Successfully created 10 Comments.'))
-
-    def create_editorial_college(self):
-        EditorialCollegeFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Editorial College\'s.'))
-
-    def create_editorial_college_fellows(self):
-        EditorialCollegeFellowshipFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Editorial College Fellows.'))
-
-    def create_news_items(self):
-        NewsItemFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 News items.'))
-
-    def create_submissions(self):
-        EICassignedSubmissionFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Submissions.'))
-
-    def create_pubset(self):
-        VolumeFactory.create_batch(5)
-        IssueFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS(
-                          'Successfully created 5x {Journal, Volume and Issue}.'))
-
-    def create_issues(self):
-        IssueFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS(
-                          'Successfully created 5 Issue.'))
-
-    def create_publications(self):
-        PublicationFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Publications.'))
-
-    def create_remarks(self):
-        SubmissionRemarkFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 Remarks.'))
-
-    def create_theses(self):
-        VettedThesisLinkFactory.create_batch(5)
-        self.stdout.write(self.style.SUCCESS('Successfully created 5 ThesisLinks.'))
diff --git a/scipost/migrations/0007_auto_20180314_1502.py b/scipost/migrations/0007_auto_20180314_1502.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f5d6e3e9487a5660baf324f47590992396fcddc
--- /dev/null
+++ b/scipost/migrations/0007_auto_20180314_1502.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-14 14:02
+from __future__ import unicode_literals
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0006_auto_20180220_2120'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='contributor',
+            name='orcid_id',
+            field=models.CharField(blank=True, max_length=20, validators=[django.core.validators.RegexValidator('^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}$', 'Please follow the ORCID format, e.g.: 0000-0001-2345-6789')], verbose_name='ORCID id'),
+        ),
+    ]
diff --git a/scipost/static/scipost/SciPost.css b/scipost/static/scipost/SciPost.css
index 44e1b4c27a9f5f124ba0bfed164a4ffca84c4685..616f2a6bcd0f597b9a0146c8ab314a7dcee8ef5b 100644
--- a/scipost/static/scipost/SciPost.css
+++ b/scipost/static/scipost/SciPost.css
@@ -666,22 +666,6 @@ p.publicationAuthors {
     padding: 1rem;
     border-radius: 1.4px;
 }
-#preview-strengths {
-  border: 1px solid black;
-  white-space: pre-wrap;
-}
-#preview-weaknesses {
-  border: 1px solid black;
-  white-space: pre-wrap;
-}
-#preview-report {
-  border: 1px solid black;
-  white-space: pre-wrap;
-}
-#preview-requested_changes {
-  border: 1px solid black;
-  white-space: pre-wrap;
-}
 
 /* Styling of sphinxdoc-generated docs */
 .pagination-top {
diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss
index 86ec1f0f209ca547d73c19b50d2896fa6f16be36..0721df1ff8ee2a1c88d3afe50d431ab6a9b3984e 100644
--- a/scipost/static/scipost/assets/config/preconfig.scss
+++ b/scipost/static/scipost/assets/config/preconfig.scss
@@ -34,6 +34,7 @@ $white: #fff;
 $blue: $scipost-lightblue;  // Primary
 $green: #6ebb6e;
 $cyan: $scipost-lightestblue;
+$orange: $scipost-orange;
 $yellow: $scipost-orange;
 $gray-100: $scipost-white;
 $gray-200: #e5e5e5;
diff --git a/scipost/static/scipost/assets/css/_list_group.scss b/scipost/static/scipost/assets/css/_list_group.scss
index c2313938bd328c3d3fa2c09ed704792f1f706da6..b92386962d3936d208b009cd8c3b4fabc11fbb00 100644
--- a/scipost/static/scipost/assets/css/_list_group.scss
+++ b/scipost/static/scipost/assets/css/_list_group.scss
@@ -49,3 +49,17 @@ ul.references {
 ul.links > li.active a {
     font-weight: 700;
 }
+
+ul.sortable-list {
+    li {
+        background-color: $white;
+        border: 1px solid $scipost-darkblue;
+        padding: 0.5rem;
+        margin-bottom: -1px;
+        cursor: move;
+
+        &:hover {
+            background-color: $gray-200;
+        }
+    }
+}
diff --git a/scipost/static/scipost/assets/css/_notifications.scss b/scipost/static/scipost/assets/css/_notifications.scss
index 2338ec63b5217d12980d3c1b0ef5c2d4e70fb368..5f980c52dac31f65b2bf1b79065d8dda0013e521 100644
--- a/scipost/static/scipost/assets/css/_notifications.scss
+++ b/scipost/static/scipost/assets/css/_notifications.scss
@@ -30,20 +30,48 @@
     }
 }
 
-.popover {
-    top: 9px !important;
-}
+
 .popover-template {
     display: none;
 }
 
 .notifications {
     padding: 0;
-    min-width: 500px;
+    min-width: 450px;
+    border-color: $gray-600;
+    border-radius: 1px;
+    margin-top: 17px !important;
 
+    .inbox-header,
     .header {
-        padding: 1rem 1rem 0.5rem 1rem;
-        background-color: #f9f9f9;
+        padding: 0.5rem 0;
+        border-bottom: 1px solid $gray-600;
+        margin: 0 1rem;
+    }
+
+    .inbox-header {
+        border-top: 1px solid $gray-600;
+        border-bottom: 0;
+        margin-top: 0.25rem;
+    }
+
+    .header {
+        padding-top: 1rem;
+        padding-bottom: 1rem;
+        margin-bottom: 0.25rem;
+
+        h3,
+        .item {
+            border: 0;
+            line-height: 20px;
+            display: inline-block;
+        }
+
+        .item {
+            float: right;
+            padding-left: 0;
+            padding-right: 0;
+        }
     }
 
     li.item {
@@ -55,7 +83,7 @@
     .item {
         padding: 0.4rem 1rem;
         border-radius: 0;
-        border-top: 1px solid #fff;
+        border-top: 1px solid $white;
         border-left: 0;
         border-right: 0;
         flex-direction: row;
@@ -80,6 +108,30 @@
         }
     }
 
+    .live_notify_list {
+        max-height: 250px;
+        overflow: scroll;
+
+        &::after {
+            bottom: 0;
+            content: '';
+            display: block;
+            width: 100%;
+            position: absolute;
+            height: 20px;
+            box-shadow: inset 0 -3px 9px 0px $gray-600;
+        }
+
+        .meta {
+            font-size: 90%;
+            margin-top: 0.5rem;
+        }
+
+        .item {
+            border-color: $gray-600;
+        }
+    }
+
     a.item,
     .item a {
         color: $scipost-lightblue;
@@ -114,7 +166,48 @@
         }
     }
 
+
+    .links .item {
+        &.active,
+        &.active[href]:hover {
+            background-color: transparent;
+        }
+
+        &.active {
+            font-weight: 600;
+            text-decoration: underline;
+        }
+    }
+
     .item:hover .actions {
         opacity: 1.0;
     }
+
+    .unavailable {
+        margin: 0 1rem 0.25rem;
+        padding: 0.5rem 0 0.75rem;
+        border-bottom: 1px solid $gray-600;
+
+        .head {
+            color: $orange;
+            font-weight: 600;
+        }
+    }
+
+    .arrow {
+        top: -5px !important;
+
+        &::before {
+            border-bottom-color: $gray-600 !important;
+            border-width: 12px;
+            left: -2px;
+            top: -7px !important;
+        }
+
+        &::after {
+            border-bottom-color: $white;
+            border-width: 10px;
+            // left: -2px;
+        }
+    }
 }
diff --git a/scipost/static/scipost/assets/css/_reports.scss b/scipost/static/scipost/assets/css/_reports.scss
index 871bb5accdaf88fc863fe537b54214286203a04a..70bba0b47549c5f8cccf4d28a647276183bd4e9a 100644
--- a/scipost/static/scipost/assets/css/_reports.scss
+++ b/scipost/static/scipost/assets/css/_reports.scss
@@ -40,3 +40,22 @@
         }
     }
 }
+
+.anonymous-alert {
+    margin-bottom: 0.5rem;
+
+    .anonymous-yes {
+        color: $red;
+    }
+    .anonymous-no {
+        color: $green;
+    }
+}
+
+.report-preview {
+    .latex-preview {
+        border: 1px solid $scipost-darkblue;
+        padding: 0.5rem 0.75rem;
+        white-space: pre-wrap;
+    }
+}
diff --git a/scipost/static/scipost/assets/js/notifications.js b/scipost/static/scipost/assets/js/notifications.js
index 12f6b849bbcea9efbd0ee7b546e9ea0995cc00f3..eb7e8e671224ce41ef3a8feb1741da6118fe6434 100644
--- a/scipost/static/scipost/assets/js/notifications.js
+++ b/scipost/static/scipost/assets/js/notifications.js
@@ -63,11 +63,11 @@ function update_list_callback(data, args) {
             }
         }
         if(typeof item.timesince !== 'undefined'){
-            message += "<br><small>";
+            message += "<div class='meta'>";
             if(typeof item.forward_link !== 'undefined') {
                 message += " <a href='" + item.forward_link + "'>Direct link</a> &middot; ";
             }
-            message += "<span class='text-muted'>" + item.timesince + " ago</span></small>";
+            message += "<span class='text-muted'>" + item.timesince + " ago</span></div>";
         }
 
         // Notification actions
@@ -127,7 +127,7 @@ var badge_timer = setInterval(trigger_badge, 60000);
 function initiate_popover() {
     var template = $('.notifications_container .popover-template').html();
     $('.notifications_container a[data-toggle="popover"]').popover({
-        trigger: 'focus',
+        // trigger: 'focus',
         template: template,
         placement: 'bottom',
         title: 'empty-on-purpose'
diff --git a/scipost/static/scipost/assets/js/scripts.js b/scipost/static/scipost/assets/js/scripts.js
index 6a451fe5e5b1bed6004c85e7da4fb15275f42a5f..8adbae645179b5aea816d04c6e9f86a7f06e52b5 100644
--- a/scipost/static/scipost/assets/js/scripts.js
+++ b/scipost/static/scipost/assets/js/scripts.js
@@ -1,3 +1,6 @@
+require('jquery-ui/ui/widgets/sortable');
+require('jquery-ui/ui/disable-selection');
+
 import notifications from './notifications.js';
 
 function hide_all_alerts() {
@@ -12,6 +15,19 @@ var activate_tooltip = function() {
     });
 }
 
+
+var sort_form_list = function(list_el) {
+    $(list_el).sortable({
+        update: function(event, ui) {
+            $.each($(list_el + ' li'), function(index, el) {
+                $(el).find('input[name$=ORDER]').val(index + 1);
+            });
+        }
+    });
+};
+
+
+
 var getUrlParameter = function getUrlParameter(sParam) {
     var sPageURL = decodeURIComponent(window.location.search.substring(1)),
         sURLVariables = sPageURL.split('&'),
@@ -53,6 +69,7 @@ function init_page() {
     });
 
     activate_tooltip();
+    sort_form_list('form ul.sortable-list');
 }
 
 $(function(){
diff --git a/scipost/templates/scipost/navbar.html b/scipost/templates/scipost/navbar.html
index 2ea2260148733e03bedea67e360d526066216c6a..43fcc97810c0547f2097623df562ae982837bccf 100644
--- a/scipost/templates/scipost/navbar.html
+++ b/scipost/templates/scipost/navbar.html
@@ -70,10 +70,6 @@
                   </li>
               {% endif %}
 
-              <li class="nav-item search-item">
-                <a class="nav-link" href="{% url 'scipost:search' %}">Search</a>
-              </li>
-
             </ul>
             <form action="{% url 'scipost:search' %}" method="get" class="form-inline search-nav-form">
                 <input class="form-control mr-sm-2" id="id_q" maxlength="100" name="q" type="text" aria-label="Search" value="{{ search_query|default:'' }}">
diff --git a/scipost/templatetags/filename.py b/scipost/templatetags/filename.py
index 6a20f1384cb29b5befd3d6dbf9c8c4a95335c3a1..2bff32b21130e34881c7fbccc41b9084dda4aa3f 100644
--- a/scipost/templatetags/filename.py
+++ b/scipost/templatetags/filename.py
@@ -8,4 +8,7 @@ register = template.Library()
 
 @register.filter
 def filename(value):
-    return os.path.basename(value.file.name)
+    try:
+        return os.path.basename(value.file.name)
+    except OSError:
+        return 'Error: File not found'
diff --git a/scipost/test_views.py b/scipost/test_views.py
index 22291f792c19630af0cc6b5192667fb3b4c4d8b4..3c6a15c1d384bc65bd1588bd90fd4ec78030f8e7 100644
--- a/scipost/test_views.py
+++ b/scipost/test_views.py
@@ -2,8 +2,8 @@ from django.core.urlresolvers import reverse
 from django.contrib.auth.models import Group
 from django.test import TestCase, Client, tag
 
-from commentaries.factories import UnvettedCommentaryFactory, VettedCommentaryFactory,\
-                                   UnpublishedVettedCommentaryFactory
+from commentaries.factories import UnvettedCommentaryFactory, CommentaryFactory,\
+                                   UnpublishedCommentaryFactory
 from commentaries.forms import CommentarySearchForm
 from commentaries.models import Commentary
 
@@ -81,7 +81,7 @@ class VetCommentaryRequestsTest(TestCase):
         self.assertEquals(response.context['commentary_to_vet'], None)
 
         # Only vetted Commentaries exist!
-        VettedCommentaryFactory()
+        CommentaryFactory()
         response = self.client.get(self.view_url)
         self.assertEquals(response.context['commentary_to_vet'], None)
 
@@ -96,7 +96,7 @@ class BrowseCommentariesTest(TestCase):
     fixtures = ['groups', 'permissions']
 
     def setUp(self):
-        VettedCommentaryFactory(discipline='physics')
+        CommentaryFactory(discipline='physics')
         self.view_url = reverse('commentaries:browse', kwargs={
             'discipline': 'physics',
             'nrweeksback': '1'
@@ -118,7 +118,7 @@ class CommentaryDetailTest(TestCase):
 
     def setUp(self):
         self.client = Client()
-        self.commentary = UnpublishedVettedCommentaryFactory()
+        self.commentary = UnpublishedCommentaryFactory()
         self.target = reverse(
             'commentaries:commentary',
             kwargs={'arxiv_or_DOI_string': self.commentary.arxiv_or_DOI_string}
diff --git a/submissions/factories.py b/submissions/factories.py
index d9c32c745fd079caed4ab17d02fd3511c0605ff9..6231ec36adcb4cd8b0ea9cd86c4155ffbc482898 100644
--- a/submissions/factories.py
+++ b/submissions/factories.py
@@ -1,108 +1,175 @@
 import factory
 import pytz
+import random
 
-from django.utils import timezone
-
+from comments.factories import SubmissionCommentFactory
 from scipost.constants import SCIPOST_SUBJECT_AREAS
 from scipost.models import Contributor
 from journals.constants import SCIPOST_JOURNALS_DOMAINS
-from common.helpers import random_arxiv_identifier_without_version_number, random_scipost_journal
+from common.helpers import random_arxiv_identifier_without_version_number, random_scipost_journal,\
+    random_scipost_report_doi_label
 
 from .constants import STATUS_UNASSIGNED, STATUS_EIC_ASSIGNED, STATUS_RESUBMISSION_INCOMING,\
                        STATUS_PUBLISHED, SUBMISSION_TYPE, STATUS_RESUBMITTED, STATUS_VETTED,\
                        REFEREE_QUALIFICATION, RANKING_CHOICES, QUALITY_SPEC, REPORT_REC,\
                        REPORT_STATUSES, STATUS_UNVETTED, STATUS_DRAFT
-from .models import Submission, Report, RefereeInvitation
+from .models import Submission, Report, RefereeInvitation, EICRecommendation, EditorialAssignment
 
 from faker import Faker
 
 
 class SubmissionFactory(factory.django.DjangoModelFactory):
-    class Meta:
-        model = Submission
-
     author_list = factory.Faker('name')
     submitted_by = factory.Iterator(Contributor.objects.all())
+    submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0])
     submitted_to_journal = factory.Sequence(lambda n: random_scipost_journal())
-    title = factory.lazy_attribute(lambda x: Faker().sentence())
-    abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
+    title = factory.Faker('sentence')
+    abstract = factory.Faker('paragraph', nb_sentences=10)
     arxiv_link = factory.Faker('uri')
     arxiv_identifier_wo_vn_nr = factory.Sequence(
-                                    lambda n: random_arxiv_identifier_without_version_number())
+        lambda n: random_arxiv_identifier_without_version_number())
     subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
     domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
-    abstract = Faker().paragraph()
-    author_comments = Faker().paragraph()
-    remarks_for_editors = Faker().paragraph()
-    submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0])
+    abstract = factory.Faker('paragraph')
+    author_comments = factory.Faker('paragraph')
+    remarks_for_editors = factory.Faker('paragraph')
     is_current = True
+    arxiv_vn_nr = 1
+    arxiv_link = factory.lazy_attribute(lambda o: (
+        'https://arxiv.org/abs/%s' % o.arxiv_identifier_wo_vn_nr))
+    arxiv_identifier_w_vn_nr = factory.lazy_attribute(lambda o: '%sv%i' % (
+        o.arxiv_identifier_wo_vn_nr, o.arxiv_vn_nr))
+    submission_date = factory.Faker('date_this_decade')
+    latest_activity = factory.LazyAttribute(lambda o: Faker().date_time_between(
+        start_date=o.submission_date, end_date="now", tzinfo=pytz.UTC))
 
-    @factory.post_generation
-    def fill_arxiv_fields(self, create, extracted, **kwargs):
-        '''Fill empty arxiv fields.'''
-        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier_wo_vn_nr
-        self.arxiv_identifier_w_vn_nr = '%sv1' % self.arxiv_identifier_wo_vn_nr
-        self.arxiv_vn_nr = kwargs.get('arxiv_vn_nr', 1)
+    class Meta:
+        model = Submission
 
     @factory.post_generation
     def contributors(self, create, extracted, **kwargs):
-        contributors = list(Contributor.objects.order_by('?')[:4])
+        contributors = Contributor.objects.all()
+        if self.editor_in_charge:
+            contributors = contributors.exclude(id=self.editor_in_charge.id)
+        contributors = contributors.order_by('?')[:random.randint(1, 6)]
 
         # Auto-add the submitter as an author
-        self.submitted_by = contributors.pop()
+        self.submitted_by = contributors[0]
+        self.author_list = ', '.join([
+            '%s %s' % (c.user.first_name, c.user.last_name) for c in contributors])
 
         if not create:
             return
-        self.authors.add(self.submitted_by)
 
         # Add three random authors
-        for contrib in contributors:
-            self.authors.add(contrib)
-            self.author_list += ', %s %s' % (contrib.user.first_name, contrib.user.last_name)
-
-    @factory.post_generation
-    def dates(self, create, extracted, **kwargs):
-        timezone.now()
-        if kwargs.get('submission', False):
-            self.submission_date = kwargs['submission']
-            self.cycle.update_deadline()
-            return
-        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
-                                                         tzinfo=pytz.UTC).date()
-        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
-                                                         end_date="now", tzinfo=pytz.UTC)
+        self.authors.add(*contributors)
         self.cycle.update_deadline()
 
 
 class UnassignedSubmissionFactory(SubmissionFactory):
-    '''This Submission is a 'new request' by a Contributor for its Submission.'''
+    """
+    A new incoming Submission without any EIC assigned.
+    """
     status = STATUS_UNASSIGNED
 
 
 class EICassignedSubmissionFactory(SubmissionFactory):
+    """
+    A Submission with an EIC assigned, visible in the pool and refereeing in process.
+    """
     status = STATUS_EIC_ASSIGNED
     open_for_commenting = True
     open_for_reporting = True
 
+    @factory.lazy_attribute
+    def editor_in_charge(self):
+        return Contributor.objects.order_by('?').first()
+
     @factory.post_generation
-    def eic(self, create, extracted, **kwargs):
-        '''Assign an EIC to submission.'''
-        author_ids = list(self.authors.values_list('id', flat=True))
-        self.editor_in_charge = (Contributor.objects.order_by('?')
-                                            .exclude(pk=self.submitted_by.pk)
-                                            .exclude(pk__in=author_ids).first())
+    def eic_assignment(self, create, extracted, **kwargs):
+        if create:
+            EditorialAssignmentFactory(submission=self, to=self.editor_in_charge)
+
+    @factory.post_generation
+    def referee_invites(self, create, extracted, **kwargs):
+        for i in range(random.randint(1, 3)):
+            RefereeInvitationFactory(submission=self)
+
+        for i in range(random.randint(0, 2)):
+            AcceptedRefereeInvitationFactory(submission=self)
 
+        for i in range(random.randint(0, 2)):
+            FulfilledRefereeInvitationFactory(submission=self)
 
-class ResubmittedSubmissionFactory(SubmissionFactory):
-    '''This Submission is a `resubmitted` version.'''
+    @factory.post_generation
+    def comments(self, create, extracted, **kwargs):
+        if create:
+            for i in range(random.randint(0, 3)):
+                SubmissionCommentFactory(content_object=self)
+
+    @factory.post_generation
+    def eic_recommendation(self, create, extracted, **kwargs):
+        if create:
+            EICRecommendationFactory(submission=self)
+
+
+class ResubmittedSubmissionFactory(EICassignedSubmissionFactory):
+    """
+    A Submission that has a newer Submission version in the database
+    with a successive version number.
+    """
     status = STATUS_RESUBMITTED
     open_for_commenting = False
     open_for_reporting = False
     is_current = False
     is_resubmission = False
 
+    @factory.post_generation
+    def successive_submission(self, create, extracted, **kwargs):
+        """
+        Generate a second Submission that's the successive version of the resubmitted Submission
+        """
+        if create and extracted is not False:
+            # Prevent infinite loops by checking the extracted argument
+            ResubmissionFactory(arxiv_identifier_wo_vn_nr=self.arxiv_identifier_wo_vn_nr,
+                                previous_submission=False)
 
-class ResubmissionFactory(SubmissionFactory):
+    @factory.post_generation
+    def gather_successor_data(self, create, extracted, **kwargs):
+        """
+        Gather some data from Submission with same arxiv id such that this Submission
+        more or less looks like any regular real resubmission.
+        """
+        submission = Submission.objects.filter(
+            arxiv_identifier_wo_vn_nr=self.arxiv_identifier_wo_vn_nr).exclude(
+            arxiv_vn_nr=self.arxiv_vn_nr).first()
+        if not submission:
+            return
+
+        self.author_list = submission.author_list
+        self.submitted_by = submission.submitted_by
+        self.editor_in_charge = submission.editor_in_charge
+        self.submission_type = submission.submission_type
+        self.submitted_to_journal = submission.submitted_to_journal
+        self.title = submission.title
+        self.subject_area = submission.subject_area
+        self.domain = submission.domain
+        self.title = submission.title
+        self.authors.set(self.authors.all())
+
+    @factory.post_generation
+    def referee_invites(self, create, extracted, **kwargs):
+        """
+        This Submission is deactivated for refereeing.
+        """
+        for i in range(random.randint(0, 2)):
+            FulfilledRefereeInvitationFactory(submission=self)
+
+        for i in range(random.randint(1, 3)):
+            CancelledRefereeInvitationFactory(submission=self)
+
+
+class ResubmissionFactory(EICassignedSubmissionFactory):
     """
     This Submission is a newer version of a Submission which is
     already known by the SciPost database.
@@ -111,63 +178,99 @@ class ResubmissionFactory(SubmissionFactory):
     open_for_commenting = True
     open_for_reporting = True
     is_resubmission = True
+    arxiv_vn_nr = 2
 
     @factory.post_generation
-    def fill_arxiv_fields(self, create, extracted, **kwargs):
-        '''Fill empty arxiv fields.'''
-        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier_wo_vn_nr
-        self.arxiv_identifier_w_vn_nr = '%sv2' % self.arxiv_identifier_wo_vn_nr
-        self.arxiv_vn_nr = 2
+    def previous_submission(self, create, extracted, **kwargs):
+        if create and extracted is not False:
+            # Prevent infinite loops by checking the extracted argument
+            ResubmittedSubmissionFactory(arxiv_identifier_wo_vn_nr=self.arxiv_identifier_wo_vn_nr,
+                                         successive_submission=False)
 
     @factory.post_generation
-    def eic(self, create, extracted, **kwargs):
-        '''Assign an EIC to submission.'''
-        author_ids = list(self.authors.values_list('id', flat=True))
-        self.editor_in_charge = (Contributor.objects.order_by('?')
-                                            .exclude(pk=self.submitted_by.pk)
-                                            .exclude(pk__in=author_ids).first())
+    def gather_predecessor_data(self, create, extracted, **kwargs):
+        """
+        Gather some data from Submission with same arxiv id such that this Submission
+        more or less looks like any regular real resubmission.
+        """
+        submission = Submission.objects.filter(
+            arxiv_identifier_wo_vn_nr=self.arxiv_identifier_wo_vn_nr).exclude(
+            arxiv_vn_nr=self.arxiv_vn_nr).first()
+        if not submission:
+            return
+
+        self.author_list = submission.author_list
+        self.submitted_by = submission.submitted_by
+        self.editor_in_charge = submission.editor_in_charge
+        self.submission_type = submission.submission_type
+        self.submitted_to_journal = submission.submitted_to_journal
+        self.title = submission.title
+        self.subject_area = submission.subject_area
+        self.domain = submission.domain
+        self.title = submission.title
+        self.authors.set(self.authors.all())
 
     @factory.post_generation
-    def dates(self, create, extracted, **kwargs):
-        """Overwrite the parent `dates` method to skip the update_deadline call."""
-        timezone.now()
-        if kwargs.get('submission', False):
-            self.submission_date = kwargs['submission']
-            return
-        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
-                                                         tzinfo=pytz.UTC).date()
-        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
-                                                         end_date="now", tzinfo=pytz.UTC)
+    def referee_invites(self, create, extracted, **kwargs):
+        """
+        Referees for resubmissions are invited once the cycle has been chosen.
+        """
+        pass
 
 
-class PublishedSubmissionFactory(SubmissionFactory):
+class PublishedSubmissionFactory(EICassignedSubmissionFactory):
     status = STATUS_PUBLISHED
     open_for_commenting = False
     open_for_reporting = False
 
+    @factory.post_generation
+    def generate_publication(self, create, extracted, **kwargs):
+        if create and extracted is not False:
+            from journals.factories import PublicationFactory
+            PublicationFactory(
+                journal=self.submitted_to_journal,
+                accepted_submission=self, title=self.title, author_list=self.author_list)
 
-class ReportFactory(factory.django.DjangoModelFactory):
-    class Meta:
-        model = Report
+    @factory.post_generation
+    def eic_assignment(self, create, extracted, **kwargs):
+        if create:
+            EditorialAssignmentFactory(submission=self, to=self.editor_in_charge, completed=True)
 
+    @factory.post_generation
+    def referee_invites(self, create, extracted, **kwargs):
+        for i in range(random.randint(2, 4)):
+            FulfilledRefereeInvitationFactory(submission=self)
+
+        for i in range(random.randint(0, 2)):
+            CancelledRefereeInvitationFactory(submission=self)
+
+
+class ReportFactory(factory.django.DjangoModelFactory):
     status = factory.Iterator(REPORT_STATUSES, getter=lambda c: c[0])
     submission = factory.Iterator(Submission.objects.all())
-    date_submitted = Faker().date_time_between(start_date="-3y", end_date="now", tzinfo=pytz.UTC)
+    date_submitted = factory.Faker('date_time_this_decade')
     vetted_by = factory.Iterator(Contributor.objects.all())
     author = factory.Iterator(Contributor.objects.all())
-    qualification = factory.Iterator(REFEREE_QUALIFICATION, getter=lambda c: c[0])
-    strengths = Faker().paragraph()
-    weaknesses = Faker().paragraph()
-    report = Faker().paragraph()
-    requested_changes = Faker().paragraph()
-    validity = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
-    significance = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
-    originality = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
-    clarity = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
-    formatting = factory.Iterator(QUALITY_SPEC, getter=lambda c: c[0])
-    grammar = factory.Iterator(QUALITY_SPEC, getter=lambda c: c[0])
-    recommendation = factory.Iterator(REPORT_REC, getter=lambda c: c[0])
-    remarks_for_editors = Faker().paragraph()
+    strengths = factory.Faker('paragraph')
+    weaknesses = factory.Faker('paragraph')
+    report = factory.Faker('paragraph')
+    requested_changes = factory.Faker('paragraph')
+
+    qualification = factory.Iterator(REFEREE_QUALIFICATION[1:], getter=lambda c: c[0])
+    validity = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0])
+    significance = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0])
+    originality = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0])
+    clarity = factory.Iterator(RANKING_CHOICES[1:], getter=lambda c: c[0])
+    formatting = factory.Iterator(QUALITY_SPEC[1:], getter=lambda c: c[0])
+    grammar = factory.Iterator(QUALITY_SPEC[1:], getter=lambda c: c[0])
+    recommendation = factory.Iterator(REPORT_REC[1:], getter=lambda c: c[0])
+
+    remarks_for_editors = factory.Faker('paragraph')
+    flagged = factory.Faker('boolean', chance_of_getting_true=10)
+    anonymous = factory.Faker('boolean', chance_of_getting_true=75)
+
+    class Meta:
+        model = Report
 
 
 class DraftReportFactory(ReportFactory):
@@ -182,26 +285,88 @@ class UnVettedReportFactory(ReportFactory):
 
 class VettedReportFactory(ReportFactory):
     status = STATUS_VETTED
+    needs_doi = True
+    doideposit_needs_updating = factory.Faker('boolean')
+    doi_label = factory.lazy_attribute(lambda n: random_scipost_report_doi_label())
+    pdf_report = factory.Faker('file_name', extension='pdf')
 
 
 class RefereeInvitationFactory(factory.django.DjangoModelFactory):
+    submission = factory.SubFactory('submissions.factories.SubmissionFactory')
+    referee = factory.lazy_attribute(lambda o: Contributor.objects.exclude(
+        id__in=o.submission.authors.all()).order_by('?').first())
+
+    title = factory.lazy_attribute(lambda o: o.referee.title)
+    first_name = factory.lazy_attribute(lambda o: o.referee.user.first_name)
+    last_name = factory.lazy_attribute(lambda o: o.referee.user.last_name)
+    email_address = factory.lazy_attribute(lambda o: o.referee.user.email)
+    date_invited = factory.lazy_attribute(lambda o: o.submission.latest_activity)
+    nr_reminders = factory.lazy_attribute(lambda o: random.randint(0, 4))
+    date_last_reminded = factory.lazy_attribute(lambda o: o.submission.latest_activity)
+
+    invitation_key = factory.Faker('md5')
+    invited_by = factory.lazy_attribute(lambda o: o.submission.editor_in_charge)
+
     class Meta:
         model = RefereeInvitation
 
-    submission = factory.SubFactory('submissions.factories.SubmissionFactory')
-    referee = factory.Iterator(Contributor.objects.all())
 
-    invitation_key = factory.Faker('md5')
-    invited_by = factory.Iterator(Contributor.objects.all())
+class AcceptedRefereeInvitationFactory(RefereeInvitationFactory):
+    accepted = True
+    date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between(
+        start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC))
 
     @factory.post_generation
-    def contributor_fields(self, create, extracted, **kwargs):
-        self.title = self.referee.title
-        self.first_name = self.referee.user.first_name
-        self.last_name = self.referee.user.last_name
-        self.email_address = self.referee.user.email
+    def report(self, create, extracted, **kwargs):
+        if create:
+            VettedReportFactory(submission=self.submission, author=self.referee)
 
 
-class AcceptedRefereeInvitationFactory(RefereeInvitationFactory):
+class FulfilledRefereeInvitationFactory(AcceptedRefereeInvitationFactory):
+    fulfilled = True
+    date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between(
+        start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC))
+
+    @factory.post_generation
+    def report(self, create, extracted, **kwargs):
+        if create:
+            VettedReportFactory(submission=self.submission, author=self.referee)
+
+
+class CancelledRefereeInvitationFactory(AcceptedRefereeInvitationFactory):
+    fulfilled = False
+    cancelled = True
+    date_responded = factory.lazy_attribute(lambda o: Faker().date_time_between(
+        start_date=o.date_invited, end_date="now", tzinfo=pytz.UTC))
+
+
+class EICRecommendationFactory(factory.django.DjangoModelFactory):
+    submission = factory.Iterator(Submission.objects.all())
+    date_submitted = factory.lazy_attribute(lambda o: Faker().date_time_between(
+        start_date=o.submission.submission_date, end_date="now", tzinfo=pytz.UTC))
+    remarks_for_authors = factory.Faker('paragraph')
+    requested_changes = factory.Faker('paragraph')
+    remarks_for_editorial_college = factory.Faker('paragraph')
+    recommendation = factory.Iterator(REPORT_REC[1:], getter=lambda c: c[0])
+    version = 1
+    active = True
+
+    class Meta:
+        model = EICRecommendation
+
+
+class EditorialAssignmentFactory(factory.django.DjangoModelFactory):
+    """
+    A EditorialAssignmentFactory should always have a `submission` explicitly assigned. This will
+    mostly be done using the post_generation hook in any SubmissionFactory.
+    """
+    submission = None
+    to = factory.Iterator(Contributor.objects.all())
     accepted = True
-    date_responded = Faker().date_time_between(start_date="-1y", end_date="now", tzinfo=pytz.UTC)
+    deprecated = False
+    completed = False
+    date_created = factory.lazy_attribute(lambda o: o.submission.latest_activity)
+    date_answered = factory.lazy_attribute(lambda o: o.submission.latest_activity)
+
+    class Meta:
+        model = EditorialAssignment
diff --git a/submissions/management/commands/create_submissions.py b/submissions/management/commands/create_submissions.py
new file mode 100644
index 0000000000000000000000000000000000000000..575f01ec60414c1293e1c5dd342d606be7a07b4c
--- /dev/null
+++ b/submissions/management/commands/create_submissions.py
@@ -0,0 +1,36 @@
+from django.core.management.base import BaseCommand
+
+from submissions import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Submission objects by using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of submissions to add',
+        )
+        parser.add_argument(
+            '-s', '--status',
+            choices=['unassigned', 'assigned', 'resubmitted', 'resubmission', 'published'],
+            action='store', dest='status', default='assigned',
+            help='Current status of the Submission',
+        )
+
+    def handle(self, *args, **kwargs):
+        if kwargs['number']:
+            self.create_submissions(kwargs['number'], status=kwargs['status'])
+
+    def create_submissions(self, n, status='assigned'):
+        if status == 'unassigned':
+            factories.UnassignedSubmissionFactory.create_batch(n)
+        elif status == 'assigned':
+            factories.EICassignedSubmissionFactory.create_batch(n)
+        elif status == 'resubmitted':
+            factories.ResubmittedSubmissionFactory.create_batch(n)
+        elif status == 'resubmission':
+            factories.ResubmissionFactory.create_batch(n)
+        elif status == 'published':
+            factories.PublishedSubmissionFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Submissions.'.format(n=n)))
diff --git a/submissions/migrations/0010_auto_20180314_1607.py b/submissions/migrations/0010_auto_20180314_1607.py
new file mode 100644
index 0000000000000000000000000000000000000000..7bbd633195f6d1b3e4dfb44a9552f18d9586275f
--- /dev/null
+++ b/submissions/migrations/0010_auto_20180314_1607.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-14 15:07
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('submissions', '0009_auto_20180220_2120'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='submission',
+            name='submission_type',
+            field=models.CharField(choices=[('Letter', 'Letter (broad-interest breakthrough results)'), ('Article', 'Article (in-depth reports on specialized research)'), ('Review', 'Review (candid snapshot of current research in a given area)')], default='', max_length=10),
+            preserve_default=False,
+        ),
+    ]
diff --git a/submissions/models.py b/submissions/models.py
index f9cdcfd6f731c28c9ef43b839e2b76e04d9e12ba..95c4c2df27f360282c3f8cc176d3a4ab1268ed64 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -66,8 +66,7 @@ class Submission(models.Model):
                                      related_name='pool')
     subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS,
                                     verbose_name='Primary subject area', default='Phys:QP')
-    submission_type = models.CharField(max_length=10, choices=SUBMISSION_TYPE,
-                                       blank=True, null=True, default=None)
+    submission_type = models.CharField(max_length=10, choices=SUBMISSION_TYPE)
     submitted_by = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
                                      related_name='submitted_submissions')
     voting_fellows = models.ManyToManyField('colleges.Fellowship', blank=True,
diff --git a/submissions/signals.py b/submissions/signals.py
index be833cb833d351eab88e1d0d384e638ef4992762..9bb75251f1999445672a0cbfcaff2a5a5242c16b 100644
--- a/submissions/signals.py
+++ b/submissions/signals.py
@@ -85,3 +85,17 @@ def notify_invitation_overdue(sender, instance, created, **kwargs):
                         verb=(' would like to remind you that your Refereeing Task is overdue, '
                               'please submit your Report'),
                         target=instance.submission, type=NOTIFICATION_REFEREE_OVERDUE)
+
+
+def notify_manuscript_accepted(sender, instance, created, **kwargs):
+    """
+    Notify authors about their manuscript decision.
+
+    instance --- Submission
+    """
+    college = Group.objects.get(name='Editorial College')
+    authors = User.objects.filter(contributor__submissions=instance)
+    for user in authors:
+        notify.send(sender=sender, recipient=user, actor=college,
+                    verb=' has accepted your manuscript for publication.',
+                    target=instance)
diff --git a/submissions/templates/partials/submissions/submission_status.html b/submissions/templates/partials/submissions/submission_status.html
index 82438028e92314b6c417d505f29cc6b90da17efc..fb727c41e9c6865bfc23117c7ff1a69bf9b30559 100644
--- a/submissions/templates/partials/submissions/submission_status.html
+++ b/submissions/templates/partials/submissions/submission_status.html
@@ -2,6 +2,12 @@
 <div class="d-inline">
     <span class="label label-secondary">{{submission.get_status_display}}</span>
     {% if submission.publication and submission.publication.is_published %}
-        as <a href="{{submission.publication.get_absolute_url}}">{{submission.publication.in_issue.in_volume.in_journal.abbreviation_citation}} <strong>{{submission.publication.in_issue.in_volume.number}}</strong>, {{submission.publication.get_paper_nr}} ({{submission.publication.publication_date|date:'Y'}})</a>
+        as <a href="{{submission.publication.get_absolute_url}}">
+            {% if submission.publication.in_issue %}
+                {{submission.publication.in_issue.in_volume.in_journal.abbreviation_citation}} <strong>{{submission.publication.in_issue.in_volume.number}}</strong>, {{submission.publication.get_paper_nr}}
+            {% else %}
+                {{submission.publication.in_journal.abbreviation_citation}}, {{submission.publication.paper_nr}}
+            {% endif %}
+            ({{submission.publication.publication_date|date:'Y'}})</a>
     {% endif %}
 </div>
diff --git a/submissions/templates/submissions/report_form.html b/submissions/templates/submissions/report_form.html
index 5c6c813aefffc2db5a0df7c883b9dfd8b90beb7b..bb7a3639093272957da2538408c4245801735b3f 100644
--- a/submissions/templates/submissions/report_form.html
+++ b/submissions/templates/submissions/report_form.html
@@ -10,56 +10,27 @@
 {% block pagetitle %}: submit report{% endblock pagetitle %}
 
 {% block content %}
-
-<script>
-  $(document).ready(function(){
-
-    var strengths_input = $("#id_strengths");
-    function set_strengths(value) {
-      $("#preview-strengths").text(value)
-    }
-    set_strengths(strengths_input.val())
-    strengths_input.keyup(function(){
-      var new_text = $(this).val()
-      set_strengths(new_text)
-      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
-    })
-
-    var weaknesses_input = $("#id_weaknesses");
-    function set_weaknesses(value) {
-      $("#preview-weaknesses").text(value)
-    }
-    set_weaknesses(weaknesses_input.val())
-    weaknesses_input.keyup(function(){
-      var new_text = $(this).val()
-      set_weaknesses(new_text)
-      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
-    })
-
-    var report_input = $("#id_report");
-    function set_report(value) {
-      $("#preview-report").text(value)
-    }
-    set_report(report_input.val())
-    report_input.keyup(function(){
-      var new_text = $(this).val()
-      set_report(new_text)
-      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
-    })
-
-    var requested_changes_input = $("#id_requested_changes");
-    function set_requested_changes(value) {
-      $("#preview-requested_changes").text(value)
-    }
-    set_requested_changes(requested_changes_input.val())
-    requested_changes_input.keyup(function(){
-      var new_text = $(this).val()
-      set_requested_changes(new_text)
-      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
-    })
-
-  });
-</script>
+    <script>
+        $(function(){
+            function set_preview(el) {
+                $('#preview-' + $(el).attr('id')).text($(el).val())
+            }
+            $('#id_weaknesses, #id_strengths, #id_report, #id_requested_changes').on('keyup', function(){
+                set_preview(this)
+                MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
+            })
+
+            $('input[name$="anonymous"]').on('change', function() {
+                $('.anonymous-alert').show()
+                .children('h3').hide()
+                if ($(this).prop('checked')) {
+                    $('.anonymous-yes').show();
+                } else {
+                    $('.anonymous-no').show();
+                }
+            }).trigger('change');
+        });
+    </script>
 
 
 {% if user.is_authenticated %}
@@ -76,7 +47,7 @@
         </div>
     </div>
 
-    <hr>
+    <hr class="divider">
     <div class="row">
         <div class="col-12">
             <div class="card card-grey">
@@ -97,9 +68,14 @@
                     </div>
                 </div>
             {% endif %}
+            <br>
             <form action="{% url 'submissions:submit_report' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}" method="post">
                 {% csrf_token %}
                 {{ form|bootstrap:'3,9' }}
+                <div class="anonymous-alert" style="display: none;">
+                    <h3 class="anonymous-yes">Your Report will remain anonymous.</h3>
+                    <h3 class="anonymous-no">Your Report will be signed. Thank you very much!</h3>
+                </div>
                 <p>Any fields with an asterisk (*) are required.</p>
                 <input class="btn btn-primary" type="submit" name="save_submit" value="Submit your report"/>
                 <input class="btn btn-secondary ml-2" type="submit" name="save_draft" value="Save your report as draft"/>
@@ -118,20 +94,20 @@
 
     <hr>
     <div class="row">
-        <div class="col-12">
+        <div class="col-12 report-preview">
             <h3>Preview of your report (text areas only):</h3>
 
             <h4>Strengths:</h4>
-            <p class="p-2" id="preview-strengths"></p>
+            <p class="latex-preview" id="preview-id_strengths"></p>
 
             <h4>Weaknesses:</h4>
-            <p class="p-2" id="preview-weaknesses"></p>
+            <p class="latex-preview" id="preview-id_weaknesses"></p>
 
             <h4>Report:</h4>
-            <p class="p-2" id="preview-report"></p>
+            <p class="latex-preview" id="preview-id_report"></p>
 
             <h4>Requested changes:</h4>
-            <p class="p-2" id="preview-requested_changes"></p>
+            <p class="latex-preview" id="preview-id_requested_changes"></p>
         </div>
     </div>
 
diff --git a/submissions/utils.py b/submissions/utils.py
index 0610215599b89987167ca1540b4a2c06b0db2849..dcf46c20424e7e761a3cbbe88d7a8a9d7f5b680f 100644
--- a/submissions/utils.py
+++ b/submissions/utils.py
@@ -118,9 +118,6 @@ class BaseSubmissionCycle:
         Reset the reporting deadline according to current datetime and default cycle length.
         New reporting deadline may be explicitly given as datetime instance.
         """
-        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
-            raise CycleUpdateDeadlineError('Submission has invalid status: %s'
-                                           % self.submission.status)
         delta_d = period or self.default_days
         deadline = timezone.now() + datetime.timedelta(days=delta_d)
         self.submission.reporting_deadline = deadline
diff --git a/submissions/views.py b/submissions/views.py
index 3df758bca45b6ff60eeed2fce5ed7112228d29ea..da7983022760b0242c0cfd6655d18ab2ed22e821 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -34,6 +34,7 @@ from .forms import SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSe
                    EICRecommendationForm, ReportForm, VetReportForm, VotingEligibilityForm,\
                    SubmissionCycleChoiceForm, ReportPDFForm, SubmissionReportsForm,\
                    iThenticateReportForm, SubmissionPoolFilterForm
+from .signals import notify_manuscript_accepted
 from .utils import SubmissionUtils
 
 from colleges.permissions import fellowship_required, fellowship_or_admin_required
@@ -1573,6 +1574,7 @@ def fix_College_decision(request, rec_id):
         # Add SubmissionEvent for authors
         # Do not write a new event for minor/major modification: already done at moment of
         # creation.
+        notify_manuscript_accepted(request.user, submission, False)
         submission.add_event_for_author('An Editorial Recommendation has been formulated: %s.'
                                         % recommendation.get_recommendation_display())
     elif recommendation.recommendation == -3:
diff --git a/theses/factories.py b/theses/factories.py
index c5461a1a6160fe74397f854ed00309966ed6c06c..91e4e2edce654395ed1be66847ba6ad83459f3c3 100644
--- a/theses/factories.py
+++ b/theses/factories.py
@@ -1,7 +1,5 @@
 import factory
 
-from django.utils import timezone
-
 from common.helpers.factories import FormFactory
 from journals.constants import SCIPOST_JOURNALS_DOMAINS
 from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS
@@ -11,32 +9,45 @@ from .models import ThesisLink
 from .forms import VetThesisLinkForm
 from .constants import THESIS_TYPES
 
-from faker import Faker
-
-timezone.now()
-
 
-class ThesisLinkFactory(factory.django.DjangoModelFactory):
+class BaseThesisLinkFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = ThesisLink
+        abstract = True
 
     requested_by = factory.Iterator(Contributor.objects.all())
+    vetted_by = factory.Iterator(Contributor.objects.all())
+    vetted = True
+
     type = factory.Iterator(THESIS_TYPES, getter=lambda c: c[0])
     domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
     discipline = factory.Iterator(SCIPOST_DISCIPLINES, getter=lambda c: c[0])
     subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
-    title = factory.Faker('text')
+    title = factory.Faker('sentence')
     pub_link = factory.Faker('uri')
     author = factory.Faker('name')
     supervisor = factory.Faker('name')
     institution = factory.Faker('company')
-    defense_date = factory.Faker('date')
-    abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
-
-
-class VettedThesisLinkFactory(ThesisLinkFactory):
-    vetted_by = factory.Iterator(Contributor.objects.all())
-    vetted = True
+    defense_date = factory.Faker('date_this_decade')
+    abstract = factory.Faker('paragraph')
+
+    @factory.post_generation
+    def author_as_cont(self, create, extracted, **kwargs):
+        if not create:
+            # Simple build, do nothing.
+            return
+
+        if extracted:
+            # A list of groups were passed in, use them
+            for contributor in extracted:
+                self.author_as_cont.add(contributor)
+        elif factory.Faker('boolean'):
+            contributor = Contributor.objects.order_by('?').first()
+            self.author_as_cont.add(contributor)
+
+
+class ThesisLinkFactory(BaseThesisLinkFactory):
+    pass
 
 
 class VetThesisLinkFormFactory(FormFactory):
diff --git a/theses/management/__init__.py b/theses/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/theses/management/commands/__init__.py b/theses/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/theses/management/commands/create_theses.py b/theses/management/commands/create_theses.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c98134929bcf3ff3c5aecc8be334ba2df9c900e
--- /dev/null
+++ b/theses/management/commands/create_theses.py
@@ -0,0 +1,19 @@
+from django.core.management.base import BaseCommand
+
+from theses import factories
+
+
+class Command(BaseCommand):
+    help = 'Create random Thesis objects using the factories.'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'number', action='store', default=0, type=int,
+            help='Number of Theses to add')
+
+    def handle(self, *args, **kwargs):
+        self.create_theses(kwargs['number'])
+
+    def create_theses(self, n):
+        factories.ThesisLinkFactory.create_batch(n)
+        self.stdout.write(self.style.SUCCESS('Successfully created {n} Theses.'.format(n=n)))
diff --git a/theses/test_views.py b/theses/test_views.py
index 91e37fc3c6b157d72e479765f8dc7d3c5ba6344e..33d8eed56faa8bd9fa7cafd75a8ff150f0eed209 100644
--- a/theses/test_views.py
+++ b/theses/test_views.py
@@ -13,7 +13,7 @@ from comments.factories import CommentFactory
 from comments.forms import CommentForm
 from comments.models import Comment
 from .views import RequestThesisLink, VetThesisLink, thesis_detail
-from .factories import ThesisLinkFactory, VettedThesisLinkFactory, VetThesisLinkFormFactory
+from .factories import ThesisLinkFactory, ThesisLinkFactory, VetThesisLinkFormFactory
 from .models import ThesisLink
 from .forms import VetThesisLinkForm
 from common.helpers import model_form_data
@@ -163,14 +163,14 @@ class TestTheses(TestCase):
         self.target = reverse('theses:theses')
 
     def test_empty_search_query(self):
-        thesislink = VettedThesisLinkFactory()
+        thesislink = ThesisLinkFactory()
         response = self.client.get(self.target)
         search_results = response.context["object_list"]
         self.assertTrue(thesislink in search_results)
 
     def test_search_query_on_author(self):
-        thesislink = VettedThesisLinkFactory()
-        other_thesislink = VettedThesisLinkFactory()
+        thesislink = ThesisLinkFactory()
+        other_thesislink = ThesisLinkFactory()
         form_data = {'author': thesislink.author}
         response = self.client.get(self.target, form_data)
         search_results = response.context['object_list']