diff --git a/commentaries/factories.py b/commentaries/factories.py
index 940e958cb31ba3d2039c0e44c8ce1f58640930df..1f0110a5d0a44caa1ffb3519f8f86a3703a2871d 100644
--- a/commentaries/factories.py
+++ b/commentaries/factories.py
@@ -23,6 +23,9 @@ class CommentaryFactory(factory.django.DjangoModelFactory):
     pub_title = factory.Faker('text')
     pub_DOI = factory.Sequence(lambda n: random_external_doi())
     arxiv_identifier = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
+    author_list = factory.Faker('name')
+    pub_abstract = factory.Faker('text')
+    pub_date = factory.Faker('date')
     arxiv_link = factory.Faker('uri')
     pub_abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
 
@@ -56,3 +59,7 @@ class UnpublishedVettedCommentaryFactory(VettedCommentaryFactory):
 
 class UnvettedCommentaryFactory(CommentaryFactory):
     vetted = False
+
+class UnvettedArxivPreprintCommentaryFactory(CommentaryFactory):
+    vetted = False
+    pub_DOI = None
diff --git a/commentaries/forms.py b/commentaries/forms.py
index 8d95c58d896ace0b3f84b7b3da68b4b2c6ced1d9..0af8bc931ce0914fa1842ed03e30607ed194ebb4 100644
--- a/commentaries/forms.py
+++ b/commentaries/forms.py
@@ -2,107 +2,154 @@ import re
 
 from django import forms
 from django.shortcuts import get_object_or_404
+from django.urls import reverse
+from django.utils.safestring import mark_safe
+from django.template.loader import get_template
+from django.template import Context
 
 from .models import Commentary
+from .constants import COMMENTARY_PUBLISHED, COMMENTARY_PREPRINT
 
+from scipost.services import DOICaller, ArxivCaller
 from scipost.models import Contributor
 
+import strings
+
 
 class DOIToQueryForm(forms.Form):
-    doi = forms.CharField(widget=forms.TextInput(
-        {'label': 'DOI', 'placeholder': 'ex.: 10.21468/00.000.000000'}))
+    VALID_DOI_REGEXP = r'^(?i)10.\d{4,9}/[-._;()/:A-Z0-9]+$'
+    doi = forms.RegexField(regex=VALID_DOI_REGEXP, strip=True, help_text=strings.doi_query_help_text,
+        error_messages={'invalid': strings.doi_query_invalid},
+        widget=forms.TextInput({'label': 'DOI', 'placeholder': strings.doi_query_placeholder}))
+
+    def clean_doi(self):
+        input_doi = self.cleaned_data['doi']
+
+        commentary = Commentary.objects.filter(pub_DOI=input_doi)
+        if commentary.exists():
+            error_message = get_template('commentaries/_doi_query_commentary_exists.html').render(
+                Context({'arxiv_or_DOI_string': commentary[0].arxiv_or_DOI_string})
+            )
+            raise forms.ValidationError(mark_safe(error_message))
+
+        caller = DOICaller(input_doi)
+        if caller.is_valid:
+            self.crossref_data = DOICaller(input_doi).data
+        else:
+            error_message = 'Could not find a resource for that DOI.'
+            raise forms.ValidationError(error_message)
 
+        return input_doi
 
-class IdentifierToQueryForm(forms.Form):
-    identifier = forms.CharField(widget=forms.TextInput(
-        {'label': 'arXiv identifier',
-         'placeholder': 'new style ####.####(#)v# or old-style e.g. cond-mat/#######'}))
+    def request_published_article_form_prefill_data(self):
+        additional_form_data = {'pub_DOI': self.cleaned_data['doi']}
+        return {**self.crossref_data, **additional_form_data}
 
-    def clean(self, *args, **kwargs):
-        cleaned_data = super(IdentifierToQueryForm, self).clean(*args, **kwargs)
-
-        identifierpattern_new = re.compile("^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$")
-        identifierpattern_old = re.compile("^[-.a-z]+/[0-9]{7,}v[0-9]{1,2}$")
-
-        if not (identifierpattern_new.match(cleaned_data['identifier']) or
-                identifierpattern_old.match(cleaned_data['identifier'])):
-                msg = ('The identifier you entered is improperly formatted '
-                       '(did you forget the version number?)')
-                self.add_error('identifier', msg)
-
-        try:
-            commentary = Commentary.objects.get(arxiv_identifier=cleaned_data['identifier'])
-        except (Commentary.DoesNotExist, KeyError):
-            # Commentary either does not exists or form is invalid
-            commentary = None
-
-        if commentary:
-            msg = 'There already exists a Commentary Page on this preprint, see %s' % (
-                    commentary.title_label())
-            self.add_error('identifier', msg)
-        return cleaned_data
 
+class ArxivQueryForm(forms.Form):
+    IDENTIFIER_PATTERN_NEW = r'^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$'
+    IDENTIFIER_PATTERN_OLD = r'^[-.a-z]+/[0-9]{7,}v[0-9]{1,2}$'
+    VALID_ARXIV_IDENTIFIER_REGEX = "(?:{})|(?:{})".format(IDENTIFIER_PATTERN_NEW, IDENTIFIER_PATTERN_OLD)
 
-class RequestCommentaryForm(forms.ModelForm):
-    """Create new valid Commetary by user request"""
-    existing_commentary = None
+    identifier = forms.RegexField(regex=VALID_ARXIV_IDENTIFIER_REGEX, strip=True,
+        help_text=strings.arxiv_query_help_text, error_messages={'invalid': strings.arxiv_query_invalid},
+        widget=forms.TextInput( {'placeholder': strings.arxiv_query_placeholder}))
+
+    def clean_identifier(self):
+        identifier = self.cleaned_data['identifier']
 
+        commentary = Commentary.objects.filter(arxiv_identifier=identifier)
+        if commentary.exists():
+            error_message = get_template('commentaries/_doi_query_commentary_exists.html').render(
+                Context({'arxiv_or_DOI_string': commentary[0].arxiv_or_DOI_string})
+            )
+            raise forms.ValidationError(mark_safe(error_message))
+
+        caller = ArxivCaller(identifier)
+        if caller.is_valid:
+            self.arxiv_data = ArxivCaller(identifier).data
+        else:
+            error_message = 'Could not find a resource for that arXiv identifier.'
+            raise forms.ValidationError(error_message)
+
+        return identifier
+
+    def request_arxiv_preprint_form_prefill_data(self):
+        additional_form_data = {'arxiv_identifier': self.cleaned_data['identifier']}
+        return {**self.arxiv_data, **additional_form_data}
+
+
+class RequestCommentaryForm(forms.ModelForm):
     class Meta:
         model = Commentary
-        fields = ['type', 'discipline', 'domain', 'subject_area',
-                  'pub_title', 'author_list',
-                  'metadata',
-                  'journal', 'volume', 'pages', 'pub_date',
-                  'arxiv_identifier',
-                  'pub_DOI', 'pub_abstract']
+        fields = [
+            'discipline', 'domain', 'subject_area', 'pub_title', 'author_list', 'pub_date', 'pub_abstract'
+        ]
+        placeholders = {
+            'pub_date': 'Format: YYYY-MM-DD'
+        }
+
+    def save(self, *args, **kwargs):
+        self.instance.parse_links_into_urls()
+        return super().save(self, *args, **kwargs)
+
+
+class RequestArxivPreprintForm(RequestCommentaryForm):
+    class Meta(RequestCommentaryForm.Meta):
+        model = Commentary
+        fields = RequestCommentaryForm.Meta.fields + ['arxiv_identifier']
 
     def __init__(self, *args, **kwargs):
-        self.user = kwargs.pop('user', None)
-        super(RequestCommentaryForm, self).__init__(*args, **kwargs)
-        self.fields['metadata'].widget = forms.HiddenInput()
-        self.fields['pub_date'].widget.attrs.update({'placeholder': 'Format: YYYY-MM-DD'})
-        self.fields['arxiv_identifier'].widget.attrs.update(
-            {'placeholder': 'ex.:  1234.56789v1 or cond-mat/1234567v1'})
-        self.fields['pub_DOI'].widget.attrs.update({'placeholder': 'ex.: 10.21468/00.000.000000'})
-        self.fields['pub_abstract'].widget.attrs.update({'cols': 100})
+        super(RequestArxivPreprintForm, self).__init__(*args, **kwargs)
+        # We want arxiv_identifier to be a required field.
+        # Since it can be blank on the model, we have to override this property here.
+        self.fields['arxiv_identifier'].required = True
 
