diff --git a/README.md b/README.md
index 68684ffab21961cff447deb30eb129a90eb61f5b..2f3301f5bcea84d447400ccf2c6dd1c94598d037 100644
--- a/README.md
+++ b/README.md
@@ -215,7 +215,7 @@ Any regular method or class based view may be used together with the builtin wys
 
 ```python
 from django.views.generic.edit import UpdateView
-from mails.mixins import MailEditorMixin
+from mails.views import MailEditorMixin
 
 class AnyUpdateView(MailEditorMixin, UpdateView):
     mail_code = '<any_valid_mail_code>'
diff --git a/funders/forms.py b/funders/forms.py
index b7db43fcb0ecb60b2ed0e9e8ebb0e3afacc2b6fb..a5c7d8d29a2f363cf7f8adb125e5b9eff9ad782f 100644
--- a/funders/forms.py
+++ b/funders/forms.py
@@ -2,6 +2,7 @@ from django import forms
 
 from .models import Funder, Grant
 
+from scipost.forms import HttpRefererFormMixin
 from scipost.models import Contributor
 
 
@@ -12,20 +13,20 @@ class FunderRegistrySearchForm(forms.Form):
 class FunderForm(forms.ModelForm):
     class Meta:
         model = Funder
-        fields = ['name', 'acronym', 'identifier',]
+        fields = ['name', 'acronym', 'identifier']
 
 
 class FunderSelectForm(forms.Form):
     funder = forms.ModelChoiceField(queryset=Funder.objects.all())
 
 
-class GrantForm(forms.ModelForm):
+class GrantForm(HttpRefererFormMixin, forms.ModelForm):
     class Meta:
         model = Grant
         fields = ['funder', 'number', 'recipient_name', 'recipient', 'further_details']
 
     def __init__(self, *args, **kwargs):
-        super(GrantForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['recipient'] = forms.ModelChoiceField(
             queryset=Contributor.objects.all().order_by('user__last_name'),
             required=False)
diff --git a/funders/templates/funders/grant_form.html b/funders/templates/funders/grant_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..65f59883b8a8e2577796626395becace74bba7b2
--- /dev/null
+++ b/funders/templates/funders/grant_form.html
@@ -0,0 +1,25 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: Create new Grant{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-12">
+        <h1 class="highlight">Create new Grant</h1>
+  </div>
+</div>
+
+<div class="row">
+    <div class="col-12">
+        <form method="post">
+          {% csrf_token %}
+          {{ form|bootstrap }}
+          <input class="btn btn-primary" type="submit" value="Submit">
+        </form>
+	</div>
+</div>
+
+{% endblock content %}
diff --git a/funders/urls.py b/funders/urls.py
index 6766a095b6aead907943bc6f501317e76d1ce5ff..c885eeeecfdb062270f1273d507e0607b24d000e 100644
--- a/funders/urls.py
+++ b/funders/urls.py
@@ -9,5 +9,5 @@ urlpatterns = [
     url(r'^add$', views.add_funder, name='add_funder'),
     url(r'^(?P<funder_id>[0-9]+)/$', views.funder_publications,
         name='funder_publications'),
-    url(r'^grants/add$', views.add_grant, name='add_grant'),
+    url(r'^grants/add$', views.CreateGrantView.as_view(), name='add_grant'),
 ]
diff --git a/funders/views.py b/funders/views.py
index b11b7a15baf0991b61bb864575f8d9e4a34a510f..c909254843a9b6fc1b5a06ee8d0874b73c6b1015 100644
--- a/funders/views.py
+++ b/funders/views.py
@@ -3,19 +3,24 @@ import json
 
 from django.contrib import messages
 from django.contrib.auth.decorators import permission_required
-from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse, reverse_lazy
+from django.db import transaction
+from django.utils.decorators import method_decorator
+from django.views.generic.edit import CreateView
 from django.shortcuts import get_object_or_404, render, redirect
 
 from .models import Funder, Grant
 from .forms import FunderRegistrySearchForm, FunderForm, GrantForm
 
+from scipost.mixins import PermissionsMixin
+
 
 @permission_required('scipost.can_view_all_funding_info', raise_exception=True)
 def funders(request):
     funders = Funder.objects.all()
     form = FunderRegistrySearchForm()
     grants = Grant.objects.all()
-    grant_form = GrantForm()
+    grant_form = GrantForm(request=request)
     context = {'form': form, 'funders': funders,
                'grants': grants, 'grant_form': grant_form}
     return render(request, 'funders/funders.html', context)
@@ -53,7 +58,6 @@ def add_funder(request):
     return redirect(reverse('funders:funders'))
 
 
-# @permission_required('scipost.can_view_all_funding_info', raise_exception=True)
 def funder_publications(request, funder_id):
     """
     See details of specific Funder (publicly accessible).
@@ -63,13 +67,24 @@ def funder_publications(request, funder_id):
     return render(request, 'funders/funder_details.html', context)
 
 
-@permission_required('scipost.can_view_all_funding_info', raise_exception=True)
-def add_grant(request):
-    grant_form = GrantForm(request.POST or None)
-    if grant_form.is_valid():
-        grant = grant_form.save()
-        messages.success(request, ('<h3>Grant %s successfully added</h3>') %
-                         str(grant))
-    elif grant_form.has_changed():
-        messages.warning(request, 'The form was invalidly filled (grant already exists?).')
-    return redirect(reverse('funders:funders'))
+class HttpRefererMixin:
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['request'] = self.request
+        return kwargs
+
+    def form_valid(self, form):
+        if form.cleaned_data.get('http_referer'):
+            self.success_url = form.cleaned_data['http_referer']
+        return super().form_valid(form)
+
+
+@method_decorator(transaction.atomic, name='dispatch')
+class CreateGrantView(PermissionsMixin, HttpRefererMixin, CreateView):
+    """
+    Create a Grant in a separate window which may also be used by Production Supervisors.
+    """
+    permission_required = 'scipost.can_create_grants'
+    model = Grant
+    form_class = GrantForm
+    success_url = reverse_lazy('funders:funders')
diff --git a/invitations/mixins.py b/invitations/mixins.py
index bf6629acd43fdf91ca9bb2d7d7e1fb4f6d59e9e4..fbb722b95b33dde713ecd2c21197b3ee5d095e54 100644
--- a/invitations/mixins.py
+++ b/invitations/mixins.py
@@ -1,6 +1,5 @@
 from django.db import transaction
 from django.contrib import messages
-from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
 
 from .constants import INVITATION_EDITORIAL_FELLOW
 from .models import RegistrationInvitation
@@ -16,10 +15,6 @@ class RequestArgumentMixin:
         return kwargs
 
 
-class PermissionsMixin(LoginRequiredMixin, PermissionRequiredMixin):
-    pass
-
-
 class BaseFormViewMixin:
     send_mail = None
 
diff --git a/invitations/views.py b/invitations/views.py
index 6967619d1a03a73963b67a2916df0dbffb0bf685..fdad1c7896970f468b2236f0e3eddc0ef941cb44 100644
--- a/invitations/views.py
+++ b/invitations/views.py
@@ -10,12 +10,12 @@ from .forms import RegistrationInvitationForm, RegistrationInvitationReminderFor
     RegistrationInvitationMarkForm, RegistrationInvitationMapToContributorForm,\
     CitationNotificationForm, SuggestionSearchForm, RegistrationInvitationFilterForm,\
     CitationNotificationProcessForm, RegistrationInvitationAddCitationForm
-from .mixins import RequestArgumentMixin, PermissionsMixin, SaveAndSendFormMixin, SendMailFormMixin
+from .mixins import RequestArgumentMixin, SaveAndSendFormMixin, SendMailFormMixin
 from .models import RegistrationInvitation, CitationNotification
 
 from scipost.models import Contributor
-from scipost.mixins import PaginationMixin
-from mails.mixins import MailEditorMixin
+from scipost.mixins import PaginationMixin, PermissionsMixin
+from mails.views import MailEditorMixin
 
 
 class RegistrationInvitationsView(PaginationMixin, PermissionsMixin, ListView):
diff --git a/journals/admin.py b/journals/admin.py
index 8fccd05cca1475fe757dfacc81748b78ab7fa9f2..955fef5eddbb325a49a02e51f3796c06b4c4d0f8 100644
--- a/journals/admin.py
+++ b/journals/admin.py
@@ -64,7 +64,7 @@ class AuthorsInline(admin.TabularInline):
 
 class PublicationAdmin(admin.ModelAdmin):
     search_fields = ['title', 'author_list']
-    list_display = ['title', 'author_list', 'in_issue', 'doi_string', 'publication_date']
+    list_display = ['title', 'author_list', 'in_issue', 'doi_string', 'publication_date', 'status']
     date_hierarchy = 'publication_date'
     list_filter = ['in_issue']
     inlines = [AuthorsInline, ReferenceInline]
diff --git a/journals/constants.py b/journals/constants.py
index da5c678eae99d18964113ca71b204208c397769e..8bbd0497025de767f66a792dcc78ddcf0baeea26 100644
--- a/journals/constants.py
+++ b/journals/constants.py
@@ -57,6 +57,13 @@ ISSUE_STATUSES = (
     (STATUS_PUBLISHED, 'Published'),
 )
 
+PUBLICATION_PREPUBLISHED, PUBLICATION_PUBLISHED = ('prepub', 'pub')
+PUBLICATION_STATUSES = (
+    (STATUS_DRAFT, 'Draft'),
+    (PUBLICATION_PREPUBLISHED, 'Pre-published'),
+    (PUBLICATION_PUBLISHED, 'Published'),
+)
+
 CCBY4 = 'CC BY 4.0'
 CCBYSA4 = 'CC BY-SA 4.0'
 CCBYNC4 = 'CC BY-NC 4.0'
diff --git a/journals/factories.py b/journals/factories.py
index 802dc70c73bd9ebd6ebe630e85adfb757de6a093..e6b9ef92328909e0a8826e93e4cd34113c8a0989 100644
--- a/journals/factories.py
+++ b/journals/factories.py
@@ -80,7 +80,7 @@ class PublicationFactory(factory.django.DjangoModelFactory):
 
     @factory.post_generation
     def doi(self, create, extracted, **kwargs):
-        paper_nr = self.in_issue.publication_set.count()
+        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')
 
diff --git a/journals/forms.py b/journals/forms.py
index 55d0cb2bcdf4c8435ba6be7605d2a634442845ce..5bbddf4ab3502e1fb62c95322d7b5d3027a4476e 100644
--- a/journals/forms.py
+++ b/journals/forms.py
@@ -1,73 +1,82 @@
+import hashlib
+import os
+import random
 import re
+import string
 
 from datetime import datetime
 
 from django import forms
+from django.conf import settings
 from django.forms import BaseModelFormSet, modelformset_factory
+from django.template import loader
 from django.utils import timezone
 
-from .models import Issue, Publication, Reference, UnregisteredAuthor
+from .constants import STATUS_DRAFT, PUBLICATION_PREPUBLISHED, PUBLICATION_PUBLISHED
+from .exceptions import PaperNumberingError
+from .models import Issue, Publication, Reference, UnregisteredAuthor, PublicationAuthorsTable
+from .utils import JournalUtils
 
+
+from funders.models import Grant, Funder
+from mails.utils import DirectMailUtil
+from production.constants import PROOFS_PUBLISHED
+from production.models import ProductionEvent
+from production.signals import notify_stream_status_change
+from scipost.forms import RequestFormMixin
 from scipost.services import DOICaller
 from submissions.models import Submission
 
 
-class InitiatePublicationForm(forms.Form):
-    accepted_submission = forms.ModelChoiceField(queryset=Submission.objects.accepted())
-    to_be_issued_in = forms.ModelChoiceField(
-        queryset=Issue.objects.filter(until_date__gte=timezone.now()))
-
-    def __init__(self, *args, **kwargs):
-        super(InitiatePublicationForm, self).__init__(*args, **kwargs)
-
-
-class ValidatePublicationForm(forms.ModelForm):
-    class Meta:
-        model = Publication
-        exclude = ['authors_claims', 'authors_false_claims',
-                   'metadata', 'metadata_xml', 'authors_registered',
-                   'authors_unregistered', 'latest_activity']
-
-
 class UnregisteredAuthorForm(forms.ModelForm):
     class Meta:
         model = UnregisteredAuthor
         fields = ('first_name', 'last_name')
 
 
-class CitationListBibitemsForm(forms.Form):
+class CitationListBibitemsForm(forms.ModelForm):
     latex_bibitems = forms.CharField(widget=forms.Textarea())
 
+    class Meta:
+        model = Publication
+        fields = ()
+
     def __init__(self, *args, **kwargs):
-        super(CitationListBibitemsForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['latex_bibitems'].widget.attrs.update(
-            {'rows': 30, 'cols': 50, 'placeholder': 'Paste the .tex bibitems here'})
+            {'placeholder': 'Paste the .tex bibitems here'})
 
     def extract_dois(self):
         entries_list = self.cleaned_data['latex_bibitems']
         entries_list = re.sub(r'(?m)^\%.*\n?', '', entries_list)
         entries_list = entries_list.split('\doi{')
         dois = []
-        nentries = 1
+        n_entry = 1
         for entry in entries_list[1:]:  # drop first bit before first \doi{
             dois.append(
-                {'key': 'ref' + str(nentries),
+                {'key': 'ref' + str(n_entry),
                  'doi': entry.partition('}')[0], }
             )
-            nentries += 1
+            n_entry += 1
         return dois
 
+    def save(self, *args, **kwargs):
+        self.instance.metadata['citation_list'] = self.extract_dois()
+        return super().save(*args, **kwargs)
+
 
 class FundingInfoForm(forms.ModelForm):
     funding_statement = forms.CharField(widget=forms.Textarea({
-        'rows': 10,
-        'placeholder': 'Paste the funding info statement here'
-    }))
+        'placeholder': 'Paste the funding info statement here'}))
 
     class Meta:
         model = Publication
         fields = ()
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['funding_statement'].initial = self.instance.metadata.get('funding_statement')
+
     def save(self, *args, **kwargs):
         self.instance.metadata['funding_statement'] = self.cleaned_data['funding_statement']
         return super().save(*args, **kwargs)
@@ -79,8 +88,40 @@ class CreateMetadataXMLForm(forms.ModelForm):
         fields = ['metadata_xml']
 
     def __init__(self, *args, **kwargs):
+        kwargs['initial'] = {
+            'metadata_xml': self.new_xml(kwargs.get('instance'))
+        }
         super().__init__(*args, **kwargs)
-        self.fields['metadata_xml'].widget.attrs.update({'rows': 50})
+
+    def save(self, *args, **kwargs):
+        self.instance.latest_metadata_update = timezone.now()
+        return super().save(*args, **kwargs)
+
+    def new_xml(self, publication):
+        """
+        Create new XML structure, return as a string
+        """
+        # Create a doi_batch_id
+        salt = ""
+        for i in range(5):
+            salt = salt + random.choice(string.ascii_letters)
+        salt = salt.encode('utf8')
+        idsalt = publication.title[:10]
+        idsalt = idsalt.encode('utf8')
+        doi_batch_id = hashlib.sha1(salt+idsalt).hexdigest()
+
+        funders = (Funder.objects.filter(grant__in=publication.grants.all())
+                   | publication.funders_generic.all()).distinct()
+
+        # Render from template
+        template = loader.get_template('xml/publication_crossref.html')
+        context = {
+            'publication': publication,
+            'doi_batch_id': doi_batch_id,
+            'deposit_email': settings.CROSSREF_DEPOSIT_EMAIL,
+            'funders': funders,
+        }
+        return template.render(context)
 
 
 class CreateMetadataDOAJForm(forms.ModelForm):
@@ -234,3 +275,260 @@ class ReferenceForm(forms.ModelForm):
 
 ReferenceFormSet = modelformset_factory(Reference, formset=BaseReferenceFormSet,
                                         form=ReferenceForm, can_delete=True)
+
+
+class DraftPublicationForm(forms.ModelForm):
+    """
+    This Form is used by the Production Supervisors to create a new Publication object
+    and prefill all data. It is only able to create a `draft` version of a Publication object.
+    """
+    class Meta:
+        model = Publication
+        fields = [
+            'doi_label',
+            'pdf_file',
+            'in_issue',
+            'paper_nr',
+            'title',
+            'author_list',
+            'abstract',
+            'discipline',
+            'domain',
+            'subject_area',
+            'secondary_areas',
+            'cc_license',
+            'BiBTeX_entry',
+            'submission_date',
+            'acceptance_date',
+            'publication_date']
+
+    def __init__(self, data=None, arxiv_identifier_w_vn_nr=None, issue_id=None, *args, **kwargs):
+        # Use separate instance to be able to prefill the form without any existing Publication
+        self.submission = None
+        self.issue = None
+        if arxiv_identifier_w_vn_nr:
+            try:
+                self.submission = Submission.objects.accepted().get(
+                    arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr)
+            except Submission.DoesNotExist:
+                self.submission = None
+        if issue_id:
+            try:
+                self.issue = self.get_possible_issues().get(id=issue_id)
+            except Issue.DoesNotExist:
+                self.issue = None
+
+        super().__init__(data, *args, **kwargs)
+        if kwargs.get('instance') or self.issue:
+            # When updating: fix in_issue, because many fields are directly related to the issue.
+            del self.fields['in_issue']
+            self.prefill_fields()
+        else:
+            self.fields['in_issue'].queryset = self.get_possible_issues()
+            self.delete_secondary_fields()
+
+    def get_possible_issues(self):
+        return Issue.objects.filter(until_date__gte=timezone.now())
+
+    def delete_secondary_fields(self):
+        """
+        Delete fields from the self.fields dictionary. Later on, this submitted sparse form can
+        be used to prefill these secondary fields.
+        """
+        del self.fields['doi_label']
+        del self.fields['pdf_file']
+        del self.fields['paper_nr']
+        del self.fields['title']
+        del self.fields['author_list']
+        del self.fields['abstract']
+        del self.fields['discipline']
+        del self.fields['domain']
+        del self.fields['subject_area']
+        del self.fields['secondary_areas']
+        del self.fields['cc_license']
+        del self.fields['BiBTeX_entry']
+        del self.fields['submission_date']
+        del self.fields['acceptance_date']
+        del self.fields['publication_date']
+
+    def save(self, *args, **kwargs):
+        """
+        Save the Publication object always as a draft and prefill the Publication with
+        related Submission data only when appending the Publication.
+        """
+        do_prefill = False
+        if not self.instance.id:
+            do_prefill = True
+            if self.submission:
+                self.instance.accepted_submission = self.submission
+            self.instance.in_issue = self.issue
+        self.instance = super().save(*args, **kwargs)
+        if do_prefill:
+            self.first_time_fill()
+        return self.instance
+
+    def first_time_fill(self):
+        """
+        Take over fields from related Submission object. This can only be done after
+        the Publication object has been added to the database due to m2m relations.
+        """
+        self.instance.status = STATUS_DRAFT
+
+        if self.submission:
+            # Copy all existing author and non-author relations to Publication
+            for submission_author in self.submission.authors.all():
+                PublicationAuthorsTable.objects.create(
+                    publication=self.instance, contributor=submission_author)
+            self.instance.authors_claims.add(*self.submission.authors_claims.all())
+            self.instance.authors_false_claims.add(*self.submission.authors_false_claims.all())
+
+        # Add Institutions to the publication related to the current authors
+        for author in self.instance.authors_registered.all():
+            for current_affiliation in author.affiliations.active():
+                self.instance.institutions.add(current_affiliation.institution)
+
+    def prefill_fields(self):
+        if self.submission:
+            self.fields['title'].initial = self.submission.title
+            self.fields['author_list'].initial = self.submission.author_list
+            self.fields['abstract'].initial = self.submission.abstract
+            self.fields['discipline'].initial = self.submission.discipline
+            self.fields['domain'].initial = self.submission.domain
+            self.fields['subject_area'].initial = self.submission.subject_area
+            self.fields['secondary_areas'].initial = self.submission.secondary_areas
+            self.fields['submission_date'].initial = self.submission.submission_date
+            self.fields['acceptance_date'].initial = self.submission.acceptance_date
+
+        # Fill data that may be derived from the issue data
+        issue = self.instance.in_issue if hasattr(self.instance, 'in_issue') else self.issue
+        if issue:
+            # Determine next available paper number:
+            paper_nr = Publication.objects.filter(in_issue__in_volume=issue.in_volume).count()
+            paper_nr += paper_nr
+            if paper_nr > 999:
+                raise PaperNumberingError(paper_nr)
+            self.fields['paper_nr'].initial = str(paper_nr)
+            doi_label = '{journal}.{vol}.{issue}.{paper}'.format(
+                journal=issue.in_volume.in_journal.name,
+                vol=issue.in_volume.number,
+                issue=issue.number,
+                paper=str(paper_nr).rjust(3, '0'))
+            self.fields['doi_label'].initial = doi_label
+
+            doi_string = '10.21468/{doi}'.format(doi=doi_label)
+            bibtex_entry = (
+                '@Article{%s,\n'
+                '\ttitle={{%s},\n'
+                '\tauthor={%s},\n'
+                '\tjournal={%s},\n'
+                '\tvolume={%i},\n'
+                '\tissue={%i},\n'
+                '\tpages={%i},\n'
+                '\tyear={%s},\n'
+                '\tpublisher={SciPost},\n'
+                '\tdoi={%s},\n'
+                '\turl={https://scipost.org/%s},\n'
+                '}'
+            ) % (
+                doi_string,
+                self.submission.title,
+                self.submission.author_list.replace(',', ' and'),
+                issue.in_volume.in_journal.get_abbreviation_citation(),
+                issue.in_volume.number,
+                issue.number,
+                paper_nr,
+                issue.until_date.strftime('%Y'),
+                doi_string,
+                doi_string)
+            self.fields['BiBTeX_entry'].initial = bibtex_entry
+            if not self.instance.BiBTeX_entry:
+                self.instance.BiBTeX_entry = bibtex_entry
+
+
+class DraftPublicationApprovalForm(forms.ModelForm):
+    class Meta:
+        model = Publication
+        fields = ()
+
+    def save(self, commit=True):
+        self.instance.status = PUBLICATION_PREPUBLISHED
+        if commit:
+            self.instance.save()
+            mail_sender = DirectMailUtil(mail_code='publication_ready', instance=self.instance)
+            mail_sender.send()
+        return self.instance
+
+
+class PublicationGrantsForm(forms.ModelForm):
+    grant = forms.ModelChoiceField(queryset=Grant.objects.none())
+
+    class Meta:
+        model = Publication
+        fields = []
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['grant'].queryset = Grant.objects.exclude(
+            id__in=self.instance.grants.values_list('id', flat=True))
+
+    def save(self, commit=True):
+        if commit:
+            self.instance.grants.add(self.cleaned_data['grant'])
+        return self.instance
+
+
+class PublicationPublishForm(RequestFormMixin, forms.ModelForm):
+    class Meta:
+        model = Publication
+        fields = []
+
+    def move_pdf(self):
+        # Move file to final location
+        initial_path = self.instance.pdf_file.path
+        new_dir = (settings.MEDIA_ROOT + self.instance.in_issue.path + '/'
+                   + self.instance.get_paper_nr())
+        new_path = new_dir + '/' + self.instance.doi_label.replace('.', '_') + '.pdf'
+        os.makedirs(new_dir)
+        os.rename(initial_path, new_path)
+        self.instance.pdf_file.name = new_path
+        self.instance.status = PUBLICATION_PUBLISHED
+        self.instance.save()
+
+    def update_submission(self):
+        # Mark the submission as having been published:
+        submission = self.instance.accepted_submission
+        submission.published_as = self.instance
+        submission.status = 'published'
+        submission.save()
+
+        # Add SubmissionEvents
+        submission.add_general_event(
+            'The Submission has been published as %s.' % self.instance.doi_label)
+
+    def update_stream(self):
+        # Update ProductionStream
+        submission = self.instance.accepted_submission
+        if hasattr(submission, 'production_stream'):
+            stream = submission.production_stream
+            stream.status = PROOFS_PUBLISHED
+            stream.save()
+            if self.request.user.production_user:
+                prodevent = ProductionEvent(
+                    stream=stream,
+                    event='status',
+                    comments=' published the manuscript.',
+                    noted_by=self.request.user.production_user
+                )
+                prodevent.save()
+            notify_stream_status_change(self.request.user, stream, False)
+
+    def save(self, commit=True):
+        if commit:
+            self.move_pdf()
+            self.update_submission()
+            self.update_stream()
+
+            # Email authors
+            JournalUtils.load({'publication': self.instance})
+            JournalUtils.send_authors_paper_published_email()
+        return self.instance
diff --git a/journals/managers.py b/journals/managers.py
index 0b058c1b3a78d47baca5c366cf9d94c8a71e60a3..9d9bb31a1e25049162b5c0737079c214ec400723 100644
--- a/journals/managers.py
+++ b/journals/managers.py
@@ -2,7 +2,7 @@ from django.db import models
 from django.http import Http404
 from django.utils import timezone
 
-from .constants import STATUS_PUBLISHED, STATUS_DRAFT
+from .constants import STATUS_PUBLISHED, STATUS_DRAFT, PUBLICATION_PUBLISHED
 
 
 class JournalManager(models.Manager):
@@ -42,12 +42,18 @@ class IssueManager(models.Manager):
 class PublicationQuerySet(models.QuerySet):
     def get_published(self, *args, **kwargs):
         try:
-            return self.published(*args, **kwargs)[0]
+            return self.published().filter(*args, **kwargs)[0]
         except IndexError:
             raise Http404
 
     def published(self, **kwargs):
-        return self.filter(in_issue__status=STATUS_PUBLISHED, **kwargs)
+        return self.filter(status=PUBLICATION_PUBLISHED, in_issue__status=STATUS_PUBLISHED)
+
+    def unpublished(self):
+        return self.exclude(status=PUBLICATION_PUBLISHED)
 
     def in_draft(self, **kwargs):
         return self.filter(in_issue__status=STATUS_DRAFT, **kwargs)
+
+    def drafts(self):
+        return self.filter(status=STATUS_DRAFT)
diff --git a/journals/migrations/0014_publication_status.py b/journals/migrations/0014_publication_status.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a10cb32d3d71e98d10230537c9ec76a05e038a6
--- /dev/null
+++ b/journals/migrations/0014_publication_status.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-02 13:03
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('journals', '0013_auto_20180216_0850'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='publication',
+            name='status',
+            field=models.CharField(choices=[('draft', 'Draft'), ('prepub', 'Pre-published'), ('pub', 'Published')], default='pub', max_length=8),
+        ),
+    ]
diff --git a/journals/migrations/0015_auto_20180302_1404.py b/journals/migrations/0015_auto_20180302_1404.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1efbff642228e4db55409f7a6a2268b56672584
--- /dev/null
+++ b/journals/migrations/0015_auto_20180302_1404.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-02 13:04
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('journals', '0014_publication_status'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='publication',
+            name='status',
+            field=models.CharField(choices=[('draft', 'Draft'), ('prepub', 'Pre-published'), ('pub', 'Published')], default='draft', max_length=8),
+        ),
+    ]
diff --git a/journals/migrations/0016_auto_20180303_0918.py b/journals/migrations/0016_auto_20180303_0918.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9448c4ab78a4ec88146dd6d6dd9d8a4cfd8fcb2
--- /dev/null
+++ b/journals/migrations/0016_auto_20180303_0918.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-03-03 08:18
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+def null_to_blank(apps, schema_editor):
+    Publication = apps.get_model('journals', 'Publication')
+    for pub in Publication.objects.all():
+        if pub.BiBTeX_entry is None:
+            pub.BiBTeX_entry = ''
+        if pub.metadata_xml is None:
+            pub.metadata_xml = ''
+        pub.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('journals', '0015_auto_20180302_1404'),
+    ]
+
+    operations = [
+        migrations.RunPython(null_to_blank),
+        migrations.AlterField(
+            model_name='publication',
+            name='BiBTeX_entry',
+            field=models.TextField(blank=True, default=''),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='publication',
+            name='metadata_xml',
+            field=models.TextField(blank=True, default=''),
+            preserve_default=False,
+        ),
+    ]
diff --git a/journals/mixins.py b/journals/mixins.py
new file mode 100644
index 0000000000000000000000000000000000000000..730d920a08ce0f4947af6da6376019dae9ec175e
--- /dev/null
+++ b/journals/mixins.py
@@ -0,0 +1,22 @@
+from .models import Publication
+
+from scipost.mixins import PermissionsMixin
+
+
+class PublicationMixin:
+    model = Publication
+    slug_field = slug_url_kwarg = 'doi_label'
+
+
+class ProdSupervisorPublicationPermissionMixin(PermissionsMixin):
+    """
+    This will give permission to Production Supervisors if Publication is in_draft.
+    If Publication is not in draft, it will only give permission to administrators.
+    """
+    permission_required = 'scipost.can_draft_publication'
+
+    def has_permission(self):
+        has_perm = super().has_permission()
+        if has_perm and self.get_object().is_draft:
+            return True
+        return self.request.user.has_perm('scipost.can_publish_accepted_submission')
diff --git a/journals/models.py b/journals/models.py
index 31c9cdc87eec453db0c84da6cf35349172b8c8ee..b0d64fbeca8a2bb2c703b5c9e669562e89505235 100644
--- a/journals/models.py
+++ b/journals/models.py
@@ -9,8 +9,8 @@ from django.urls import reverse
 from .behaviors import doi_journal_validator, doi_volume_validator,\
                        doi_issue_validator, doi_publication_validator
 from .constants import SCIPOST_JOURNALS, SCIPOST_JOURNALS_DOMAINS,\
-                       STATUS_DRAFT, STATUS_PUBLISHED, ISSUE_STATUSES,\
-                       CCBY4, CC_LICENSES, CC_LICENSES_URI
+                       STATUS_DRAFT, STATUS_PUBLISHED, ISSUE_STATUSES, PUBLICATION_PUBLISHED,\
+                       CCBY4, CC_LICENSES, CC_LICENSES_URI, PUBLICATION_STATUSES
 from .helpers import paper_nr_string, journal_name_abbrev_citation
 from .managers import IssueManager, PublicationQuerySet, JournalManager
 
@@ -264,8 +264,11 @@ class Publication(models.Model):
     # Publication data
     accepted_submission = models.OneToOneField('submissions.Submission', on_delete=models.CASCADE,
                                                related_name='publication')
-    in_issue = models.ForeignKey('journals.Issue', on_delete=models.CASCADE)
+    in_issue = models.ForeignKey('journals.Issue', on_delete=models.CASCADE,
+                                 related_name='publications')
     paper_nr = models.PositiveSmallIntegerField()
+    status = models.CharField(max_length=8,
+                              choices=PUBLICATION_STATUSES, default=STATUS_DRAFT)
 
     # Core fields
     title = models.CharField(max_length=300)
@@ -306,11 +309,11 @@ class Publication(models.Model):
 
     # Metadata
     metadata = JSONField(default={}, blank=True, null=True)
-    metadata_xml = models.TextField(blank=True, null=True)  # for Crossref deposit
+    metadata_xml = models.TextField(blank=True)  # for Crossref deposit
     metadata_DOAJ = JSONField(default={}, blank=True, null=True)
     doi_label = models.CharField(max_length=200, unique=True, db_index=True,
                                  validators=[doi_publication_validator])
-    BiBTeX_entry = models.TextField(blank=True, null=True)
+    BiBTeX_entry = models.TextField(blank=True)
     doideposit_needs_updating = models.BooleanField(default=False)
     citedby = JSONField(default={}, blank=True, null=True)
 
@@ -343,6 +346,30 @@ class Publication(models.Model):
     def doi_string(self):
         return '10.21468/' + self.doi_label
 
+    @property
+    def is_draft(self):
+        return self.status == STATUS_DRAFT
+
+    @property
+    def is_published(self):
+        return self.status == PUBLICATION_PUBLISHED and self.in_issue.status == STATUS_PUBLISHED
+
+    @property
+    def has_xml_metadata(self):
+        return self.metadata_xml != ''
+
+    @property
+    def has_bibtex_entry(self):
+        return self.BiBTeX_entry != ''
+
+    @property
+    def has_citation_list(self):
+        return 'citation_list' in self.metadata and len(self.metadata['citation_list']) > 0
+
+    @property
+    def has_funding_statement(self):
+        return 'funding_statement' in self.metadata and self.metadata['funding_statement']
+
     def get_paper_nr(self):
         return paper_nr_string(self.paper_nr)
 
diff --git a/journals/search_indexes.py b/journals/search_indexes.py
index 08c3b2898fcfc8613a8e4eb8d48a398ca914d87f..89035e4731cb1cd2644d7c25225f5ca29ec414d0 100644
--- a/journals/search_indexes.py
+++ b/journals/search_indexes.py
@@ -1,5 +1,3 @@
-# import datetime
-
 from haystack import indexes
 
 from .models import Publication
diff --git a/journals/templates/journals/create_citation_list_metadata.html b/journals/templates/journals/create_citation_list_metadata.html
index c6f5f6d5f79e130b4ed33a83ce96a1371c370480..724637a48de011831f16a8f857cd0ee4fd414e28 100644
--- a/journals/templates/journals/create_citation_list_metadata.html
+++ b/journals/templates/journals/create_citation_list_metadata.html
@@ -22,15 +22,14 @@
 <div class="row">
   <div class="col-12">
     <h1 class="highlight">Create citation list metadata page for <a href="{{ publication.get_absolute_url }}">{{ publication.doi_label }}</a></h1>
-  </div>
-</div>
-
+    <p>
+        The following field is prefilled with the current citation list of the Publication object. Once you submit, it will overwrite the current citation list, shown below.
+    </p>
+    <br>
 
-<div class="row">
-  <div class="col-12">
       <form action="{% url 'journals:create_citation_list_metadata' publication.doi_label %}" method="post">
         {% csrf_token %}
-        {{ bibitems_form|bootstrap }}
+        {{ form|bootstrap }}
         <input type="submit" class="btn btn-primary" value="Submit">
         <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}" class="ml-3 btn btn-link">Back to Admin for {{ publication.doi_label }}</a>
       </form>
@@ -38,17 +37,14 @@
       <hr class="divider">
 
       <h3>Current citation list metadata:</h3>
+      <br>
       <table class="table">
-        {% for citation in citation_list %}
+        {% for citation in publication.metadata.citation_list %}
           <tr>
             <td>{{ citation.key }}</td><td>{{ citation.doi }}</td>
           </tr>
         {% endfor %}
       </table>
-
-      <hr>
-
-      <p>Once you're happy with this metadata, you can <a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></p>
     </div>
 </div>
 
diff --git a/journals/templates/journals/create_funding_info_metadata.html b/journals/templates/journals/create_funding_info_metadata.html
index 2373370f7b3d9ca053ec85ace517e5e520b3e775..43c046b30c36fe66ce7be51270f324f28d50bd1c 100644
--- a/journals/templates/journals/create_funding_info_metadata.html
+++ b/journals/templates/journals/create_funding_info_metadata.html
@@ -22,32 +22,21 @@
 <div class="row">
     <div class="col-12">
         <h1 class="highlight">Create funding info metadata page for <a href="{{ publication.get_absolute_url }}">{{ publication.doi_label }}</a></h1>
-    </div>
-</div>
-
-
-<div class="row">
-    <div class="col-12">
-
-      {% if errormessage %}
-        <h2 class="text-danger">{{ errormessage }}</h2>
-      {% endif %}
+        <p>
+            The following field is prefilled with the current funding info of the Publication object. Once you submit, it will overwrite the current funding info, shown below.
+        </p>
+        <br>
 
       <form method="post">
         {% csrf_token %}
-        {{ funding_info_form|bootstrap }}
+        {{ form|bootstrap }}
         <input type="submit" class="btn btn-primary" value="Submit">
         <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}" class="ml-3 btn btn-link">Back to Admin for {{ publication.doi_label }}</a>
       </form>
 
      <hr class="divider">
         <h3>Current funding info metadata:</h3>
-        <p>{{ funding_statement|linebreaksbr }}</p>
-
-      <hr>
-
-      <p class="mb-0">Once you're happy with this metadata, you can <a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></p>
-
+        <p class="mb-0">{{ publication.metadata.funding_statement|linebreaksbr }}</p>
     </div>
 </div>
 
diff --git a/journals/templates/journals/create_metadata_xml.html b/journals/templates/journals/create_metadata_xml.html
index a6627c2567926544804cc917b2105849d0ee91b6..dff180d06c8f5142f944b9bd3121165b7d9f74a7 100644
--- a/journals/templates/journals/create_metadata_xml.html
+++ b/journals/templates/journals/create_metadata_xml.html
@@ -22,18 +22,13 @@
 <div class="row">
     <div class="col-12">
         <h1 class="highlight">Create metadata XML (for Crossref deposit)</h1>
-    </div>
-</div>
-
-<div class="row">
-    <div class="col-12">
-      {% if errormessage %}
-          <h2 class="text-danger">{{ errormessage }}</h2>
-      {% endif %}
-
+        <p>
+            The following field is prefilled with data from the Publication object. Once you accept them, they will overwrite the current metadata, shown below.
+        </p>
+        <br>
       <form action="{% url 'journals:create_metadata_xml' publication.doi_label %}" method="post">
         {% csrf_token %}
-        {{ create_metadata_xml_form|bootstrap }}
+        {{ form|bootstrap }}
         <input type="submit" class="btn btn-primary" value="Accept the metadata">
         <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}" class="ml-3 btn btn-link">Back to Admin for {{ publication.doi_label }}</a>
       </form>
@@ -43,9 +38,6 @@
       <h3>Current metadata xml</h3>
       <br>
       <pre><code>{{ publication.metadata_xml|linebreaksbr }}</code></pre>
-      <br>
-      <p class="mb-0">Once you're happy with this metadata, you can <a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a></p>
-
     </div>
 </div>
 
diff --git a/journals/templates/journals/grants_form.html b/journals/templates/journals/grants_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..75c4ed4f2488b3a92f707bde1c93d2c21e2ee38c
--- /dev/null
+++ b/journals/templates/journals/grants_form.html
@@ -0,0 +1,34 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Publication related grant(s){% endblock pagetitle %}
+
+{% block content %}
+
+
+<h1>Publication related grant(s)</h1>
+
+
+<h3>Add existing grant to this Publication</h3>
+<form method="post" enctype="multipart/form-data">
+    {% csrf_token %}
+    {{ form|bootstrap }}
+    <input type="submit" class="btn btn-primary" value="Add">
+    <a class="btn btn-link ml-2" href="{% url 'funders:add_grant' %}">Create new Grant</a>
+</form>
+
+<hr class="divider">
+
+
+<h3>Current grant(s)</h3>
+<ul>
+    {% for grant in publication.grants.all %}
+        <li>{{ grant }} - <a class="text-danger" href="{% url 'journals:remove_grant' form.instance.doi_label grant.id %}">Remove grant from Publication</a></li>
+    {% empty %}
+        <li><em>No grants added</em></li>
+    {% endfor %}
+</ul>
+
+
+{% endblock %}
diff --git a/journals/templates/journals/initiate_publication.html b/journals/templates/journals/initiate_publication.html
deleted file mode 100644
index c9fa7acc87fb2a42b4829a2ce0363c7bec8d1863..0000000000000000000000000000000000000000
--- a/journals/templates/journals/initiate_publication.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends 'scipost/base.html' %}
-
-{% load bootstrap %}
-
-{% block pagetitle %}: Initiate publication{% endblock pagetitle %}
-
-{% block content %}
-
-
-<h1>Initiate publication page</h1>
-
-<form action="{% url 'journals:initiate_publication' %}" method="post" enctype="multipart/form-data">
-    {% csrf_token %}
-    {{ initiate_publication_form|bootstrap }}
-    <input type="submit" class="btn btn-primary" value="Submit">
-</form>
-
-
-
-
-{% endblock %}
diff --git a/journals/templates/journals/journal_landing_page.html b/journals/templates/journals/journal_landing_page.html
index 1cd761620af9cf7acc1489e50227f2a4c8443e1c..c7c7f3fa13c4ea8156d6f11d2d1ab29a2796884e 100644
--- a/journals/templates/journals/journal_landing_page.html
+++ b/journals/templates/journals/journal_landing_page.html
@@ -20,7 +20,7 @@
         <div class="row">
             <div class="col-12">
                 <ul class="list-unstyled">
-                    {% for paper in current_issue.publication_set.all|dictsort:"paper_nr" %}
+                    {% for paper in current_issue.publications.published|dictsort:"paper_nr" %}
                         <li>
                             {% include 'partials/journals/publication_card.html' with publication=paper %}
                         </li>
@@ -43,7 +43,7 @@
         <div class="row">
             <div class="col-12">
                 <ul class="list-unstyled">
-                    {% for paper in latest_issue.publication_set.all|dictsort:"paper_nr" %}
+                    {% for paper in latest_issue.publications.published|dictsort:"paper_nr" %}
                         <li>
                             {% include 'partials/journals/publication_card.html' with publication=paper %}
                         </li>
diff --git a/journals/templates/journals/manage_metadata.html b/journals/templates/journals/manage_metadata.html
index fc3c61d69d847dd2b23a96a8daaf7722bc7514bb..d77c5d434fc2d2de50eccbccec42be9fe947d996 100644
--- a/journals/templates/journals/manage_metadata.html
+++ b/journals/templates/journals/manage_metadata.html
@@ -53,7 +53,7 @@ event: "focusin"
 
   <tbody id="accordion" role="tablist" aria-multiselectable="true">
     {% for publication in publications %}
-    <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ publication.id }}" aria-expanded="true" aria-controls="collapse{{ publication.id }}" style="cursor: pointer;">
+    <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ publication.id }}" aria-expanded="true" aria-controls="collapse{{ publication.id }}" style="cursor: pointer;"{% if not publication.is_published %} class="table-warning"{% endif %}>
       <td><a href="{{ publication.get_absolute_url }}">{{ publication.doi_label }}</a></td>
       <td>{{ publication.publication_date }}</td>
       {% if publication.latest_metadata_update %}
@@ -68,11 +68,16 @@ event: "focusin"
       <td>{{ publication|latest_successful_DOAJ_deposit }}</td>
     </tr>
     <tr id="collapse{{ publication.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ publication.id }}" style="background-color: #fff;">
-      <td colspan="6">
-
-	<h2 class="ml-3">Actions</h2>
-	<div class="row">
-          <div class="col-md-5">
+      <td colspan="6" class="py-3">
+        <div class="row">
+          {% if not publication.is_published %}
+            <div class="col-12">
+                <h3 class="text-center bg-warning text-white mb-3">Current status: <em>{{ publication.get_status_display }}</em></h3>
+            </div>
+          {% endif %}
+
+          <div class="col-md-6">
+              <h2 class="ml-3">Actions</h2>
             <ul>
               <li>Mark the first author
                 <ul class="list-unstyled pl-4">
@@ -93,10 +98,14 @@ event: "focusin"
 	      <li><a href="{% url 'journals:produce_metadata_DOAJ' doi_label=publication.doi_label %}">Produce DOAJ metadata</a></li>
 	      <li><a href="{% url 'journals:metadata_DOAJ_deposit' doi_label=publication.doi_label %}">Deposit the metadata to DOAJ</a></li>
               <li><a href="{% url 'journals:harvest_citedby_links' publication.doi_label %}">Update Crossref cited-by links</a></li>
+              {% if not publication.is_published %}
+                  <li><a href="{% url 'journals:update_publication' publication.accepted_submission.arxiv_identifier_w_vn_nr %}">Update Publication object</a></li>
+                  <li><strong><a href="{% url 'journals:publish_publication' publication.doi_label %}">Publish this Publication</a></strong></li>
+              {% endif %}
             </ul>
           </div>
 
-          <div class="col-md-5">
+          <div class="col-md-6">
 	    <h2>Funding statement for this publication:</h2>
 	    {% if publication.metadata.funding_statement %}
 	    <p>{{ publication.metadata.funding_statement }}</p>
diff --git a/journals/templates/journals/metadata_xml_deposit.html b/journals/templates/journals/metadata_xml_deposit.html
index 960e3737b67c6a3036eb52c7d1d9b177b94967a2..a95c2c9d2c8979034fa4ec9cb586a44e30e52ef3 100644
--- a/journals/templates/journals/metadata_xml_deposit.html
+++ b/journals/templates/journals/metadata_xml_deposit.html
@@ -34,8 +34,10 @@
           <h3 class="mt-3">Response text:</h3>
           <pre><code>{{ response_text|linebreaks }}</code></pre>
 
-          <br>
-          <p><a href="{{publication.get_absolute_url}}">return to the publication's page</a>, to the <a href="{% url 'journals:manage_metadata' %}">general metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></p>
+          {% if perms.scipost.can_publish_accepted_submission %}
+              <br>
+              <p><a href="{{publication.get_absolute_url}}">return to the publication's page</a>, to the <a href="{% url 'journals:manage_metadata' %}">general metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></p>
+          {% endif %}
 
 
         </div>
@@ -48,8 +50,11 @@
                 You might want to <a href="{% url 'journals:create_metadata_xml' doi_label=publication.doi_label %}">produce new metadata</a> to do a new deposit instead.
             <p>
 
-            <br>
-            <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">Back to Admin for {{ publication.doi_label }}</a>
+            {% if perms.scipost.can_publish_accepted_submission %}
+                <p>
+                    <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">Back to Admin for {{ publication.doi_label }}</a>
+                </p>
+            {% endif %}
         </div>
     </div>
 {% endif %}
diff --git a/journals/templates/journals/publication_approval_form.html b/journals/templates/journals/publication_approval_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..4daa978337c5de968add6bda355b8327ee3d4499
--- /dev/null
+++ b/journals/templates/journals/publication_approval_form.html
@@ -0,0 +1,46 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Send Publication for approval{% endblock pagetitle %}
+
+{% block content %}
+
+
+<h1 class="highlight">Send Publication for approval</h1>
+{% include 'partials/journals/publication_summary.html' with publication=form.instance %}
+
+<h3>Authors</h3>
+<ul>
+    {% for author in form.instance.authors.all %}
+        <li>{{ author }}</li>
+    {% empty %}
+        <li>No authors assigned</li>
+    {% endfor %}
+</ul>
+
+<h3>Funding statement</h3>
+<p>{{ form.instance.metadata.funding_statement|default:'<em>No funding statement found.</em>' }}</p>
+
+<h3>Grants</h3>
+<ul>
+    {% for grant in form.instance.grants.all %}
+        <li>{{ grant }}</li>
+    {% empty %}
+        <li>No grants assigned</li>
+    {% endfor %}
+</ul>
+
+{% include 'partials/journals/references.html' with publication=form.instance %}
+
+<br>
+<form method="post" enctype="multipart/form-data">
+    {% csrf_token %}
+    {{ form|bootstrap }}
+    <input type="submit" class="btn btn-primary" value="Submit">
+</form>
+
+
+
+
+{% endblock %}
diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html
index 1522969e8df4a5fd4fd4940b92cd7d863b92509a..f8e1d9ecf2c8156e4fee4b90ee7e2b66f06f79e6 100644
--- a/journals/templates/journals/publication_detail.html
+++ b/journals/templates/journals/publication_detail.html
@@ -1,6 +1,7 @@
 {% extends 'journals/_base.html' %}
 
 {% load journals_extras %}
+{% load publication_administration %}
 {% load staticfiles %}
 {% load scipost_extras %}
 {% load user_groups %}
@@ -34,7 +35,6 @@
     <meta name="citation_issue" content="{{ publication.in_issue.number }}"/>
     <meta name="citation_firstpage" content="{{ publication.paper_nr|paper_nr_string_filter }}"/>
     <meta name="citation_pdf_url" content="https://scipost.org/{{ publication.doi_string }}/pdf"/>
-
     <meta name="dc.identifier" content="doi:{{ publication.doi_string }}"/>
 
     <script>
@@ -50,6 +50,17 @@
 {% endblock headsup %}
 
 {% block content %}
+    {% if not publication.is_published %}
+        <div class="card bg-warning text-white">
+            <div class="card-body">
+                <p class="card-text text-center">
+                    This Publication is not published yet.
+                    Current status: {{ publication.get_status_display }}
+                </p>
+            </div>
+        </div>
+    {% endif %}
+
     {% is_edcol_admin request.user as is_edcol_admin %}
 
     {% include 'partials/journals/publication_summary.html' with publication=publication %}
@@ -127,35 +138,63 @@
         </ul>
     {% endif %}
 
+    {% if publication.status == 'draft' and perms.scipost.can_draft_publication %}
+        <hr class="divider">
+        <div class="row">
+            <div class="col-12">
+                <h3>Publication preparation</h3>
+                <ul class="fa-ul">
+                    <li><i class="fa-li fa fa-check-square text-success"></i><a href="{% url 'journals:update_publication' publication.accepted_submission.arxiv_identifier_w_vn_nr %}">Create/update Publication object</a></li>
+                    <li><i class="fa-li fa {% if publication|has_all_author_relations %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:add_author' publication.doi_label %}">Create all author relations</a></li>
+                    <li><i class="fa-li fa {% if publication.has_citation_list %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:create_citation_list_metadata' publication.doi_label %}">Create/update citation metadata</a></li>
+                    <li><i class="fa-li fa {% if publication.has_funding_statement %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:create_funding_info_metadata' publication.doi_label %}">Create/update funding info metadata</a></li>
+                    <li><i class="fa-li fa {% if publication.grants.exists %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:update_grants' publication.doi_label %}">Create/update grants</a></li>
+                    <li><i class="fa-li fa {% if publication.has_xml_metadata %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:create_metadata_xml' publication.doi_label %}">Create/update Crossref metadata</a> <em>(please do after citation and funding info are added)</em></li>
+                    <li><i class="fa-li fa {% if publication.references.exists %}fa-check-square text-success{% else %}fa-square{% endif %}"></i><a href="{% url 'journals:update_references' doi_label=publication.doi_label %}">Create/update references</a></li>
+                </ul>
+
+                <h3>Tools</h3>
+                <ul>
+                    <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'test' %}">Test Crossref deposit</a></li>
+                </ul>
+                Preparation completed? <a href="{% url 'journals:send_publication_for_approval' publication.doi_label %}">Send to administration for approval</a>.
+            </div>
+        </div>
+    {% endif %}
+
     {% if is_edcol_admin %}
-    <hr>
-    <div class="row">
-        <div class="col-12">
-            <h3>Editorial Administration tools: </h3>
-            <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: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>
-              <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'test' %}">Test metadata deposit (via Crossref test server)</a></li>
-              <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'deposit' %}">Deposit the metadata to Crossref</a></li>
-              <li><a href="{% url 'journals:harvest_citedby_links' publication.doi_label %}">Update Crossref cited-by links</a></li>
-              <li><a href="{% url 'journals:manage_metadata' %}">Metadata management page</a></li>
-    	      <li><a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">This publication's metadata management page</a></li>
-              <li><a href="{% url 'journals:update_references' doi_label=publication.doi_label %}">Update references</a></li>
-            </ul>
+        <hr class="divider">
+        <div class="row">
+            <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: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>
+                  <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'test' %}">Test metadata deposit (via Crossref test server)</a></li>
+                  <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'deposit' %}">Deposit the metadata to Crossref</a></li>
+                  <li><a href="{% url 'journals:harvest_citedby_links' publication.doi_label %}">Update Crossref cited-by links</a></li>
+                  <li><a href="{% url 'journals:manage_metadata' %}">Metadata management page</a></li>
+        	      <li><a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">This publication's metadata management page</a></li>
+                  <li><a href="{% url 'journals:update_references' doi_label=publication.doi_label %}">Update references</a></li>
+                  {% if not publication.is_published %}
+                      <li><a href="{% url 'journals:update_publication' publication.accepted_submission.arxiv_identifier_w_vn_nr %}">Update Publication object</a></li>
+                      <li><strong><a href="{% url 'journals:publish_publication' publication.doi_label %}">Publish this Publication</a></strong></li>
+                  {% endif %}
+                </ul>
+            </div>
         </div>
-    </div>
     {% endif %}
 
 {% endblock content %}
diff --git a/journals/templates/journals/publication_form.html b/journals/templates/journals/publication_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..6217eef9d55b80a892301f5dc2cc4f45310b2702
--- /dev/null
+++ b/journals/templates/journals/publication_form.html
@@ -0,0 +1,32 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Draft publication{% endblock pagetitle %}
+
+{% block content %}
+
+
+<h1>Draft publication</h1>
+
+{% if request.GET.issue or form.instance.id %}
+    <form method="post" enctype="multipart/form-data">
+        {% csrf_token %}
+        {{ form|bootstrap }}
+        <input type="submit" class="btn btn-primary" value="Save">
+    </form>
+{% else %}
+    <h3>Pick the Issue to publish in</h3>
+    <ul>
+        {% for issue in form.get_possible_issues %}
+            <li><a href="?issue={{ issue.id }}">{{ issue }}</a></li>
+        {% empty %}
+            <li><em>No Issues found. Please contact administration</em></li>
+        {% endfor %}
+    </ul>
+{% endif %}
+
+
+
+
+{% endblock %}
diff --git a/journals/templates/journals/publication_publish_form.html b/journals/templates/journals/publication_publish_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..b361b0c290a7bf02204221da22a49548b99e4d4d
--- /dev/null
+++ b/journals/templates/journals/publication_publish_form.html
@@ -0,0 +1,57 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Publish Publication{% endblock pagetitle %}
+
+{% block content %}
+
+
+<h1 class="highlight">Publish Publication</h1>
+{% include 'partials/journals/publication_summary.html' with publication=form.instance %}
+
+<h3>Authors</h3>
+<ul>
+    {% for author in form.instance.authors.all %}
+        <li>{{ author }}</li>
+    {% empty %}
+        <li>No authors assigned</li>
+    {% endfor %}
+</ul>
+
+<h3>Funding statement</h3>
+<p>{{ form.instance.metadata.funding_statement|default:'<em>No funding statement found.</em>' }}</p>
+
+<h3>Grants</h3>
+<ul>
+    {% for grant in form.instance.grants.all %}
+        <li>{{ grant }}</li>
+    {% empty %}
+        <li>No grants assigned</li>
+    {% endfor %}
+</ul>
+
+{% include 'partials/journals/references.html' with publication=form.instance %}
+
+<hr class="divider">
+<h3>Publishing will do the following:</h3>
+<div>
+    <ul>
+        <li>Move the pdf file to the appropriate folder</li>
+        <li>Update the Submission status</li>
+        <li>Update the Production Stream status</li>
+        <li>Send the authors a publication-email</li>
+    </ul>
+    <em>Reminder: Is the metadata already deposited at Crossref?</em>
+</div>
+<br>
+<form method="post" enctype="multipart/form-data">
+    {% csrf_token %}
+    {{ form|bootstrap }}
+    <input type="submit" class="btn btn-primary" value="Publish">
+</form>
+
+
+
+
+{% endblock %}
diff --git a/journals/templates/journals/validate_publication.html b/journals/templates/journals/validate_publication.html
deleted file mode 100644
index db78a13e090435434f2826e8dee528feed773071..0000000000000000000000000000000000000000
--- a/journals/templates/journals/validate_publication.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends 'scipost/base.html' %}
-
-{% load bootstrap %}
-
-{% block pagetitle %}: Validate publication{% endblock pagetitle %}
-
-{% block content %}
-
-<div class="row">
-    <div class="col-12">
-        <h1 class="highlight">Validate publication page</h1>
-    </div>
-</div>
-
-<div class="row justify-content-center">
-    <div class="col-lg-10">
-        {% if errormessage %}
-            <h2 style="color: red;">{{ errormessage }}</h2>
-        {% endif %}
-
-        <form action="{% url 'journals:validate_publication' %}" method="post" enctype="multipart/form-data">
-            {% csrf_token %}
-            {{ validate_publication_form|bootstrap }}
-            <input class="btn btn-secondary" type="submit" value="Submit">
-        </form>
-    </div>
-</div>
-
-{% endblock content %}
diff --git a/journals/templates/partials/journals/publication_summary.html b/journals/templates/partials/journals/publication_summary.html
index 014da1f23f015d0a82d9b59bf0be546a35470a09..2042920f8142d3eb59fab3a87a31db0142ad2289 100644
--- a/journals/templates/partials/journals/publication_summary.html
+++ b/journals/templates/partials/journals/publication_summary.html
@@ -1,7 +1,7 @@
 
 <div class="row">
     <div class="col-12">
-        <h2 class="pb-1 text-blue">{{publication.title}}</h2>
+        <h2 class="pb-1 text-blue">{{publication.title}}{% if publication.status == 'draft' %} <label class="label label-warning label-sm">{{ publication.get_status_display }}</label>{% endif %}</h2>
 
         <p class="mb-1">{{ publication.author_list }}</p>
         <p class="text-muted mb-0">
diff --git a/journals/templates/xml/publication_crossref.html b/journals/templates/xml/publication_crossref.html
new file mode 100644
index 0000000000000000000000000000000000000000..99d744783788e9cf739c5ee08bc568c14b6b54be
--- /dev/null
+++ b/journals/templates/xml/publication_crossref.html
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<doi_batch version="4.4.0" xmlns="http://www.crossref.org/schema/4.4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fr="http://www.crossref.org/fundref.xsd" xsi:schemaLocation="http://www.crossref.org/schema/4.4.0 http://www.crossref.org/shema/deposit/crossref4.4.0.xsd" xmlns:ai="http://www.crossref.org/AccessIndicators.xsd">
+    <head>
+        <doi_batch_id>{{ doi_batch_id }}</doi_batch_id>
+        <timestamp>{% now "YmdHis" %}</timestamp>
+        <depositor>
+            <depositor_name>scipost</depositor_name>
+            <email_address>{{ deposit_email }}</email_address>
+        </depositor>
+        <registrant>scipost</registrant>
+    </head>
+    <body>
+        <journal>
+            <journal_metadata>
+                <full_title>{{ publication.in_issue.in_volume.in_journal.get_name_display }}</full_title>
+                <abbrev_title>{{ publication.in_issue.in_volume.in_journal.get_abbreviation_citation }}</abbrev_title>
+                <issn media_type='electronic'>{{ publication.in_issue.in_volume.in_journal.issn }}</issn>
+                <doi_data>
+                    <doi>{{ publication.in_issue.in_volume.in_journal.doi_string }}</doi>
+                    <resource>https://scipost.org/{{ publication.in_issue.in_volume.in_journal.doi_string }}</resource>
+                </doi_data>
+            </journal_metadata>
+            <journal_issue>
+                <publication_date media_type='online'>
+                    <year>{{ publication.publication_date|date:'Y' }}</year>
+                </publication_date>
+                <journal_volume>
+                    <volume>{{ publication.in_issue.in_volume.number }}</volume>
+                </journal_volume>
+                <issue>{{ publication.in_issue.number }}</issue>
+            </journal_issue>
+            <journal_article publication_type='full_text'>
+                <titles>
+                    <title>{{ publication.title }}</title>
+                </titles>
+
+                <contributors>
+                    {% for author_object in publication.authors.all %}
+                        {% if author_object.order == 1 %}
+                            <person_name sequence='first' contributor_role='author'>
+                        {% else %}
+                            <person_name sequence='additional' contributor_role='author'>
+                        {% endif %}
+                        <given_name>{{ author_object.first_name }}</given_name>
+                        <surname>{{ author_object.last_name }}</surname>
+                        {% if author_object.contributor and author_object.contributor.orcid_id %}
+                            <ORCID>http://orcid.org/'{{ author_object.contributor.orcid_id }}</ORCID>
+                        {% endif %}
+                        </person_name>
+                    {% endfor %}
+                </contributors>
+
+                <publication_date media_type='online'>
+                    <month>{{ publication.publication_date|date:'m' }}</month>
+                    <day>{{ publication.publication_date|date:'d' }}</day>
+                    <year>{{ publication.publication_date|date:'Y' }}</year>
+                </publication_date>
+                <publisher_item>
+                    <item_number item_number_type='article_number'>{{ publication.paper_nr }}</item_number>
+                </publisher_item>
+                <crossmark>
+                    <crossmark_policy>10.21468/SciPost.CrossmarkPolicy</crossmark_policy>
+                    <crossmark_domains>
+                        <crossmark_domain><domain>scipost.org</domain></crossmark_domain>
+                    </crossmark_domains>
+                    <crossmark_domain_exclusive>false</crossmark_domain_exclusive>
+                    <custom_metadata>
+                        {% if funders %}
+                            <fr:program name='fundref'>
+                                {% for funder in funders %}
+                                    {% if funders|length > 1 %}
+                                        <fr:assertion name='fundgroup'>
+                                    {% endif %}
+
+                                    <fr:assertion name='funder_name'>{{ funder.name }}
+                                        <fr:assertion name='funder_identifier'>{{ funder.identifier }}</fr:assertion>
+                                    </fr:assertion>
+
+                                    {% for grant in publication.grants.all %}
+                                        {% if grant.funder == funder %}
+                                            <fr:assertion name='award_number'>{{ grant.number }}</fr:assertion>
+                                        {% endif %}
+                                    {% endfor %}
+
+                                    {% if funders|length > 1 %}
+                                        </fr:assertion>
+                                    {% endif %}
+                                {% endfor %}
+                            </fr:program>
+                        {% endif %}
+
+                        <ai:program name="AccessIndicators">
+                            <ai:license_ref>{{ publication.get_cc_license_URI }}</ai:license_ref>
+                        </ai:program>
+                    </custom_metadata>
+                </crossmark>
+                <archive_locations>
+                    <archive name="CLOCKSS"></archive>
+                </archive_locations>
+                <doi_data>
+                    <doi>{{ publication.doi_string }}</doi>
+                    <resource>https://scipost.org/{{ publication.doi_string }}</resource>
+                    <collection property='crawler-based'>
+                        <item crawler='iParadigms'>
+                            <resource>https://scipost.org/{{ publication.doi_string }}/pdf</resource>
+                        </item>
+                    </collection>
+                    <collection property='text-mining'>
+                        <item>
+                            <resource mime_type='application/pdf'>https://scipost.org/{{ publication.doi_string }}/pdf</resource>
+                        </item>
+                    </collection>
+                </doi_data>
+                {% if publication.metadata.citation_list %}
+                    <citation_list>
+                        {% for ref in publication.metadata.citation_list %}
+                            <citation key='{{ ref.key }}'>
+                                <doi>{{ ref.doi }}</doi>
+                            </citation>
+                        {% endfor %}
+                    </citation_list>
+                {% endif %}
+            </journal_article>
+        </journal>
+    </body>
+</doi_batch>
diff --git a/journals/templatetags/journals_extras.py b/journals/templatetags/journals_extras.py
index fa6370f73bf45bfcb683840a82f28d5767d9e93d..ee55822049fe25ea07c7e8408e4a1af39154bb08 100644
--- a/journals/templatetags/journals_extras.py
+++ b/journals/templatetags/journals_extras.py
@@ -9,6 +9,7 @@ register = template.Library()
 def paper_nr_string_filter(nr):
     return paper_nr_string(nr)
 
+
 @register.filter(name='latest_successful_crossref_deposit')
 def latest_successful_crossref_deposit(publication):
     latest = publication.deposit_set.filter(
@@ -18,6 +19,7 @@ def latest_successful_crossref_deposit(publication):
     else:
         return "No successful deposit found"
 
+
 @register.filter(name='latest_successful_DOAJ_deposit')
 def latest_successful_DOAJ_deposit(publication):
     latest = publication.doajdeposit_set.filter(
@@ -27,8 +29,9 @@ def latest_successful_DOAJ_deposit(publication):
     else:
         return "No successful deposit found"
 
+
 @register.filter(name='latest_successful_crossref_deposit_report')
-def latest_successful_crossref_deposit(report):
+def latest_successful_crossref_deposit_report(report):
     latest = report.genericdoideposit.filter(
         deposit_successful=True).order_by('-deposition_date').first()
     if latest:
@@ -36,8 +39,9 @@ def latest_successful_crossref_deposit(report):
     else:
         return "No successful deposit found"
 
+
 @register.filter(name='latest_successful_crossref_deposit_comment')
-def latest_successful_crossref_deposit(comment):
+def latest_successful_crossref_deposit_comment(comment):
     latest = comment.genericdoideposit.filter(
         deposit_successful=True).order_by('-deposition_date').first()
     if latest:
diff --git a/journals/templatetags/publication_administration.py b/journals/templatetags/publication_administration.py
new file mode 100644
index 0000000000000000000000000000000000000000..96e108fbd1f9e9a68e0d0bc3557d1d340b776d20
--- /dev/null
+++ b/journals/templatetags/publication_administration.py
@@ -0,0 +1,11 @@
+from django import template
+
+register = template.Library()
+
+
+@register.filter
+def has_all_author_relations(publication):
+    """
+    Check if all authors are added to the Publication object, just by counting.
+    """
+    return len(publication.author_list.split(',')) == publication.authors.count()
diff --git a/journals/urls/general.py b/journals/urls/general.py
index ca60342a93f2fb64dbd834e5fbe14fb8a7f44b56..0293ca0430c050edf854f57ed0956195414ce73e 100644
--- a/journals/urls/general.py
+++ b/journals/urls/general.py
@@ -2,6 +2,8 @@ from django.conf.urls import url
 from django.urls import reverse_lazy
 from django.views.generic import TemplateView, RedirectView
 
+from submissions.constants import SUBMISSIONS_COMPLETE_REGEX
+
 from journals import views as journals_views
 
 urlpatterns = [
@@ -16,13 +18,24 @@ urlpatterns = [
         TemplateView.as_view(template_name='journals/crossmark_policy.html'),
         name='crossmark_policy'),
 
+    # Publication creation
+    url(r'^admin/publications/{regex}/$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
+        journals_views.DraftPublicationUpdateView.as_view(),
+        name='update_publication'),
+    url(r'^admin/publications/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/publish$',
+        journals_views.PublicationPublishView.as_view(),
+        name='publish_publication'),
+    url(r'^admin/publications/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/approval$',
+        journals_views.DraftPublicationApprovalView.as_view(),
+        name='send_publication_for_approval'),
+    url(r'^admin/publications/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/grants$',
+        journals_views.PublicationGrantsView.as_view(),
+        name='update_grants'),
+    url(r'^admin/publications/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/grants/(?P<grant_id>[0-9]+)/remove$',
+        journals_views.PublicationGrantsRemovalView.as_view(),
+        name='remove_grant'),
+
     # Editorial and Administrative Workflow
-    url(r'^admin/initiate_publication$',
-        journals_views.initiate_publication,
-        name='initiate_publication'),
-    url(r'^admin/validate_publication$',
-        journals_views.validate_publication,
-        name='validate_publication'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/authors/add/(?P<contributor_id>[0-9]+)$',
         journals_views.add_author,
         name='add_author'),
@@ -42,12 +55,12 @@ urlpatterns = [
         journals_views.manage_metadata,
         name='manage_metadata'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/citation_list_metadata$',
-        journals_views.create_citation_list_metadata,
+        journals_views.CitationUpdateView.as_view(),
         name='create_citation_list_metadata'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/update_references$',
         journals_views.update_references, name='update_references'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/funders/create_metadata$',
-        journals_views.create_funding_info_metadata,
+        journals_views.FundingInfoView.as_view(),
         name='create_funding_info_metadata'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/funders/add_generic$',
         journals_views.add_generic_funder,
@@ -58,7 +71,7 @@ urlpatterns = [
 
     # Metadata handling
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/metadata/crossref/create$',
-        journals_views.create_metadata_xml,
+        journals_views.CreateMetadataXMLView.as_view(),
         name='create_metadata_xml'),
     url(r'^admin/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/metadata/crossref/deposit/(?P<option>[a-z]+)$',
         journals_views.metadata_xml_deposit,
diff --git a/journals/views.py b/journals/views.py
index 432e6dc162641a77220292eb28ea14f78ed1d0cd..5b237c52282bca6040b796f4b9e77dbb098c3b58 100644
--- a/journals/views.py
+++ b/journals/views.py
@@ -10,6 +10,7 @@ import xml.etree.ElementTree as ET
 
 from django.contrib.auth.decorators import login_required
 from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.core.urlresolvers import reverse
 from django.conf import settings
@@ -17,27 +18,29 @@ from django.contrib import messages
 from django.db import transaction
 from django.http import Http404, HttpResponse
 from django.utils import timezone
+from django.utils.decorators import method_decorator
+from django.views.generic.detail import DetailView
+from django.views.generic.edit import UpdateView
 from django.shortcuts import get_object_or_404, render, redirect
 
-from .exceptions import PaperNumberingError
+from .constants import STATUS_DRAFT
 from .helpers import paper_nr_string, issue_doi_label_from_doi_label
 from .models import Journal, Issue, Publication, Deposit, DOAJDeposit,\
                     GenericDOIDeposit, PublicationAuthorsTable
-from .forms import FundingInfoForm, InitiatePublicationForm, ValidatePublicationForm,\
+from .forms import FundingInfoForm,\
                    UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm,\
-                   ReferenceFormSet, CreateMetadataDOAJForm
+                   ReferenceFormSet, CreateMetadataDOAJForm, DraftPublicationForm,\
+                   PublicationGrantsForm, DraftPublicationApprovalForm, PublicationPublishForm
+from .mixins import PublicationMixin, ProdSupervisorPublicationPermissionMixin
 from .utils import JournalUtils
 
 from comments.models import Comment
-from funders.models import Funder
-from submissions.models import Submission, Report
-from scipost.models import Contributor
-from production.constants import PROOFS_PUBLISHED
-from production.models import ProductionEvent
-from production.signals import notify_stream_status_change
-
 from funders.forms import FunderSelectForm, GrantSelectForm
+from funders.models import Funder, Grant
+from submissions.models import Submission, Report
 from scipost.forms import ConfirmationForm
+from scipost.models import Contributor
+from scipost.mixins import PermissionsMixin, RequestViewMixin
 
 from guardian.decorators import permission_required
 
@@ -94,7 +97,7 @@ def recent(request, doi_label):
     Display page for the most recent 20 publications in SciPost Physics.
     """
     journal = get_object_or_404(Journal, doi_label=doi_label)
-    recent_papers = Publication.objects.published(
+    recent_papers = Publication.objects.published().filter(
         in_issue__in_volume__in_journal=journal).order_by('-publication_date',
                                                           '-paper_nr')[:20]
     context = {
@@ -140,7 +143,7 @@ def issue_detail(request, doi_label):
     issue = Issue.objects.get_published(doi_label=doi_label)
     journal = issue.in_volume.in_journal
 
-    papers = issue.publication_set.order_by('paper_nr')
+    papers = issue.publications.published().order_by('paper_nr')
     next_issue = (Issue.objects.published(in_volume__in_journal=journal,
                                           start_date__gt=issue.start_date)
                                .order_by('start_date').first())
@@ -160,155 +163,82 @@ def issue_detail(request, doi_label):
 #######################
 # Publication process #
 #######################
-
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-@transaction.atomic
-def initiate_publication(request):
+class PublicationGrantsView(PermissionsMixin, UpdateView):
     """
-    Called by an Editorial Administrator.
-    Publish the manuscript after proofs have been accepted.
-    This method prefills a ValidatePublicationForm for further
-    processing (verification in validate_publication method).
+    Add/update grants associated to a Publication.
     """
-    initiate_publication_form = InitiatePublicationForm(request.POST or None)
-    if initiate_publication_form.is_valid():
-        submission = initiate_publication_form.cleaned_data['accepted_submission']
-        current_issue = initiate_publication_form.cleaned_data['to_be_issued_in']
-
-        # Determine next available paper number:
-        paper_nr = Publication.objects.filter(in_issue__in_volume=current_issue.in_volume).count()
-        paper_nr += 1
-        if paper_nr > 999:
-            raise PaperNumberingError(paper_nr)
-
-        # Build form data
-        doi_label = (
-            current_issue.in_volume.in_journal.name
-            + '.' + str(current_issue.in_volume.number)
-            + '.' + str(current_issue.number) + '.' + paper_nr_string(paper_nr)
-        )
-        doi_string = '10.21468/' + doi_label
-        BiBTeX_entry = (
-            '@Article{' + doi_label + ',\n'
-            '\ttitle={{' + submission.title + '}},\n'
-            '\tauthor={' + submission.author_list.replace(',', ' and') + '},\n'
-            '\tjournal={'
-            + current_issue.in_volume.in_journal.get_abbreviation_citation()
-            + '},\n'
-            '\tvolume={' + str(current_issue.in_volume.number) + '},\n'
-            '\tissue={' + str(current_issue.number) + '},\n'
-            '\tpages={' + paper_nr_string(paper_nr) + '},\n'
-            '\tyear={' + current_issue.until_date.strftime('%Y') + '},\n'
-            '\tpublisher={SciPost},\n'
-            '\tdoi={' + doi_string + '},\n'
-            '\turl={https://scipost.org/' + doi_string + '},\n'
-            '}\n'
-        )
-        initial = {
-            'accepted_submission': submission,
-            'in_issue': current_issue,
-            'paper_nr': paper_nr,
-            'discipline': submission.discipline,
-            'domain': submission.domain,
-            'subject_area': submission.subject_area,
-            'secondary_areas': submission.secondary_areas,
-            'title': submission.title,
-            'author_list': submission.author_list,
-            'abstract': submission.abstract,
-            'BiBTeX_entry': BiBTeX_entry,
-            'doi_label': doi_label,
-            'acceptance_date': submission.acceptance_date,
-            'submission_date': submission.submission_date,
-            'publication_date': timezone.now(),
-        }
-        validate_publication_form = ValidatePublicationForm(initial=initial)
-        context = {'validate_publication_form': validate_publication_form}
-        return render(request, 'journals/validate_publication.html', context)
-
-    context = {'initiate_publication_form': initiate_publication_form}
-    return render(request, 'journals/initiate_publication.html', context)
+    permission_required = 'scipost.can_draft_publication'
+    queryset = Publication.objects.drafts()
+    slug_field = slug_url_kwarg = 'doi_label'
+    form_class = PublicationGrantsForm
+    template_name = 'journals/grants_form.html'
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-@transaction.atomic
-def validate_publication(request):
+class PublicationGrantsRemovalView(PermissionsMixin, DetailView):
     """
-    This creates a Publication instance from the ValidatePublicationForm,
-    pre-filled by the initiate_publication method above.
+    Remove grant associated to a Publication.
     """
-    # TODO: move from uploads to Journal folder
-    # TODO: create metadata
-    # TODO: set DOI, register with Crossref
-    # TODO: add funding info
-    context = {}
-    validate_publication_form = ValidatePublicationForm(request.POST or None,
-                                                        request.FILES or None)
-    if validate_publication_form.is_valid():
-        publication = validate_publication_form.save()
-
-        # Fill remaining data
-        submission = publication.accepted_submission
-
-        for submission_author in submission.authors.all():
-            PublicationAuthorsTable.objects.create(
-                publication=publication, contributor=submission_author)
-        publication.authors_claims.add(*submission.authors_claims.all())
-        publication.authors_false_claims.add(*submission.authors_false_claims.all())
-
-        # Add Institutions to the publication
-        for author in publication.authors_registered.all():
-            for current_affiliation in author.affiliations.active():
-                publication.institutions.add(current_affiliation.institution)
-
-        # Save the beast
-        publication.save()
+    permission_required = 'scipost.can_draft_publication'
+    queryset = Publication.objects.drafts()
+    slug_field = slug_url_kwarg = 'doi_label'
 
-        # Move file to final location
-        initial_path = publication.pdf_file.path
-        new_dir = (settings.MEDIA_ROOT + publication.in_issue.path + '/'
-                   + publication.get_paper_nr())
-        new_path = new_dir + '/' + publication.doi_label.replace('.', '_') + '.pdf'
-        os.makedirs(new_dir)
-        os.rename(initial_path, new_path)
-        publication.pdf_file.name = new_path
-        publication.save()
+    def get(self, request, *args, **kwargs):
+        super().get(request, *args, **kwargs)
+        grant = get_object_or_404(Grant, id=kwargs.get('grant_id'))
+        self.object.grants.remove(grant)
+        return redirect(reverse('journals:update_grants', args=(self.object.doi_label,)))
 
-        # Mark the submission as having been published:
-        submission.published_as = publication
-        submission.status = 'published'
-        submission.save()
-
-        # Update ProductionStream
-        if hasattr(submission, 'production_stream'):
-            stream = submission.production_stream
-            stream.status = PROOFS_PUBLISHED
-            stream.save()
-            if request.user.production_user:
-                prodevent = ProductionEvent(
-                    stream=stream,
-                    event='status',
-                    comments=' published the manuscript.',
-                    noted_by=request.user.production_user
-                )
-                prodevent.save()
-            notify_stream_status_change(request.user, stream, False)
-
-        # TODO: Create a Commentary Page
-        # Email authors
-        JournalUtils.load({'publication': publication})
-        JournalUtils.send_authors_paper_published_email()
-
-        # Add SubmissionEvents
-        submission.add_general_event('The Submission has been published as %s.'
-                                     % publication.doi_label)
-
-        messages.success(request, 'The publication has been validated.')
-        return redirect(publication.get_absolute_url())
-    else:
-        context['errormessage'] = 'The form was invalid.'
 
-    context['validate_publication_form'] = validate_publication_form
-    return render(request, 'journals/validate_publication.html', context)
+class DraftPublicationUpdateView(PermissionsMixin, UpdateView):
+    """
+    Any Production Officer or Administrator can draft a new publication without publishing here.
+    The actual publishing is done lin a later stadium, after the draft has been finished.
+    """
+    permission_required = 'scipost.can_draft_publication'
+    queryset = Publication.objects.unpublished()
+    slug_url_kwarg = 'arxiv_identifier_w_vn_nr'
+    slug_field = 'accepted_submission__arxiv_identifier_w_vn_nr'
+    form_class = DraftPublicationForm
+    template_name = 'journals/publication_form.html'
+
+    def get_object(self, queryset=None):
+        try:
+            publication = Publication.objects.get(
+                accepted_submission__arxiv_identifier_w_vn_nr=self.kwargs.get(
+                    'arxiv_identifier_w_vn_nr'))
+        except Publication.DoesNotExist:
+            if Submission.objects.accepted().filter(arxiv_identifier_w_vn_nr=self.kwargs.get(
+              'arxiv_identifier_w_vn_nr')).exists():
+                return None
+            raise Http404('No accepted Submission found')
+        if publication.status == STATUS_DRAFT:
+            return publication
+        if self.request.user.has_perm('scipost.can_publish_accepted_submission'):
+            return publication
+        raise Http404('Found Publication is not in draft')
+
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['arxiv_identifier_w_vn_nr'] = self.kwargs.get('arxiv_identifier_w_vn_nr')
+        kwargs['issue_id'] = self.request.GET.get('issue')
+        return kwargs
+
+
+class DraftPublicationApprovalView(PermissionsMixin, UpdateView):
+    permission_required = 'scipost.can_draft_publication'
+    queryset = Publication.objects.drafts()
+    slug_field = slug_url_kwarg = 'doi_label'
+    form_class = DraftPublicationApprovalForm
+    template_name = 'journals/publication_approval_form.html'
+
+
+@method_decorator(transaction.atomic, name='dispatch')
+class PublicationPublishView(PermissionsMixin, RequestViewMixin, UpdateView):
+    permission_required = 'scipost.can_publish_accepted_submission'
+    queryset = Publication.objects.unpublished()
+    slug_field = slug_url_kwarg = 'doi_label'
+    form_class = PublicationPublishForm
+    template_name = 'journals/publication_publish_form.html'
 
 
 @permission_required('scipost.can_publish_accepted_submission', return_403=True)
@@ -351,7 +281,7 @@ def mark_first_author(request, publication_id, author_object_id):
                             kwargs={'doi_label': publication.doi_label}))
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
+@permission_required('scipost.can_draft_publication', return_403=True)
 @transaction.atomic
 def add_author(request, doi_label, contributor_id=None, unregistered_author_id=None):
     """
@@ -361,6 +291,9 @@ def add_author(request, doi_label, contributor_id=None, unregistered_author_id=N
     This is important for the Crossref metadata, in which all authors must appear.
     """
     publication = get_object_or_404(Publication, doi_label=doi_label)
+    if not publication.is_draft and not request.user.has_perm('can_publish_accepted_submission'):
+        raise Http404('You do not have permission to edit this non-draft Publication')
+
     if contributor_id:
         contributor = get_object_or_404(Contributor, id=contributor_id)
         PublicationAuthorsTable.objects.create(contributor=contributor, publication=publication)
@@ -393,38 +326,16 @@ def add_author(request, doi_label, contributor_id=None, unregistered_author_id=N
     return render(request, 'journals/add_author.html', context)
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-@transaction.atomic
-def create_citation_list_metadata(request, doi_label):
-    """
-    Called by an Editorial Administrator.
-    This populates the citation_list dictionary entry
-    in the metadata field in a Publication instance.
-    """
-    publication = get_object_or_404(Publication, doi_label=doi_label)
-    bibitems_form = CitationListBibitemsForm(request.POST or None, request.FILES or None)
-    if bibitems_form.is_valid():
-        publication.metadata['citation_list'] = bibitems_form.extract_dois()
-        publication.save()
-        messages.success(request, 'Updated citation list')
-        return redirect(reverse('journals:create_citation_list_metadata',
-                        kwargs={'doi_label': publication.doi_label}))
-    context = {
-        'publication': publication,
-        'bibitems_form': bibitems_form,
-        'citation_list': publication.metadata.get('citation_list', '')
-    }
-    return render(request, 'journals/create_citation_list_metadata.html', context)
-
-
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
+@permission_required('scipost.can_draft_publication', return_403=True)
 def update_references(request, doi_label):
     """
     Update the References for a certain Publication.
     """
     publication = get_object_or_404(Publication, doi_label=doi_label)
-    references = publication.references.all()
+    if not publication.is_draft and not request.user.has_perm('can_publish_accepted_submission'):
+        raise Http404('You do not have permission to edit this non-draft Publication')
 
+    references = publication.references.all()
     formset = ReferenceFormSet(request.POST or None, queryset=references, publication=publication,
                                extra=request.GET.get('extra'))
 
@@ -443,34 +354,20 @@ def update_references(request, doi_label):
     return render(request, 'journals/update_references.html', context)
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-@transaction.atomic
-def create_funding_info_metadata(request, doi_label):
+class CitationUpdateView(PublicationMixin, ProdSupervisorPublicationPermissionMixin, UpdateView):
     """
-    Called by an Editorial Administrator.
-    This populates the funding_info dictionary entry
-    in the metadata field in a Publication instance.
+    Populates the citation_list dictionary entry in the metadata field in a Publication instance.
     """
-    publication = get_object_or_404(Publication, doi_label=doi_label)
+    form_class = CitationListBibitemsForm
+    template_name = 'journals/create_citation_list_metadata.html'
 
-    funding_statement = publication.metadata.get('funding_statement', '')
-    initial = {
-        'funding_statement': funding_statement,
-    }
-    form = FundingInfoForm(request.POST or None, instance=publication, initial=initial)
-    if form.is_valid():
-        form.save()
-        messages.success(request, 'Updated funding info')
-        return redirect(reverse('journals:create_funding_info_metadata',
-                                kwargs={'doi_label': publication.doi_label}))
-
-    context = {
-        'publication': publication,
-        'funding_info_form': form,
-        'funding_statement': funding_statement,
-    }
 
-    return render(request, 'journals/create_funding_info_metadata.html', context)
+class FundingInfoView(PublicationMixin, ProdSupervisorPublicationPermissionMixin, UpdateView):
+    """
+    Add/update funding statement to the xml_metadata
+    """
+    form_class = FundingInfoForm
+    template_name = 'journals/create_funding_info_metadata.html'
 
 
 @permission_required('scipost.can_publish_accepted_submission', return_403=True)
@@ -507,195 +404,19 @@ def add_generic_funder(request, doi_label):
                             kwargs={'doi_label': doi_label}))
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
-@transaction.atomic
-def create_metadata_xml(request, doi_label):
+class CreateMetadataXMLView(PublicationMixin,
+                            ProdSupervisorPublicationPermissionMixin,
+                            UpdateView):
     """
-    To be called by an EdAdmin after the citation_list,
-    funding_info entries have been filled.
-    Populates the metadata_xml field of a Publication instance.
+    To be called by an EdAdmin (or Production Supervisor) after the citation_list, funding_info
+    entries have been filled. Populates the metadata_xml field of a Publication instance.
     The contents can then be sent to Crossref for registration.
     """
-    publication = get_object_or_404(Publication, doi_label=doi_label)
-
-    # create a doi_batch_id
-    salt = ""
-    for i in range(5):
-        salt = salt + random.choice(string.ascii_letters)
-    salt = salt.encode('utf8')
-    idsalt = publication.title[:10]
-    idsalt = idsalt.encode('utf8')
-    doi_batch_id = hashlib.sha1(salt+idsalt).hexdigest()
-
-    initial = {'metadata_xml': ''}
-    initial['metadata_xml'] += (
-        '<?xml version="1.0" encoding="UTF-8"?>\n'
-        '<doi_batch version="4.4.0" xmlns="http://www.crossref.org/schema/4.4.0" '
-        'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
-        'xmlns:fr="http://www.crossref.org/fundref.xsd" '
-        'xsi:schemaLocation="http://www.crossref.org/schema/4.4.0 '
-        'http://www.crossref.org/shema/deposit/crossref4.4.0.xsd" '
-        'xmlns:ai="http://www.crossref.org/AccessIndicators.xsd">\n'
-        '<head>\n'
-        '<doi_batch_id>' + str(doi_batch_id) + '</doi_batch_id>\n'
-        '<timestamp>' + timezone.now().strftime('%Y%m%d%H%M%S') + '</timestamp>\n'
-        '<depositor>\n'
-        '<depositor_name>scipost</depositor_name>\n'
-        '<email_address>' + settings.CROSSREF_DEPOSIT_EMAIL + '</email_address>\n'
-        '</depositor>\n'
-        '<registrant>scipost</registrant>\n'
-        '</head>\n'
-        '<body>\n'
-        '<journal>\n'
-        '<journal_metadata>\n'
-        '<full_title>' + publication.in_issue.in_volume.in_journal.get_name_display()
-        + '</full_title>\n'
-        '<abbrev_title>'
-        + publication.in_issue.in_volume.in_journal.get_abbreviation_citation() +
-        '</abbrev_title>\n'
-        '<issn media_type=\'electronic\'>' + publication.in_issue.in_volume.in_journal.issn
-        + '</issn>\n'
-        '<doi_data>\n'
-        '<doi>' + publication.in_issue.in_volume.in_journal.doi_string + '</doi>\n'
-        '<resource>https://scipost.org/'
-        + publication.in_issue.in_volume.in_journal.doi_string + '</resource>\n'
-        '</doi_data>\n'
-        '</journal_metadata>\n'
-        '<journal_issue>\n'
-        '<publication_date media_type=\'online\'>\n'
-        '<year>' + publication.publication_date.strftime('%Y') + '</year>\n'
-        '</publication_date>\n'
-        '<journal_volume>\n'
-        '<volume>' + str(publication.in_issue.in_volume.number) + '</volume>\n'
-        '</journal_volume>\n'
-        '<issue>' + str(publication.in_issue.number) + '</issue>\n'
-        '</journal_issue>\n'
-        '<journal_article publication_type=\'full_text\'>\n'
-        '<titles><title>' + publication.title + '</title></titles>\n'
-    )
-
-    # Precondition: all authors MUST be listed in authors field of publication instance,
-    # this to be checked by EdAdmin before publishing.
-    initial['metadata_xml'] += '<contributors>\n'
-    for author_object in publication.authors.all():
-        if author_object.order == 1:
-            initial['metadata_xml'] += (
-                '<person_name sequence=\'first\' contributor_role=\'author\'> '
-                '<given_name>' + author_object.first_name + '</given_name> '
-                '<surname>' + author_object.last_name + '</surname> '
-            )
-        else:
-            initial['metadata_xml'] += (
-                '<person_name sequence=\'additional\' contributor_role=\'author\'> '
-                '<given_name>' + author_object.first_name + '</given_name> '
-                '<surname>' + author_object.last_name + '</surname> '
-            )
-        if author_object.contributor and author_object.contributor.orcid_id:
-            initial['metadata_xml'] += (
-                '<ORCID>http://orcid.org/' + author_object.contributor.orcid_id + '</ORCID>'
-            )
-        initial['metadata_xml'] += '</person_name>\n'
-    initial['metadata_xml'] += '</contributors>\n'
-
-    initial['metadata_xml'] += (
-        '<publication_date media_type=\'online\'>\n'
-        '<month>' + publication.publication_date.strftime('%m') + '</month>'
-        '<day>' + publication.publication_date.strftime('%d') + '</day>'
-        '<year>' + publication.publication_date.strftime('%Y') + '</year>'
-        '</publication_date>\n'
-        '<publisher_item><item_number item_number_type="article_number">'
-        + paper_nr_string(publication.paper_nr) +
-        '</item_number></publisher_item>\n'
-        '<crossmark>\n'
-        '<crossmark_policy>10.21468/SciPost.CrossmarkPolicy</crossmark_policy>\n'
-        '<crossmark_domains>\n'
-        '<crossmark_domain><domain>scipost.org</domain></crossmark_domain>\n'
-        '</crossmark_domains>\n'
-        '<crossmark_domain_exclusive>false</crossmark_domain_exclusive>\n'
-        )
-    funders = (Funder.objects.filter(grant__in=publication.grants.all())
-               | publication.funders_generic.all()).distinct()
-    nr_funders = funders.count()
-    initial['metadata_xml'] += '<custom_metadata>\n'
-    if nr_funders > 0:
-        initial['metadata_xml'] += '<fr:program name="fundref">\n'
-        for funder in funders:
-            if nr_funders > 1:
-                initial['metadata_xml'] += '<fr:assertion name="fundgroup">\n'
-            initial['metadata_xml'] += (
-                '<fr:assertion name="funder_name">' + funder.name + '\n'
-                '<fr:assertion name="funder_identifier">'
-                + funder.identifier + '</fr:assertion>\n'
-                '</fr:assertion>\n')
-            for grant in publication.grants.all():
-                if grant.funder == funder:
-                    initial['metadata_xml'] += (
-                        '<fr:assertion name="award_number">'
-                        + grant.number + '</fr:assertion>\n')
-            if nr_funders > 1:
-                initial['metadata_xml'] += '</fr:assertion>\n'
-        initial['metadata_xml'] += '</fr:program>\n'
-    initial['metadata_xml'] += (
-        '<ai:program name="AccessIndicators">\n'
-        '<ai:license_ref>' + publication.get_cc_license_URI() +
-        '</ai:license_ref>\n'
-        '</ai:program>\n'
-    )
-    initial['metadata_xml'] += '</custom_metadata>\n'
-    initial['metadata_xml'] += (
-        '</crossmark>\n'
-        '<archive_locations><archive name="CLOCKSS"></archive></archive_locations>\n'
-        '<doi_data>\n'
-        '<doi>' + publication.doi_string + '</doi>\n'
-        '<resource>https://scipost.org/' + publication.doi_string + '</resource>\n'
-        '<collection property="crawler-based">\n'
-        '<item crawler="iParadigms">\n'
-        '<resource>https://scipost.org/'
-        + publication.doi_string + '/pdf</resource>\n'
-        '</item></collection>\n'
-        '<collection property="text-mining">\n'
-        '<item><resource mime_type="application/pdf">'
-        'https://scipost.org/' + publication.doi_string + '/pdf</resource></item>\n'
-        '</collection>'
-        '</doi_data>\n'
-    )
-    try:
-        if publication.metadata['citation_list']:
-            initial['metadata_xml'] += '<citation_list>\n'
-            for ref in publication.metadata['citation_list']:
-                initial['metadata_xml'] += (
-                    '<citation key="' + ref['key'] + '">'
-                    '<doi>' + ref['doi'] + '</doi>'
-                    '</citation>\n'
-                )
-        initial['metadata_xml'] += '</citation_list>\n'
-    except KeyError:
-        pass
-    initial['metadata_xml'] += (
-        '</journal_article>\n'
-        '</journal>\n'
-    )
-    initial['metadata_xml'] += '</body>\n</doi_batch>'
-
-    create_metadata_xml_form = CreateMetadataXMLForm(request.POST or None,
-                                                     instance=publication,
-                                                     initial=initial)
-    if create_metadata_xml_form.is_valid():
-        create_metadata_xml_form.save()
-        messages.success(request, 'Metadata XML saved')
-        return redirect(reverse('journals:manage_metadata',
-                                kwargs={'doi_label': doi_label}))
-
-    publication.latest_metadata_update = timezone.now()
-    publication.save()
-    context = {
-        'publication': publication,
-        'create_metadata_xml_form': create_metadata_xml_form,
-    }
-    return render(request, 'journals/create_metadata_xml.html', context)
+    form_class = CreateMetadataXMLForm
+    template_name = 'journals/create_metadata_xml.html'
 
 
-@permission_required('scipost.can_publish_accepted_submission', return_403=True)
+@permission_required('scipost.can_draft_publication', return_403=True)
 @transaction.atomic
 def metadata_xml_deposit(request, doi_label, option='test'):
     """
@@ -704,6 +425,10 @@ def metadata_xml_deposit(request, doi_label, option='test'):
     Makes use of the python requests module.
     """
     publication = get_object_or_404(Publication, doi_label=doi_label)
+    if not publication.is_draft and not request.user.has_perm('can_publish_accepted_submission'):
+        raise Http404('You do not have permission to access this non-draft Publication')
+    if not request.user.has_perm('can_publish_accepted_submission') and option != 'test':
+        raise PermissionDenied('You do not have permission to do real Crossref deposits')
 
     if publication.metadata_xml is None:
         messages.warning(
@@ -741,7 +466,9 @@ def metadata_xml_deposit(request, doi_label, option='test'):
             'login_passwd': settings.CROSSREF_LOGIN_PASSWORD,
             }
         files = {
-            'fname': ('metadata.xml', publication.metadata_xml.encode('utf-8'), 'multipart/form-data')
+            'fname': ('metadata.xml',
+                      publication.metadata_xml.encode('utf-8'),
+                      'multipart/form-data')
         }
         r = requests.post(url, params=params, files=files)
         response_headers = r.headers
@@ -1299,7 +1026,14 @@ def author_reply_detail(request, doi_label):
 
 
 def publication_detail(request, doi_label):
-    publication = Publication.objects.get_published(doi_label=doi_label)
+    """
+    The actual Publication detail page. This is visible for everyone if published or
+    visible for Production Supervisors and Administrators if in draft.
+    """
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    if not publication.is_published and not request.user.has_perm('scipost.can_draft_publication'):
+        raise Http404('Publication is not publicly visible')
+
     journal = publication.in_issue.in_volume.in_journal
 
     context = {
@@ -1310,7 +1044,14 @@ def publication_detail(request, doi_label):
 
 
 def publication_detail_pdf(request, doi_label):
-    publication = Publication.objects.get_published(doi_label=doi_label)
+    """
+    The actual Publication pdf. This is visible for everyone if published or
+    visible for Production Supervisors and Administrators if in draft.
+    """
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    if not publication.is_published and not request.user.has_perm('scipost.can_draft_publication'):
+        raise Http404('Publication is not publicly visible')
+
     response = HttpResponse(publication.pdf_file.read(), content_type='application/pdf')
     response['Content-Disposition'] = ('filename='
                                        + publication.doi_label.replace('.', '_') + '.pdf')
diff --git a/mails/mixins.py b/mails/mixins.py
index e34dd97123125c66c502413ace9248fba4a3d93e..8b88a62e291e80b7d640ff13ffd5ec8d3175721e 100644
--- a/mails/mixins.py
+++ b/mails/mixins.py
@@ -4,7 +4,6 @@ import inspect
 from html2text import HTML2Text
 
 from django.core.mail import EmailMultiAlternatives
-from django.contrib import messages
 from django.contrib.auth import get_user_model
 from django.conf import settings
 from django.template import loader
@@ -12,79 +11,6 @@ from django.template import loader
 from scipost.models import Contributor
 
 
-from . import forms
-
-
-class MailEditorMixin:
-    """
-    Use MailEditorMixin in edit CBVs to automatically implement the mail editor as
-    a post-form_valid hook.
-
-    The view must specify the `mail_code` variable.
-    """
-    object = None
-    mail_form = None
-    has_permission_to_send_mail = True
-    alternative_from_address = None  # Tuple: ('from_name', 'from_address')
-
-    def __init__(self, *args, **kwargs):
-        if not self.mail_code:
-            raise AttributeError(self.__class__.__name__ + ' object has no attribute `mail_code`')
-        super().__init__(*args, **kwargs)
-
-    def get_template_names(self):
-        """
-        The mail editor form has its own template.
-        """
-        if self.mail_form and not self.mail_form.is_valid():
-            return ['mails/mail_form.html']
-        return super().get_template_names()
-
-    def post(self, request, *args, **kwargs):
-        """
-        Handle POST requests, but interpect the data if the mail form data isn't valid.
-        """
-        if not self.has_permission_to_send_mail:
-            # Don't use the mail form; don't send out the mail.
-            return super().post(request, *args, **kwargs)
-        self.object = self.get_object()
-        form = self.get_form()
-        if form.is_valid():
-            self.mail_form = forms.EmailTemplateForm(request.POST or None,
-                                                     mail_code=self.mail_code,
-                                                     instance=self.object)
-            if self.mail_form.is_valid():
-                return self.form_valid(form)
-
-            return self.render_to_response(
-                self.get_context_data(form=self.mail_form,
-                                      transfer_data_form=forms.HiddenDataForm(form)))
-        else:
-            return self.form_invalid(form)
-
-    def form_valid(self, form):
-        """
-        If both the regular form and mailing form are valid, save the form and run the mail form.
-        """
-        # Don't use the mail form; don't send out the mail.
-        if not self.has_permission_to_send_mail:
-            return super().form_valid(form)
-
-        if self.alternative_from_address:
-            # Set different from address if given.
-            self.mail_form.set_alternative_sender(
-                self.alternative_from_address[0], self.alternative_from_address[1])
-
-        response = super().form_valid(form)
-        try:
-            self.mail_form.send()
-        except AttributeError:
-            # self.mail_form is None
-            raise AttributeError('Did you check the order in which MailEditorMixin is used?')
-        messages.success(self.request, 'Mail sent')
-        return response
-
-
 class MailUtilsMixin:
     """
     This mixin takes care of inserting the default data into the Utils or Form.
@@ -126,14 +52,7 @@ class MailUtilsMixin:
         self.mail_template = mail_template.render(kwargs)
 
         # Gather Recipients data
-        self.original_recipient = ''
-        if self.object:
-            recipient = self.object
-            for attr in self.mail_data.get('to_address').split('.'):
-                recipient = getattr(recipient, attr)
-                if inspect.ismethod(recipient):
-                    recipient = recipient()
-            self.original_recipient = recipient
+        self.original_recipient = self._validate_single_entry(self.mail_data.get('to_address'))[0]
 
         self.subject = self.mail_data['subject']
 
@@ -148,18 +67,20 @@ class MailUtilsMixin:
                 # Email string
                 return [entry]
             else:
-                bcc_to = self.object
+                mail_to = self.object
                 for attr in entry.split('.'):
                     try:
-                        bcc_to = getattr(bcc_to, attr)
+                        mail_to = getattr(mail_to, attr)
+                        if inspect.ismethod(mail_to):
+                            mail_to = mail_to()
                     except AttributeError:
-                        # Invalid property, don't use bcc
+                        # Invalid property/mail
                         return []
 
-                if not isinstance(bcc_to, list):
-                    return [bcc_to]
+                if not isinstance(mail_to, list):
+                    return [mail_to]
                 else:
-                    return bcc_to
+                    return mail_to
         elif re.match("[^@]+@[^@]+\.[^@]+", entry):
             return [entry]
 
@@ -170,8 +91,9 @@ class MailUtilsMixin:
         """
         # Get recipients list. Try to send through BCC to prevent privacy issues!
         self.bcc_list = []
-        for bcc_entry in self.mail_data.get('bcc_to', '').split(','):
-            self.bcc_list += self._validate_single_entry(bcc_entry)
+        if self.mail_data.get('bcc_to'):
+            for bcc_entry in self.mail_data['bcc_to'].split(','):
+                self.bcc_list += self._validate_single_entry(bcc_entry)
 
     def validate_recipients(self):
         # Check the send list
diff --git a/mails/templates/mail_templates/publication_ready.html b/mails/templates/mail_templates/publication_ready.html
new file mode 100644
index 0000000000000000000000000000000000000000..a3ebebbd5a05a8bd1103ead6d5db36f23ee1d4ca
--- /dev/null
+++ b/mails/templates/mail_templates/publication_ready.html
@@ -0,0 +1,14 @@
+<p>
+    The following Publication is drafted and ready for publication.
+</p>
+<p>
+    <a href="https://scipost.org/{{ publication.get_absolute_url }}">{{ publication.title }}</a><br>
+    by {{ publication.author_list }}
+</p>
+<p>
+    Please review the Publication and proceed with the publication process.
+</p>
+
+<p>
+    <em>This mail is automatically generated from the SciPost platform</em>.
+</p>
diff --git a/mails/templates/mail_templates/publication_ready.json b/mails/templates/mail_templates/publication_ready.json
new file mode 100644
index 0000000000000000000000000000000000000000..fed59bff8e9546c56590e69f485cb82051b81119
--- /dev/null
+++ b/mails/templates/mail_templates/publication_ready.json
@@ -0,0 +1,5 @@
+{
+    "subject": "SciPost: manuscript ready for publication",
+    "to_address": "admin@scipost.org",
+    "context_object": "publication"
+}
diff --git a/mails/utils.py b/mails/utils.py
index ee0d910d627e623f9c64064bb1894ce615aec123..f03191e05c44552b442dff0ad3c999c966cd4bb3 100644
--- a/mails/utils.py
+++ b/mails/utils.py
@@ -1,7 +1,7 @@
-from . import mixins
+from .mixins import MailUtilsMixin
 
 
-class DirectMailUtil(mixins.MailUtilsMixin):
+class DirectMailUtil(MailUtilsMixin):
     """
     Same templates and json files as the form EmailTemplateForm, but this will directly send
     the mails out, without intercepting and showing the mail editor to the user.
diff --git a/mails/views.py b/mails/views.py
index 61e4e3ccc9b79ca7e5221092335e3df649aa853c..51f68766cfd89afbaeaf25d94803fb2a094bda7a 100644
--- a/mails/views.py
+++ b/mails/views.py
@@ -26,3 +26,72 @@ class MailEditingSubView(object):
     def return_render(self):
         self.context['form'] = self.mail_form
         return render(self.request, self.template_name, self.context)
+
+
+class MailEditorMixin:
+    """
+    Use MailEditorMixin in edit CBVs to automatically implement the mail editor as
+    a post-form_valid hook.
+
+    The view must specify the `mail_code` variable.
+    """
+    object = None
+    mail_form = None
+    has_permission_to_send_mail = True
+    alternative_from_address = None  # Tuple: ('from_name', 'from_address')
+
+    def __init__(self, *args, **kwargs):
+        if not self.mail_code:
+            raise AttributeError(self.__class__.__name__ + ' object has no attribute `mail_code`')
+        super().__init__(*args, **kwargs)
+
+    def get_template_names(self):
+        """
+        The mail editor form has its own template.
+        """
+        if self.mail_form and not self.mail_form.is_valid():
+            return ['mails/mail_form.html']
+        return super().get_template_names()
+
+    def post(self, request, *args, **kwargs):
+        """
+        Handle POST requests, but interpect the data if the mail form data isn't valid.
+        """
+        if not self.has_permission_to_send_mail:
+            # Don't use the mail form; don't send out the mail.
+            return super().post(request, *args, **kwargs)
+        self.object = self.get_object()
+        form = self.get_form()
+        if form.is_valid():
+            self.mail_form = EmailTemplateForm(request.POST or None, mail_code=self.mail_code,
+                                               instance=self.object)
+            if self.mail_form.is_valid():
+                return self.form_valid(form)
+
+            return self.render_to_response(
+                self.get_context_data(form=self.mail_form,
+                                      transfer_data_form=HiddenDataForm(form)))
+        else:
+            return self.form_invalid(form)
+
+    def form_valid(self, form):
+        """
+        If both the regular form and mailing form are valid, save the form and run the mail form.
+        """
+        # Don't use the mail form; don't send out the mail.
+        if not self.has_permission_to_send_mail:
+            return super().form_valid(form)
+
+        if self.alternative_from_address:
+            # Set different from address if given.
+            self.mail_form.set_alternative_sender(
+                self.alternative_from_address[0], self.alternative_from_address[1])
+
+        response = super().form_valid(form)
+        try:
+            self.mail_form.send()
+        except AttributeError:
+            # self.mail_form is None
+            raise AttributeError('Did you check the order in which MailEditorMixin is used?')
+        messages.success(self.request, 'Mail sent')
+        return response
diff --git a/production/templates/production/partials/production_stream_card.html b/production/templates/production/partials/production_stream_card.html
index 1637b4bfe06682c252a2b3e733a99d74db397785..3cd23ca4f56947a8e666ef733d1999446c75bb76 100644
--- a/production/templates/production/partials/production_stream_card.html
+++ b/production/templates/production/partials/production_stream_card.html
@@ -99,11 +99,11 @@
               {% endif %}
 
               {% if perms.scipost.can_publish_accepted_submission %}
-                  {% if not stream.submission.publication %}
-                    <li><a href="{% url 'journals:initiate_publication' %}">Initiate the publication process</a></li>
-                  {% endif %}
                     <li><a href="{% url 'production:mark_as_completed' stream_id=stream.id %}">Mark this stream as completed</a></li>
               {% endif %}
+              {% if perms.scipost.can_draft_publication and stream.status == 'accepted' %}
+                    <li><a href="{% url 'journals:update_publication' stream.submission.arxiv_identifier_w_vn_nr %}">Draft Publication</a></li>
+              {% endif %}
           </ul>
     {% endif %}
 {% endblock %}
diff --git a/production/templates/production/partials/production_stream_card_completed.html b/production/templates/production/partials/production_stream_card_completed.html
index 38f1c7fa637e5fafee9117caf225492c8dad3abb..555799b9e3d39eb0c5b124c9664f98351774fe0c 100644
--- a/production/templates/production/partials/production_stream_card_completed.html
+++ b/production/templates/production/partials/production_stream_card_completed.html
@@ -8,6 +8,9 @@
     {% include 'partials/submissions/submission_card_content.html' with submission=stream.submission %}
 </div>
 <div class="card-body">
+      {% if perms.scipost.can_draft_publication and stream.status == 'accepted' %}
+        <p>The proofs have been accepted. Please start <a href="{% url 'journals:update_publication' stream.submission.arxiv_identifier_w_vn_nr %}">drafting the Publication here</a>.</p>
+      {% endif %}
       <h3>Stream details</h3>
       <ul>
           <li>Status: <span class="label label-secondary label-sm">{{ stream.get_status_display }}</span></li>
diff --git a/scipost/feeds.py b/scipost/feeds.py
index 738a1c9567a60b8004ad3fba2f543edec9056c6e..e58758d2fe56a7b4aa24b1e0083541e757515824 100644
--- a/scipost/feeds.py
+++ b/scipost/feeds.py
@@ -1,6 +1,7 @@
 import datetime
 
 from django.contrib.syndication.views import Feed
+from django.http import Http404
 from django.utils.feedgenerator import Atom1Feed
 from django.core.urlresolvers import reverse
 from django.db.models import Q
@@ -34,7 +35,7 @@ class LatestCommentsFeedRSS(Feed):
         elif item.submission:
             return reverse('submissions:submission',
                            kwargs={'arxiv_identifier_w_vn_nr':
-                                   item.submission.arxiv_identifier_w_vn_nr,})
+                                   item.submission.arxiv_identifier_w_vn_nr})
         elif item.thesislink:
             return reverse('theses:thesis',
                            kwargs={'thesislink_id': item.thesislink.id})
@@ -142,27 +143,26 @@ class LatestPublicationsFeedRSS(Feed):
     link = "/journals/"
 
     def get_object(self, request, subject_area=''):
-        if subject_area != '':
-            queryset = Publication.objects.filter(
-                Q(subject_area=subject_area) | Q(secondary_areas__contains=[subject_area])
-            ).order_by('-publication_date')[:10]
-            queryset.subject_area = subject_area
-        else:
-            queryset = Publication.objects.order_by('-publication_date')[:10]
-            queryset.subject_area = None
-        return queryset
+        if subject_area and subject_area not in subject_areas_dict:
+            raise Http404('Invalid subject area')
+        qs = Publication.objects.published()
+        if subject_area:
+            qs = qs.filter(
+                Q(subject_area=subject_area) | Q(secondary_areas__contains=[subject_area]))
+        self.subject_area = subject_area
+        return qs.order_by('-publication_date')[:10]
 
     def title(self, obj):
         title_text = 'SciPost: Latest Publications'
-        if obj.subject_area:
-            title_text += ' in %s' % subject_areas_dict[obj.subject_area]
+        if self.subject_area:
+            title_text += ' in %s' % subject_areas_dict.get(self.subject_area)
         return title_text
 
     def description(self, obj):
         desc = 'SciPost: most recent publications'
         try:
-            if obj.subject_area:
-                desc += ' in %s' % subject_areas_dict[obj.subject_area]
+            if self.subject_area:
+                desc += ' in %s' % subject_areas_dict.get(self.subject_area)
         except KeyError:
             pass
         return desc
diff --git a/scipost/forms.py b/scipost/forms.py
index 9f68b07836b683bf59804cdf3fb3e37647dd66d0..61da0a666a8247b176858890bdb2b2662e05c494 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -42,6 +42,27 @@ REGISTRATION_REFUSAL_CHOICES = (
 reg_ref_dict = dict(REGISTRATION_REFUSAL_CHOICES)
 
 
+class RequestFormMixin:
+    """
+    This mixin lets the Form accept `request` as an argument.
+    """
+    def __init__(self, *args, **kwargs):
+        self.request = kwargs.pop('request')
+        super().__init__(*args, **kwargs)
+
+
+class HttpRefererFormMixin(RequestFormMixin):
+    """
+    This mixin adds a HiddenInput to the form which tracks the previous url, which can
+    be used to redirect to.
+    """
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['http_referer'] = forms.URLField(widget=forms.HiddenInput(), required=False)
+        if self.request:
+            self.fields['http_referer'].initial = self.request.META.get('HTTP_REFERER')
+
+
 class RegistrationForm(forms.Form):
     """
     Use this form to process the registration of new accounts.
diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py
index 9a85a5165eb7b6cec44d41b2af231ea5701e3ef2..aa4d6d5ce525bbbdf5dcef55708ae01cedae68a3 100644
--- a/scipost/management/commands/add_groups_and_permissions.py
+++ b/scipost/management/commands/add_groups_and_permissions.py
@@ -248,6 +248,14 @@ class Command(BaseCommand):
             codename='can_view_all_funding_info',
             name='Can view all Funders info',
             content_type=content_type)
+        can_create_grants, created = Permission.objects.get_or_create(
+            codename='can_create_grants',
+            name='Can create Grant',
+            content_type=content_type)
+        can_draft_publication, created = Permission.objects.get_or_create(
+            codename='can_draft_publication',
+            name='Can draft Publication',
+            content_type=content_type)
 
         # Documentation
         can_view_docs_scipost, created = Permission.objects.get_or_create(
@@ -325,7 +333,9 @@ class Command(BaseCommand):
             can_view_production,
             can_view_timesheets,
             can_publish_accepted_submission,
+            can_draft_publication,
             can_view_all_funding_info,
+            can_create_grants,
             can_attend_VGMs,
             can_manage_reports,
             can_assign_production_supervisor,
@@ -376,6 +386,8 @@ class Command(BaseCommand):
         ProductionSupervisors.permissions.set([
             can_assign_production_officer,
             can_take_decisions_related_to_proofs,
+            # can_draft_publication,
+            # can_create_grants,
             can_view_all_production_streams,
             can_run_proofs_by_authors,
             can_view_docs_scipost,
diff --git a/scipost/management/commands/models.py b/scipost/management/commands/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..0447660e2a189556e389b610e0c5d6bc797218aa
--- /dev/null
+++ b/scipost/management/commands/models.py
@@ -0,0 +1,188 @@
+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/mixins.py b/scipost/mixins.py
index 0cdb24844cca643934797c665b3357a01caabf82..b7d76dc854dfffc2ed458f0cc0b49ace0d2a66fd 100644
--- a/scipost/mixins.py
+++ b/scipost/mixins.py
@@ -1,15 +1,21 @@
+from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
+
 from .paginator import SciPostPaginator
 
 
+class PermissionsMixin(LoginRequiredMixin, PermissionRequiredMixin):
+    pass
+
+
 class PaginationMixin:
     """
     Mixin for generic class-based views (e.g. django.views.generic.ListView)
     """
     paginator_class = SciPostPaginator
 
-    # def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
-    #     # Pass the request object to the paginator to keep the parameters in the
-    #     # url querystring ("?page=2&old_param=...")
-    #     request = self.request
-    #     return self.paginator_class(queryset, per_page, orphans=orphans,
-    #                                 allow_empty_first_page=allow_empty_first_page, request=request)
+
+class RequestViewMixin:
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['request'] = self.request
+        return kwargs
diff --git a/scipost/static/scipost/assets/js/scripts.js b/scipost/static/scipost/assets/js/scripts.js
index 4e96974ec3df7596e5d8cdc9f2fbafa8daedad99..6a451fe5e5b1bed6004c85e7da4fb15275f42a5f 100644
--- a/scipost/static/scipost/assets/js/scripts.js
+++ b/scipost/static/scipost/assets/js/scripts.js
@@ -1,7 +1,7 @@
 import notifications from './notifications.js';
 
 function hide_all_alerts() {
-    $(".alert").fadeOut(300);
+    $(".alert").remove('.no-dismiss').fadeOut(300);
 }
 
 var activate_tooltip = function() {
diff --git a/scipost/views.py b/scipost/views.py
index 3097897d4be736bc225a97543b6cc026cebd921c..bf965b1f1d9323209cfcd6c94e997c279de75d3d 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -511,7 +511,7 @@ def _personal_page_publications(request):
     contributor = request.user.contributor
     context = {
         'contributor': contributor,
-        'own_publications': contributor.publications.order_by('-publication_date')
+        'own_publications': contributor.publications.published().order_by('-publication_date')
     }
     context['nr_publication_authorships_to_claim'] = Publication.objects.filter(
         author_list__contains=request.user.last_name).exclude(
diff --git a/submissions/forms.py b/submissions/forms.py
index 7dc9bc305a3a998a3c345678da02edf64172fe09..850fcbc52a7d743d9eb4747012ab219cf081e4f5 100644
--- a/submissions/forms.py
+++ b/submissions/forms.py
@@ -495,11 +495,8 @@ class VotingEligibilityForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        # Do we need this discipline filter still with the new Pool construction???
-        # -- JdW; Oct 20th, 2017
         self.fields['eligible_fellows'].queryset = Contributor.objects.filter(
                 fellowships__pool=self.instance.submission,
-                discipline=self.instance.submission.discipline,
                 expertises__contains=[self.instance.submission.subject_area]
                 ).order_by('user__last_name')
 
diff --git a/submissions/models.py b/submissions/models.py
index 61d1f8555517f1afd33a60a1a6dd7b0a5c1f589b..f9cdcfd6f731c28c9ef43b839e2b76e04d9e12ba 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -139,11 +139,9 @@ class Submission(models.Model):
             header += ' (current version)'
         else:
             header += ' (deprecated version ' + str(self.arxiv_vn_nr) + ')'
-        try:
+        if hasattr(self, 'publication') and self.publication.is_published:
             header += ' (published as %s (%s))' % (
                 self.publication.doi_string, self.publication.publication_date.strftime('%Y'))
-        except Publication.DoesNotExist:
-            pass
         return header
 
     def touch(self):
diff --git a/submissions/templates/partials/submissions/pool/submission_details.html b/submissions/templates/partials/submissions/pool/submission_details.html
index d8b17662f5918fb642d1aa70ac453b794fcfd5ad..bc0e006926e75d812324d4f9a9f33afdd495c099 100644
--- a/submissions/templates/partials/submissions/pool/submission_details.html
+++ b/submissions/templates/partials/submissions/pool/submission_details.html
@@ -94,7 +94,7 @@
                 {# Accepted submission actions #}
                 {% if submission.status == 'accepted' %}
                     <li><a href="{% url 'submissions:treated_submission_pdf_compile' submission.arxiv_identifier_w_vn_nr %}">Update the Refereeing Package pdf</a></li>
-                    <li>After proofs have been accepted, you can <a href="{% url 'journals:initiate_publication' %}">initiate the publication process</a> (leads to the validation page)</li>
+                    <li><a href="{% url 'journals:update_publication' submission.arxiv_identifier_w_vn_nr %}">Draft Publication</a></li>
                 {% endif %}
 
             </ul>
diff --git a/submissions/templates/partials/submissions/submission_card_content.html b/submissions/templates/partials/submissions/submission_card_content.html
index aa9128c75d3e9ac2f5ff0e87f181f13e5c4bcceb..80ca856c5ff151077fdc79ce337ed439706983da 100644
--- a/submissions/templates/partials/submissions/submission_card_content.html
+++ b/submissions/templates/partials/submissions/submission_card_content.html
@@ -4,7 +4,7 @@
     <p class="text-muted mb-0">
         Version {{ submission.arxiv_vn_nr }} ({% if submission.is_current %}current version{% else %}deprecated version {{ submission.arxiv_vn_nr }}{% endif %})
         <br>
-        {% if submission.publication %}
+        {% if submission.publication and submission.publication.is_published %}
             Published as <a href="{{ submission.publication.get_absolute_url }}">{{ submission.publication.in_issue.in_volume.in_journal.get_abbreviation_citation }} <strong>{{ submission.publication.in_issue.in_volume.number }}</strong>, {{ submission.publication.get_paper_nr }} ({{ submission.publication.publication_date|date:'Y' }})</a>
         {% else %}
             Submitted {{ submission.submission_date }} to {{ submission.get_submitted_to_journal_display }}
diff --git a/submissions/templates/partials/submissions/submission_status.html b/submissions/templates/partials/submissions/submission_status.html
index f4812d1d3a9a41215a6f8d8c637707088e4d4e41..60a391c36da52871975d2ae6efb0c428d73238e2 100644
--- a/submissions/templates/partials/submissions/submission_status.html
+++ b/submissions/templates/partials/submissions/submission_status.html
@@ -1,7 +1,7 @@
 <h4 class="d-inline-block">Current status:</h4>
 <div class="d-inline">
     <span class="label label-secondary">{{submission.get_status_display}}</span>
-    {% if submission.publication %}
+    {% if submission.publication and submission.publication.is_published %}
         as <a href="{{submission.publication.get_absolute_url}}">{{submission.publication.in_issue.in_volume.in_journal.get_abbreviation_citation}} <strong>{{submission.publication.in_issue.in_volume.number}}</strong>, {{submission.publication.get_paper_nr}} ({{submission.publication.publication_date|date:'Y'}})</a>
     {% endif %}
 </div>
diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html
index 0d6dd800738d0cec86087a939f8efd0969f93639..b43d69f9553777e50f70a918299e59e5f6872a41 100644
--- a/submissions/templates/submissions/submission_detail.html
+++ b/submissions/templates/submissions/submission_detail.html
@@ -26,7 +26,7 @@
         <h3 class="mb-3">by {{submission.author_list}}</h3>
 
         <div class="pl-2">
-            {% if submission.publication %}
+            {% if submission.publication and submission.publication.is_published %}
                 <h3>- Published as <a href="{{submission.publication.get_absolute_url}}">{{submission.publication.in_issue.in_volume.in_journal.get_abbreviation_citation}} <strong>{{submission.publication.in_issue.in_volume.number}}</strong>, {{submission.publication.get_paper_nr}} ({{submission.publication.publication_date|date:'Y'}})</a></h3>
             {% endif %}
 
@@ -42,25 +42,6 @@
             {% if not submission.is_current %}
                 <h3><span class="text-danger">- This is not the current version.</span></h3>
             {% endif %}
-
-            {% comment %}
-                {% if submission.other_versions or not submission.is_current %}
-                    <ul class="mt-3 mb-1 list-unstyled pl-4">
-                        {% if not submission.is_current %}
-                            <li><h3 class="text-danger">This is not the current version.</h3></li>
-                        {% endif %}
-
-                        {% if submission.other_versions %}
-                            <li>Other versions of this Submission (with Reports) exist:</li>
-                            <ul class="list-unstyled">
-                                {% for vn in submission.other_versions %}
-                                    <li>{% include 'partials/submissions/submission_version.html' with submission=vn %}</li>
-                                {% endfor %}
-                            </ul>
-                        {% endif %}
-                    </ul>
-                {% endif %}
-            {% endcomment %}
         </div>
 
         <h3 class="mt-2">Submission summary</h3>