diff --git a/.gitignore b/.gitignore
index 4d43f387e85330c273f8f85b730634253f3f74e7..0eeb06ba57fd256d5cd7eab1ba083b5be4f0cc83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ __pycache__
 *webpack-stats.json
 
 .python-version
+*secrets.json
 
 /uploads*
 /media*
diff --git a/README.md b/README.md
index fc63276cd5eea6b4756d47419c148c7f7f4a47ad..4964856aaf986dfcde25c82e5ea719ff786481bc 100644
--- a/README.md
+++ b/README.md
@@ -35,40 +35,32 @@ Now install dependencies:
 (scipostenv) $ npm install
 ```
 
-### Host-specific settings
-In this project, host-specific settings are defined in the `scipost-host-settings.json` file in the directory *above* the project root. The structure is as follows:
+### Settings
+In this project, most settings are tracked using Git. Some settings however, are still secret are and should stay that way. These settings may be saved into the  `secrets.json` file in the root of the project. The minimum required structure is as follows, please mind the non-empty, but still invalid `SECRET_KEY`:
 
 ```json
 {
-    "SECRET_KEY": "<change_me>",
-    "CERTFILE": "none",
-    "DEBUG": true,
-    "ADMINS": "",
-    "MANAGERS": "",
-    "ALLOWED_HOSTS": ["localhost", "127.0.0.1"],
-    "SESSION_COOKIE_SECURE": false,
-    "CSRF_COOKIE_SECURE": false,
-    "DB_NAME": "scipost",
-    "DB_USER": "scipost",
-    "DB_PWD": "",
-    "MEDIA_ROOT": "<media_dir>",
-    "MEDIA_URL": "/media/",
-    "STATIC_URL": "/static/",
-    "STATIC_ROOT": "<static_dir>",
-    "EMAIL_BACKEND": "django.core.mail.backends.filebased.EmailBackend",
-    "EMAIL_FILE_PATH": "<email_dir>",
-    "EMAIL_HOST": "",
-    "EMAIL_HOST_USER": "",
-    "EMAIL_HOST_PASSWORD": "",
-    "DEFAULT_FROM_EMAIL": "",
-    "SERVER_EMAIL": "",
-    "JOURNALS_DIR": "<journals_dir>",
-    "CROSSREF_LOGIN_ID": "",
-    "CROSSREF_LOGIN_PASSWORD": "",
-    "HAYSTACK_PATH": "<haystack_dir>"
+  "SECRET_KEY": "<key>",
+  "DB_NAME": "",
+  "DB_USER": "",
+  "DB_PWD": ""
 }
 ```
 
+The settings files itself are saved into `SciPost_v1/settings/local_<name>.py`. Be sure to *wildcard import* the `base.py` file in the top of your settings file. To run the server, one can do it two ways. Either:
+
+```shell
+(scipostenv) $ ./manage.py runserver --settings=SciPost_v1.settings.local_<name>
+```
+
+...or for convenience export the same settingsfile path to the `DJANGO_SETTINGS_MODULE` variable, so that one can run the django commands are default:
+
+```shell
+(scipostenv) $ export DJANGO_SETTINGS_MODULE="SciPost_v1.settings.local_<name>"
+```
+
+One can of course also add the variable to the `~/.bash_profile` for convenience.
+
 ### Check, double check
 To make sure everything is setup and configured well, run:
 
diff --git a/SciPost_v1/settings/__init__.py b/SciPost_v1/settings/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/SciPost_v1/settings.py b/SciPost_v1/settings/base.py
similarity index 71%
rename from SciPost_v1/settings.py
rename to SciPost_v1/settings/base.py
index e4845d32cb516f74121ee8912819b656093d23ca..ec132d3dd4453c2710cf181d9fbda302e98005c3 100644
--- a/SciPost_v1/settings.py
+++ b/SciPost_v1/settings/base.py
@@ -1,3 +1,4 @@
+
 """
 Django settings for SciPost_v1 project.
 
@@ -16,33 +17,37 @@ import json
 
 from django.utils.translation import ugettext_lazy as _
 
+from django.core.exceptions import ImproperlyConfigured
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# Build paths inside the project
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+PROJECT_ROOT = os.path.dirname(BASE_DIR)
 
-host_settings_path = os.path.join(os.path.dirname(BASE_DIR), "scipost-host-settings.json")
-host_settings = json.load(open(host_settings_path))
+# JSON-based secrets
+secrets = json.load(open(os.path.join(BASE_DIR, "secrets.json")))
 
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
 
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = host_settings["SECRET_KEY"]
+def get_secret(setting, secrets=secrets):
+    """Get the secret variable or return explicit exception."""
+    try:
+        return secrets[setting]
+    except KeyError:
+        error_msg = "Set the {0} environment variable".format(setting)
+        raise ImproperlyConfigured(error_msg)
 
-CERTFILE = host_settings["CERTFILE"]
 
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = host_settings["DEBUG"]
+SECRET_KEY = get_secret("SECRET_KEY")
+CERTFILE = ''
 
-# Emails for server error reporting
-ADMINS = host_settings["ADMINS"]
-MANAGERS = host_settings["MANAGERS"]
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
 
-ALLOWED_HOSTS = host_settings["ALLOWED_HOSTS"]
+ALLOWED_HOSTS = ['localhost', '127.0.0.1']
 
 # Secure proxy SSL header and secure cookies
 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
-SESSION_COOKIE_SECURE = host_settings["SESSION_COOKIE_SECURE"]
-CSRF_COOKIE_SECURE = host_settings["CSRF_COOKIE_SECURE"]
+SESSION_COOKIE_SECURE = False
+CSRF_COOKIE_SECURE = False
 
 AUTHENTICATION_BACKENDS = (
     'django.contrib.auth.backends.ModelBackend',
@@ -90,7 +95,7 @@ INSTALLED_APPS = (
 HAYSTACK_CONNECTIONS = {
     'default': {
         'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
-        'PATH': host_settings['HAYSTACK_PATH'],
+        'PATH': 'local_files/haystack/',
     },
 }
 
@@ -105,7 +110,13 @@ CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
 
 SHELL_PLUS_POST_IMPORTS = (
     ('theses.factories', ('ThesisLinkFactory')),
-    ('comments.factories', 'CommentFactory'),
+    ('comments.factories', ('CommentFactory')),
+    ('submissions.factories', ('SubmissionFactory', 'EICassignedSubmissionFactory')),
+    ('commentaries.factories',
+        ('EmptyCommentaryFactory',
+         'VettedCommentaryFactory',
+         'UnvettedCommentaryFactory',
+         'UnpublishedVettedCommentaryFactory',)),
 )
 
 MATHJAX_ENABLED = True
@@ -158,9 +169,9 @@ WSGI_APPLICATION = 'SciPost_v1.wsgi.application'
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.postgresql_psycopg2',
-        'NAME': host_settings["DB_NAME"],
-        'USER': host_settings["DB_USER"],
-        'PASSWORD': host_settings["DB_PWD"],
+        'NAME': get_secret("DB_NAME"),
+        'USER': get_secret("DB_USER"),
+        'PASSWORD': get_secret("DB_PWD"),
         'HOST': '127.0.0.1',
         'PORT': '5432',
     }
@@ -188,15 +199,13 @@ DATETIME_FORMAT = 'Y-m-d H:i'
 USE_TZ = True
 
 # MEDIA
-MEDIA_ROOT = host_settings['MEDIA_ROOT']
-MEDIA_URL = host_settings['MEDIA_URL']
+MEDIA_URL = '/media/'
+MEDIA_ROOT = 'local_files/media/'
 MAX_UPLOAD_SIZE = "1310720"  # Default max attachment size in Bytes
 
 # Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.8/howto/static-files/
-
-STATIC_URL = host_settings["STATIC_URL"]
-STATIC_ROOT = host_settings["STATIC_ROOT"]
+STATIC_URL = '/static/'
+STATIC_ROOT = 'local_files/static/'
 STATICFILES_DIRS = (
     os.path.join(BASE_DIR, 'static_bundles'),
 )
@@ -205,7 +214,7 @@ STATICFILES_DIRS = (
 WEBPACK_LOADER = {
     'DEFAULT': {
         'CACHE': not DEBUG,
-        'BUNDLE_DIR_NAME': host_settings["STATIC_ROOT"] + 'bundles/',
+        'BUNDLE_DIR_NAME': 'local_files/static/bundles/',
         'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
         'POLL_INTERVAL': 0.1,
         'TIMEOUT': None,
@@ -214,17 +223,16 @@ WEBPACK_LOADER = {
 }
 
 # Email
-EMAIL_BACKEND = host_settings["EMAIL_BACKEND"]
-EMAIL_FILE_PATH = host_settings["EMAIL_FILE_PATH"]
-EMAIL_HOST = host_settings["EMAIL_HOST"]
-EMAIL_HOST_USER = host_settings["EMAIL_HOST_USER"]
-EMAIL_HOST_PASSWORD = host_settings["EMAIL_HOST_PASSWORD"]
-DEFAULT_FROM_EMAIL = host_settings["DEFAULT_FROM_EMAIL"]
-SERVER_EMAIL = host_settings["SERVER_EMAIL"]
-
+EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
+EMAIL_FILE_PATH = 'local_files/email/'
 
 # Own settings
-JOURNALS_DIR = host_settings["JOURNALS_DIR"]
+JOURNALS_DIR = 'journals'
+
+CROSSREF_LOGIN_ID = ''
+CROSSREF_LOGIN_PASSWORD = ''
 
-CROSSREF_LOGIN_ID = host_settings["CROSSREF_LOGIN_ID"]
-CROSSREF_LOGIN_PASSWORD = host_settings["CROSSREF_LOGIN_PASSWORD"]
+# Google reCaptcha
+RECAPTCHA_PUBLIC_KEY = ''
+RECAPTCHA_PRIVATE_KEY = ''
+NOCAPTCHA = True
diff --git a/SciPost_v1/settings/local_jorran.py b/SciPost_v1/settings/local_jorran.py
new file mode 100644
index 0000000000000000000000000000000000000000..1eef068e824bdaaa5e51f1b16a15cf80cee2b724
--- /dev/null
+++ b/SciPost_v1/settings/local_jorran.py
@@ -0,0 +1,10 @@
+from .base import *
+
+# THE MAIN THING HERE
+DEBUG = True
+
+# Static and media
+STATIC_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/'
+MEDIA_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/media/'
+WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] =\
+    '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/bundles/'
diff --git a/SciPost_v1/settings/production.py b/SciPost_v1/settings/production.py
new file mode 100644
index 0000000000000000000000000000000000000000..eca480636f82799460b375205fe268077e53affd
--- /dev/null
+++ b/SciPost_v1/settings/production.py
@@ -0,0 +1,41 @@
+from .base import *
+
+# THE MAIN THING HERE
+DEBUG = False
+CERTFILE = get_secret("CERTFILE")
+ALLOWED_HOSTS = ['www.scipost.org', 'scipost.org']
+
+# Static and media
+STATIC_URL = 'https://scipost.org/static/'
+STATIC_ROOT = '/home/jscaux/webapps/scipost_static/'
+MEDIA_URL = 'https://scipost.org/media/'
+MEDIA_ROOT = '/home/jscaux/webapps/scipost_media/'
+
+# Recaptcha
+RECAPTCHA_PUBLIC_KEY = get_secret("GOOGLE_RECAPTCHA_PUBLIC_KEY")
+RECAPTCHA_PRIVATE_KEY = get_secret("GOOGLE_RECAPTCHA_PRIVATE_KEY")
+
+WEBPACK_LOADER['DEFAULT']['CACHE'] = True
+WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] = '/home/jscaux/webapps/scipost_static/bundles/'
+
+# Error reporting
+ADMINS = MANAGERS = (('J.S.Caux', 'J.S.Caux@uva.nl'), )
+
+# Cookies
+SESSION_COOKIE_SECURE = True
+CSRF_COOKIE_SECURE = True
+
+# Email
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+
+EMAIL_HOST = get_secret("EMAIL_HOST")
+EMAIL_HOST_USER = get_secret("EMAIL_HOST_USER")
+EMAIL_HOST_PASSWORD = get_secret("EMAIL_HOST_PASSWORD")
+
+DEFAULT_FROM_EMAIL = 'admin@scipost.org'
+SERVER_EMAIL = get_secret("SERVER_EMAIL")
+
+# Other
+CROSSREF_LOGIN_ID = get_secret("CROSSREF_LOGIN_ID")
+CROSSREF_LOGIN_PASSWORD = get_secret("CROSSREF_LOGIN_PASSWORD")
+HAYSTACK_CONNECTIONS['default']['PATH'] = '/home/jscaux/webapps/scipost/SciPost_v1/whoosh_index'
diff --git a/SciPost_v1/settings/staging_release.py b/SciPost_v1/settings/staging_release.py
new file mode 100644
index 0000000000000000000000000000000000000000..74bfb6ed80ee434084ee0fd36f6bc9f1813af50c
--- /dev/null
+++ b/SciPost_v1/settings/staging_release.py
@@ -0,0 +1,28 @@
+from .base import *
+
+# This file is meant for the server used for the release branches
+#
+
+# THE MAIN THING HERE
+DEBUG = False
+ALLOWED_HOSTS = ['jdewit.webfactional.com']
+
+# Recaptcha
+RECAPTCHA_PUBLIC_KEY = get_secret("GOOGLE_RECAPTCHA_PUBLIC_KEY")
+RECAPTCHA_PRIVATE_KEY = get_secret("GOOGLE_RECAPTCHA_PRIVATE_KEY")
+
+# Static and media
+STATIC_URL = '/static/'
+STATIC_ROOT = '/home/jdewit/webapps/scipost_static/'
+MEDIA_URL = '/media/'
+MEDIA_ROOT = '/home/jdewit/webapps/scipost_media/'
+
+WEBPACK_LOADER['DEFAULT']['CACHE'] = True
+WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] = '/home/jdewit/webapps/scipost_static/bundles/'
+
+# Error reporting
+ADMINS = MANAGERS = (('J. de Wit', 'jorrandewit@outlook.com'), )
+
+# Cookies
+SESSION_COOKIE_SECURE = True
+CSRF_COOKIE_SECURE = True
diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py
index 8bb1cbd455cf3f734dcc129e7f8c02ba31d6cf1f..9b85d34e8e3967fdfc82a646d9d8c76443eb3c9a 100644
--- a/SciPost_v1/urls.py
+++ b/SciPost_v1/urls.py
@@ -31,6 +31,5 @@ urlpatterns = [
     url(r'^submission/', include('submissions.urls', namespace="submissions")),
     url(r'^theses/', include('theses.urls', namespace="theses")),
     url(r'^thesis/', include('theses.urls', namespace="theses")),
-    url(r'^captcha/', include('captcha.urls')),
+    # url(r'^captcha/', include('captcha.urls')),
 ]
-
diff --git a/commentaries/factories.py b/commentaries/factories.py
index 18e72a7b59715315a717f78316e9011ea1f34477..31f1354fa2c7c811daccdbfa7bd9a27cdf55850f 100644
--- a/commentaries/factories.py
+++ b/commentaries/factories.py
@@ -1,11 +1,11 @@
 import factory
 
-from .models import Commentary, COMMENTARY_TYPES
-
 from scipost.constants import DISCIPLINE_PHYSICS, SCIPOST_SUBJECT_AREAS
 from scipost.factories import ContributorFactory
 from journals.models import SCIPOST_JOURNALS_DOMAINS
+from common.helpers import random_arxiv_identifier_with_version_number
 
+from .models import Commentary, COMMENTARY_TYPES
 
 class CommentaryFactory(factory.django.DjangoModelFactory):
     class Meta:
@@ -18,12 +18,16 @@ class CommentaryFactory(factory.django.DjangoModelFactory):
     discipline = DISCIPLINE_PHYSICS
     domain = SCIPOST_JOURNALS_DOMAINS[0][0]
     subject_area = SCIPOST_SUBJECT_AREAS[0][1][0][0]
-    pub_title = factory.Sequence(lambda n: "Commentary %d" % n)
+    pub_title = factory.Faker('bs')
     pub_DOI = '10.1103/PhysRevB.92.214427'
-    arxiv_identifier = '1610.06911v1'
+    arxiv_identifier = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
     author_list = factory.Faker('name')
     pub_abstract = factory.Faker('text')
 
+    @factory.post_generation
+    def create_urls(self, create, extracted, **kwargs):
+        self.parse_links_into_urls(commit=create)
+
 
 class EmptyCommentaryFactory(CommentaryFactory):
     pub_DOI = None
@@ -34,5 +38,9 @@ class VettedCommentaryFactory(CommentaryFactory):
     vetted = True
 
 
-class UnVettedCommentaryFactory(CommentaryFactory):
+class UnpublishedVettedCommentaryFactory(VettedCommentaryFactory):
+    pub_DOI = ''
+
+
+class UnvettedCommentaryFactory(CommentaryFactory):
     vetted = False
diff --git a/commentaries/forms.py b/commentaries/forms.py
index 6f781e535afca05c1f68b160d01c49b51180fcbe..8d95c58d896ace0b3f84b7b3da68b4b2c6ced1d9 100644
--- a/commentaries/forms.py
+++ b/commentaries/forms.py
@@ -214,13 +214,13 @@ class VetCommentaryForm(forms.Form):
 
 class CommentarySearchForm(forms.Form):
     """Search for Commentary specified by user"""
-    pub_author = forms.CharField(max_length=100, required=False, label="Author(s)")
-    pub_title_keyword = forms.CharField(max_length=100, required=False, label="Title")
-    pub_abstract_keyword = forms.CharField(max_length=1000, required=False, label="Abstract")
+    author = forms.CharField(max_length=100, required=False, label="Author(s)")
+    title = forms.CharField(max_length=100, required=False, label="Title")
+    abstract = forms.CharField(max_length=1000, required=False, label="Abstract")
 
     def search_results(self):
         """Return all Commentary objects according to search"""
         return Commentary.objects.vetted(
-            pub_title__icontains=self.cleaned_data['pub_title_keyword'],
-            pub_abstract__icontains=self.cleaned_data['pub_abstract_keyword'],
-            author_list__icontains=self.cleaned_data['pub_author']).order_by('-pub_date')
+            pub_title__icontains=self.cleaned_data['title'],
+            pub_abstract__icontains=self.cleaned_data['abstract'],
+            author_list__icontains=self.cleaned_data['author']).order_by('-pub_date')
diff --git a/commentaries/models.py b/commentaries/models.py
index e42eb41640b13a46ca98fa5e6a012af8c91e215e..37b54c8caa499ee143360e8d086d5840dd510f39 100644
--- a/commentaries/models.py
+++ b/commentaries/models.py
@@ -1,6 +1,7 @@
 from django.db import models
 from django.contrib.postgres.fields import JSONField
 from django.template import Template, Context
+from django.template.loader import get_template
 
 from journals.models import SCIPOST_JOURNALS_DOMAINS
 from scipost.behaviors import ArxivCallable
@@ -135,36 +136,10 @@ class Commentary(ArxivCallable, TimeStampedModel):
         template = Template('<a href="{{scipost_url}}" class="pubtitleli">{{pub_title}}</a>')
         return template.render(context)
 
-
     def header_as_li(self):
-        # for display in search lists
-        context = Context({'scipost_url': self.scipost_url(), 'pub_title': self.pub_title,
-                           'author_list': self.author_list,
-                           'latest_activity': self.latest_activity.strftime('%Y-%m-%d %H:%M')})
-        header = ('<li>'
-                  # '<div class="flex-container">'
-                  # '<div class="flex-whitebox0">'
-                  '<p><a href="{{ scipost_url }}" '
-                  'class="pubtitleli">{{ pub_title }}</a></p>'
-                  '<p>by {{ author_list }}')
-        if self.type == 'published':
-            header += ', {{ journal }} {{ volume }}, {{ pages }}'
-            context['journal'] = self.journal
-            context['volume'] = self.volume
-            context['pages'] = self.pages
-        elif self.type == 'preprint':
-            header += ', <a href="{{ arxiv_link }}">{{ arxiv_link }}</a>'
-            context['arxiv_link'] = self.arxiv_link
-        header += '</p>'
-        if self.pub_date:
-            header += '<p> (published {{ pub_date }}) - '
-            context['pub_date'] = str(self.pub_date)
-        header += ('latest activity: {{ latest_activity }}</p>'
-                   # '</div></div>'
-                   '</li>')
-        template = Template(header)
-
-        return template.render(context)
+        # TO BE REMOVED!!!!
+        template = get_template('commentaries/commentary_header_li.html')
+        return template.render({'object': self})
 
     def simple_header_as_li(self):
         # for display in Lists
diff --git a/commentaries/templates/commentaries/commentary_detail.html b/commentaries/templates/commentaries/commentary_detail.html
index e468065f95c17ca29d3c3cf5d850192aa9fbd558..b9c5fcd2f9a3691477692d54a4441d17f4b8c913 100644
--- a/commentaries/templates/commentaries/commentary_detail.html
+++ b/commentaries/templates/commentaries/commentary_detail.html
@@ -58,32 +58,6 @@
 
 {% include 'scipost/comments_block.html' %}
 
-
-{% if user.is_authenticated and commentary.open_for_commenting and perms.scipost.can_submit_comments %}
-<hr>
-
-<div class="row">
-    <div class="col-12">
-        <div class="panel">
-            <h2>Contribute a Comment:</h2>
-        </div>
-    </div>
-</div>
-<div class="row">
-    <div class="col-12">
-        <form enctype="multipart/form-data" action="{% url 'commentaries:commentary' arxiv_or_DOI_string=commentary.arxiv_or_DOI_string %}" method="post">
-          {% csrf_token %}
-          {% load crispy_forms_tags %}
-          {% crispy form %}
-        </form>
-    </div>
-    <div class="col-12">
-        <h3>Preview of your comment:</h3>
-        <p id="preview-comment_text"></p>
-    </div>
-</div>
-
-{% endif %}
-
+{% include 'comments/new_comment.html' with object_id=commentary.id type_of_object='commentary' open_for_commenting=commentary.open_for_commenting %}
 
 {% endblock content %}
diff --git a/commentaries/templates/commentaries/commentary_header_li.html b/commentaries/templates/commentaries/commentary_header_li.html
new file mode 100644
index 0000000000000000000000000000000000000000..56fd1ee2e850419b883521ed1b89f6d5b34c70f9
--- /dev/null
+++ b/commentaries/templates/commentaries/commentary_header_li.html
@@ -0,0 +1,13 @@
+<li>
+    <p>
+        <a href="{{ object.scipost_url }}" class="pubtitleli">{{ object.pub_title }}</a>
+    </p>
+    <p>
+        by {{ object.author_list }}
+        {% if object.type == 'published' %}, {{ object.journal }} {{ object.volume }}, {{ object.pages }}
+        {% elif object.type == 'preprint' %}, <a href="{{ object.arxiv_link }}">{{ object.arxiv_link }}</a>{% endif %}
+    </p>
+    <p>
+        {% if object.pub_date %}(published {{ object.pub_date }}) - {% endif %}latest activity: {{ object.latest_activity }}
+    </p>
+</li>
diff --git a/commentaries/templates/commentaries/commentaries.html b/commentaries/templates/commentaries/commentary_list.html
similarity index 60%
rename from commentaries/templates/commentaries/commentaries.html
rename to commentaries/templates/commentaries/commentary_list.html
index 8414ca91ab9e0cbd56269e503f245f572b909d4c..4a420102c155cd83ed008a0b4c8362792c7093bf 100644
--- a/commentaries/templates/commentaries/commentaries.html
+++ b/commentaries/templates/commentaries/commentary_list.html
@@ -1,6 +1,7 @@
 {% extends 'scipost/base.html' %}
 
 {% load bootstrap %}
+{% load request_filters %}
 
 {% block pagetitle %}: Commentaries{% endblock pagetitle %}
 
@@ -9,9 +10,6 @@
 {% endblock headsup %}
 
 {% block content %}
-
-
-{# <section>#}
 <div class="row">
     <div class="col-md-4">
         <div class="panel page-header-panel">
@@ -23,10 +21,9 @@
     <div class="col-md-4">
     	<div class="panel page-header-panel">
             <h2>Search SciPost Commentaries:</h2>
-            <form action="{% url 'commentaries:commentaries' %}" class="small" method="post">
-          	{% csrf_token %}
+            <form action="{% url 'commentaries:commentaries' %}" class="small" method="get">
     	  {{ form|bootstrap:'4,8,sm' }}
-          	<input class="btn btn-sm btn-secondary" type="submit" name="Submit" />
+          	<input class="btn btn-sm btn-secondary" type="submit"/>
             </form>
         </div>
     </div>
@@ -40,69 +37,51 @@
     </div>
 </div>
 
-  {% if commentary_search_list %}
-    <div class="row">
-        <div class="col-12">
-            <hr class="hr12">
-            <h3>Search results:</h3>
-            <ul>
-              {% for commentary in commentary_search_list %}
-                {{ commentary.header_as_li }}
-              {% endfor %}
-            </ul>
-        </div>
-    </div>
-  {% elif form.has_changed %}
-      <div class="row">
-          <div class="col-12">
-            <h3>No match found for your search query.</h3>
-        </div>
-    </div>
-  {% endif %}
-
-
-
-{% if comment_recent_list %}
-  <div class="row">
-      <div class="col-12">
+{% if not browse and recent %}
+<div class="row">
+    <div class="col-12">
         <hr class="hr12">
         <h2>Recent Comments</h2>
         <ul>
-            {% for comment in comment_recent_list %}
+            {% for comment in comment_list %}
                 {{ comment.simple_header_as_li }}
             {% endfor %}
         </ul>
     </div>
-  </div>
-{% endif %}
-
-{% if commentary_recent_list %}
-<div class="row">
-    <div class="col-12">
-  <hr class="hr12">
-  <h2>Recently active Commentaries:</h2>
-  <ul>
-    {% for commentary in commentary_recent_list %}
-    {{ commentary.header_as_li }}
-    {% endfor %}
-  </ul>
-  </div>
 </div>
 {% endif %}
 
-{% if commentary_browse_list %}
 <div class="row">
     <div class="col-12">
-      <hr class="hr12">
-      <h2>Commentaries in {{ discipline }} in the last {{ nrweeksback }} week{% if nrweeksback == '1' %}{% else %}s{% endif %}:</h2>
-      <ul>
-        {% for commentary in commentary_browse_list %}
-        {{ commentary.header_as_li }}
-        {% endfor %}
-      </ul>
-  </div>
+        <hr class="hr12">
+        {% if recent %}
+          <h2>Recently active Commentaries:</h2>
+        {% elif browse %}
+          <h2>Commentaries in {{ discipline }} in the last {{ nrweeksback }} week{{ nrweeksback|pluralize }}:</h2>
+        {% else %}
+          <h2>Search results:</h3>
+        {% endif %}
+        {% if object_list %}
+            {% if is_paginated %}
+              <p>
+              {% if page_obj.has_previous %}
+                <a href="?{% url_replace page=page_obj.previous_page_number %}">Previous</a>
+              {% endif %}
+              Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
+              {% if page_obj.has_next %}
+                <a href="?{% url_replace page=page_obj.next_page_number %}">Next</a>
+              {% endif %}
+              </p>
+            {% endif %}
+            <ul>
+                {% for object in object_list %}
+                    {% include 'commentaries/commentary_header_li.html' with object=object %}
+                {% endfor %}
+            </ul>
+        {% else %}
+            <h3>No match found for your search query.</h3>
+        {% endif %}
+    </div>
 </div>
-{% endif %}
-
 
 {% endblock content %}
diff --git a/commentaries/templates/commentaries/request_commentary.html b/commentaries/templates/commentaries/request_commentary.html
index 5e2444256cd08e363b982a1b3299bb249fe11fd4..99409292c3a2ad78fff5e2383fac45f1e164ad29 100644
--- a/commentaries/templates/commentaries/request_commentary.html
+++ b/commentaries/templates/commentaries/request_commentary.html
@@ -84,7 +84,7 @@
     {% if errormessage %}
         <h3 style="color: red;">Error: {{ errormessage }}</h3>
         {% if existing_commentary %}
-            <ul>{{ existing_commentary.header_as_li }}</ul>
+            <ul>{% include 'commentaries/commentary_header_li.html' with object=existing_commentary %}</ul>
         {% endif %}
         <br/>
     {% endif %}
diff --git a/commentaries/test_forms.py b/commentaries/test_forms.py
index 8a81d2abb6dd2c55da24c687e78ccde109544c71..cec31d3071f2944cc61a8c0cebb785adb186f27f 100644
--- a/commentaries/test_forms.py
+++ b/commentaries/test_forms.py
@@ -3,7 +3,7 @@ from django.test import TestCase
 from common.helpers import model_form_data
 from scipost.factories import UserFactory
 
-from .factories import VettedCommentaryFactory, UnVettedCommentaryFactory
+from .factories import VettedCommentaryFactory, UnvettedCommentaryFactory
 from .forms import RequestCommentaryForm, VetCommentaryForm
 from .models import Commentary
 
@@ -12,7 +12,7 @@ class TestVetCommentaryForm(TestCase):
     fixtures = ['permissions', 'groups']
 
     def setUp(self):
-        self.commentary = UnVettedCommentaryFactory.create()
+        self.commentary = UnvettedCommentaryFactory.create()
         self.user = UserFactory()
         self.form_data = {
             'action_option': VetCommentaryForm.ACTION_ACCEPT,
diff --git a/commentaries/test_views.py b/commentaries/test_views.py
index d724770ab2f0f57b8229618661ef257897fcfdd1..50d6850fd46c2b7166370a078492a227e04dcda5 100644
--- a/commentaries/test_views.py
+++ b/commentaries/test_views.py
@@ -1,10 +1,10 @@
 from django.core.urlresolvers import reverse
 from django.contrib.auth.models import Group
-from django.test import TestCase
+from django.test import TestCase, Client
 
 from scipost.factories import ContributorFactory
 
-from .factories import UnVettedCommentaryFactory, VettedCommentaryFactory
+from .factories import UnvettedCommentaryFactory, VettedCommentaryFactory, UnpublishedVettedCommentaryFactory
 from .forms import CommentarySearchForm
 from .models import Commentary
 
@@ -83,7 +83,7 @@ class VetCommentaryRequestsTest(TestCase):
         self.assertEquals(response.context['commentary_to_vet'], None)
 
         # Unvetted Commentaries do exist!
-        UnVettedCommentaryFactory()
+        UnvettedCommentaryFactory()
         response = self.client.get(self.view_url)
         self.assertTrue(type(response.context['commentary_to_vet']) is Commentary)
 
@@ -108,3 +108,19 @@ class BrowseCommentariesTest(TestCase):
         self.assertTrue(response.context['commentary_browse_list'].count() >= 1)
         # The search form is passed trough the view...
         self.assertTrue(type(response.context['form']) is CommentarySearchForm)
+
+
+class CommentaryDetailTest(TestCase):
+    fixtures = ['permissions', 'groups']
+
+    def setUp(self):
+        self.client = Client()
+        self.commentary = UnpublishedVettedCommentaryFactory()
+        self.target = reverse(
+            'commentaries:commentary',
+            kwargs={'arxiv_or_DOI_string': self.commentary.arxiv_or_DOI_string}
+        )
+
+    def test_status_code_200(self):
+        response = self.client.get(self.target)
+        self.assertEqual(response.status_code, 200)
diff --git a/commentaries/urls.py b/commentaries/urls.py
index 2f203ac38365a3ebef2892e851f2562b6bcd64f5..a1b5ba5ec119853ae337ab52c7213cb4feadea8f 100644
--- a/commentaries/urls.py
+++ b/commentaries/urls.py
@@ -5,21 +5,28 @@ from . import views
 
 urlpatterns = [
     # Commentaries
-    url(r'^$', views.commentaries, name='commentaries'),
-    url(r'^browse/(?P<discipline>[a-z]+)/(?P<nrweeksback>[0-9]+)/$', views.browse, name='browse'),
+    url(r'^$', views.CommentaryListView.as_view(), name='commentaries'),
+    url(r'^browse/(?P<discipline>[a-z]+)/(?P<nrweeksback>[0-9]+)/$',
+        views.CommentaryListView.as_view(), name='browse'),
     url(r'^howto$', TemplateView.as_view(template_name='commentaries/howto.html'), name='howto'),
 
     # Match a DOI-based link:
-    url(r'^(?P<arxiv_or_DOI_string>10.[0-9]{4,9}/[-._;()/:a-zA-Z0-9]+)/$', views.commentary_detail, name='commentary'),
+    url(r'^(?P<arxiv_or_DOI_string>10.[0-9]{4,9}/[-._;()/:a-zA-Z0-9]+)/$',
+        views.commentary_detail, name='commentary'),
     # Match an arxiv-based link:
     # new style identifiers:
-    url(r'^(?P<arxiv_or_DOI_string>arXiv:[0-9]{4,}.[0-9]{5,}(v[0-9]+)?)/$', views.commentary_detail, name='commentary'),
+    url(r'^(?P<arxiv_or_DOI_string>arXiv:[0-9]{4,}.[0-9]{5,}(v[0-9]+)?)/$',
+        views.commentary_detail, name='commentary'),
     # old style identifiers:
-    url(r'^(?P<arxiv_or_DOI_string>arXiv:[a-z-]+/[0-9]{7,}(v[0-9]+)?)/$', views.commentary_detail, name='commentary'),
+    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'^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'^vet_commentary_requests$', views.vet_commentary_requests, name='vet_commentary_requests'),
-    url(r'^vet_commentary_request_ack/(?P<commentary_id>[0-9]+)$', views.vet_commentary_request_ack, name='vet_commentary_request_ack'),
+    url(r'^prefill_using_identifier$', views.PrefillUsingIdentifierView.as_view(),
+        name='prefill_using_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]+)$',
+        views.vet_commentary_request_ack, name='vet_commentary_request_ack'),
 ]
diff --git a/commentaries/views.py b/commentaries/views.py
index 339bab4f935cf0a3bcaefbf36a682982be70c215..640100a7c22436864ce3aab1e7b49e76f10a0c67 100644
--- a/commentaries/views.py
+++ b/commentaries/views.py
@@ -1,9 +1,6 @@
-import datetime
-import feedparser
 import re
 import requests
 
-from django.utils import timezone
 from django.shortcuts import get_object_or_404, render
 from django.contrib import messages
 from django.contrib.auth.decorators import permission_required
@@ -13,6 +10,7 @@ from django.core.urlresolvers import reverse, reverse_lazy
 from django.shortcuts import redirect
 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 .models import Commentary
@@ -39,7 +37,7 @@ class RequestCommentaryMixin(object):
             kwargs['request_commentary_form'] = RequestCommentaryForm()
         context = super(RequestCommentaryMixin, self).get_context_data(**kwargs)
 
-        context['existing_commentary'] = None  # context['request_commentary_form'].get_existing_commentary()
+        context['existing_commentary'] = None
         context['doiform'] = DOIToQueryForm()
         context['identifierform'] = IdentifierToQueryForm()
         return context
@@ -267,77 +265,46 @@ def vet_commentary_request_ack(request, commentary_id):
     return render(request, 'scipost/acknowledgement.html', context)
 
 
-def commentaries(request):
-    """List and search all commentaries"""
-    form = CommentarySearchForm(request.POST or None)
-    if form.is_valid() and form.has_changed():
-        commentary_search_list = form.search_results()
-    else:
-        commentary_search_list = []
+class CommentaryListView(ListView):
+    model = Commentary
+    form = CommentarySearchForm
+    paginate_by = 10
 
-    comment_recent_list = Comment.objects.filter(status='1').order_by('-date_submitted')[:10]
-    commentary_recent_list = Commentary.objects.vetted().order_by('-latest_activity')[:10]
-    context = {
-        'form': form, 'commentary_search_list': commentary_search_list,
-        'comment_recent_list': comment_recent_list,
-        'commentary_recent_list': commentary_recent_list}
-    return render(request, 'commentaries/commentaries.html', context)
+    def get_queryset(self):
+        '''Perform search form here already to get the right pagination numbers.'''
+        self.form = self.form(self.request.GET)
+        if self.form.is_valid() and self.form.has_changed():
+            return self.form.search_results()
+        return self.model.objects.vetted().order_by('-latest_activity')
 
+    def get_context_data(self, **kwargs):
+        # Call the base implementation first to get a context
+        context = super().get_context_data(**kwargs)
+
+        # Get newest comments
+        context['comment_list'] = Comment.objects.vetted().order_by('-date_submitted')[:10]
 
-def browse(request, discipline, nrweeksback):
-    """List all commentaries for discipline and period"""
-    commentary_browse_list = Commentary.objects.vetted(
-        discipline=discipline,
-        latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback)))
-    context = {
-        'form': CommentarySearchForm(),
-        'discipline': discipline, 'nrweeksback': nrweeksback,
-        'commentary_browse_list': commentary_browse_list}
-    return render(request, 'commentaries/commentaries.html', context)
+        # Form into the context!
+        context['form'] = self.form
+
+        # To customize display in the template
+        if 'discipline' in self.kwargs:
+            context['discipline'] = self.kwargs['discipline']
+            context['nrweeksback'] = self.kwargs['nrweeksback']
+            context['browse'] = True
+        elif not any(self.request.GET[field] for field in self.request.GET):
+            context['recent'] = True
+
+        return context
 
 
 def commentary_detail(request, arxiv_or_DOI_string):
     commentary = get_object_or_404(Commentary, arxiv_or_DOI_string=arxiv_or_DOI_string)
     comments = commentary.comment_set.all()
-    if request.method == 'POST':
-        form = CommentForm(request.POST, request.FILES)
-        if form.is_valid():
-            author = Contributor.objects.get(user=request.user)
-            newcomment = Comment(commentary=commentary, author=author,
-                                 is_rem=form.cleaned_data['is_rem'],
-                                 is_que=form.cleaned_data['is_que'],
-                                 is_ans=form.cleaned_data['is_ans'],
-                                 is_obj=form.cleaned_data['is_obj'],
-                                 is_rep=form.cleaned_data['is_rep'],
-                                 is_val=form.cleaned_data['is_val'],
-                                 is_lit=form.cleaned_data['is_lit'],
-                                 is_sug=form.cleaned_data['is_sug'],
-                                 file_attachment=form.cleaned_data['file_attachment'],
-                                 comment_text=form.cleaned_data['comment_text'],
-                                 remarks_for_editors=form.cleaned_data['remarks_for_editors'],
-                                 date_submitted=timezone.now(),
-                                 )
-            newcomment.save()
-            author.nr_comments = Comment.objects.filter(author=author).count()
-            author.save()
-            context = {'ack_header': 'Thank you for contributing a Comment.',
-                       'ack_message': 'It will soon be vetted by an Editor.',
-                       'followup_message': 'Back to the ',
-                       'followup_link': reverse(
-                           'commentaries:commentary',
-                           kwargs={
-                                'arxiv_or_DOI_string': newcomment.commentary.arxiv_or_DOI_string
-                           }
-                       ),
-                       'followup_link_label': ' Commentary page you came from'
-                       }
-            return render(request, 'scipost/acknowledgement.html', context)
-    else:
-        form = CommentForm()
+    form = CommentForm()
     try:
-        author_replies = Comment.objects.filter(commentary=commentary,
-                                                is_author_reply=True,
-                                                status__gte=1)
+        author_replies = Comment.objects.filter(
+            commentary=commentary, is_author_reply=True, status__gte=1)
     except Comment.DoesNotExist:
         author_replies = ()
     context = {'commentary': commentary,
diff --git a/comments/managers.py b/comments/managers.py
new file mode 100644
index 0000000000000000000000000000000000000000..2875a7030b1cb75cf16b364cd6c409e70e90f0b2
--- /dev/null
+++ b/comments/managers.py
@@ -0,0 +1,6 @@
+from django.db import models
+
+
+class CommentManager(models.Manager):
+    def vetted(self):
+        return self.filter(status__gte=1)
diff --git a/comments/migrations/0010_auto_20170216_1831.py b/comments/migrations/0010_auto_20170219_1006.py
similarity index 94%
rename from comments/migrations/0010_auto_20170216_1831.py
rename to comments/migrations/0010_auto_20170219_1006.py
index 2a44e624ab9cbfb9c17af0907de9493ab2e19ccb..10a486d2f7338d7077ef688617775862476d3dde 100644
--- a/comments/migrations/0010_auto_20170216_1831.py
+++ b/comments/migrations/0010_auto_20170219_1006.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-02-16 17:31
+# Generated by Django 1.10.3 on 2017-02-19 09:06
 from __future__ import unicode_literals
 
 import comments.behaviors
diff --git a/comments/models.py b/comments/models.py
index e449d2a7382c0b0054082c1772ac5f46cf689b64..3851d0737a0a93519a09ae986e63be0dad6498f5 100644
--- a/comments/models.py
+++ b/comments/models.py
@@ -10,6 +10,8 @@ from scipost.models import TimeStampedModel, Contributor
 from submissions.models import Submission, Report
 from theses.models import ThesisLink
 
+from .managers import CommentManager
+
 COMMENT_CATEGORIES = (
     ('ERR', 'erratum'),
     ('REM', 'remark'),
@@ -37,19 +39,24 @@ class Comment(TimeStampedModel):
     on a particular publication or in reply to an earlier Comment. """
 
     status = models.SmallIntegerField(default=0)
-    vetted_by = models.ForeignKey(Contributor, blank=True, null=True,
-                                  on_delete=models.CASCADE,
-                                  related_name='comment_vetted_by')
-    file_attachment = models.FileField(upload_to='uploads/comments/%Y/%m/%d/', blank=True,
-                                       validators=[validate_file_extension,
-                                                   validate_max_file_size])
+    vetted_by = models.ForeignKey(
+        Contributor,
+        blank=True,
+        null=True,
+        on_delete=models.CASCADE,
+        related_name='comment_vetted_by'
+    )
+    file_attachment = models.FileField(
+        upload_to='uploads/comments/%Y/%m/%d/', blank=True,
+        validators=[validate_file_extension,
+        validate_max_file_size]
+    )
     # a Comment is either for a Commentary or Submission or a ThesisLink.
     commentary = models.ForeignKey(Commentary, blank=True, null=True, on_delete=models.CASCADE)
     submission = models.ForeignKey(Submission, blank=True, null=True, on_delete=models.CASCADE)
     thesislink = models.ForeignKey(ThesisLink, blank=True, null=True, on_delete=models.CASCADE)
     is_author_reply = models.BooleanField(default=False)
-    in_reply_to_comment = models.ForeignKey('self', blank=True, null=True,
-                                            on_delete=models.CASCADE)
+    in_reply_to_comment = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
     in_reply_to_report = models.ForeignKey(Report, blank=True, null=True, on_delete=models.CASCADE)
     author = models.ForeignKey(Contributor, on_delete=models.CASCADE)
     anonymous = models.BooleanField(default=False, verbose_name='Publish anonymously')
@@ -64,19 +71,20 @@ class Comment(TimeStampedModel):
     is_lit = models.BooleanField(default=False, verbose_name='pointer to related literature')
     is_sug = models.BooleanField(default=False, verbose_name='suggestion for further work')
     comment_text = models.TextField()
-    remarks_for_editors = models.TextField(default='', blank=True,
-                                           verbose_name='optional remarks for the Editors only')
+    remarks_for_editors = models.TextField(default='', blank=True, verbose_name='optional remarks for the Editors only')
     date_submitted = models.DateTimeField('date submitted')
     # Opinions
     nr_A = models.PositiveIntegerField(default=0)
-    in_agreement = models.ManyToManyField(Contributor,
-                                          related_name='in_agreement', blank=True)
+    in_agreement = models.ManyToManyField(Contributor, related_name='in_agreement', blank=True)
     nr_N = models.PositiveIntegerField(default=0)
-    in_notsure = models.ManyToManyField(Contributor,
-                                        related_name='in_notsure', blank=True)
+    in_notsure = models.ManyToManyField(Contributor, related_name='in_notsure', blank=True)
     nr_D = models.PositiveIntegerField(default=0)
-    in_disagreement = models.ManyToManyField(Contributor,
-                                             related_name='in_disagreement', blank=True)
+    in_disagreement = models.ManyToManyField(
+        Contributor,
+        related_name='in_disagreement',
+        blank=True
+    )
+    objects = CommentManager()
 
     def __str__(self):
         return ('by ' + self.author.user.first_name + ' ' + self.author.user.last_name +
diff --git a/comments/templates/comments/new_comment.html b/comments/templates/comments/new_comment.html
new file mode 100644
index 0000000000000000000000000000000000000000..80f19afad8798a122d2dd5873019c139d02849ec
--- /dev/null
+++ b/comments/templates/comments/new_comment.html
@@ -0,0 +1,27 @@
+{% if user.is_authenticated and open_for_commenting and perms.scipost.can_submit_comments %}
+<hr />
+
+<div class="row">
+    <div class="col-12">
+        <div class="panel">
+            <h2>Contribute a Comment:</h2>
+        </div>
+    </div>
+</div>
+<div class="row">
+    <div class="col-12">
+        <form enctype="multipart/form-data"
+            action="{% url 'comments:new_comment' object_id=object_id type_of_object=type_of_object %}"
+            method="post">
+          {% csrf_token %}
+          {% load crispy_forms_tags %}
+          {% crispy form %}
+        </form>
+    </div>
+    <div class="col-12">
+        <h3>Preview of your comment:</h3>
+        <p id="preview-comment_text"></p>
+    </div>
+</div>
+
+{% endif %}
diff --git a/comments/test_views.py b/comments/test_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1824139a5c2dd7def2f41ae2f6c6fd742914e41
--- /dev/null
+++ b/comments/test_views.py
@@ -0,0 +1,126 @@
+from django.test import TestCase, RequestFactory, Client
+from django.urls import reverse, reverse_lazy
+from django.contrib.auth.models import Group
+from django.contrib.messages.storage.fallback import FallbackStorage
+from django.core.exceptions import PermissionDenied
+
+from scipost.factories import ContributorFactory
+from theses.factories import ThesisLinkFactory
+from submissions.factories import EICassignedSubmissionFactory
+from commentaries.factories import UnpublishedVettedCommentaryFactory
+
+from .factories import CommentFactory
+from .forms import CommentForm
+from .models import Comment
+from .views import new_comment
+
+from common.helpers import model_form_data
+
+
+class TestNewComment(TestCase):
+    fixtures = ['groups', 'permissions']
+
+    def install_messages_middleware(self, request):
+        # I don't know what the following three lines do, but they help make a RequestFactory
+        # work with the messages middleware
+        setattr(request, 'session', 'session')
+        messages = FallbackStorage(request)
+        setattr(request, '_messages', messages)
+
+    def test_submitting_comment_on_thesislink_creates_comment_and_redirects(self):
+        """ Valid Comment gets saved """
+
+        contributor = ContributorFactory()
+        thesislink = ThesisLinkFactory()
+        valid_comment_data = model_form_data(CommentFactory.build(), CommentForm)
+        target = reverse('comments:new_comment', kwargs={'object_id': thesislink.id, 'type_of_object': 'thesislink'})
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 0)
+
+        request = RequestFactory().post(target, valid_comment_data)
+        self.install_messages_middleware(request)
+        request.user = contributor.user
+        response = new_comment(request, object_id=thesislink.id, type_of_object='thesislink')
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 1)
+
+        response.client = Client()
+        expected_redirect_link = reverse('theses:thesis', kwargs={'thesislink_id': thesislink.id})
+        self.assertRedirects(response, expected_redirect_link)
+
+    def test_submitting_comment_on_submission_creates_comment_and_redirects(self):
+        contributor = ContributorFactory()
+        submission = EICassignedSubmissionFactory()
+        submission.open_for_commenting = True
+        submission.save()
+        valid_comment_data = model_form_data(CommentFactory.build(), CommentForm)
+        target = reverse(
+            'comments:new_comment',
+            kwargs={'object_id': submission.id, 'type_of_object': 'submission'},
+        )
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 0)
+
+        request = RequestFactory().post(target, valid_comment_data)
+        self.install_messages_middleware(request)
+        request.user = contributor.user
+        response = new_comment(request, object_id=submission.id, type_of_object='submission')
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 1)
+
+        response.client = Client()
+        expected_redirect_link = reverse(
+            'submissions:submission',
+            kwargs={'arxiv_identifier_w_vn_nr': submission.arxiv_identifier_w_vn_nr}
+        )
+        self.assertRedirects(response, expected_redirect_link)
+
+
+    def test_submitting_comment_on_commentary_creates_comment_and_redirects(self):
+        """ Valid Comment gets saved """
+
+        contributor = ContributorFactory()
+        commentary = UnpublishedVettedCommentaryFactory()
+        valid_comment_data = model_form_data(CommentFactory.build(), CommentForm)
+        target = reverse('comments:new_comment', kwargs={'object_id': commentary.id, 'type_of_object': 'commentary'})
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 0)
+
+        request = RequestFactory().post(target, valid_comment_data)
+        self.install_messages_middleware(request)
+        request.user = contributor.user
+        response = new_comment(request, object_id=commentary.id, type_of_object='commentary')
+
+        comment_count = commentary.comment_set.count()
+        self.assertEqual(comment_count, 1)
+
+        response.client = Client()
+        expected_redirect_link = reverse(
+            'commentaries:commentary', kwargs={'arxiv_or_DOI_string': commentary.arxiv_or_DOI_string})
+        self.assertRedirects(response, expected_redirect_link)
+
+
+    def test_submitting_comment_on_submission_that_is_not_open_for_commenting_should_be_impossible(self):
+        contributor = ContributorFactory()
+        submission = EICassignedSubmissionFactory()
+        submission.open_for_commenting = False
+        submission.save()
+        valid_comment_data = model_form_data(CommentFactory.build(), CommentForm)
+        target = reverse(
+            'comments:new_comment',
+            kwargs={'object_id': submission.id, 'type_of_object': 'submission'},
+        )
+
+        comment_count = Comment.objects.filter(author=contributor).count()
+        self.assertEqual(comment_count, 0)
+
+        request = RequestFactory().post(target, valid_comment_data)
+        self.install_messages_middleware(request)
+        request.user = contributor.user
+        with self.assertRaises(PermissionDenied):
+            response = new_comment(request, object_id=submission.id, type_of_object='submission')
diff --git a/comments/urls.py b/comments/urls.py
index b417f028aa127ff3d16f325d953216416131e4a9..3d1747ee28e087ff4a52d0da5cd0a794032fc935 100644
--- a/comments/urls.py
+++ b/comments/urls.py
@@ -10,4 +10,5 @@ urlpatterns = [
     url(r'^vet_submitted_comment_ack/(?P<comment_id>[0-9]+)$', views.vet_submitted_comment_ack, name='vet_submitted_comment_ack'),
     url(r'^express_opinion/(?P<comment_id>[0-9]+)$', views.express_opinion, name='express_opinion'),
     url(r'^express_opinion/(?P<comment_id>[0-9]+)/(?P<opinion>[AND])$', views.express_opinion, name='express_opinion'),
+    url(r'^new_comment/(?P<type_of_object>[a-z]+)/(?P<object_id>[0-9]+)$', views.new_comment, name='new_comment')
 ]
diff --git a/comments/views.py b/comments/views.py
index 254206ca81d8248a8dadc41049646b1566b4ba8d..ce859b86553018a6788aee4a82472c71cfa388f8 100644
--- a/comments/views.py
+++ b/comments/views.py
@@ -1,17 +1,79 @@
 from django.utils import timezone
-from django.shortcuts import get_object_or_404, render
+from django.shortcuts import get_object_or_404, render, redirect
 from django.contrib.auth.decorators import permission_required
+from django.contrib import messages
 from django.core.mail import EmailMessage
 from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
+from django.core.exceptions import PermissionDenied
+from django.http import HttpResponseRedirect, Http404
+
+import strings
 
 from .models import Comment
 from .forms import CommentForm, VetCommentForm, comment_refusal_dict
 
 from scipost.models import Contributor, title_dict
+from theses.models import ThesisLink
 from submissions.utils import SubmissionUtils
-from submissions.models import Report
+from submissions.models import Submission, Report
+from commentaries.models import Commentary
+
+@permission_required('scipost.can_submit_comments', raise_exception=True)
+def new_comment(request, **kwargs):
+    if request.method == "POST":
+        form = CommentForm(request.POST)
+        if form.is_valid():
+            author = Contributor.objects.get(user=request.user)
+            object_id = int(kwargs["object_id"])
+            type_of_object = kwargs["type_of_object"]
+            new_comment = Comment(
+                author=author,
+                is_rem=form.cleaned_data['is_rem'],
+                is_que=form.cleaned_data['is_que'],
+                is_ans=form.cleaned_data['is_ans'],
+                is_obj=form.cleaned_data['is_obj'],
+                is_rep=form.cleaned_data['is_rep'],
+                is_val=form.cleaned_data['is_val'],
+                is_lit=form.cleaned_data['is_lit'],
+                is_sug=form.cleaned_data['is_sug'],
+                file_attachment=form.cleaned_data['file_attachment'],
+                comment_text=form.cleaned_data['comment_text'],
+                remarks_for_editors=form.cleaned_data['remarks_for_editors'],
+                date_submitted=timezone.now(),
+            )
+            if type_of_object == "thesislink":
+                thesislink = ThesisLink.objects.get(id=object_id)
+                if not thesislink.open_for_commenting:
+                    raise PermissionDenied
+                new_comment.thesislink = thesislink
+                redirect_link = reverse('theses:thesis', kwargs={"thesislink_id":thesislink.id})
+            elif type_of_object == "submission":
+                submission = Submission.objects.get(id=object_id)
+                if not submission.open_for_commenting:
+                    raise PermissionDenied
+                new_comment.submission = submission
+                redirect_link = reverse(
+                    'submissions:submission',
+                    kwargs={"arxiv_identifier_w_vn_nr": submission.arxiv_identifier_w_vn_nr}
+                )
+            elif type_of_object == "commentary":
+                commentary = Commentary.objects.get(id=object_id)
+                if not commentary.open_for_commenting:
+                    raise PermissionDenied
+                new_comment.commentary = commentary
+                redirect_link = reverse(
+                    'commentaries:commentary', kwargs={'arxiv_or_DOI_string': commentary.arxiv_or_DOI_string}
+                )
 
+            new_comment.save()
+            author.nr_comments = Comment.objects.filter(author=author).count()
+            author.save()
+            messages.add_message(
+                request, messages.SUCCESS, strings.acknowledge_submit_comment)
+            return redirect(redirect_link)
+    else:
+        # This view is only accessible by POST request
+        raise Http404
 
 @permission_required('scipost.can_vet_comments', raise_exception=True)
 def vet_submitted_comments(request):
diff --git a/common/helpers/__init__.py b/common/helpers/__init__.py
index d91d1905c12f9529071e5406d04f90cd16891b0f..d1875920b99074217a6ce3d80fa10128bd4e568a 100644
--- a/common/helpers/__init__.py
+++ b/common/helpers/__init__.py
@@ -1,3 +1,6 @@
+import random
+import string
+
 def model_form_data(model, form_class, form_kwargs={}):
     '''
     Returns a dict that can be used to instantiate a form object.
@@ -25,6 +28,14 @@ def model_form_data(model, form_class, form_kwargs={}):
     form_fields = list(form_class(**form_kwargs).fields.keys())
     return filter_keys(model_data, form_fields)
 
+def random_arxiv_identifier_with_version_number():
+    return random_arxiv_identifier_without_version_number() + "v0"
+
+def random_arxiv_identifier_without_version_number():
+    return random_digits(4) + "." + random_digits(5)
+
+def random_digits(n):
+    return "".join(random.choice(string.digits) for _ in range(n))
 
 def filter_keys(dictionary, keys_to_keep):
     # Field is empty if not on model.
diff --git a/docs/contributors/editorial_administrators/production.rst b/docs/contributors/editorial_administrators/production.rst
index 006101e1637c2d8e71ca3b0e2a93665f4236090c..f5e4472fc52e7f62cc195b9ea4ee7bf12567e6be 100644
--- a/docs/contributors/editorial_administrators/production.rst
+++ b/docs/contributors/editorial_administrators/production.rst
@@ -334,6 +334,15 @@ Problems
        Here is an equation that should not be broken: ${E=mc^2}$.
 
 
+   * Equation/table or other text/maths element is just too wide.
+     Option: locally change the fontsize by embedding the object in a ``\fontsize`` block,::
+
+       \begingroup
+       \fontsize{new font size, e.g. 10pt}{skip, 120% of previous}\selectfont
+       [ element]
+       \endgroup
+
+
    * package ``MnSymbol`` is problematic and clashes with amsmath.
 
      One solution is to import individual symbols according to these
@@ -382,6 +391,10 @@ References formatting
       \J. Stat. Mech. is annoying (volume number is year). Manually remove volume nr for
       these, so the format becomes ``A. Bee, \emp{Bee's nice paper}, J. Stat. Mech.: Th. Exp. [P,L]##### (20##), \doi{10...}.``
 
+      \J. Phys. A is also annoying. Up to and including volume 39 (2006), it's
+      \J. Phys. A: Math. Gen. Afterwards, volume 40 (2007) onwards, it's
+      \J. Phys. A: Math. Theor.
+
 
 Layout verification
 ~~~~~~~~~~~~~~~~~~~
@@ -439,7 +452,9 @@ Preparation of final version of record
    #. Make sure linenumbers are deactivated.
 
    #. Does the table of contents (if present) look OK? (Beware of hanging closing
-      line pushed to top of second page.)
+      line pushed to top of second page). If needed, adjust the ``\vspace`` spacings
+      defined around the table of contents, and/or insert an additional ``vspace``
+      with negative spacing before the abstract.
 
    #. If the author-accepted proofs version used BiBTeX, copy the contents of the bbl
       file into the .tex file, so that BiBTeX is not needed anymore.
diff --git a/requirements.txt b/requirements.txt
index f486993b4c640c6c32030fda6ece23aaafe4e8d3..4e1cfb752935824e29985016edb1fd1bbd4c30e8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,8 +9,8 @@ django-guardian==1.4.6
 django-haystack==2.5.1
 django-mathjax==0.0.5
 django-mptt==0.8.6
-django-simple-captcha==0.5.3
 django-sphinxdoc==1.5.1
+django-recaptcha==1.2.1
 django-webpack-loader==0.4.1
 djangorestframework==3.5.3
 docutils==0.12
diff --git a/scipost/factories.py b/scipost/factories.py
index 27ebc93ef459df1de461cf65af874680d0ecbb48..e32c408deb837497d49cfbffff1e8e8c49b76b23 100644
--- a/scipost/factories.py
+++ b/scipost/factories.py
@@ -12,7 +12,7 @@ class ContributorFactory(factory.django.DjangoModelFactory):
 
     title = "MR"
     user = factory.SubFactory('scipost.factories.UserFactory', contributor=None)
-    status = 1
+    status = 1  # normal user
     vetted_by = factory.SubFactory('scipost.factories.ContributorFactory', vetted_by=None)
 
 
diff --git a/scipost/forms.py b/scipost/forms.py
index b4d7422a13dda2bc52cc11d3d2c9a4e228c00234..7c628d2f224eff4db20149f95e7765ffc3137e63 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -6,7 +6,7 @@ from django.db.models import Q
 from django_countries import countries
 from django_countries.widgets import CountrySelectWidget
 from django_countries.fields import LazyTypedChoiceField
-from captcha.fields import CaptchaField
+from captcha.fields import ReCaptchaField
 
 from crispy_forms.helper import FormHelper
 from crispy_forms.layout import Layout, Div, Field, HTML, Submit
@@ -60,7 +60,9 @@ class RegistrationForm(forms.Form):
     username = forms.CharField(label='* Username', max_length=100)
     password = forms.CharField(label='* Password', widget=forms.PasswordInput())
     password_verif = forms.CharField(label='* Verify pwd', widget=forms.PasswordInput())
-    captcha = CaptchaField(label='* Answer this simple maths question:')
+    captcha = ReCaptchaField(attrs={
+  'theme' : 'clean',
+}, label='* Answer this simple maths question:')
 
 
 class DraftInvitationForm(forms.ModelForm):
diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss
index e19eda442bfb4966be75761eb61a333ea5bf16fd..f42196c54f4b213910ad48974fb68c15c601b79a 100644
--- a/scipost/static/scipost/assets/config/preconfig.scss
+++ b/scipost/static/scipost/assets/config/preconfig.scss
@@ -6,11 +6,12 @@
 //
 $grid-gutter-width-base: 20px;
 $container-max-widths: (
-  sm: 546px,
-  md: 738px,
-  lg: 962px,
-  xl: 1170px
+    sm: 768px,
+    md: 992px,
+    lg: 1200px,
+    xl: 1240px
 );
+
 .row {
     margin-bottom: 1rem;
 }
diff --git a/scipost/templates/scipost/about.html b/scipost/templates/scipost/about.html
index 512c16b64c48e0f3c7b606b8f59d591fc9d42250..110047ecb983230817065416eb8de8739ca60712 100644
--- a/scipost/templates/scipost/about.html
+++ b/scipost/templates/scipost/about.html
@@ -171,7 +171,7 @@
 	<li>Prof. <a href="http://researchers.uq.edu.au/researcher/1134">Matthew Davis</a><br/>(U. of Queensland)</li>
 	<li>Prof. <a href="http://www-thphys.physics.ox.ac.uk/people/FabianEssler/">Fabian Essler</a><br/>(U. of Oxford)</li>
 	<li>Prof. <a href="http://www.ms.unimelb.edu.au/~jdgier@unimelb/">Jan de Gier</a><br/>(U. of Melbourne)</li>
-	<li>Prof. <a href="http://physics.berkeley.edu/people/faculty/beate-heinemann">Beate Heinemann</a><br/>(Berkeley)</li>
+	<li>Prof. <a href="http://www.desy.de/about_desy/leading_scientists/beate_heinemann/index_eng.html">Beate Heinemann</a><br/>(DESY; Freiburg)</li>
 	<li>Prof. <a href="http://katzgraber.org">Helmut Katzgraber</a><br/>(Texas A&amp;M)</li>
 	<li>Prof. <a href="https://www.pmmh.espci.fr/~jorge/">Jorge Kurchan</a><br/>(<a href="https://www.pmmh.espci.fr/?-Home-">PMMH</a> Paris, CNRS)</li>
 	<li>Prof. <a href="http://www.ens-lyon.fr/PHYSIQUE/presentation/annuaire/maillet-jean-michel">Jean Michel Maillet</a><br/>(ENS Lyon)</li>
@@ -188,6 +188,7 @@
 	<li>Prof. <a href="http://www.thp.uni-koeln.de/rosch">Achim Rosch</a><br/>(U. of Cologne)</li>
 	<li>Prof. <a href="http://saleur.sandvox.net">Hubert Saleur</a><br/>(CEA Saclay/USC)</li>
 	<li>Prof. <a href="http://www.phy.ohiou.edu/people/faculty/sandler.html">Nancy Sandler</a><br/>(Ohio)</li>
+	<li>Prof. <a href="http://www-thphys.physics.ox.ac.uk/people/SubirSarkar/">Subir Sarkar</a><br/>(Oxford; Niels Bohr Institute)</li>
 	<li>Prof. <a href="https://staff.fnwi.uva.nl/c.j.m.schoutens/">Kareljan Schoutens</a><br/>(U. van Amsterdam)</li>
 	<li>Prof. <a href="http://www-thphys.physics.ox.ac.uk/people/SteveSimon/">Steve Simon</a><br/>(U. of Oxford)</li>
 	<li>Prof. <a href="http://bec.science.unitn.it/infm-bec/people/stringari.html">Sandro Stringari</a><br/>(Trento)</li>
diff --git a/scipost/templates/scipost/claim_authorships.html b/scipost/templates/scipost/claim_authorships.html
index fb2e0c33e2e71e2324c4a015978d83539ed48cae..2554bd2ed89b73fde73120e1ff6720ae69f5d8e0 100644
--- a/scipost/templates/scipost/claim_authorships.html
+++ b/scipost/templates/scipost/claim_authorships.html
@@ -63,7 +63,7 @@
     <h3>Potential authorships to claim (auto-detected)</h3>
     <ul>
       {% for thesis in thesis_authorships_to_claim %}
-        {{ thesis.header_as_li }}
+        {% include 'theses/_thesislink_header_as_li.html' with thesislink=thesis %}
         <form action="{% url 'scipost:claim_thesis_authorship' thesis_id=thesis.id claim=1%}" method="post">
           {% csrf_token %}
           <input type="submit" value="I am an author" />
diff --git a/scipost/templates/scipost/contributor_info.html b/scipost/templates/scipost/contributor_info.html
index 1f6bde0d3035145732ad6f9b7d3629764ec66bb2..be6f2d0bbbafb4e05494ee4c822a85d8c8c12dc9 100644
--- a/scipost/templates/scipost/contributor_info.html
+++ b/scipost/templates/scipost/contributor_info.html
@@ -103,7 +103,7 @@
     <h3>Theses for which this Contributor is identified as an author:</h3>
     <ul>
       {% for thesis in contributor_theses %}
-        {{ thesis.header_as_li }}
+        {% include 'theses/_thesislink_header_as_li.html' with thesislink=thesis %}
       {% endfor %}
     </ul>
   </div>
diff --git a/scipost/templates/scipost/list.html b/scipost/templates/scipost/list.html
index 006f57fbfbf18bb2773eeec4e0492de818972547..2ef95a3ddec56248a25e0ace8db3bc053675d9aa 100644
--- a/scipost/templates/scipost/list.html
+++ b/scipost/templates/scipost/list.html
@@ -72,7 +72,8 @@
   <h3>Theses:</h3>
   <ul>
     {% for thesislink in thesislink_search_list %}
-    {{ thesislink.header_as_li }}
+
+        {% include 'theses/_thesislink_header_as_li.html' with thesislink=thesislink %}
     <form action="{% url 'scipost:list_add_element' list_id=list.id type='T' element_id=thesislink.id %}" method="post">
       {% csrf_token %}
       <input class="AddItemToList" type="submit" value="Add"/>
diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html
index be0639c3f23f25eb3ecdcb1df6a6146208ced3f4..151c11eb9e901a98ddaf2ab62aa65d523e5e31aa 100644
--- a/scipost/templates/scipost/personal_page.html
+++ b/scipost/templates/scipost/personal_page.html
@@ -471,7 +471,7 @@
             <h3>Theses for which you are identified as an author:</h3>
             <ul>
                 {% for thesis in own_thesislinks %}
-                    {{ thesis.header_as_li }}
+                    {% include 'theses/_thesislink_header_as_li.html' with thesislink=thesis %}
                 {% endfor %}
             </ul>
         </div>
diff --git a/scipost/templates/scipost/search.html b/scipost/templates/scipost/search.html
index 419dcf4f430bce230a5541a72dc2719b334ba095..050dbafd542fae2959975c7f27117d6c7cece837 100644
--- a/scipost/templates/scipost/search.html
+++ b/scipost/templates/scipost/search.html
@@ -105,7 +105,7 @@
   <h3>Theses:</h3>
   <ul>
     {% for thesislink in thesislink_search_list %}
-    {{ thesislink.header_as_li }}
+        {% include 'theses/_thesislink_header_as_li.html' with thesislink=thesislink %}
     {% endfor %}
   </ul>
   {% endif %}
diff --git a/scipost/templates/scipost/vet_authorship_claims.html b/scipost/templates/scipost/vet_authorship_claims.html
index a5e85d78262a227608d194e51de695008a351f42..ba894d8b264de40d938f78bc9eb1677f8646f19d 100644
--- a/scipost/templates/scipost/vet_authorship_claims.html
+++ b/scipost/templates/scipost/vet_authorship_claims.html
@@ -27,7 +27,7 @@
     {{ claim.commentary.header_as_li }}
     {% elif claim.thesislink %}
     <h4>Contributor {{ claim.claimant.user.first_name }} {{ claim.claimant.user.last_name }} claims to be an author of Thesis:</h4>
-    {{ claim.thesislink.header_as_li }}
+    {% include 'theses/_thesislink_header_as_li.html' with thesislink=claim.thesislink %}
     {% endif %}
 
     <form action="{% url 'scipost:vet_authorship_claim' claim_id=claim.id claim=1%}" method="post">
diff --git a/scipost/templatetags/request_filters.py b/scipost/templatetags/request_filters.py
new file mode 100644
index 0000000000000000000000000000000000000000..d28599705956a345fecefd81acf1492296bc6e14
--- /dev/null
+++ b/scipost/templatetags/request_filters.py
@@ -0,0 +1,11 @@
+from django import template
+from urllib.parse import urlencode
+
+register = template.Library()
+
+
+@register.simple_tag(takes_context=True)
+def url_replace(context, **kwargs):
+    query = context['request'].GET.dict()
+    query.update(kwargs)
+    return urlencode(query)
diff --git a/strings/__init__.py b/strings/__init__.py
index f51088e3aeecb9d059e14d341717c77936519e6c..acb0df6ceafab9bb8a85bae4049a9ccb7fdc8cec 100644
--- a/strings/__init__.py
+++ b/strings/__init__.py
@@ -7,6 +7,9 @@ acknowledge_request_commentary = (
     "Your request will soon be handled by an Editor. <br>"
     "Thank you for your request for a Commentary Page."
 )
+acknowledge_submit_comment = (
+    "Thank you for contributing a Comment. It will soon be vetted by an Editor."
+)
 
 # Arxiv response is not valid
 arxiv_caller_errormessages = {
diff --git a/submissions/factories.py b/submissions/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..1abdf6974e4c7caa28ae6ebcb49a9eb1096df83a
--- /dev/null
+++ b/submissions/factories.py
@@ -0,0 +1,26 @@
+import factory
+
+from scipost.factories import ContributorFactory
+from common.helpers import random_arxiv_identifier_with_version_number
+
+from .models import Submission
+
+
+class SubmissionFactory(factory.django.DjangoModelFactory):
+    class Meta:
+        model = Submission
+
+    author_list = factory.Faker('name')
+    submitted_by = factory.SubFactory(ContributorFactory)
+    submitted_to_journal = 'SciPost Physics'
+    title = factory.Faker('bs')
+    abstract = factory.Faker('text')
+    arxiv_link = factory.Faker('uri')
+    arxiv_identifier_w_vn_nr = factory.Sequence(lambda n: random_arxiv_identifier_with_version_number())
+    domain = 'E'
+
+
+class EICassignedSubmissionFactory(SubmissionFactory):
+    status = 'EICassigned'
+    editor_in_charge = factory.SubFactory(ContributorFactory)
+    open_for_commenting = True
diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html
index 68750602f0e9eddaf708bac9af84bef5b2d2d317..3e793e6ced6e4fdf739845e90fc5ef1584e0fb95 100644
--- a/submissions/templates/submissions/submission_detail.html
+++ b/submissions/templates/submissions/submission_detail.html
@@ -205,7 +205,7 @@
 				    <p>
 				      <a target="_blank" href="{{ reply.file_attachment.url }}">
 					{% if reply.file_attachment|is_image %}
-					<img class="attachment attachment-comment" src="{{ reply.file_attachment.url }}">                                                                              
+					<img class="attachment attachment-comment" src="{{ reply.file_attachment.url }}">
 					{% else %}
 					{{ reply.file_attachment|filename }}<br><small>{{ reply.file_attachment.size|filesizeformat }}</small>
 					{% endif %}
@@ -304,37 +304,8 @@
 
 {% endif %}
 
-
-
 {% include 'scipost/comments_block.html' %}
 
-{% if user.is_authenticated and submission.open_for_commenting and perms.scipost.can_submit_comments %}
-<hr>
-<div id="contribute_comment">
-    <div class="row">
-        <div class="col-12">
-            <div class="panel">
-                <h2>Contribute a Comment:</h2>
-            </div>
-        </div>
-    </div>
-    <div class="row">
-        <div class="col-12">
-            <form enctype="multipart/form-data" action="{% url 'submissions:submission' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}" method="post">
-                {% csrf_token %}
-                {% load crispy_forms_tags %}
-                {% crispy form %}
-            </form>
-        </div>
-    </div>
-
-    <div class="row">
-        <div class="col-12">
-            <h3>Preview of your comment:</h3>
-            <p id="preview-comment_text"></p>
-        </div>
-    </div>
-</div>
-{% endif %}
+{% include 'comments/new_comment.html' with object_id=submission.id type_of_object='submission' open_for_commenting=submission.open_for_commenting %}
 
 {% endblock content %}
diff --git a/submissions/templates/submissions/submissions.html b/submissions/templates/submissions/submissions.html
index ff662cd12f5c49cb141c879400d3a6fc60072bfd..0307dacacd39c88bd18e39a2e497de1c92868d6e 100644
--- a/submissions/templates/submissions/submissions.html
+++ b/submissions/templates/submissions/submissions.html
@@ -1,6 +1,8 @@
 {% extends 'scipost/base.html' %}
 
 {% load bootstrap %}
+{% load submissions_extras %}
+{% load request_filters %}
 
 {% block pagetitle %}: Submissions{% endblock pagetitle %}
 
@@ -16,8 +18,7 @@
     <div class="col-md-4">
         <div class="panel page-header-panel">
           <h2>Search SciPost Submissions:</h2>
-          <form action="{% url 'submissions:submissions' %}" class="small" method="post">
-            {% csrf_token %}
+          <form action="{% url 'submissions:submissions' %}" class="small" method="get">
             {{ form|bootstrap:'4,8,sm' }}
             <input class="btn btn-sm btn-secondary" type="submit" name="Submit" />
           </form>
@@ -33,50 +34,37 @@
     </div>
 </div>
 
-{% if submission_search_list or form.has_changed %}
 <div class="row">
     <div class="col-12">
         <hr class="hr12">
-        <h3>Search results:</h3>
-        {% if submission_search_list %}
+        {% if recent %}
+          <h2>Recent Submissions:</h2>
+        {% elif browse %}
+          <h2>Submissions in {{ discipline }} in the last {{ nrweeksback }} week{% if nrweeksback == '1' %}{% else %}s{% endif %}:</h2>
+        {% else %}
+          <h2>Search results:</h3>
+        {% endif %}
+        {% if object_list %}
+            {% if is_paginated %}
+              <p>
+              {% if page_obj.has_previous %}
+                <a href="?{% url_replace page=page_obj.previous_page_number %}">Previous</a>
+              {% endif %}
+              Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
+              {% if page_obj.has_next %}
+                <a href="?{% url_replace page=page_obj.next_page_number %}">Next</a>
+              {% endif %}
+              </p>
+            {% endif %}
             <ul>
-                {% for submission in submission_search_list %}
+                {% for submission in object_list %}
                 {{ submission.header_as_li }}
                 {% endfor %}
             </ul>
-        {% elif form.has_changed %}
+        {% else %}
             <h3>No match found for your search query.</h3>
         {% endif %}
     </div>
 </div>
-{% endif %}
-
-{% if submission_recent_list %}
-<div class="row">
-    <div class="col-12">
-        <hr class="hr12">
-        <h2>Recent Submissions:</h2>
-        <ul>
-            {% for submission in submission_recent_list %}
-            {{ submission.header_as_li }}
-            {% endfor %}
-        </ul>
-    </div>
-</div>
-{% endif %}
-
-{% if submission_browse_list %}
-<div class="row">
-    <div class="col-12">
-        <hr class="hr12">
-        <h2>Submissions in {{ discipline }} in the last {{ nrweeksback }} week{% if nrweeksback == '1' %}{% else %}s{% endif %}:</h2>
-        <ul>
-            {% for submission in submission_browse_list %}
-            {{ submission.header_as_li }}
-            {% endfor %}
-        </ul>
-    </div>
-</div>
-{% endif %}
 
 {% endblock content %}
diff --git a/submissions/templatetags/submissions_extras.py b/submissions/templatetags/submissions_extras.py
index 0ae2ea8d199f6414894e0256bc3a01955fba13eb..7460a3c30745b62cf6f7ce4f29aff1f7bc5ab83b 100644
--- a/submissions/templatetags/submissions_extras.py
+++ b/submissions/templatetags/submissions_extras.py
@@ -3,6 +3,7 @@ import datetime
 from django import template
 from django.utils import timezone
 from django.utils.timesince import timesince
+from urllib.parse import urlencode
 
 from submissions.models import SUBMISSION_STATUS_OUT_OF_POOL
 from submissions.models import Submission
diff --git a/submissions/test_views.py b/submissions/test_views.py
index 5eef315b06ebc633dbb967284d7d0336a8073e17..bb177e9292a3dc57e2c9a01a91c3a161e47ffb89 100644
--- a/submissions/test_views.py
+++ b/submissions/test_views.py
@@ -3,6 +3,8 @@ from django.test import Client
 from submissions.views import *
 import django.core.urlresolvers
 
+from .factories import EICassignedSubmissionFactory
+
 
 class PrefillUsingIdentifierTest(TestCase):
     fixtures = ['permissions', 'groups', 'contributors']
@@ -45,3 +47,19 @@ class SubmitManuscriptTest(TestCase):
                                {**params, **extras})
 
         self.assertEqual(response.status_code, 200)
+
+
+class SubmissionDetailTest(TestCase):
+    fixtures = ['permissions', 'groups']
+
+    def setUp(self):
+        self.client = Client()
+        self.submission = EICassignedSubmissionFactory()
+        self.target = reverse(
+            'submissions:submission',
+            kwargs={'arxiv_identifier_w_vn_nr': self.submission.arxiv_identifier_w_vn_nr}
+        )
+
+    def test_status_code_200(self):
+        response = self.client.get(self.target)
+        self.assertEqual(response.status_code, 200)
diff --git a/submissions/urls.py b/submissions/urls.py
index 1b3ee7bbc3fad480b1f0ac5698a521abfeae6603..2c19bc0054f9299414f9ff48b2a2dd9e1cf19cb4 100644
--- a/submissions/urls.py
+++ b/submissions/urls.py
@@ -5,9 +5,9 @@ from . import views
 
 urlpatterns = [
     # Submissions
-    url(r'^$', views.submissions, name='submissions'),
-    url(r'^(?P<to_journal>[a-zA-Z]+)/$', views.submissions, name='submissions'),
-    url(r'^browse/(?P<discipline>[a-z]+)/(?P<nrweeksback>[0-9]+)/$', views.browse, name='browse'),
+    url(r'^$', views.SubmissionListView.as_view(), name='submissions'),
+    url(r'^(?P<to_journal>[a-zA-Z]+)/$', views.SubmissionListView.as_view(), name='submissions'),
+    url(r'^browse/(?P<discipline>[a-z]+)/(?P<nrweeksback>[0-9]+)/$', views.SubmissionListView.as_view(), name='browse'),
     url(r'^sub_and_ref_procedure$',
         TemplateView.as_view(template_name='submissions/sub_and_ref_procedure.html'),
         name='sub_and_ref_procedure'),
diff --git a/submissions/views.py b/submissions/views.py
index 451a8f47f1f4a8a6be177563711c5da0f0388666..2681da5f9dfafd7d11f42ee3bb6f18a085af8dc5 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -10,8 +10,10 @@ from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render, redirect
 from django.template import Template, Context
 from django.utils import timezone
+from django.utils.decorators import method_decorator
 
 from guardian.decorators import permission_required_or_403
+from guardian.mixins import LoginRequiredMixin, PermissionRequiredMixin
 from guardian.shortcuts import assign_perm
 
 from .models import Submission, EICRecommendation, EditorialAssignment,\
@@ -38,106 +40,18 @@ from strings import arxiv_caller_errormessages_submissions
 from comments.forms import CommentForm
 
 from django.views.generic.edit import CreateView, FormView
+from django.views.generic.list import ListView
 
 
 ###############
 # SUBMISSIONS:
 ###############
 
-# @permission_required('scipost.can_submit_manuscript', raise_exception=True)
-# def prefill_using_identifier(request):
-#     if request.method == "POST":
-#         identifierform = SubmissionIdentifierForm(request.POST)
-#         if identifierform.is_valid():
-#             # Use the ArxivCaller class to make the API calls
-#             caller = ArxivCaller()
-#             caller.process(identifierform.cleaned_data['identifier'])
-#
-#             if caller.is_valid():
-#                 # Arxiv response is valid and can be shown
-#
-#                 metadata = caller.metadata
-#                 is_resubmission = caller.resubmission
-#                 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 = {'is_resubmission': is_resubmission,
-#                                'metadata': metadata,
-#                                'title': title, 'author_list': authorlist,
-#                                'arxiv_identifier_w_vn_nr': caller.identifier_with_vn_nr,
-#                                'arxiv_identifier_wo_vn_nr': caller.identifier_without_vn_nr,
-#                                'arxiv_vn_nr': caller.version_nr,
-#                                'arxiv_link': arxiv_link, 'abstract': abstract}
-#                 if is_resubmission:
-#                     resubmessage = ('There already exists a preprint with this arXiv identifier '
-#                                     'but a different version number. \nYour Submission will be '
-#                                     'handled as a resubmission.')
-#                     initialdata['submitted_to_journal'] = previous_submissions[0].submitted_to_journal
-#                     initialdata['submission_type'] = previous_submissions[0].submission_type
-#                     initialdata['discipline'] = previous_submissions[0].discipline
-#                     initialdata['domain'] = previous_submissions[0].domain
-#                     initialdata['subject_area'] = previous_submissions[0].subject_area
-#                     initialdata['secondary_areas'] = previous_submissions[0].secondary_areas
-#                     initialdata['referees_suggested'] = previous_submissions[0].referees_suggested
-#                     initialdata['referees_flagged'] = previous_submissions[0].referees_flagged
-#                 else:
-#                     resubmessage = ''
-#
-#                 form = SubmissionForm(initial=initialdata)
-#                 context = {'identifierform': identifierform,
-#                            'form': form,
-#                            'resubmessage': resubmessage}
-#                 return render(request, 'submissions/submit_manuscript.html', context)
-#
-#             else:
-#                 # Arxiv response is not valid
-#                 errormessages = {
-#                     'preprint_does_not_exist':
-#                         'A preprint associated to this identifier does not exist.',
-#                     'paper_published_journal_ref':
-#                         ('This paper has been published as ' + caller.arxiv_journal_ref +
-#                          '. You cannot submit it to SciPost anymore.'),
-#                     'paper_published_doi':
-#                         ('This paper has been published under DOI ' + caller.arxiv_doi +
-#                          '. You cannot submit it to SciPost anymore.'),
-#                     'arxiv_timeout': 'Arxiv did not respond in time. Please try again later',
-#                     'arxiv_bad_request':
-#                         ('There was an error with requesting identifier ' +
-#                          caller.identifier_with_vn_nr +
-#                          ' from Arxiv. Please check the identifier and try again.'),
-#                     'previous_submission_undergoing_refereeing':
-#                         ('There exists a preprint with this arXiv identifier '
-#                          'but an earlier version number, which is still undergoing '
-#                          'peer refereeing.'
-#                          'A resubmission can only be performed after request '
-#                          'from the Editor-in-charge. Please wait until the '
-#                          'closing of the previous refereeing round and '
-#                          'formulation of the Editorial Recommendation '
-#                          'before proceeding with a resubmission.'),
-#                     'preprint_already_submitted': 'This preprint version has already been submitted to SciPost.'
-#                 }
-#
-#                 identifierform.add_error(None, errormessages[caller.errorcode])
-#                 form = SubmissionForm()
-#                 return render(request, 'submissions/submit_manuscript.html',
-#                               {'identifierform': identifierform, 'form': form})
-#         else:
-#             form = SubmissionForm()
-#             return render(request, 'submissions/submit_manuscript.html',
-#                           {'identifierform': identifierform, 'form': form})
-#     # return redirect(reverse('submissions:submit_manuscript'))
-#     form = SubmissionForm()
-#     identifierform = SubmissionIdentifierForm()
-#     return render(request, 'submissions/submit_manuscript.html',
-#                   {'identifierform': identifierform, 'form': form})
-
-
-class PrefillUsingIdentifierView(FormView):
+class PrefillUsingIdentifierView(PermissionRequiredMixin, FormView):
     form_class = SubmissionIdentifierForm
     template_name = 'submissions/prefill_using_identifier.html'
+    permission_required = 'scipost.can_submit_manuscript'
+    raise_exception = True
 
     def post(self, request):
         identifierform = SubmissionIdentifierForm(request.POST)
@@ -196,7 +110,7 @@ class PrefillUsingIdentifierView(FormView):
                           {'form': identifierform})
 
 
-class SubmissionCreateView(CreateView):
+class SubmissionCreateView(PermissionRequiredMixin, CreateView):
     model = Submission
     fields = [
         'is_resubmission',
@@ -222,6 +136,11 @@ class SubmissionCreateView(CreateView):
     ]
 
     template_name = 'submissions/new_submission.html'
+    permission_required = 'scipost.can_submit_manuscript'
+    # Required to use Guardian's CBV PermissionRequiredMixin with a CreateView
+    # (see https://github.com/django-guardian/django-guardian/pull/433)
+    permission_object = None
+    raise_exception = True
 
     def get(self, request):
         # Only use prefilled forms
@@ -283,170 +202,66 @@ class SubmissionCreateView(CreateView):
         )
 
 
-# @login_required
-# @permission_required('scipost.can_submit_manuscript', raise_exception=True)
-# @transaction.atomic
-# def submit_manuscript(request):
-#     if request.method == 'POST':
-#         form = SubmissionForm(request.POST)
-#         if form.is_valid():
-#             submitted_by = Contributor.objects.get(user=request.user)
-#             # Verify if submitter is among the authors
-#            if submitted_by.user.last_name not in form.cleaned_data['author_list']:
-#                errormessage = ('Your name does not match that of any of the authors. '
-#                                'You are not authorized to submit this preprint.')
-#                identifierform = SubmissionIdentifierForm()
-#                return render(request, 'submissions/submit_manuscript.html',
-#                              {'identifierform': identifierform, 'form': form,
-#                               'errormessage': errormessage})
-#             submission = Submission(
-#                 is_current=True,
-#                 is_resubmission=form.cleaned_data['is_resubmission'],
-#                 submitted_by=submitted_by,
-#                 submitted_to_journal=form.cleaned_data['submitted_to_journal'],
-#                 submission_type=form.cleaned_data['submission_type'],
-#                 discipline=form.cleaned_data['discipline'],
-#                 domain=form.cleaned_data['domain'],
-#                 subject_area=form.cleaned_data['subject_area'],
-#                 secondary_areas=form.cleaned_data['secondary_areas'],
-#                 status='unassigned',
-#                 title=form.cleaned_data['title'],
-#                 author_list=form.cleaned_data['author_list'],
-#                 abstract=form.cleaned_data['abstract'],
-#                 arxiv_identifier_w_vn_nr=form.cleaned_data['arxiv_identifier_w_vn_nr'],
-#                 arxiv_identifier_wo_vn_nr=form.cleaned_data['arxiv_identifier_wo_vn_nr'],
-#                 arxiv_vn_nr=form.cleaned_data['arxiv_vn_nr'],
-#                 arxiv_link=form.cleaned_data['arxiv_link'],
-#                 metadata=form.cleaned_data['metadata'],
-#                 submission_date=timezone.now(),
-#                 remarks_for_editors=form.cleaned_data['remarks_for_editors'],
-#                 referees_suggested=form.cleaned_data['referees_suggested'],
-#                 referees_flagged=form.cleaned_data['referees_flagged'],
-#             )
-#             submission.save()
-#             submission.authors.add(submitted_by)  # must be author to be able to submit
-#             submission.save()
-#             # If this is a resubmission, mark previous submissions as deprecated:
-#             if form.cleaned_data['is_resubmission']:
-#                 previous_submissions = Submission.objects.filter(
-#                     arxiv_identifier_wo_vn_nr=form.cleaned_data['arxiv_identifier_wo_vn_nr']
-#                 ).exclude(pk=submission.id).order_by('-arxiv_vn_nr')
-#                 for sub in previous_submissions:
-#                     sub.is_current = False
-#                     sub.open_for_reporting = False
-#                     sub.status = 'resubmitted'
-#                     sub.save()
-#                 # Handle this submission in same way as if assignment had been accepted
-#                 submission.open_for_reporting = True
-#                 deadline = timezone.now() + datetime.timedelta(days=28)  # for papers
-#                 if submission.submitted_to_journal == 'SciPost Physics Lecture Notes':
-#                     deadline += datetime.timedelta(days=28)
-#                 submission.reporting_deadline = deadline
-#                 submission.open_for_commenting = True
-#                 submission.latest_activity = timezone.now()
-#                 # We keep the same (most recent) Editor-in-charge by default
-#                 submission.editor_in_charge = previous_submissions[0].editor_in_charge
-#                 submission.status = 'EICassigned'
-#                 # Keep the info about authors:
-#                 for author in previous_submissions[0].authors.all():
-#                     submission.authors.add(author)
-#                 for author in previous_submissions[0].authors_claims.all():
-#                     submission.authors_claims.add(author)
-#                 for author in previous_submissions[0].authors_false_claims.all():
-#                     submission.authors_false_claims.add(author)
-#                 submission.author_comments = form.cleaned_data['author_comments']
-#                 submission.list_of_changes = form.cleaned_data['list_of_changes']
-#                 submission.save()
-#                 assignment = EditorialAssignment(
-#                     submission=submission,
-#                     to=submission.editor_in_charge,
-#                     accepted=True,
-#                     date_created=timezone.now(),
-#                     date_answered=timezone.now(),
-#                 )
-#                 assignment.save()
-#                 SubmissionUtils.load({'submission': submission})
-#                 SubmissionUtils.send_authors_resubmission_ack_email()
-#                 assign_perm('can_take_editorial_actions', submission.editor_in_charge.user, submission)
-#                 ed_admins = Group.objects.get(name='Editorial Administrators')
-#                 assign_perm('can_take_editorial_actions', ed_admins, submission)
-#                 SubmissionUtils.send_EIC_reappointment_email()
-#             else:
-#                 SubmissionUtils.load({'submission': submission})
-#                 SubmissionUtils.send_authors_submission_ack_email()
-#
-#             # return HttpResponseRedirect(reverse('submissions:submit_manuscript_ack'))
-#             context = {'ack_header': 'Thank you for your Submission to SciPost',
-#                        'ack_message': 'Your Submission will soon be handled by an Editor. ',
-#                        'followup_message': 'Return to your ',
-#                        'followup_link': reverse('scipost:personal_page'),
-#                        'followup_link_label': 'personal page'}
-#             return render(request, 'scipost/acknowledgement.html', context)
-#         else:  # form is invalid
-#             pass
-#     else:
-#         form = SubmissionForm()
-#     identifierform = SubmissionIdentifierForm()
-#     return render(request, 'submissions/submit_manuscript.html',
-#                   {'identifierform': identifierform, 'form': form})
-
-
-def submissions(request, to_journal=None):
-    """
-    Main method for viewing Submissions.
-    """
-    if request.method == 'POST':
-        form = SubmissionSearchForm(request.POST)
-        if form.is_valid() and form.has_changed():
-            submission_search_list = Submission.objects.filter(
-                title__icontains=form.cleaned_data['title_keyword'],
-                author_list__icontains=form.cleaned_data['author'],
-                abstract__icontains=form.cleaned_data['abstract_keyword'],
-            ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED,
-                      ).order_by('-submission_date')
-        else:
-            submission_search_list = []
-
-    else:
-        form = SubmissionSearchForm()
-        submission_search_list = []
-
-    submission_recent_list = Submission.objects.filter(
-        latest_activity__gte=timezone.now() + datetime.timedelta(days=-60)
-    ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED
-              ).exclude(is_current=False).order_by('-submission_date')
-    # If doing a journal-specific listing:
-    if to_journal is not None:
-        submission_recent_list.filter(submitted_to_journal=to_journal)
-    context = {'form': form, 'submission_search_list': submission_search_list,
-               'submission_recent_list': submission_recent_list}
-    return render(request, 'submissions/submissions.html', context)
-
-
-def browse(request, discipline, nrweeksback):
-    if request.method == 'POST':
-        form = SubmissionSearchForm(request.POST)
-        if form.is_valid() and form.has_changed():
-            submission_search_list = Submission.objects.filter(
-                title__icontains=form.cleaned_data['title_keyword'],
-                author_list__icontains=form.cleaned_data['author'],
-                abstract__icontains=form.cleaned_data['abstract_keyword'],
-            ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED,
-                      ).order_by('-submission_date')
+class SubmissionListView(ListView):
+    model = Submission
+    template_name = 'submissions/submissions.html'
+    form = SubmissionSearchForm()
+    submission_search_list = []
+    paginate_by = 10
+
+    def get_queryset(self):
+        if 'to_journal' in self.kwargs:
+            queryset = Submission.objects.filter(
+                latest_activity__gte=timezone.now() + datetime.timedelta(days=-60),
+                submitted_to_journal=self.kwargs['to_journal']
+            ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED
+                      ).exclude(is_current=False).order_by('-submission_date')
+            # Submission.objects.filter(submitted_to_journal=self.kwargs['to_journal'])
+        elif 'discipline' in self.kwargs and 'nrweeksback' in self.kwargs:
+            discipline = self.kwargs['discipline']
+            nrweeksback = self.kwargs['nrweeksback']
+            queryset = Submission.objects.filter(
+                discipline=discipline,
+                latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback)))
+        elif 'Submit' in self.request.GET:
+            queryset = Submission.objects.filter(
+                title__icontains=self.request.GET.get('title_keyword', ''),
+                author_list__icontains=self.request.GET.get('author', ''),
+                abstract__icontains=self.request.GET.get('abstract_keyword', '')
+            )
         else:
-            submission_search_list = []
-        context = {'form': form, 'submission_search_list': submission_search_list}
-        return HttpResponseRedirect(request, 'submissions/submissions.html', context)
-    else:
-        form = SubmissionSearchForm()
-    submission_browse_list = Submission.objects.filter(
-        discipline=discipline,
-        latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback))
-    ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED
-              ).exclude(is_current=False).order_by('-submission_date')
-    context = {'form': form, 'discipline': discipline, 'nrweeksback': nrweeksback,
-               'submission_browse_list': submission_browse_list}
-    return render(request, 'submissions/submissions.html', context)
+            queryset = Submission.objects.filter(
+                latest_activity__gte=timezone.now() + datetime.timedelta(days=-60)
+            ).exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED
+                      ).exclude(is_current=False).order_by('-submission_date')
+
+        queryset = queryset.exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED,
+                                    ).order_by('-submission_date')
+        return queryset
+
+    def get_context_data(self, **kwargs):
+        # Call the base implementation first to get a context
+        context = super(SubmissionListView, self).get_context_data(**kwargs)
+
+        # Keep any search fields previously filled
+        initialdata = {
+            'author': self.request.GET.get('author', ''),
+            'title_keyword': self.request.GET.get('title_keyword', ''),
+            'abstract_keyword': self.request.GET.get('abstract_keyword', '')
+        }
+        context['form'] = SubmissionSearchForm(initial=initialdata)
+
+        # To customize display in the template
+        if 'to_journal' in self.kwargs:
+            context['to_journal'] = self.kwargs['to_journal']
+        if 'discipline' in self.kwargs:
+            context['discipline'] = self.kwargs['discipline']
+            context['nrweeksback'] = self.kwargs['nrweeksback']
+            context['browse'] = True
+        elif 'Submit' not in self.request.GET:
+            context['recent'] = True
+
+        return context
 
 
 def submission_detail_wo_vn_nr(request, arxiv_identifier_wo_vn_nr):
@@ -470,41 +285,8 @@ def submission_detail(request, arxiv_identifier_w_vn_nr):
     other_versions = Submission.objects.filter(
         arxiv_identifier_wo_vn_nr=submission.arxiv_identifier_wo_vn_nr
     ).exclude(pk=submission.id)
-    if request.method == 'POST':
-        form = CommentForm(request.POST, request.FILES)
-        if form.is_valid():
-            author = Contributor.objects.get(user=request.user)
-            newcomment = Comment(
-                submission=submission,
-                author=author,
-                is_rem=form.cleaned_data['is_rem'],
-                is_que=form.cleaned_data['is_que'],
-                is_ans=form.cleaned_data['is_ans'],
-                is_obj=form.cleaned_data['is_obj'],
-                is_rep=form.cleaned_data['is_rep'],
-                is_val=form.cleaned_data['is_val'],
-                is_lit=form.cleaned_data['is_lit'],
-                is_sug=form.cleaned_data['is_sug'],
-                file_attachment=form.cleaned_data['file_attachment'],
-                comment_text=form.cleaned_data['comment_text'],
-                remarks_for_editors=form.cleaned_data['remarks_for_editors'],
-                date_submitted=timezone.now(),
-            )
-            newcomment.save()
-            author.nr_comments = Comment.objects.filter(author=author).count()
-            author.save()
-            context = {'ack_header': 'Thank you for contributing a Comment.',
-                       'ack_message': 'It will soon be vetted by an Editor.',
-                       'followup_message': 'Back to the ',
-                       'followup_link': reverse(
-                           'submissions:submission',
-                           kwargs={'arxiv_identifier_w_vn_nr': newcomment.submission.arxiv_identifier_w_vn_nr}
-                       ),
-                       'followup_link_label': ' Submission page you came from'
-                       }
-            return render(request, 'scipost/acknowledgement.html', context)
-    else:
-        form = CommentForm()
+
+    form = CommentForm()
 
     reports = submission.report_set.all()
     try:
diff --git a/theses/factories.py b/theses/factories.py
index 550d5a73e004b69b8e40ac52ed2dc4618e9309c9..bf4a32440a8b5ddbbe1932c0bb2a27feaca7a464 100644
--- a/theses/factories.py
+++ b/theses/factories.py
@@ -23,6 +23,11 @@ class ThesisLinkFactory(factory.django.DjangoModelFactory):
     domain = 'ET'
 
 
+class VettedThesisLinkFactory(ThesisLinkFactory):
+    vetted_by = factory.SubFactory(ContributorFactory)
+    vetted = True
+
+
 class VetThesisLinkFormFactory(FormFactory):
     class Meta:
         model = VetThesisLinkForm
diff --git a/theses/forms.py b/theses/forms.py
index 6f8a0e2c1757dec0992cec6e55417a3aad15a17c..3f5dfcc0e5784131d4e8a0d9f7039bad5b07064a 100644
--- a/theses/forms.py
+++ b/theses/forms.py
@@ -108,5 +108,3 @@ class ThesisLinkSearchForm(forms.Form):
     title_keyword = forms.CharField(max_length=100, label="Title", required=False)
     abstract_keyword = forms.CharField(max_length=1000, required=False, label="Abstract")
     supervisor = forms.CharField(max_length=100, required=False, label="Supervisor")
-    from_date = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}), required=False)
-    to_date = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}), required=False)
diff --git a/theses/managers.py b/theses/managers.py
index ecd55472d300655f05c42f7c0fda4b4415b4a79b..79e971b796332fa2fd46270961a988389b00d0f2 100644
--- a/theses/managers.py
+++ b/theses/managers.py
@@ -6,15 +6,11 @@ from django.utils import timezone
 
 class ThesisLinkManager(models.Manager):
     def search_results(self, form):
-        from_date = form.cleaned_data['from_date'] if form.cleaned_data['from_date'] else datetime.date(1600, 1, 1)
-        to_date = form.cleaned_data['to_date'] if form.cleaned_data['to_date'] else timezone.now()
-
         return self.vetted().filter(
             title__icontains=form.cleaned_data['title_keyword'],
             author__icontains=form.cleaned_data['author'],
             abstract__icontains=form.cleaned_data['abstract_keyword'],
             supervisor__icontains=form.cleaned_data['supervisor'],
-            defense_date__range=(from_date, to_date)
         ).order_by('-defense_date')
 
     def latest(self, n):
diff --git a/theses/templates/theses/_thesislink_information.html b/theses/templates/theses/_thesislink_information.html
index 1c4c0e7592f2d3902dc4273fb1475df1e8ffb962..93c9c91fcfc87b5fad54d1ce52857cb56e446842 100644
--- a/theses/templates/theses/_thesislink_information.html
+++ b/theses/templates/theses/_thesislink_information.html
@@ -12,7 +12,7 @@
         {% if thesislink.author_as_cont.all %}
             {% for author in thesislink.author_as_cont.all %}
                 <td><a href= {% url 'scipost:contributor_info' author.id %}>
-                        author.user.first_name author.user.last_name
+                        {{ author.user.first_name }} {{ author.user.last_name }}
                 </a></td>
             {% endfor %}
         {% else %}
diff --git a/theses/templates/theses/thesis_detail.html b/theses/templates/theses/thesis_detail.html
index f355fc701ff43ee369ebbfe9f8fc5d13e60f7270..edde03144f188effb6c6fad2c30bf6783b4ac6b9 100644
--- a/theses/templates/theses/thesis_detail.html
+++ b/theses/templates/theses/thesis_detail.html
@@ -50,30 +50,6 @@
 
 {% include 'scipost/comments_block.html' %}
 
-{% if user.is_authenticated and thesislink.open_for_commenting and perms.scipost.can_submit_comments %}
-<hr />
-
-<div class="row">
-    <div class="col-12">
-        <div class="panel">
-            <h2>Contribute a Comment:</h2>
-        </div>
-    </div>
-</div>
-<div class="row">
-    <div class="col-12">
-        <form enctype="multipart/form-data" action="{% url 'theses:thesis' thesislink_id=thesislink.id %}" method="post">
-          {% csrf_token %}
-          {% load crispy_forms_tags %}
-          {% crispy form %}
-        </form>
-    </div>
-    <div class="col-12">
-        <h3>Preview of your comment:</h3>
-        <p id="preview-comment_text"></p>
-    </div>
-</div>
-
-{% endif %}
+{% include 'comments/new_comment.html' with object_id=thesislink.id type_of_object='thesislink' open_for_commenting=thesislink.open_for_commenting %}
 
 {% endblock content %}
diff --git a/theses/test_views.py b/theses/test_views.py
index a237707266bf81c328ba2344ff39f5f28533e1b3..8f6d5f202018e73ecbec408158447ede95c0cc6b 100644
--- a/theses/test_views.py
+++ b/theses/test_views.py
@@ -13,7 +13,7 @@ from comments.factories import CommentFactory
 from comments.forms import CommentForm
 from comments.models import Comment
 from .views import RequestThesisLink, VetThesisLink, thesis_detail
-from .factories import ThesisLinkFactory, VetThesisLinkFormFactory
+from .factories import ThesisLinkFactory, VettedThesisLinkFactory, VetThesisLinkFormFactory
 from .models import ThesisLink
 from .forms import VetThesisLinkForm
 from common.helpers import model_form_data
@@ -30,24 +30,6 @@ class TestThesisDetail(TestCase):
         response = client.post(target)
         self.assertEqual(response.status_code, 200)
 
-    def test_submitting_comment_creates_comment(self):
-        """ Valid Comment gets saved """
-
-        contributor = ContributorFactory()
-        thesislink = ThesisLinkFactory()
-        valid_comment_data = model_form_data(CommentFactory.build(), CommentForm)
-        target = reverse('theses:thesis', kwargs={'thesislink_id': thesislink.id})
-
-        comment_count = Comment.objects.filter(author=contributor).count()
-        self.assertEqual(comment_count, 0)
-
-        request = RequestFactory().post(target, valid_comment_data)
-        request.user = contributor.user
-        response = thesis_detail(request, thesislink_id=thesislink.id)
-
-        comment_count = Comment.objects.filter(author=contributor).count()
-        self.assertEqual(comment_count, 1)
-
 
 class TestRequestThesisLink(TestCase):
     fixtures = ['groups', 'permissions']
@@ -173,3 +155,26 @@ class TestVetThesisLinkRequests(TestCase):
         self.assertEqual(self.thesislink.vetted_by, contributor)
         self.assertEqual(len(mail.outbox), 1)
         self.assertEqual(mail.outbox[0].subject, 'SciPost Thesis Link activated')
+
+class TestTheses(TestCase):
+    fixtures = ['groups', 'permissions']
+
+    def setUp(self):
+        self.client = Client()
+        self.target = reverse('theses:theses')
+
+    def test_empty_search_query(self):
+        thesislink = VettedThesisLinkFactory()
+        response = self.client.get(self.target)
+        search_results = response.context["search_results"]
+        recent_theses = response.context["recent_theses"]
+        self.assertEqual(search_results, [])
+        self.assertEqual(recent_theses.exists(), True)
+
+    def test_search_query_on_author(self):
+        thesislink = VettedThesisLinkFactory()
+        form_data = {'author': thesislink.author}
+        response = self.client.get(self.target, form_data)
+        search_results = response.context['search_results']
+        self.assertTrue(thesislink in search_results)
+        self.assertEqual(len(search_results), 1)
diff --git a/theses/views.py b/theses/views.py
index b000a467910850a4be93dde39ab4db7acc7036d6..87d39a7f6d182336dc43c3e32c1de106acd1bdc7 100644
--- a/theses/views.py
+++ b/theses/views.py
@@ -131,48 +131,12 @@ def browse(request, discipline, nrweeksback):
 
 def thesis_detail(request, thesislink_id):
     thesislink = get_object_or_404(ThesisLink, pk=thesislink_id)
-    comments = thesislink.comment_set.all()
-    if request.method == 'POST':
-        form = CommentForm(request.POST)
-        if form.is_valid():
-            author = Contributor.objects.get(user=request.user)
-            new_comment = Comment(
-                thesislink=thesislink,
-                author=author,
-                is_rem=form.cleaned_data['is_rem'],
-                is_que=form.cleaned_data['is_que'],
-                is_ans=form.cleaned_data['is_ans'],
-                is_obj=form.cleaned_data['is_obj'],
-                is_rep=form.cleaned_data['is_rep'],
-                is_val=form.cleaned_data['is_val'],
-                is_lit=form.cleaned_data['is_lit'],
-                is_sug=form.cleaned_data['is_sug'],
-                comment_text=form.cleaned_data['comment_text'],
-                remarks_for_editors=form.cleaned_data['remarks_for_editors'],
-                date_submitted=timezone.now(),
-            )
-            new_comment.save()
-            author.nr_comments = Comment.objects.filter(author=author).count()
-            author.save()
-            context = {
-                'ack_header': 'Thank you for contributing a Comment.',
-                'ack_message': 'It will soon be vetted by an Editor.',
-                'followup_message': 'Back to the ',
-                'followup_link': reverse(
-                    'theses:thesis',
-                    kwargs={'thesislink_id': new_comment.thesislink.id}
-                ),
-                'followup_link_label': ' Thesis Link page you came from'
-            }
-            return render(request, 'scipost/acknowledgement.html', context)
-    else:
-        form = CommentForm()
+    form = CommentForm()
+
+    comments = thesislink.comment_set
+    author_replies = comments.filter(is_author_reply=True)
 
-    try:
-        author_replies = Comment.objects.filter(thesislink=thesislink, is_author_reply=True)
-    except Comment.DoesNotExist:
-        author_replies = ()
     context = {'thesislink': thesislink,
-               'comments': comments.filter(status__gte=1).order_by('date_submitted'),
+               'comments': comments.vetted().order_by('date_submitted'),
                'author_replies': author_replies, 'form': form}
     return render(request, 'theses/thesis_detail.html', context)