-    def clean(self, *args, **kwargs):
-        """Check if form is valid and contains an unique identifier"""
-        cleaned_data = super(RequestCommentaryForm, self).clean(*args, **kwargs)
-
-        # Either Arxiv-ID or DOI is given
-        if not cleaned_data['arxiv_identifier'] and not cleaned_data['pub_DOI']:
-            msg = ('You must provide either a DOI (for a published paper) '
-                   'or an arXiv identifier (for a preprint).')
-            self.add_error('arxiv_identifier', msg)
-            self.add_error('pub_DOI', msg)
-        elif (cleaned_data['arxiv_identifier'] and
-              (Commentary.objects
-               .filter(arxiv_identifier=cleaned_data['arxiv_identifier']).exists())):
-            msg = 'There already exists a Commentary Page on this preprint, see'
-            self.existing_commentary = get_object_or_404(
-                Commentary,
-                arxiv_identifier=cleaned_data['arxiv_identifier'])
-            self.add_error('arxiv_identifier', msg)
-        elif (cleaned_data['pub_DOI'] and
-              Commentary.objects.filter(pub_DOI=cleaned_data['pub_DOI']).exists()):
-            msg = 'There already exists a Commentary Page on this publication, see'
-            self.existing_commentary = get_object_or_404(
-                Commentary, pub_DOI=cleaned_data['pub_DOI'])
-            self.add_error('pub_DOI', msg)
-
-        # Current user is not known
-        if not self.user or not Contributor.objects.filter(user=self.user).exists():
-            self.add_error(None, 'Sorry, current user is not known to SciPost.')
+    # TODO: add regex here?
+    def clean_arxiv_identifier(self):
+        arxiv_identifier = self.cleaned_data['arxiv_identifier']
+
+        commentary = Commentary.objects.filter(arxiv_identifier=arxiv_identifier)
+        if commentary.exists():
+            error_message = get_template('commentaries/_doi_query_commentary_exists.html').render(
+                Context({'arxiv_or_DOI_string': commentary[0].arxiv_or_DOI_string})
+            )
+            raise forms.ValidationError(mark_safe(error_message))
+
+        return arxiv_identifier
 
     def save(self, *args, **kwargs):
-        """Prefill instance before save"""
-        self.instance.requested_by = Contributor.objects.get(user=self.user)
-        return super(RequestCommentaryForm, self).save(*args, **kwargs)
+        self.instance.type = COMMENTARY_PREPRINT
+        return super().save(*args, **kwargs)
+
 
-    def get_existing_commentary(self):
-        """Get Commentary if found after validation"""
-        return self.existing_commentary
+class RequestPublishedArticleForm(RequestCommentaryForm):
+    class Meta(RequestCommentaryForm.Meta):
+        fields = RequestCommentaryForm.Meta.fields + ['journal', 'volume', 'pages', 'pub_DOI']
+        placeholders = {**RequestCommentaryForm.Meta.placeholders,
+            **{'pub_DOI': 'ex.: 10.21468/00.000.000000'}}
+
+    def __init__(self, *args, **kwargs):
+        super(RequestPublishedArticleForm, self).__init__(*args, **kwargs)
+        # We want pub_DOI to be a required field.
+        # Since it can be blank on the model, we have to override this property here.
+        self.fields['pub_DOI'].required = True
+
+    def clean_pub_DOI(self):
+        input_doi = self.cleaned_data['pub_DOI']
+
+        commentary = Commentary.objects.filter(pub_DOI=input_doi)
+        if commentary.exists():
+            error_message = get_template('commentaries/_doi_query_commentary_exists.html').render(
+                Context({'arxiv_or_DOI_string': commentary[0].arxiv_or_DOI_string})
+            )
+            raise forms.ValidationError(mark_safe(error_message))
+
+        return input_doi
+
+    def save(self, *args, **kwargs):
+        self.instance.type = COMMENTARY_PUBLISHED
+        return super().save(*args, **kwargs)
 
 
 class VetCommentaryForm(forms.Form):
diff --git a/commentaries/templates/commentaries/_doi_query_commentary_exists.html b/commentaries/templates/commentaries/_doi_query_commentary_exists.html
new file mode 100644
index 0000000000000000000000000000000000000000..2cc621178767dbf6e36cca6df4d874c0d9703ed1
--- /dev/null
+++ b/commentaries/templates/commentaries/_doi_query_commentary_exists.html
@@ -0,0 +1 @@
+There already exists a <a href="{% url 'commentaries:commentary' arxiv_or_DOI_string=arxiv_or_DOI_string %}">Commentary Page</a> on this publication.
diff --git a/commentaries/templates/commentaries/commentary_list.html b/commentaries/templates/commentaries/commentary_list.html
index a8c92a52df708b0f81a43c36bd0b77d6817d63ef..4c1b1269164c93330a273c7137de77a89abc017a 100644
--- a/commentaries/templates/commentaries/commentary_list.html
+++ b/commentaries/templates/commentaries/commentary_list.html
@@ -71,7 +71,7 @@
         {% else %}
           <h2>Search results:</h3>
         {% endif %}
-        {% if object_list %}
+        {% if commentary_list %}
             {% if is_paginated %}
               <p>
               {% if page_obj.has_previous %}
@@ -88,7 +88,7 @@
             <div class="col-12">
 
             <ul class="list-group list-group-flush">
-                {% for object in object_list %}
+                {% for object in commentary_list %}
                     <li class="list-group-item">
                         {% include 'commentaries/_commentary_card_content.html' with commentary=object %}
                     </li>
diff --git a/commentaries/templates/commentaries/request_arxiv_preprint.html b/commentaries/templates/commentaries/request_arxiv_preprint.html
new file mode 100644
index 0000000000000000000000000000000000000000..44ef80f88251608066b436348d7ef430db147308
--- /dev/null
+++ b/commentaries/templates/commentaries/request_arxiv_preprint.html
@@ -0,0 +1,35 @@
+{% extends 'scipost/base.html' %}
+{% load bootstrap %}
+{% load scipost_extras %}
+
+{% block pagetitle %}: request Commentary{% endblock pagetitle %}
+
+{% block content %}
+
+<div class="row">
+    <div class="col-12">
+      <h1 class="page-header">Request Activation of a Commentary Page</h1>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-12 col-md-8">
+        <form action="{% url 'commentaries:prefill_using_arxiv_identifier' %}" method="post">
+          {% csrf_token %}
+          {{ query_form|bootstrap }}
+          <input class="btn btn-secondary" type="submit" value="Query arXiv"/>
+        </form>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-12 col-md-8">
+        <form id="requestForm" action="{% url 'commentaries:request_arxiv_preprint' %}" method="post">
+            {% csrf_token %}
+            {{ form|bootstrap }}
+            <input class="btn btn-primary" type="submit" value="Submit"/>
+        </form>
+    </div>
+</div>
+
+{% endblock content%}
diff --git a/commentaries/templates/commentaries/request_commentary.html b/commentaries/templates/commentaries/request_commentary.html
index dec478082ee981283b6a946ff54db43c78383faa..c89649093d40e31bd66aef4d366d9ed2033f5c46 100644
--- a/commentaries/templates/commentaries/request_commentary.html
+++ b/commentaries/templates/commentaries/request_commentary.html
@@ -8,112 +8,21 @@
 
 {% block content %}
 
-  <script>
-  $(document).ready(function(){
-
-    var allToggableRows = $('#requestForm .form-group').slice(1)
-    var type_selector = $('select#id_type')
-
-    var preprint = [5,6,7,8,10]
-    var published = [9]
-
-    function show(indices){
-      allToggableRows.each(function(index){
-        if($.inArray( index, indices) != -1){
-          $(this).hide()
-        }else{
-          $(this).show()
-        }
-
-      })
-    }
-
-    switch (type_selector.val()) {
-     case "":
-       allToggableRows.hide()
-       $("#DOIprefill").hide();
-       $("#arXivprefill").hide();
-       break;
-     case "published":
-       show(published)
-       $("#DOIprefill").show()
-       $("#arXivprefill").hide();
-       break;
-     case "preprint":
-       show(preprint)
-       $("#DOIprefill").hide()
-       $("#arXivprefill").show();
-       break;
-     default:
-       allToggableRows.hide()
-       $("#DOIprefill").hide();
-       $("#arXivprefill").hide();
-    }
-
-    type_selector.on('change', function() {
-      var selection = $(this).val();
-      switch(selection){
-        case "published":
-          show(published)
-          $("#DOIprefill").show()
-          $("#arXivprefill").hide();
-          break;
-        case "preprint":
-          show(preprint)
-          $("#DOIprefill").hide()
-          $("#arXivprefill").show();
-          break;
-        default:
-          $("#DOIprefill").hide()
-          $("#arXivprefill").hide();
-          allToggableRows.hide()
-      }
-    });
-  });
-</script>
-
 <div class="row">
     <div class="col-12">
         <div class="panel">
             <h1>Request Activation of a Commentary Page:</h1>
         </div>
     </div>
-</div>
-<div class="row">
-    <div class="col-md-6 offset-md-3">
-    {% if errormessage %}
-        <h3 style="color: red;">Error: {{ errormessage }}</h3>
-        {% if existing_commentary %}
-            <ul>{% include 'commentaries/_commentary_card_content.html' with object=existing_commentary %}</ul>
-        {% endif %}
-        <br/>
-    {% endif %}
-
-  <div id="DOIprefill">
-    <h3><em>For published papers, you can prefill the form (except for domain, subject area and abstract) using the DOI:</em></h3>
-    <p><em>(give the DOI as 10.[4 to 9 digits]/[string], without prefix, as per the placeholder)</em></p>
-    <form action="{% url 'commentaries:prefill_using_DOI' %}" method="post">
-      {% csrf_token %}
-      {{ doiform|bootstrap }}
-      <input class="btn btn-secondary" type="submit" value="Query DOI"/>
-    </form>
-  </div>
-  <div id="arXivprefill">
-    <h3><em>For preprints, you can prefill the form using the arXiv identifier:</em></h3>
-    <p><em>(give the identifier without prefix, as per the placeholder)</em></p>
-    <form action="{% url 'commentaries:prefill_using_identifier' %}" method="post">
-      {% csrf_token %}
-      {{ identifierform|bootstrap }}
-      <input class="btn btn-secondary" type="submit" value="Query arXiv"/>
-    </form>
-  </div>
-  <br/>
-  <form id="requestForm" action="{% url 'commentaries:request_commentary' %}" method="post">
-    {% csrf_token %}
-      {{ request_commentary_form|bootstrap }}
-    <input class="btn btn-primary" type="submit" value="Submit"/>
-  </form>
-
+    <div class="col-12">
+        <ul>
+            <li>
+                <a href="{% url 'commentaries:request_published_article' %}">Click here to submit a published article</a>
+            </li>
+            <li>
+                <a href="{% url 'commentaries:request_arxiv_preprint' %}">Click here to submit an arXiv preprint</a>
+            </li>
+        </ul>
     </div>
 </div>
 
diff --git a/commentaries/templates/commentaries/request_commentary_old.html b/commentaries/templates/commentaries/request_commentary_old.html
new file mode 100644
index 0000000000000000000000000000000000000000..e5c38d7f6b93bf7a5ce8a41d050776222d0a2d35
--- /dev/null
+++ b/commentaries/templates/commentaries/request_commentary_old.html
@@ -0,0 +1,56 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% load scipost_extras %}
+
+{% block pagetitle %}: request Commentary{% endblock pagetitle %}
+
+{% block content %}
+
+<div class="row">
+    <div class="col-12">
+        <div class="panel">
+            <h1>Request Activation of a Commentary Page:</h1>
+        </div>
+    </div>
+</div>
+<div class="row">
+    <div class="col-md-6 offset-md-3">
+    {% if errormessage %}
+        <h3 style="color: red;">Error: {{ errormessage }}</h3>
+        {% if existing_commentary %}
+            <ul>{% include 'commentaries/_commentary_card_content.html' with object=existing_commentary %}</ul>
+        {% endif %}
+        <br/>
+    {% endif %}
+
+  <div id="DOIprefill">
+    <h3><em>For published papers, you can prefill the form (except for domain, subject area and abstract) using the DOI:</em></h3>
+    <p><em>(give the DOI as 10.[4 to 9 digits]/[string], without prefix, as per the placeholder)</em></p>
+    <form action="{% url 'commentaries:prefill_using_DOI' %}" method="post">
+      {% csrf_token %}
+      {{ doiform|bootstrap }}
+      <input class="btn btn-secondary" type="submit" value="Query DOI"/>
+    </form>
+  </div>
+  <div id="arXivprefill">
+    <h3><em>For preprints, you can prefill the form using the arXiv identifier:</em></h3>
+    <p><em>(give the identifier without prefix, as per the placeholder)</em></p>
+    <form action="{% url 'commentaries:prefill_using_identifier' %}" method="post">
+      {% csrf_token %}
+      {{ identifierform|bootstrap }}
+      <input class="btn btn-secondary" type="submit" value="Query arXiv"/>
+    </form>
+  </div>
+  <br/>
+  <form id="requestForm" action="{% url 'commentaries:request_commentary' %}" method="post">
+    {% csrf_token %}
+      {{ request_commentary_form|bootstrap }}
+    <input class="btn btn-primary" type="submit" value="Submit"/>
+  </form>
+
+    </div>
+</div>
+
+{% endblock content %}
diff --git a/commentaries/templates/commentaries/request_published_article.html b/commentaries/templates/commentaries/request_published_article.html
new file mode 100644
index 0000000000000000000000000000000000000000..b0bf7365a5956c3f77fa1559e83931758fb0fc60
--- /dev/null
+++ b/commentaries/templates/commentaries/request_published_article.html
@@ -0,0 +1,35 @@
+{% extends 'scipost/base.html' %}
+{% load bootstrap %}
+{% load scipost_extras %}
+
+{% block pagetitle %}: request Commentary{% endblock pagetitle %}
+
+{% block content %}
+
+<div class="row">
+    <div class="col-12">
+      <h1 class="page-header">Request Activation of a Commentary Page</h1>
+    </div>
+</div>
+
+<div class="row">
+    <div class='col-12 col-md-8'>
+        <form action="{% url 'commentaries:prefill_using_DOI' %}" method="post">
+          {% csrf_token %}
+          {{ query_form|bootstrap }}
+          <input class="btn btn-secondary" type="submit" value="Query DOI"/>
+        </form>
+    </div>
+</div>
+
+<div class="row">
+    <div class='col-12 col-md-8'>
+        <form id="requestForm" action="{% url 'commentaries:request_published_article' %}" method="post">
+            {% csrf_token %}
+            {{ form|bootstrap }}
+            <input class="btn btn-primary" type="submit" value="Submit"/>
+        </form>
+    </div>
+</div>
+
+{% endblock content%}
diff --git a/commentaries/test_forms.py b/commentaries/test_forms.py
index 8733064ae2267d07111ed7c9fe0c1662cc85fb3f..75d5a21dffce0a9268e62ef106c15dd9664ca97c 100644
--- a/commentaries/test_forms.py
+++ b/commentaries/test_forms.py
@@ -1,14 +1,98 @@
+import re
+
 from django.test import TestCase
 
 from common.helpers import model_form_data
 from scipost.factories import UserFactory
 
-from .factories import VettedCommentaryFactory, UnvettedCommentaryFactory
-from .forms import RequestCommentaryForm, VetCommentaryForm
+from .factories import VettedCommentaryFactory, UnvettedCommentaryFactory, UnvettedArxivPreprintCommentaryFactory
+from .forms import RequestCommentaryForm, RequestPublishedArticleForm, VetCommentaryForm, DOIToQueryForm, \
+    ArxivQueryForm, RequestArxivPreprintForm
 from .models import Commentary
 from common.helpers.test import add_groups_and_permissions
 
 
+class TestArxivQueryForm(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
+
+    def test_new_arxiv_identifier_is_valid(self):
+        new_identifier_data = {'identifier': '1612.07611v1'}
+        form = ArxivQueryForm(new_identifier_data)
+        self.assertTrue(form.is_valid())
+
+    def test_old_arxiv_identifier_is_valid(self):
+        old_identifier_data = {'identifier': 'cond-mat/0612480v1'}
+        form = ArxivQueryForm(old_identifier_data)
+        self.assertTrue(form.is_valid())
+
+    def test_invalid_arxiv_identifier(self):
+        invalid_data = {'identifier': 'i am not valid'}
+        form = ArxivQueryForm(invalid_data)
+        self.assertFalse(form.is_valid())
+
+    def test_new_arxiv_identifier_without_version_number_is_invalid(self):
+        data = {'identifier': '1612.07611'}
+        form = ArxivQueryForm(data)
+        self.assertFalse(form.is_valid())
+
+    def test_old_arxiv_identifier_without_version_number_is_invalid(self):
+        data = {'identifier': 'cond-mat/0612480'}
+        form = ArxivQueryForm(data)
+        self.assertFalse(form.is_valid())
+
+    def test_arxiv_identifier_that_already_has_commentary_page_is_invalid(self):
+        unvetted_commentary = UnvettedCommentaryFactory()
+        invalid_data = {'identifier': unvetted_commentary.arxiv_identifier}
+        form = ArxivQueryForm(invalid_data)
+        self.assertFalse(form.is_valid())
+        error_message = form.errors['identifier'][0]
+        self.assertRegexpMatches(error_message, re.compile('already exist'))
+
+    def test_valid_but_non_existent_identifier_is_invalid(self):
+        invalid_data = {'identifier': '1613.07611v1'}
+        form = ArxivQueryForm(invalid_data)
+        self.assertFalse(form.is_valid())
+
+
+class TestDOIToQueryForm(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
+
+    def test_invalid_doi_is_invalid(self):
+        invalid_data = {'doi': 'blablab'}
+        form = DOIToQueryForm(invalid_data)
+        self.assertFalse(form.is_valid())
+
+    def test_doi_that_already_has_commentary_page_is_invalid(self):
+        unvetted_commentary = UnvettedCommentaryFactory()
+        invalid_data = {'doi': unvetted_commentary.pub_DOI}
+        form = DOIToQueryForm(invalid_data)
+        self.assertFalse(form.is_valid())
+        error_message = form.errors['doi'][0]
+        self.assertRegexpMatches(error_message, re.compile('already exist'))
+
+    def test_physrev_doi_is_valid(self):
+        physrev_doi = "10.21468/SciPostPhys.2.2.010"
+        form = DOIToQueryForm({'doi': physrev_doi})
+        self.assertTrue(form.is_valid())
+
+    def test_scipost_doi_is_valid(self):
+        scipost_doi = "10.21468/SciPostPhys.2.2.010"
+        form = DOIToQueryForm({'doi': scipost_doi})
+        self.assertTrue(form.is_valid())
+
+    def test_old_doi_is_valid(self):
+        old_doi = "10.1088/0022-3719/7/6/005"
+        form = DOIToQueryForm({'doi': old_doi})
+        self.assertTrue(form.is_valid())
+
+    def test_valid_but_nonexistent_doi_is_invalid(self):
+        doi = "10.21468/NonExistentJournal.2.2.010"
+        form = DOIToQueryForm({'doi': doi})
+        self.assertEqual(form.is_valid(), False)
+
+
 class TestVetCommentaryForm(TestCase):
     def setUp(self):
         add_groups_and_permissions()
@@ -20,6 +104,7 @@ class TestVetCommentaryForm(TestCase):
             'email_response_field': 'Lorem Ipsum'
         }
 
+
     def test_valid_accepted_form(self):
         """Test valid form data and return Commentary"""
         form = VetCommentaryForm(self.form_data, commentary_id=self.commentary.id, user=self.user)
@@ -70,71 +155,53 @@ class TestVetCommentaryForm(TestCase):
         self.assertRaises(ValueError, form.process_commentary)
 
 
-class TestRequestCommentaryForm(TestCase):
+class TestRequestPublishedArticleForm(TestCase):
     def setUp(self):
         add_groups_and_permissions()
-        factory_instance = VettedCommentaryFactory.build()
+        factory_instance = UnvettedCommentaryFactory.build()
         self.user = UserFactory()
-        self.valid_form_data = model_form_data(factory_instance, RequestCommentaryForm)
-
-    def empty_and_return_form_data(self, key):
-        """Empty specific valid_form_data field and return"""
-        self.valid_form_data[key] = None
-        return self.valid_form_data
-
-    def test_valid_data_is_valid_for_arxiv(self):
-        """Test valid form for Arxiv identifier"""
-        form_data = self.valid_form_data
-        form_data['pub_DOI'] = ''
-        form = RequestCommentaryForm(form_data, user=self.user)
-        self.assertTrue(form.is_valid())
-
-        # Check if the user is properly saved to the new Commentary as `requested_by`
-        commentary = form.save()
-        self.assertTrue(commentary.requested_by)
+        self.valid_form_data = model_form_data(factory_instance, RequestPublishedArticleForm)
 
-    def test_valid_data_is_valid_for_DOI(self):
+    def test_valid_data_is_valid(self):
         """Test valid form for DOI"""
-        form_data = self.valid_form_data
-        form_data['arxiv_identifier'] = ''
-        form = RequestCommentaryForm(form_data, user=self.user)
+        form = RequestPublishedArticleForm(self.valid_form_data)
         self.assertTrue(form.is_valid())
 
-    def test_form_has_no_identifiers(self):
-        """Test invalid form has no DOI nor Arxiv ID"""
-        form_data = self.valid_form_data
-        form_data['pub_DOI'] = ''
-        form_data['arxiv_identifier'] = ''
-        form = RequestCommentaryForm(form_data, user=self.user)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('arxiv_identifier' in form.errors)
-        self.assertTrue('pub_DOI' in form.errors)
-
-    def test_form_with_duplicate_DOI(self):
-        """Test form response with already existing DOI"""
-        # Create a factory instance containing Arxiv ID and DOI
-        VettedCommentaryFactory.create()
-
-        # Test duplicate DOI entry
-        form_data = self.empty_and_return_form_data('arxiv_identifier')
-        form = RequestCommentaryForm(form_data, user=self.user)
-        self.assertTrue('pub_DOI' in form.errors)
-        self.assertFalse(form.is_valid())
+    def test_doi_that_already_has_commentary_page_is_invalid(self):
+        unvetted_commentary = UnvettedCommentaryFactory()
+        invalid_data = {**self.valid_form_data, **{'pub_DOI': unvetted_commentary.pub_DOI}}
+        form = RequestPublishedArticleForm(invalid_data)
+        self.assertEqual(form.is_valid(), False)
+        error_message = form.errors['pub_DOI'][0]
+        self.assertRegexpMatches(error_message, re.compile('already exist'))
 
-        # Check is existing commentary is valid
-        existing_commentary = form.get_existing_commentary()
-        self.assertEqual(existing_commentary.pub_DOI, form_data['pub_DOI'])
+    def test_commentary_without_pub_DOI_is_invalid(self):
+        invalid_data = {**self.valid_form_data, **{'pub_DOI': ''}}
+        form = RequestPublishedArticleForm(invalid_data)
+        self.assertEqual(form.is_valid(), False)
 
-    def test_form_with_duplicate_arxiv_id(self):
-        """Test form response with already existing Arxiv ID"""
-        VettedCommentaryFactory.create()
 
-        # Test duplicate Arxiv entry
-        form_data = self.empty_and_return_form_data('pub_DOI')
-        form = RequestCommentaryForm(form_data, user=self.user)
-        self.assertTrue('arxiv_identifier' in form.errors)
-        self.assertFalse(form.is_valid())
+class TestRequestArxivPreprintForm(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
+        factory_instance = UnvettedArxivPreprintCommentaryFactory.build()
+        self.user = UserFactory()
+        self.valid_form_data = model_form_data(factory_instance, RequestPublishedArticleForm)
+        self.valid_form_data['arxiv_identifier'] = factory_instance.arxiv_identifier
+
+    def test_valid_data_is_valid(self):
+        form = RequestArxivPreprintForm(self.valid_form_data)
+        self.assertTrue(form.is_valid())
 
-        # Check is existing commentary is valid
-        existing_commentary = form.get_existing_commentary()
-        self.assertEqual(existing_commentary.arxiv_identifier, form_data['arxiv_identifier'])
+    def test_identifier_that_already_has_commentary_page_is_invalid(self):
+        commentary = UnvettedArxivPreprintCommentaryFactory()
+        invalid_data = {**self.valid_form_data, **{'arxiv_identifier': commentary.arxiv_identifier}}
+        form = RequestArxivPreprintForm(invalid_data)
+        self.assertEqual(form.is_valid(), False)
+        error_message = form.errors['arxiv_identifier'][0]
+        self.assertRegexpMatches(error_message, re.compile('already exist'))
+
+    def test_commentary_without_arxiv_identifier_is_invalid(self):
+        invalid_data = {**self.valid_form_data, **{'arxiv_identifier': ''}}
+        form = RequestArxivPreprintForm(invalid_data)
+        self.assertEqual(form.is_valid(), False)
diff --git a/commentaries/test_models.py b/commentaries/test_models.py
index e4defabc8afbec48d89189ef98a5e9697d1657d0..5fb96f3ef5ac04ae31bf79dd6b63da465137836e 100644
--- a/commentaries/test_models.py
+++ b/commentaries/test_models.py
@@ -1 +1,11 @@
-# from django.test import TestCase
+from django.test import TestCase
+
+from common.helpers.test import add_groups_and_permissions
+
+from scipost.factories import ContributorFactory
+from .factories import UnvettedCommentaryFactory
+
+
+class TestCommentary(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
diff --git a/commentaries/test_views.py b/commentaries/test_views.py
index 17ea43b4c1d38d48a40b41d0e9edddd86930d61a..dbb0bb6c61464681abcf3e41acd0da83b533d17d 100644
--- a/commentaries/test_views.py
+++ b/commentaries/test_views.py
@@ -2,38 +2,74 @@ from django.core.urlresolvers import reverse
 from django.contrib.auth.models import Group
 from django.test import TestCase, Client, RequestFactory
 
+from scipost.models import Contributor
 from scipost.factories import ContributorFactory, UserFactory
 
-from .factories import UnvettedCommentaryFactory, VettedCommentaryFactory, UnpublishedVettedCommentaryFactory
-from .forms import CommentarySearchForm
+from .factories import UnvettedCommentaryFactory, VettedCommentaryFactory, UnpublishedVettedCommentaryFactory, \
+    UnvettedArxivPreprintCommentaryFactory
+from .forms import CommentarySearchForm, RequestPublishedArticleForm
 from .models import Commentary
-from .views import RequestCommentary
+from .views import RequestPublishedArticle, prefill_using_DOI, RequestArxivPreprint
 from common.helpers.test import add_groups_and_permissions
+from common.helpers import model_form_data
 
 
-class RequestCommentaryTest(TestCase):
-    """Test cases for `request_commentary` view method"""
+class PrefillUsingDOITest(TestCase):
     def setUp(self):
         add_groups_and_permissions()
-        self.view_url = reverse('commentaries:request_commentary')
-        self.login_url = reverse('scipost:login')
-        self.redirected_login_url = '%s?next=%s' % (self.login_url, self.view_url)
-
-    def test_redirects_if_not_logged_in(self):
-        request = self.client.get(self.view_url)
-        self.assertRedirects(request, self.redirected_login_url)
+        self.target = reverse('commentaries:prefill_using_DOI')
+        self.physrev_doi = '10.1103/PhysRevB.92.214427'
 
-    def test_valid_response_if_logged_in(self):
-        """Test different GET requests on view"""
-        request = RequestFactory().get(self.view_url)
+    def test_submit_valid_physrev_doi(self):
+        post_data = {'doi': self.physrev_doi}
+        request = RequestFactory().post(self.target, post_data)
         request.user = UserFactory()
-        response = RequestCommentary.as_view()(request)
+
+        response = prefill_using_DOI(request)
         self.assertEqual(response.status_code, 200)
 
-    def test_post_invalid_forms(self):
-        """Test different kind of invalid RequestCommentaryForm submits"""
-        raise NotImplementedError
 
+class RequestPublishedArticleTest(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
+        self.target = reverse('commentaries:request_published_article')
+        self.commentary_instance = UnvettedCommentaryFactory.build(requested_by=ContributorFactory())
+        self.valid_form_data = model_form_data(self.commentary_instance, RequestPublishedArticleForm)
+
+    def test_commentary_gets_created_with_correct_type_and_link(self):
+        request = RequestFactory().post(self.target, self.valid_form_data)
+        request.user = UserFactory()
+
+        self.assertEqual(Commentary.objects.count(), 0)
+        response = RequestPublishedArticle.as_view()(request)
+        self.assertEqual(Commentary.objects.count(), 1)
+        commentary = Commentary.objects.first()
+        self.assertEqual(commentary.pub_DOI, self.valid_form_data['pub_DOI'])
+        self.assertEqual(commentary.type, 'published')
+        self.assertEqual(commentary.arxiv_or_DOI_string, commentary.pub_DOI)
+
+
+class RequestArxivPreprintTest(TestCase):
+    def setUp(self):
+        add_groups_and_permissions()
+        self.target = reverse('commentaries:request_arxiv_preprint')
+        self.commentary_instance = UnvettedArxivPreprintCommentaryFactory.build(requested_by=ContributorFactory())
+        self.valid_form_data = model_form_data(self.commentary_instance, RequestPublishedArticleForm)
+        # The form field is called 'identifier', while the model field is called 'arxiv_identifier',
+        # so model_form_data doesn't include it.
+        self.valid_form_data['arxiv_identifier'] = self.commentary_instance.arxiv_identifier
+
+    def test_commentary_gets_created_with_correct_type_and_link(self):
+        request = RequestFactory().post(self.target, self.valid_form_data)
+        request.user = UserFactory()
+
+        self.assertEqual(Commentary.objects.count(), 0)
+        response = RequestArxivPreprint.as_view()(request)
+        self.assertEqual(Commentary.objects.count(), 1)
+        commentary = Commentary.objects.first()
+        self.assertEqual(commentary.arxiv_identifier, self.valid_form_data['arxiv_identifier'])
+        self.assertEqual(commentary.type, 'preprint')
+        self.assertEqual(commentary.arxiv_or_DOI_string, "arXiv:" + self.commentary_instance.arxiv_identifier)
 
 class VetCommentaryRequestsTest(TestCase):
     """Test cases for `vet_commentary_requests` view method"""
@@ -77,12 +113,13 @@ class VetCommentaryRequestsTest(TestCase):
         self.assertEquals(response.context['commentary_to_vet'], None)
 
         # Only vetted Commentaries exist!
-        VettedCommentaryFactory()
+        # ContributorFactory.create_batch(5)
+        VettedCommentaryFactory(requested_by=ContributorFactory(), vetted_by=ContributorFactory())
         response = self.client.get(self.view_url)
         self.assertEquals(response.context['commentary_to_vet'], None)
 
         # Unvetted Commentaries do exist!
-        UnvettedCommentaryFactory()
+        UnvettedCommentaryFactory(requested_by=ContributorFactory())
         response = self.client.get(self.view_url)
         self.assertTrue(type(response.context['commentary_to_vet']) is Commentary)
 
@@ -92,7 +129,7 @@ class BrowseCommentariesTest(TestCase):
 
     def setUp(self):
         add_groups_and_permissions()
-        VettedCommentaryFactory(discipline='physics')
+        VettedCommentaryFactory(discipline='physics', requested_by=ContributorFactory())
         self.view_url = reverse('commentaries:browse', kwargs={
             'discipline': 'physics',
             'nrweeksback': '1'
@@ -104,7 +141,7 @@ class BrowseCommentariesTest(TestCase):
         self.assertEquals(response.status_code, 200)
 
         # The created vetted Commentary is found!
-        self.assertTrue(response.context['commentary_browse_list'].count() >= 1)
+        self.assertTrue(response.context['commentary_list'].count() >= 1)
         # The search form is passed trough the view...
         self.assertTrue(type(response.context['form']) is CommentarySearchForm)
 
@@ -113,7 +150,8 @@ class CommentaryDetailTest(TestCase):
     def setUp(self):
         add_groups_and_permissions()
         self.client = Client()
-        self.commentary = UnpublishedVettedCommentaryFactory()
+        self.commentary = UnpublishedVettedCommentaryFactory(
+            requested_by=ContributorFactory(), vetted_by=ContributorFactory())
         self.target = reverse(
             'commentaries:commentary',
             kwargs={'arxiv_or_DOI_string': self.commentary.arxiv_or_DOI_string}
diff --git a/commentaries/urls.py b/commentaries/urls.py
index a1b5ba5ec119853ae337ab52c7213cb4feadea8f..03be372fb2a4229fc4ca73cb866c92ba2a56ee1e 100644
--- a/commentaries/urls.py
+++ b/commentaries/urls.py
@@ -21,10 +21,13 @@ urlpatterns = [
     url(r'^(?P<arxiv_or_DOI_string>arXiv:[a-z-]+/[0-9]{7,}(v[0-9]+)?)/$',
         views.commentary_detail, name='commentary'),
 
-    url(r'^request_commentary$', views.RequestCommentary.as_view(), name='request_commentary'),
+    url(r'^request_commentary$', views.request_commentary, name='request_commentary'),
+    url(r'^request_commentary/published_article$', views.RequestPublishedArticle.as_view(),
+        name='request_published_article'),
+    url(r'^request_commentary/arxiv_preprint$', views.RequestArxivPreprint.as_view(), name='request_arxiv_preprint'),
     url(r'^prefill_using_DOI$', views.prefill_using_DOI, name='prefill_using_DOI'),
-    url(r'^prefill_using_identifier$', views.PrefillUsingIdentifierView.as_view(),
-        name='prefill_using_identifier'),
+    url(r'^prefill_using_arxiv_identifier$', views.prefill_using_arxiv_identifier,
+        name='prefill_using_arxiv_identifier'),
     url(r'^vet_commentary_requests$', views.vet_commentary_requests,
         name='vet_commentary_requests'),
     url(r'^vet_commentary_request_ack/(?P<commentary_id>[0-9]+)$',
diff --git a/commentaries/views.py b/commentaries/views.py
index d8554ec92e92690cf12c7c6e27802183d3f2154a..4c50ca0eb0bfb787ec9daa001339b78c54ee5c05 100644
--- a/commentaries/views.py
+++ b/commentaries/views.py
@@ -13,10 +13,11 @@ from django.template.loader import render_to_string
 from django.views.generic.edit import CreateView, FormView
 from django.views.generic.list import ListView
 from django.utils.decorators import method_decorator
+from django.http import Http404
 
 from .models import Commentary
-from .forms import RequestCommentaryForm, DOIToQueryForm, IdentifierToQueryForm
-from .forms import VetCommentaryForm, CommentarySearchForm
+from .forms import DOIToQueryForm, ArxivQueryForm, VetCommentaryForm, \
+    CommentarySearchForm, RequestPublishedArticleForm, RequestArxivPreprintForm
 
 from comments.models import Comment
 from comments.forms import CommentForm
@@ -26,180 +27,79 @@ from scipost.services import ArxivCaller
 import strings
 
 
-################
-# Commentaries
-################
-
-class RequestCommentaryMixin(object):
-    def get_context_data(self, **kwargs):
-        '''Pass the DOI and identifier forms to the context.'''
-        if 'request_commentary_form' not in kwargs:
-            # Only intercept if not prefilled
-            kwargs['request_commentary_form'] = RequestCommentaryForm()
-        context = super(RequestCommentaryMixin, self).get_context_data(**kwargs)
-
-        context['existing_commentary'] = None
-        context['doiform'] = DOIToQueryForm()
-        context['identifierform'] = IdentifierToQueryForm()
-        return context
-
+@permission_required('scipost.can_request_commentary_pages', raise_exception=True)
+def request_commentary(request):
+    return render(request, 'commentaries/request_commentary.html')
 
 @method_decorator(permission_required(
     'scipost.can_request_commentary_pages', raise_exception=True), name='dispatch')
-class RequestCommentary(LoginRequiredMixin, RequestCommentaryMixin, CreateView):
-    form_class = RequestCommentaryForm
-    template_name = 'commentaries/request_commentary.html'
+class RequestCommentary(CreateView):
     success_url = reverse_lazy('scipost:personal_page')
 
-    def get_form_kwargs(self, *args, **kwargs):
-        '''User should be included in the arguments to have a valid form.'''
-        form_kwargs = super(RequestCommentary, self).get_form_kwargs(*args, **kwargs)
-        form_kwargs['user'] = self.request.user
-        return form_kwargs
-
     def form_valid(self, form):
-        form.instance.parse_links_into_urls()
-        messages.success(self.request, strings.acknowledge_request_commentary)
-        return super(RequestCommentary, self).form_valid(form)
+        messages.success(self.request, strings.acknowledge_request_commentary, fail_silently=True)
+        return super().form_valid(form)
+
+
+class RequestPublishedArticle(RequestCommentary):
+    form_class = RequestPublishedArticleForm
+    template_name = 'commentaries/request_published_article.html'
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['query_form'] = DOIToQueryForm()
+        return context
+
+
+class RequestArxivPreprint(RequestCommentary):
+    form_class = RequestArxivPreprintForm
+    template_name = 'commentaries/request_arxiv_preprint.html'
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['query_form'] = ArxivQueryForm()
+        return context
 
 
 @permission_required('scipost.can_request_commentary_pages', raise_exception=True)
 def prefill_using_DOI(request):
-    """ Probes CrossRef API with the DOI, to pre-fill the form. """
     if request.method == "POST":
-        doiform = DOIToQueryForm(request.POST)
-        if doiform.is_valid():
-            # Check if given doi is of expected form:
-            doipattern = re.compile("^10.[0-9]{4,9}/[-._;()/:a-zA-Z0-9]+")
-            errormessage = ''
-            existing_commentary = None
-            if not doipattern.match(doiform.cleaned_data['doi']):
-                errormessage = 'The DOI you entered is improperly formatted.'
-            elif Commentary.objects.filter(pub_DOI=doiform.cleaned_data['doi']).exists():
-                errormessage = 'There already exists a Commentary Page on this publication, see'
-                existing_commentary = get_object_or_404(Commentary,
-                                                        pub_DOI=doiform.cleaned_data['doi'])
-            if errormessage:
-                form = RequestCommentaryForm()
-                identifierform = IdentifierToQueryForm()
-                context = {
-                    'request_commentary_form': form,
-                    'doiform': doiform,
-                    'identifierform': identifierform,
-                    'errormessage': errormessage,
-                    'existing_commentary': existing_commentary}
-                return render(request, 'commentaries/request_commentary.html', context)
-
-            # Otherwise we query Crossref for the information:
-            try:
-                queryurl = 'http://api.crossref.org/works/%s' % doiform.cleaned_data['doi']
-                doiquery = requests.get(queryurl)
-                doiqueryJSON = doiquery.json()
-                metadata = doiqueryJSON
-                pub_title = doiqueryJSON['message']['title'][0]
-                authorlist = (doiqueryJSON['message']['author'][0]['given'] + ' ' +
-                              doiqueryJSON['message']['author'][0]['family'])
-                for author in doiqueryJSON['message']['author'][1:]:
-                    authorlist += ', ' + author['given'] + ' ' + author['family']
-                journal = doiqueryJSON['message']['container-title'][0]
-
-                try:
-                    volume = doiqueryJSON['message']['volume']
-                except KeyError:
-                    volume = ''
-
-                pages = ''
-                try:
-                    pages = doiqueryJSON['message']['article-number']  # for Phys Rev
-                except KeyError:
-                    pass
-                try:
-                    pages = doiqueryJSON['message']['page']
-                except KeyError:
-                    pass
-
-                pub_date = ''
-                try:
-                    pub_date = (str(doiqueryJSON['message']['issued']['date-parts'][0][0]) + '-' +
-                                str(doiqueryJSON['message']['issued']['date-parts'][0][1]))
-                    try:
-                        pub_date += '-' + str(
-                            doiqueryJSON['message']['issued']['date-parts'][0][2])
-                    except (IndexError, KeyError):
-                        pass
-                except (IndexError, KeyError):
-                    pass
-                pub_DOI = doiform.cleaned_data['doi']
-                form = RequestCommentaryForm(
-                    initial={'type': 'published', 'metadata': metadata,
-                             'pub_title': pub_title, 'author_list': authorlist,
-                             'journal': journal, 'volume': volume,
-                             'pages': pages, 'pub_date': pub_date,
-                             'pub_DOI': pub_DOI})
-                identifierform = IdentifierToQueryForm()
-                context = {
-                    'request_commentary_form': form,
-                    'doiform': doiform,
-                    'identifierform': identifierform
-                }
-                context['title'] = pub_title
-                return render(request, 'commentaries/request_commentary.html', context)
-            except (IndexError, KeyError, ValueError):
-                pass
+        query_form = DOIToQueryForm(request.POST)
+        # The form checks if doi is valid and commentary doesn't already exist.
+        if query_form.is_valid():
+            prefill_data = query_form.request_published_article_form_prefill_data()
+            form = RequestPublishedArticleForm(initial=prefill_data)
+            messages.success(request, strings.acknowledge_doi_query, fail_silently=True)
         else:
-            pass
-    return redirect(reverse('commentaries:request_commentary'))
+            form = RequestPublishedArticleForm()
 
+        context = {
+            'form': form,
+            'query_form': query_form,
+        }
+        return render(request, 'commentaries/request_published_article.html', context)
+    else:
+        raise Http404
 
-@method_decorator(permission_required(
-    'scipost.can_request_commentary_pages', raise_exception=True), name='dispatch')
-class PrefillUsingIdentifierView(RequestCommentaryMixin, FormView):
-    form_class = IdentifierToQueryForm
-    template_name = 'commentaries/request_commentary.html'
-
-    def form_invalid(self, identifierform):
-        for field, errors in identifierform.errors.items():
-            for error in errors:
-                messages.warning(self.request, error)
-        return render(self.request, 'commentaries/request_commentary.html',
-                      self.get_context_data(**{}))
-
-    def form_valid(self, identifierform):
-        '''Prefill using the ArxivCaller if the Identifier is valid'''
-        caller = ArxivCaller(Commentary, identifierform.cleaned_data['identifier'])
-        caller.process()
-
-        if caller.is_valid():
-            # Prefill the form
-            metadata = caller.metadata
-            pub_title = metadata['entries'][0]['title']
-            authorlist = metadata['entries'][0]['authors'][0]['name']
-            for author in metadata['entries'][0]['authors'][1:]:
-                authorlist += ', ' + author['name']
-            arxiv_link = metadata['entries'][0]['id']
-            abstract = metadata['entries'][0]['summary']
-
-            initialdata = {
-                'type': 'preprint',
-                'metadata': metadata,
-                'pub_title': pub_title,
-                'author_list': authorlist,
-                'arxiv_identifier': identifierform.cleaned_data['identifier'],
-                'arxiv_link': arxiv_link,
-                'pub_abstract': abstract
-            }
-            context = {
-                'title': pub_title,
-                'request_commentary_form': RequestCommentaryForm(initial=initialdata)
-            }
-            messages.success(self.request, 'Arxiv completed')
-            return render(self.request, 'commentaries/request_commentary.html',
-                          self.get_context_data(**context))
+
+@permission_required('scipost.can_request_commentary_pages', raise_exception=True)
+def prefill_using_arxiv_identifier(request):
+    if request.method == "POST":
+        query_form = ArxivQueryForm(request.POST)
+        if query_form.is_valid():
+            prefill_data = query_form.request_arxiv_preprint_form_prefill_data()
+            form = RequestArxivPreprintForm(initial=prefill_data)
+            messages.success(request, strings.acknowledge_arxiv_query, fail_silently=True)
         else:
-            msg = caller.get_error_message()
-            messages.error(self.request, msg)
-            return render(self.request, 'commentaries/request_commentary.html',
-                          self.get_context_data(**{}))
+            form = RequestArxivPreprintForm()
+
+        context = {
+            'form': form,
+            'query_form': query_form,
+        }
+        return render(request, 'commentaries/request_arxiv_preprint.html', context)
+    else:
+        raise Http404
 
 
 @permission_required('scipost.can_vet_commentary_requests', raise_exception=True)
@@ -211,7 +111,6 @@ def vet_commentary_requests(request):
     context = {'contributor': contributor, 'commentary_to_vet': commentary_to_vet, 'form': form}
     return render(request, 'commentaries/vet_commentary_requests.html', context)
 
-
 @permission_required('scipost.can_vet_commentary_requests', raise_exception=True)
 def vet_commentary_request_ack(request, commentary_id):
     if request.method == 'POST':
@@ -270,6 +169,7 @@ class CommentaryListView(ListView):
     model = Commentary
     form = CommentarySearchForm
     paginate_by = 10
+    context_object_name = 'commentary_list'
 
     def get_queryset(self):
         '''Perform search form here already to get the right pagination numbers.'''
diff --git a/scipost/services.py b/scipost/services.py
index 395e61e7ea5859a147a2170994c84e43ab6c16c3..17ca3811f608486fe1a580e636a07591343d1e28 100644
--- a/scipost/services.py
+++ b/scipost/services.py
@@ -2,6 +2,8 @@
 import feedparser
 import requests
 import re
+import datetime
+import dateutil.parser
 
 from django.template import Template, Context
 from .behaviors import ArxivCallable
@@ -9,235 +11,99 @@ from .behaviors import ArxivCallable
 from strings import arxiv_caller_errormessages
 
 
-class BaseCaller(object):
-    '''Base mixin for caller (Arxiv, DOI).
-    The basic workflow is to initiate the caller, call process() to make the actual call
-    followed by is_valid() to validate the response of the call.
-
-    An actual caller should inherit at least the following:
-    > Properties:
-      - query_base_url
-      - caller_regex
-    > Methods:
-      - process()
-    '''
-    # State of the caller
-    _is_processed = False
-    caller_regex = None
-    errorcode = None
-    errorvariables = {}
-    errormessages = {}
-    identifier_without_vn_nr = ''
-    identifier_with_vn_nr = ''
-    metadata = {}
-    query_base_url = None
-    target_object = None
-    version_nr = None
-
-    def __init__(self, target_object, identifier, *args, **kwargs):
-        '''Initiate the Caller by assigning which object is used
-        the Arxiv identifier to be called.
-
-        After initiating call in specific order:
-        - process()
-        - is_valid()
-
-        Keyword arguments:
-        target_object -- The model calling the Caller (object)
-        identifier    -- The identifier used for the call (string)
-        '''
-        try:
-            self._check_valid_caller()
-        except NotImplementedError as e:
-            print('Caller invalid: %s' % e)
-            return
-
-        # Set given arguments
-        self.target_object = target_object
-        self.identifier = identifier
-        self._precheck_if_valid()
-        super(BaseCaller, self).__init__(*args, **kwargs)
-
-    def _check_identifier(self):
-        '''Split the given identifier in an article identifier and version number.'''
-        if not self.caller_regex:
-            raise NotImplementedError('No regex is set for this caller')
-
-        if re.match(self.caller_regex, self.identifier):
-            self.identifier_without_vn_nr = self.identifier.rpartition('v')[0]
-            self.identifier_with_vn_nr = self.identifier
-            self.version_nr = int(self.identifier.rpartition('v')[2])
+class DOICaller:
+    def __init__(self, doi_string):
+        self.doi_string = doi_string
+        self._call_crosslink()
+        if self.is_valid:
+            self._format_data()
+
+    def _call_crosslink(self):
+        url = 'http://api.crossref.org/works/%s' % self.doi_string
+        request = requests.get(url)
+        if request.ok:
+            self.is_valid = True
+            self._crossref_data = request.json()['message']
+        else:
+            self.is_valid = False
+
+    def _format_data(self):
+        data = self._crossref_data
+        pub_title = data['title'][0]
+        author_list = ['{} {}'.format(author['given'], author['family']) for author in data['author']]
+        # author_list is given as a comma separated list of names on the relevant models (Commentary, Submission)
+        author_list = ", ".join(author_list)
+        journal = data['container-title'][0]
+        volume = data.get('volume', '')
+        pages = self._get_pages(data)
+        pub_date = self._get_pub_date(data)
+
+        self.data = {
+            'pub_title': pub_title,
+            'author_list': author_list,
+            'journal': journal,
+            'volume': volume,
+            'pages': pages,
+            'pub_date': pub_date,
+        }
+
+    def _get_pages(self, data):
+        # For Physical Review
+        pages = data.get('article-number', '')
+        # For other journals?
+        pages = data.get('page', '')
+        return pages
+
+    def _get_pub_date(self, data):
+        date_parts = data.get('issued', {}).get('date-parts', {})
+        if date_parts:
+            date_parts = date_parts[0]
+            year = date_parts[0]
+            month = date_parts[1]
+            day = date_parts[2]
+            pub_date = datetime.date(year, month, day).isoformat()
         else:
-            self.errorvariables['identifier_with_vn_nr'] = self.identifier
-            raise ValueError('bad_identifier')
-
-    def _check_valid_caller(self):
-        '''Check if all methods and variables are set appropriately'''
-        if not self.query_base_url:
-            raise NotImplementedError('No `query_base_url` set')
-
-    def _precheck_duplicate(self):
-        '''Check if identifier for object already exists.'''
-        if self.target_object.same_version_exists(self.identifier_with_vn_nr):
-            raise ValueError('preprint_already_submitted')
-
-    def _precheck_previous_submissions_are_valid(self):
-        '''Check if previous submitted versions have the appropriate status.'''
-        try:
-            self.previous_submissions = self.target_object.different_versions(
-                                        self.identifier_without_vn_nr)
-        except AttributeError:
-            # Commentaries do not have previous version numbers?
-            pass
-
-        if self.previous_submissions:
-            for submission in [self.previous_submissions[0]]:
-                if submission.status == 'revision_requested':
-                    self.resubmission = True
-                elif submission.status in ['rejected', 'rejected_visible']:
-                    raise ValueError('previous_submissions_rejected')
-                else:
-                    raise ValueError('previous_submission_undergoing_refereeing')
-
-    def _precheck_if_valid(self):
-        '''The master method to perform all checks required during initializing Caller.'''
-        try:
-            self._check_identifier()
-            self._precheck_duplicate()
-            self._precheck_previous_submissions_are_valid()
-            # More tests should be called right here...!
-        except ValueError as e:
-            self.errorcode = str(e)
-
-        return not self.errorcode
-
-    def _post_process_checks(self):
-        '''Perform checks after process, to check received data.
-
-        Return:
-        None -- Raise ValueError with error code for an invalid check.
-        '''
-        pass
-
-    def is_valid(self):
-        '''Check if the process() call received valid data.
-
-        If `is_valid()` is overwritten in the actual caller, be
-        sure to call this parent method in the last line!
-
-        Return:
-        boolean -- True for valid data received. False otherwise.
-        '''
-        if self.errorcode:
-            return False
-        if not self._is_processed:
-            raise ValueError('`process()` should be called first!')
-        return True
-
-    def process(self):
-        '''Call to receive data.
-
-        The `process()` should be implemented in the actual
-        caller be! Be sure to call this parent method in the last line!
-        '''
-        try:
-            self._post_process_checks()
-        except ValueError as e:
-            self.errorcode = str(e)
-
-        self._is_processed = True
-
-    def get_error_message(self, errormessages={}):
-        '''Return the errormessages for a specific error code, with the possibility to
-        overrule the default errormessage dictionary for the specific Caller.
-        '''
-        try:
-            t = Template(errormessages[self.errorcode])
-        except KeyError:
-            t = Template(self.errormessages[self.errorcode])
-        return t.render(Context(self.errorvariables))
-
-
-class DOICaller(BaseCaller):
-    """Perform a DOI lookup for a given identifier."""
-    pass
-
-
-class ArxivCaller(BaseCaller):
-    """ Performs an Arxiv article lookup for given identifier """
-
-    # State of the caller
-    resubmission = False
-    previous_submissions = []
-    errormessages = arxiv_caller_errormessages
-    errorvariables = {
-        'arxiv_journal_ref': '',
-        'arxiv_doi': '',
-        'identifier_with_vn_nr': ''
-    }
-    arxiv_journal_ref = ''
-    arxiv_doi = ''
-    metadata = {}
+            pub_date = ''
+
+        return pub_date
+
+
+class ArxivCaller:
     query_base_url = 'http://export.arxiv.org/api/query?id_list=%s'
-    caller_regex = "^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$"
-
-    def __init__(self, target_object, identifier):
-        if not issubclass(target_object, ArxivCallable):
-            raise TypeError('Given target_object is not an ArxivCallable object.')
-        super(ArxivCaller, self).__init__(target_object, identifier)
-
-    def process(self):
-        '''Do the actual call the receive Arxiv information.'''
-        if self.errorcode:
-            return
-
-        queryurl = (self.query_base_url % self.identifier_with_vn_nr)
-
-        try:
-            self._response = requests.get(queryurl, timeout=4.0)
-        except requests.ReadTimeout:
-            self.errorcode = 'arxiv_timeout'
-            return
-        except requests.ConnectionError:
-            self.errorcode = 'arxiv_timeout'
-            return
-
-        self._response_content = feedparser.parse(self._response.content)
-
-        super(ArxivCaller, self).process()
-
-    def _post_process_checks(self):
-        # Check if response has at least one entry
-        if self._response.status_code == 400 or 'entries' not in self._response_content:
-            raise ValueError('arxiv_bad_request')
-
-        # Check if preprint exists
-        if not self.preprint_exists():
-            raise ValueError('preprint_does_not_exist')
-
-        # Check via journal ref if already published
-        self.arxiv_journal_ref = self.published_journal_ref()
-        self.errorvariables['arxiv_journal_ref'] = self.arxiv_journal_ref
-        if self.arxiv_journal_ref:
-            raise ValueError('paper_published_journal_ref')
-
-        # Check via DOI if already published
-        self.arxiv_doi = self.published_doi()
-        self.errorvariables['arxiv_doi'] = self.arxiv_doi
-        if self.arxiv_doi:
-            raise ValueError('paper_published_doi')
-
-        self.metadata = self._response_content
-
-    def preprint_exists(self):
-        return 'title' in self._response_content['entries'][0]
-
-    def published_journal_ref(self):
-        if 'arxiv_journal_ref' in self._response_content['entries'][0]:
-            return self._response_content['entries'][0]['arxiv_journal_ref']
-        return None
-
-    def published_doi(self):
-        if 'arxiv_doi' in self._response_content['entries'][0]:
-            return self._response_content['entries'][0]['arxiv_doi']
-        return None
+
+    def __init__(self, identifier):
+        self.identifier = identifier
+        self._call_arxiv()
+        if self.is_valid:
+            self._format_data()
+
+    def _call_arxiv(self):
+        url = self.query_base_url % self.identifier
+        request = requests.get(url)
+        arxiv_data = feedparser.parse(request.content)['entries'][0]
+        if self._search_result_present(arxiv_data):
+            self.is_valid = True
+            self._arxiv_data = arxiv_data
+        else:
+            self.is_valid = False
+
+    def _format_data(self):
+        data = self._arxiv_data
+        pub_title = data['title']
+        author_list = [author['name'] for author in data['authors']]
+        # author_list is given as a comma separated list of names on the relevant models (Commentary, Submission)
+        author_list = ", ".join(author_list)
+        arxiv_link = data['id']
+        abstract = data['summary']
+        pub_date = dateutil.parser.parse(data['published']).date()
+
+        self.data = {
+            'pub_title': pub_title,
+            'author_list': author_list,
+            'arxiv_link': arxiv_link,
+            'pub_abstract': abstract,
+            'pub_date': pub_date,
+        }
+
+    def _search_result_present(self, data):
+        return 'title' in data
diff --git a/scipost/test_services.py b/scipost/test_services.py
index e724b7856b0d5ec1a280239d440a66c1b758885b..727a20213e84256e79fc3d85f875df77f9be56a7 100644
--- a/scipost/test_services.py
+++ b/scipost/test_services.py
@@ -1,46 +1,62 @@
+import datetime
+
 from django.test import TestCase
 
-from .services import ArxivCaller
+from .services import ArxivCaller, DOICaller
 
 from submissions.models import Submission
 
 
 class ArxivCallerTest(TestCase):
-
-    def test_correct_lookup(self):
-        caller = ArxivCaller(Submission, '1611.09574v1')
-
-        caller.process()
-
-        self.assertEqual(caller.is_valid(), True)
-        self.assertIn('entries', caller.metadata)
-
-    def test_errorcode_for_non_existing_paper(self):
-        caller = ArxivCaller(Submission, '2611.09574v1')
-
-        caller.process()
-        self.assertEqual(caller.is_valid(), False)
-        self.assertEqual(caller.errorcode, 'preprint_does_not_exist')
-
-    def test_errorcode_for_bad_request(self):
-        caller = ArxivCaller(Submission, '161109574v1')
-
-        caller.process()
-        self.assertEqual(caller.is_valid(), False)
-        self.assertEqual(caller.errorcode, 'arxiv_bad_request')
-
-    def test_errorcode_for_already_published_journal_ref(self):
-        caller = ArxivCaller(Submission, '1412.0006v1')
-
-        caller.process()
-        self.assertEqual(caller.is_valid(), False)
-        self.assertEqual(caller.errorcode, 'paper_published_journal_ref')
-        self.assertNotEqual(caller.arxiv_journal_ref, '')
-
-    def test_errorcode_no_version_nr(self):
-        # Should be already caught in form validation
-        caller = ArxivCaller(Submission, '1412.0006')
-
-        caller.process()
-        self.assertEqual(caller.is_valid(), False)
-        self.assertEqual(caller.errorcode, 'bad_identifier')
+    def test_identifier_new_style(self):
+        caller = ArxivCaller('1612.07611v1')
+        self.assertTrue(caller.is_valid)
+        correct_data = {
+            'pub_abstract': 'The Berezinskii-Kosterlitz-Thouless (BKT) transitions of the six-state clock\nmodel on the square lattice are investigated by means of the corner-transfer\nmatrix renormalization group method. The classical analogue of the entanglement\nentropy $S( L, T )$ is calculated for $L$ by $L$ square system up to $L = 129$,\nas a function of temperature $T$. The entropy has a peak at $T = T^{*}_{~}( L\n)$, where the temperature depends on both $L$ and boundary conditions. Applying\nthe finite-size scaling to $T^{*}_{~}( L )$ and assuming the presence of BKT\ntransitions, the transition temperature is estimated to be $T_1^{~} = 0.70$ and\n$T_2^{~} = 0.88$. The obtained results agree with previous analyses. It should\nbe noted that no thermodynamic function is used in this study.', 'author_list': ['Roman Krčmár', 'Andrej Gendiar', 'Tomotoshi Nishino'], 'arxiv_link': 'http://arxiv.org/abs/1612.07611v1', 'pub_title': 'Phase transition of the six-state clock model observed from the\n  entanglement entropy', 'pub_date': datetime.date(2016, 12, 22)
+        }
+        self.assertEqual(caller.data, correct_data)
+
+    def test_identifier_old_style(self):
+        caller = ArxivCaller('cond-mat/0612480')
+        self.assertTrue(caller.is_valid)
+        correct_data = {
+            'author_list': ['Kouji Ueda', 'Chenglong Jin', 'Naokazu Shibata', 'Yasuhiro Hieida', 'Tomotoshi Nishino'], 'pub_date': datetime.date(2006, 12, 19), 'arxiv_link': 'http://arxiv.org/abs/cond-mat/0612480v2', 'pub_abstract': 'A kind of least action principle is introduced for the discrete time\nevolution of one-dimensional quantum lattice models. Based on this principle,\nwe obtain an optimal condition for the matrix product states on succeeding time\nslices generated by the real-time density matrix renormalization group method.\nThis optimization can also be applied to classical simulations of quantum\ncircuits. We discuss the time reversal symmetry in the fully optimized MPS.', 'pub_title': 'Least Action Principle for the Real-Time Density Matrix Renormalization\n  Group'
+        }
+        self.assertEqual(caller.data, correct_data)
+
+    def valid_but_nonexistent_identifier(self):
+        caller = ArxivCaller('1613.07611v1')
+        self.assertEqual(caller.is_valid, False)
+
+
+class DOICallerTest(TestCase):
+    def test_works_for_physrev_doi(self):
+        caller = DOICaller('10.1103/PhysRevB.92.214427')
+        correct_data = {
+            'pub_date': '2015-12-18',
+            'journal': 'Physical Review B',
+            'pages': '',
+            'author_list': [
+                'R. Vlijm', 'M. Ganahl', 'D. Fioretto', 'M. Brockmann', 'M. Haque', 'H. G. Evertz', 'J.-S. Caux'],
+            'volume': '92',
+            'pub_title': 'Quasi-soliton scattering in quantum spin chains'
+        }
+        self.assertTrue(caller.is_valid)
+        self.assertEqual(caller.data, correct_data)
+
+    def test_works_for_scipost_doi(self):
+        caller = DOICaller('10.21468/SciPostPhys.2.2.012')
+        correct_data = {
+            'pub_date': '2017-04-04',
+            'journal': 'SciPost Physics',
+            'pub_title': 'One-particle density matrix of trapped one-dimensional impenetrable bosons from conformal invariance',
+            'pages': '',
+            'volume': '2',
+            'author_list': ['Yannis Brun', 'Jerome Dubail']
+        }
+        self.assertTrue(caller.is_valid)
+        self.assertEqual(caller.data, correct_data)
+
+    def test_valid_but_non_existent_doi(self):
+        caller = DOICaller('10.21468/NonExistentJournal.2.2.012')
+        self.assertEqual(caller.is_valid, False)
diff --git a/strings/__init__.py b/strings/__init__.py
index acb0df6ceafab9bb8a85bae4049a9ccb7fdc8cec..51115395b4c01978c62b95c036d3f9a5e7445cfc 100644
--- a/strings/__init__.py
+++ b/strings/__init__.py
@@ -10,6 +10,28 @@ acknowledge_request_commentary = (
 acknowledge_submit_comment = (
     "Thank you for contributing a Comment. It will soon be vetted by an Editor."
 )
+acknowledge_doi_query = "Crossref query by DOI successful."
+acknowledge_arxiv_query = "Arxiv query successful."
+
+doi_query_placeholder = 'ex.: 10.21468/00.000.000000'
+doi_query_help_text = (
+    'For published papers, you can prefill the form (except for domain, subject area and abstract) using the DOI. '
+    "(Give the DOI as 10.[4 to 9 digits]/[string], without prefix, as per the placeholder)."
+)
+doi_query_invalid = (
+    "DOI does not match the expression supplied by CrossRef. Either it is very old or you made a mistake. "
+    "If you are sure it is correct, please enter the metadata manually. Sorry for the inconvenience."
+)
+
+arxiv_query_placeholder = (
+    "new style: YYMM.####(#)v#(#) or "
+    "old style: cond-mat/YYMM###v#(#)"
+)
+arxiv_query_help_text =  (
+    "For preprints, you can prefill the form using the arXiv identifier. "
+    "Give the identifier without prefix and do not forget the version number, as per the placeholder."
+)
+arxiv_query_invalid = 'ArXiv identifier is invalid. Did you include a version number?'
 
 # Arxiv response is not valid
 arxiv_caller_errormessages = {