diff --git a/comments/managers.py b/comments/managers.py
index c66d7f93225adf6d9e3d1883e32a118bd69f5505..dc094232df4abcdc60b907f36d6fc894af72cf17 100644
--- a/comments/managers.py
+++ b/comments/managers.py
@@ -19,3 +19,6 @@ class CommentQuerySet(models.QuerySet):
 
     def author_replies(self):
         return self.filter(is_author_reply=True)
+
+    def publicly_visible(self):
+        return self.filter(anonymous=False, status__gte=1)
diff --git a/comments/models.py b/comments/models.py
index fda1b74ae3fab6b4abb779b9b6bf09748c36fb82..23ceb52188aef09e7d162499b8e09e2a88786b4f 100644
--- a/comments/models.py
+++ b/comments/models.py
@@ -27,8 +27,10 @@ US_NOTICE = 'Warning: This field is out of service and will be removed in the fu
 
 
 class Comment(TimeStampedModel):
-    """ A Comment is an unsollicited note, submitted by a Contributor,
-    on a particular publication or in reply to an earlier Comment. """
+    """ A Comment is an unsollicited note, submitted by a Contributor.
+
+    A Comment is pointed to a particular publication or in reply to an earlier Comment. It
+    may be l"""
 
     status = models.SmallIntegerField(default=STATUS_PENDING, choices=COMMENT_STATUS)
     vetted_by = models.ForeignKey('scipost.Contributor', blank=True, null=True,
@@ -151,16 +153,16 @@ class Comment(TimeStampedModel):
             assign_perm('comments.can_vet_comments', to_object.editor_in_charge.user, self)
 
     def get_author(self):
-        '''Get author, if and only if comment is not anonymous!!!'''
+        """Return Contributor instance of object if not anonymous."""
         if not self.anonymous:
             return self.author
         return None
 
     def get_author_str(self):
-        '''Get author string, if and only if comment is not anonymous!!!'''
+        """Return author string if not anonymous."""
         author = self.get_author()
         if author:
-            return author.user.first_name + ' ' + author.user.last_name
+            return '{} {}'.format(author.get_title_display(), author.user.last_name)
         return 'Anonymous'
 
     def update_opinions(self, contributor_id, opinion):
diff --git a/comments/templates/comments/_comment_card_content.html b/comments/templates/comments/_comment_card_content.html
index fb7dff65a51631ad07bb1211b5751b85ffdb217a..6fa4f459bd55ef6e161accab805c8689b5e973b3 100644
--- a/comments/templates/comments/_comment_card_content.html
+++ b/comments/templates/comments/_comment_card_content.html
@@ -2,10 +2,11 @@
     {% block card_block_header %}{% endblock %}
     <p class="card-text">
       {% if comment.anonymous %}
-      Anonymous:
+          Anonymous:
       {% else %}
-      <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a>:
+          <a href="{{ comment.author.get_absolute_url }}">{{ comment.get_author_str }}</a>:
       {% endif %}
+
         <a href="{{comment.get_absolute_url}}">
             "{{comment.comment_text|slice:'30'}}{% if comment.comment_text|length > 30 %}...{% endif %}"
         </a>
diff --git a/comments/templates/comments/_comment_card_extended_for_author.html b/comments/templates/comments/_comment_card_extended_for_author.html
index 51c26e39304d86a3b916dce310b43132f9792d83..5fa9fa5f645c02426aa4aecdc37530cb93c9b441 100644
--- a/comments/templates/comments/_comment_card_extended_for_author.html
+++ b/comments/templates/comments/_comment_card_extended_for_author.html
@@ -5,7 +5,11 @@
     </div>
 
     <p>"{{comment.comment_text|linebreaksbr}}"</p>
-    <p class="card-text">by <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a> in {{comment.content_type|capfirst}} on <a href="{{comment.content_object.get_absolute_url}}" class="pubtitleli">{{comment.title}}</a> {% if comment.content_object.author_list %} <span class="text-muted">by {{comment.content_object.author_list}}</span>{% endif %}</p>
+    {% if comment.anonymous %}
+        <p class="card-text">by Anonymous in {{comment.content_type|capfirst}} on <a href="{{comment.content_object.get_absolute_url}}" class="pubtitleli">{{comment.title}}</a> {% if comment.content_object.author_list %} <span class="text-muted">by {{comment.content_object.author_list}}</span>{% endif %}</p>
+    {% else %}
+        <p class="card-text">by <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a> in {{comment.content_type|capfirst}} on <a href="{{comment.content_object.get_absolute_url}}" class="pubtitleli">{{comment.title}}</a> {% if comment.content_object.author_list %} <span class="text-muted">by {{comment.content_object.author_list}}</span>{% endif %}</p>
+    {% endif %}
 
     {% comment %}
         Using 'by xxx' on non-submission comments here would be ambigious. Does the `by xxx` apply to the
diff --git a/comments/templates/comments/_comment_tex_template.html b/comments/templates/comments/_comment_tex_template.html
index cb39aa0d3c78f50d5fe9f2d75f618abadc9ce8a5..05d5e1479ade5d44ab1b41c9d15647940f1773d3 100644
--- a/comments/templates/comments/_comment_tex_template.html
+++ b/comments/templates/comments/_comment_tex_template.html
@@ -4,8 +4,8 @@ Received {{comment.date_submitted|date:'d-m-Y'}}\ \\
 {% endspaceless %}
 
 {% for subcomment in comment.nested_comments.vetted %}
-    \addcontentsline{toc}{subsection}{\protect\numberline{}{% if subcomment.is_author_reply %}Author Reply{% else %}Comment{% endif %} {{forloop.counter}} by {{subcomment.author.user.first_name}} {{subcomment.author.user.last_name}} }
+    \addcontentsline{toc}{subsection}{\protect\numberline{}{% if subcomment.is_author_reply %}Author Reply{% else %}Comment{% endif %} {{forloop.counter}} by {% if subcomment.anonymous %}Anonymous{% else %}{{subcomment.author.user.first_name}} {{subcomment.author.user.last_name}}{% endif %} }
 
-    \subsection*{ {% if subcomment.is_author_reply %}Author Reply{% else %}Comment{% endif %} {{forloop.parentloop.counter}}.{{forloop.counter}} by {{subcomment.author.user.first_name}} {{subcomment.author.user.last_name}} }
+    \subsection*{ {% if subcomment.is_author_reply %}Author Reply{% else %}Comment{% endif %} {{forloop.parentloop.counter}}.{{forloop.counter}} by {% if subcomment.anonymous %}Anonymous{% else %}{{subcomment.author.user.first_name}} {{subcomment.author.user.last_name}}{% endif %} }
     {% include 'comments/_comment_tex_template.html' with comment=subcomment %}
 {% endfor %}
diff --git a/comments/templates/partials/comments/comments_list.html b/comments/templates/partials/comments/comments_list.html
index ea3e9ac08e398d5ad273a886e34f42cd4e4f6f0d..6f35351e2a6b0e59a50c0c394113cc4040648c71 100644
--- a/comments/templates/partials/comments/comments_list.html
+++ b/comments/templates/partials/comments/comments_list.html
@@ -1,7 +1,7 @@
 {% if comments %}
     <ul class="{{ css_class|default:'' }}">
         {% for comment in comments %}
-            <li><a href="{{ comment.get_absolute_url }}"{% if target_blank %} target="_blank"{% endif %}>{% if comment.is_author_reply %}Author Reply{% else %}Comment{% endif %} by {{ comment.author.get_title_display }} {{ comment.author.user.last_name }} on {{ comment.date_submitted|date:'DATE_FORMAT' }}</a></li>
+            <li><a href="{{ comment.get_absolute_url }}"{% if target_blank %} target="_blank"{% endif %}>{% if comment.is_author_reply %}Author Reply{% else %}Comment{% endif %} by {{ comment.get_author_str }} on {{ comment.date_submitted|date:'DATE_FORMAT' }}</a></li>
             {% include 'partials/comments/comments_list.html' with comments=comment.nested_comments.vetted css_class='m-0 pl-4' %}
         {% endfor %}
     </ul>
diff --git a/comments/utils.py b/comments/utils.py
index 8ffa2e4fcf1b09c19ee53965ef0eae64baaa71e0..8737a8e0d20b60b23fc0f2d9e5efe4bb5a4f313c 100644
--- a/comments/utils.py
+++ b/comments/utils.py
@@ -8,7 +8,7 @@ from common.utils import BaseMailUtil
 
 
 def validate_file_extention(value, allowed_extentions):
-    '''Check if a filefield (value) has allowed extentions.'''
+    """Check if a filefield (value) has allowed extentions."""
     ext = os.path.splitext(value.name)[1]  # [0] returns path+filename
     return ext.lower() in allowed_extentions
 
@@ -19,20 +19,30 @@ class CommentUtils(BaseMailUtil):
 
     @classmethod
     def email_comment_vet_accepted_to_author(cls):
-        """
-        Send mail after Comment is vetted: `Accept`
+        """Send mail after Comment is vetted: `Accept`.
 
         Requires loading:
         comment -- Comment
         """
+        from submissions.models import Submission, Report
+
+        comment = cls._context['comment']
+        send_mail = True
+        if isinstance(comment.content_object, Submission):
+            send_mail = comment.author not in comment.content_object.authors.all()
+        elif isinstance(comment.content_object, Report):
+            send_mail = comment.author not in comment.content_object.submission.authors.all()
+
+        if not send_mail:
+            return
+
         cls._send_mail(cls, 'comment_vet_accepted',
-                       [cls._context['comment'].author.user.email],
+                       [comment.author.user.email],
                        'SciPost Comment published')
 
     @classmethod
     def email_comment_vet_rejected_to_author(cls, email_response=''):
-        """
-        Send mail after Comment is vetted: `Reject`
+        """Send mail after Comment is vetted: `Reject`.
 
         Requires loading:
         comment -- Comment
diff --git a/comments/views.py b/comments/views.py
index f916794d000fccb427ea44366310c4a58e7b11ed..69f65864535d70908d3b47e16dbfd6d06b08f6c1 100644
--- a/comments/views.py
+++ b/comments/views.py
@@ -76,10 +76,6 @@ def vet_submitted_comment(request, comment_id):
             comment.vetted_by = request.user.contributor
             comment.save()
 
-            # Send emails
-            CommentUtils.load({'comment': comment})
-            CommentUtils.email_comment_vet_accepted_to_author()
-
             # Update `latest_activity` fields
             content_object = comment.content_object
             content_object.latest_activity = timezone.now()
@@ -100,6 +96,10 @@ def vet_submitted_comment(request, comment_id):
                     SubmissionUtils.load({'submission': content_object.submission})
                     SubmissionUtils.send_author_comment_received_email()
 
+            # Send emails
+            CommentUtils.load({'comment': comment})
+            CommentUtils.email_comment_vet_accepted_to_author()
+
         elif form.cleaned_data['action_option'] == '2':
             # The comment request is simply rejected
             comment.status = int(form.cleaned_data['refusal_reason'])
diff --git a/mailing_lists/models.py b/mailing_lists/models.py
index 5d6bdfe0d80e987b1ade7a7afa459288cc54f5d5..e9ccc0b32da6c800d3179cda24d3a3adb32cc709 100644
--- a/mailing_lists/models.py
+++ b/mailing_lists/models.py
@@ -17,7 +17,7 @@ from .constants import MAIL_LIST_STATUSES, MAIL_LIST_STATUS_ACTIVE,\
 from .managers import MailListManager
 
 from scipost.behaviors import TimeStampedModel
-from scipost.constants import CONTRIBUTOR_NORMAL
+from scipost.constants import NORMAL_CONTRIBUTOR
 from scipost.models import Contributor
 
 
@@ -88,7 +88,7 @@ class MailchimpList(TimeStampedModel):
         # are not in the list yet.
         db_subscribers = (User.objects
                           .filter(contributor__isnull=False)
-                          .filter(is_active=True, contributor__status=CONTRIBUTOR_NORMAL)
+                          .filter(is_active=True, contributor__status=NORMAL_CONTRIBUTOR)
                           .filter(contributor__accepts_SciPost_emails=True,
                                   groups__in=self.allowed_groups.all(),
                                   email__isnull=False,
diff --git a/scipost/constants.py b/scipost/constants.py
index 44c02c3b232db385cc9015db6a4c691585d2c322..dd7a72d42083715dfc7d0fa3db8fbca06b4c86f1 100644
--- a/scipost/constants.py
+++ b/scipost/constants.py
@@ -29,8 +29,7 @@ SCIPOST_SUBJECT_AREAS = (
         ('Phys:NE', 'Nuclear Physics - Experiment'),
         ('Phys:NT', 'Nuclear Physics - Theory'),
         ('Phys:QP', 'Quantum Physics'),
-        ('Phys:SM', 'Statistical and Soft Matter Physics'),
-        )
+        ('Phys:SM', 'Statistical and Soft Matter Physics'))
      ),
     ('Astrophysics', (
         ('Astro:GA', 'Astrophysics of Galaxies'),
@@ -38,8 +37,7 @@ SCIPOST_SUBJECT_AREAS = (
         ('Astro:EP', 'Earth and Planetary Astrophysics'),
         ('Astro:HE', 'High Energy Astrophysical Phenomena'),
         ('Astro:IM', 'Instrumentation and Methods for Astrophysics'),
-        ('Astro:SR', 'Solar and Stellar Astrophysics'),
-        )
+        ('Astro:SR', 'Solar and Stellar Astrophysics'))
      ),
     ('Mathematics', (
         ('Math:AG', 'Algebraic Geometry'),
@@ -73,8 +71,7 @@ SCIPOST_SUBJECT_AREAS = (
         ('Math:RA', 'Rings and Algebras'),
         ('Math:SP', 'Spectral Theory'),
         ('Math:ST', 'Statistics Theory'),
-        ('Math:SG', 'Symplectic Geometry'),
-        )
+        ('Math:SG', 'Symplectic Geometry'))
      ),
     ('Computer Science', (
         ('Comp:AI', 'Artificial Intelligence'),
@@ -115,9 +112,8 @@ SCIPOST_SUBJECT_AREAS = (
         ('Comp:SE', 'Software Engineering'),
         ('Comp:SD', 'Sound'),
         ('Comp:SC', 'Symbolic Computation'),
-        ('Comp:SY', 'Systems and Control'),
-        )
-     ),
+        ('Comp:SY', 'Systems and Control'))
+     )
 )
 subject_areas_raw_dict = dict(SCIPOST_SUBJECT_AREAS)
 
@@ -126,25 +122,20 @@ subject_areas_dict = {}
 for k in subject_areas_raw_dict.keys():
     subject_areas_dict.update(dict(subject_areas_raw_dict[k]))
 
-CONTRIBUTOR_NEWLY_REGISTERED = 0
-CONTRIBUTOR_NORMAL = 1
-CONTRIBUTOR_STATUS = (
-    # status determine the type of Contributor:
-    # 0: newly registered (unverified; not allowed to submit, comment or vote)
-    # 1: contributor has been vetted through
-    #
-    # Negative status denotes rejected requests or:
-    # -1: not a professional scientist (>= PhD student in known university)
-    # -2: other account already exists for this person
-    # -3: barred from SciPost (abusive behaviour)
-    # -4: disabled account (deceased)
-    (CONTRIBUTOR_NEWLY_REGISTERED, 'newly registered'),
-    (CONTRIBUTOR_NORMAL, 'normal user'),
-    (-1, 'not a professional scientist'),  # Soon to be deprecated
-    (-2, 'other account already exists'),
-    (-3, 'barred from SciPost'),
-    (-4, 'account disabled'),
-    )
+# Contributor types
+NEWLY_REGISTERED, NORMAL_CONTRIBUTOR = 'newly_registered', 'normal'
+NO_SCIENTIST, DOUBLE_ACCOUNT, OUT_OF_ACADEMIA = 'no_scientist', 'double_account', 'out_of_academia'
+BARRED, DISABLED, DECEASED = 'barred', 'disabled', 'deceased'
+CONTRIBUTOR_STATUSES = (
+    (NEWLY_REGISTERED, 'Newly registered'),
+    (NORMAL_CONTRIBUTOR, 'Normal user'),
+    (NO_SCIENTIST, 'Not a professional scientist'),
+    (DOUBLE_ACCOUNT, 'Other account already exists'),
+    (OUT_OF_ACADEMIA, 'Out of academia'),
+    (BARRED, 'Barred from SciPost'),
+    (DISABLED, 'Account disabled'),
+    (DECEASED, 'Person deceased')
+)
 
 TITLE_CHOICES = (
     ('PR', 'Prof.'),
diff --git a/scipost/decorators.py b/scipost/decorators.py
index cebaccc0221244949c78412b12bd918903266fda..fea4dc361ee19a9b8c0909cdea45f6be1b3074ff 100644
--- a/scipost/decorators.py
+++ b/scipost/decorators.py
@@ -2,13 +2,24 @@ __copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
+from django.contrib.auth.decorators import user_passes_test
+
 from .models import Contributor
 
 
 def has_contributor(user):
-    """Requires user to be related to any Contributor."""
+    """Require user to be related to any Contributor."""
     try:
         user.contributor
         return True
     except Contributor.DoesNotExist:
         return False
+
+
+def is_contributor_user():
+    """Dceorator checking if user is related to any Contributor."""
+    def test(u):
+        if u.is_authenticated():
+            return has_contributor(u)
+        return False
+    return user_passes_test(test)
diff --git a/scipost/factories.py b/scipost/factories.py
index 4c7e91d45a9430c899f093416fc7a36e69b5e57f..d058e515701c72e7a0229d2e79af31867d51fd55 100644
--- a/scipost/factories.py
+++ b/scipost/factories.py
@@ -18,7 +18,7 @@ from .constants import TITLE_CHOICES, SCIPOST_SUBJECT_AREAS
 class ContributorFactory(factory.django.DjangoModelFactory):
     title = factory.Iterator(TITLE_CHOICES, getter=lambda c: c[0])
     user = factory.SubFactory('scipost.factories.UserFactory', contributor=None)
-    status = 1  # normal user
+    status = 'normal'  # normal user
     vetted_by = factory.Iterator(Contributor.objects.all())
     personalwebpage = factory.Faker('uri')
     expertises = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: [c[0]])
diff --git a/scipost/feeds.py b/scipost/feeds.py
index 8417bbdc3c5dce05ba8aab4441d1bcbfc97d5912..e5426b9bf44f00ca6709654d9637da739cc56dea 100644
--- a/scipost/feeds.py
+++ b/scipost/feeds.py
@@ -11,11 +11,13 @@ from django.core.urlresolvers import reverse
 from django.db.models import Q
 
 from comments.models import Comment
+from commentaries.models import Commentary
 from journals.models import Publication
 from news.models import NewsItem
 from scipost.models import subject_areas_dict
 from submissions.constants import SUBMISSION_STATUS_PUBLICLY_INVISIBLE
 from submissions.models import Submission
+from theses.models import ThesisLink
 
 
 class LatestCommentsFeedRSS(Feed):
@@ -24,7 +26,7 @@ class LatestCommentsFeedRSS(Feed):
     link = "/comments/"
 
     def items(self):
-        return Comment.objects.filter(status__gte=0).order_by('-date_submitted')[:10]
+        return Comment.objects.vetted().order_by('-date_submitted')[:10]
 
     def item_title(self, item):
         return item.comment_text[:50]
@@ -33,14 +35,14 @@ class LatestCommentsFeedRSS(Feed):
         return item.comment_text[:50]
 
     def item_link(self, item):
-        if item.commentary:
+        if isinstance(item.content_object, Commentary):
             return reverse('commentaries:commentary',
-                           kwargs={'arxiv_or_DOI_string': item.commentary.arxiv_or_DOI_string})
-        elif item.submission:
+                           kwargs={'arxiv_or_DOI_string': item.content_object.arxiv_or_DOI_string})
+        elif isinstance(item.content_object, Submission):
             return reverse('submissions:submission',
                            kwargs={'arxiv_identifier_w_vn_nr':
-                                   item.submission.arxiv_identifier_w_vn_nr})
-        elif item.thesislink:
+                                   item.content_object.arxiv_identifier_w_vn_nr})
+        elif isinstance(item.content_object, ThesisLink):
             return reverse('theses:thesis',
                            kwargs={'thesislink_id': item.thesislink.id})
         else:
diff --git a/scipost/forms.py b/scipost/forms.py
index 73a2a142d803ee568904776fb34bd6349caab0a7..7beb0185ee5b3c26ef020df792e04b290ff2effd 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -24,10 +24,11 @@ from ajax_select.fields import AutoCompleteSelectField
 from haystack.forms import ModelSearchForm as HayStackSearchForm
 
 from .behaviors import orcid_validator
-from .constants import SCIPOST_DISCIPLINES, TITLE_CHOICES, SCIPOST_FROM_ADDRESSES
+from .constants import (
+    SCIPOST_DISCIPLINES, TITLE_CHOICES, SCIPOST_FROM_ADDRESSES, NO_SCIENTIST, DOUBLE_ACCOUNT,
+    BARRED)
 from .decorators import has_contributor
-from .models import Contributor, DraftInvitation,\
-                    UnavailabilityPeriod, PrecookedEmail
+from .models import Contributor, DraftInvitation, UnavailabilityPeriod, PrecookedEmail
 
 from affiliations.models import Affiliation, Institution
 from common.forms import MonthYearWidget
@@ -39,11 +40,11 @@ from submissions.models import Report
 
 
 REGISTRATION_REFUSAL_CHOICES = (
-    (0, '-'),
-    (-1, 'not a professional scientist (>= PhD student)'),
-    (-2, 'another account already exists for this person'),
-    (-3, 'barred from SciPost (abusive behaviour)'),
-    )
+    (None, '-'),
+    (NO_SCIENTIST, 'not a professional scientist (>= PhD student)'),
+    (DOUBLE_ACCOUNT, 'another account already exists for this person'),
+    (BARRED, 'barred from SciPost (abusive behaviour)')
+)
 reg_ref_dict = dict(REGISTRATION_REFUSAL_CHOICES)
 
 
@@ -80,10 +81,10 @@ class RegistrationForm(forms.Form):
     last_name = forms.CharField(label='* Last name', max_length=100)
     email = forms.EmailField(label='* Email address')
     invitation_key = forms.CharField(max_length=40, widget=forms.HiddenInput(), required=False)
-    orcid_id = forms.CharField(label="ORCID id", max_length=20, required=False,
-                               validators=[orcid_validator],
-                               widget=forms.TextInput(
-                                    {'placeholder': 'Recommended. Get one at orcid.org'}))
+    orcid_id = forms.CharField(
+        label="ORCID id", max_length=20, required=False, validators=[orcid_validator],
+        widget=forms.TextInput({
+            'placeholder': 'Recommended. Get one at orcid.org'}))
     discipline = forms.ChoiceField(choices=SCIPOST_DISCIPLINES, label='* Main discipline')
     country_of_employment = LazyTypedChoiceField(
         choices=countries, label='* Country of employment', initial='NL',
@@ -93,12 +94,10 @@ class RegistrationForm(forms.Form):
     affiliation = forms.CharField(label='* Affiliation', max_length=300)
     address = forms.CharField(
         label='Address', max_length=1000,
-        widget=forms.TextInput({'placeholder': 'For postal correspondence'}),
-        required=False)
+        widget=forms.TextInput({'placeholder': 'For postal correspondence'}), required=False)
     personalwebpage = forms.URLField(
-        label='Personal web page',
-        widget=forms.TextInput({'placeholder': 'full URL, e.g. http://www.[yourpage].com'}),
-        required=False)
+        label='Personal web page', required=False,
+        widget=forms.TextInput({'placeholder': 'full URL, e.g. http://www.[yourpage].com'}))
     username = forms.CharField(label='* Username', max_length=100)
     password = forms.CharField(label='* Password', widget=forms.PasswordInput())
     password_verif = forms.CharField(label='* Verify password', widget=forms.PasswordInput(),
@@ -283,8 +282,9 @@ class AuthenticationForm(forms.Form):
     next = forms.CharField(widget=forms.HiddenInput(), required=False)
 
     def user_is_inactive(self):
-        """
-        Check if the User is active but only if the password is valid, to prevent any
+        """Check if the User is active only if the password is valid.
+
+        Only check  to prevent any
         possible clue (?) of the password.
         """
         username = self.cleaned_data['username']
diff --git a/scipost/management/commands/setup_contributor.py b/scipost/management/commands/setup_contributor.py
index c4798f122ef1db1b8029c14139b0a52541e95ffd..f4b98c3abb3404ebc1730601a1417ecc482dfc82 100644
--- a/scipost/management/commands/setup_contributor.py
+++ b/scipost/management/commands/setup_contributor.py
@@ -5,6 +5,7 @@ __license__ = "AGPL v3"
 from django.core.management.base import BaseCommand
 from django.contrib.auth.models import User
 
+from ...constants import NORMAL_CONTRIBUTOR
 from ...models import Contributor
 
 
@@ -16,7 +17,7 @@ class Command(BaseCommand):
 
     def create_contributor(self, username):
         user = User.objects.get(username=username)
-        contributor = Contributor(user=user, status=1, title="MR")
+        contributor = Contributor(user=user, status=NORMAL_CONTRIBUTOR, title="MR")
         contributor.vetted_by = contributor
         contributor.save()
 
diff --git a/scipost/managers.py b/scipost/managers.py
index b552f3adff613cfc6d807ce8c9e817339e7ff171..df5a60d44e538eeee8c77149556af1af3f07822e 100644
--- a/scipost/managers.py
+++ b/scipost/managers.py
@@ -6,7 +6,7 @@ from django.db import models
 from django.db.models import Q
 from django.utils import timezone
 
-from .constants import CONTRIBUTOR_NORMAL, CONTRIBUTOR_NEWLY_REGISTERED, AUTHORSHIP_CLAIM_PENDING
+from .constants import NORMAL_CONTRIBUTOR, NEWLY_REGISTERED, AUTHORSHIP_CLAIM_PENDING
 
 today = timezone.now().date()
 
@@ -23,7 +23,7 @@ class FellowManager(models.Manager):
 
 class ContributorQuerySet(models.QuerySet):
     def active(self):
-        return self.filter(user__is_active=True, status=CONTRIBUTOR_NORMAL)
+        return self.filter(user__is_active=True, status=NORMAL_CONTRIBUTOR)
 
     def available(self):
         return self.exclude(
@@ -31,10 +31,10 @@ class ContributorQuerySet(models.QuerySet):
             unavailability_periods__end__gte=today)
 
     def awaiting_validation(self):
-        return self.filter(user__is_active=False, status=CONTRIBUTOR_NEWLY_REGISTERED)
+        return self.filter(user__is_active=False, status=NEWLY_REGISTERED)
 
     def awaiting_vetting(self):
-        return self.filter(user__is_active=True, status=CONTRIBUTOR_NEWLY_REGISTERED)
+        return self.filter(user__is_active=True, status=NEWLY_REGISTERED)
 
     def fellows(self):
         return self.filter(user__groups__name='Editorial College')
diff --git a/scipost/migrations/0011_contributor_new_status.py b/scipost/migrations/0011_contributor_new_status.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ad51995a44018ac139414bc2d66744c5dfd611b
--- /dev/null
+++ b/scipost/migrations/0011_contributor_new_status.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-04-14 20:12
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0010_merge_20180327_2022'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='contributor',
+            name='new_status',
+            field=models.CharField(choices=[('newly_registered', 'Newly registered'), ('normal', 'Normal user'), ('no_scientist', 'Not a professional scientist'), ('double_account', 'Other account already exists'), ('out_of_academia', 'Out of academia'), ('barred', 'Barred from SciPost'), ('disabled', 'Account disabled'), ('deceased', 'Person deceased')], default='newly_registered', max_length=16),
+        ),
+    ]
diff --git a/scipost/migrations/0012_auto_20180414_2212.py b/scipost/migrations/0012_auto_20180414_2212.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3faed8fd33b8ffbd9a04432e42a627949b3b9ab
--- /dev/null
+++ b/scipost/migrations/0012_auto_20180414_2212.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-04-14 20:12
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+def update_contributor_status_field(apps, schema_editor):
+    Contributor = apps.get_model('scipost', 'Contributor')
+
+    Contributor.objects.filter(status=-4).update(new_status='disabled')
+    Contributor.objects.filter(status=-3).update(new_status='barred')
+    Contributor.objects.filter(status=-2).update(new_status='double_account')
+    Contributor.objects.filter(status=-1).update(new_status='no_scientist')
+    Contributor.objects.filter(status=0).update(new_status='newly_registered')
+    Contributor.objects.filter(status=1).update(new_status='normal')
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0011_contributor_new_status'),
+    ]
+
+    operations = [
+        migrations.RunPython(update_contributor_status_field)
+    ]
diff --git a/scipost/migrations/0013_remove_contributor_status.py b/scipost/migrations/0013_remove_contributor_status.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dfb7cc212142c45870d0fbea11d73a5f972f7cc
--- /dev/null
+++ b/scipost/migrations/0013_remove_contributor_status.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-04-14 20:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0012_auto_20180414_2212'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='contributor',
+            name='status',
+        ),
+    ]
diff --git a/scipost/migrations/0014_auto_20180414_2218.py b/scipost/migrations/0014_auto_20180414_2218.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2782391c11e94bc712aa0c98de6c7a3ee192a3c
--- /dev/null
+++ b/scipost/migrations/0014_auto_20180414_2218.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-04-14 20:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0013_remove_contributor_status'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='contributor',
+            old_name='new_status',
+            new_name='status',
+        ),
+    ]
diff --git a/scipost/models.py b/scipost/models.py
index 9f358b52c9bac2ee022a4ce26f65cb76523c76d6..d5bc608c42a3a3d4eca6b37aab37128958c259d9 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -15,40 +15,39 @@ from django.db import models
 from django.utils import timezone
 
 from .behaviors import TimeStampedModel, orcid_validator
-from .constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\
-                       subject_areas_dict, CONTRIBUTOR_STATUS, TITLE_CHOICES,\
-                       INVITATION_STYLE, INVITATION_TYPE,\
-                       INVITATION_CONTRIBUTOR, INVITATION_FORMAL,\
-                       AUTHORSHIP_CLAIM_PENDING, AUTHORSHIP_CLAIM_STATUS,\
-                       CONTRIBUTOR_NEWLY_REGISTERED
+from .constants import (
+    SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS, subject_areas_dict, DISABLED,
+    TITLE_CHOICES, INVITATION_STYLE, INVITATION_TYPE, INVITATION_CONTRIBUTOR, INVITATION_FORMAL,
+    AUTHORSHIP_CLAIM_PENDING, AUTHORSHIP_CLAIM_STATUS, CONTRIBUTOR_STATUSES, NEWLY_REGISTERED)
 from .fields import ChoiceArrayField
-from .managers import FellowManager, ContributorQuerySet,\
-                      UnavailabilityPeriodManager, AuthorshipClaimQuerySet
+from .managers import (
+    FellowManager, ContributorQuerySet, UnavailabilityPeriodManager, AuthorshipClaimQuerySet)
 
 today = timezone.now().date()
 
 
 def get_sentinel_user():
-    '''
-    Temporary fix: eventually the 'to-be-removed-Contributor' should be
-    status: "deactivated" and anonymized.
+    """Temporary fix to be able to delete Contributor instances.
+
+    Eventually the 'to-be-removed-Contributor' should be status: "deactivated" and anonymized.
     Fallback user for models relying on Contributor that is being deleted.
-    '''
+    """
     user, __ = get_user_model().objects.get_or_create(username='deleted')
-    return Contributor.objects.get_or_create(status=-4, user=user)[0]
+    return Contributor.objects.get_or_create(status=DISABLED, user=user)[0]
 
 
 class Contributor(models.Model):
-    """
+    """A Contributor is an academic extention of the User model.
+
     All *science* users of SciPost are Contributors.
     username, password, email, first_name and last_name are inherited from User.
     """
+
     user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, unique=True)
     invitation_key = models.CharField(max_length=40, blank=True)
     activation_key = models.CharField(max_length=40, blank=True)
     key_expires = models.DateTimeField(default=timezone.now)
-    status = models.SmallIntegerField(default=CONTRIBUTOR_NEWLY_REGISTERED,
-                                      choices=CONTRIBUTOR_STATUS)
+    status = models.CharField(max_length=16, choices=CONTRIBUTOR_STATUSES, default=NEWLY_REGISTERED)
     title = models.CharField(max_length=4, choices=TITLE_CHOICES)
     discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
                                   default='physics', verbose_name='Main discipline')
@@ -57,16 +56,12 @@ class Contributor(models.Model):
         blank=True, null=True)
     orcid_id = models.CharField(max_length=20, verbose_name="ORCID id",
                                 blank=True, validators=[orcid_validator])
-    address = models.CharField(max_length=1000, verbose_name="address",
-                               blank=True)
-    personalwebpage = models.URLField(verbose_name='personal web page',
-                                      blank=True)
+    address = models.CharField(max_length=1000, verbose_name="address", blank=True)
+    personalwebpage = models.URLField(verbose_name='personal web page', blank=True)
     vetted_by = models.ForeignKey('self', on_delete=models.SET(get_sentinel_user),
-                                  related_name="contrib_vetted_by",
-                                  blank=True, null=True)
+                                  related_name="contrib_vetted_by", blank=True, null=True)
     accepts_SciPost_emails = models.BooleanField(
-        default=True,
-        verbose_name="I accept to receive SciPost emails")
+        default=True, verbose_name="I accept to receive SciPost emails")
 
     objects = ContributorQuerySet.as_manager()
 
@@ -74,48 +69,50 @@ class Contributor(models.Model):
         return '%s, %s' % (self.user.last_name, self.user.first_name)
 
     def save(self, *args, **kwargs):
+        """Generate new activitation key if not set."""
         if not self.activation_key:
             self.generate_key()
         return super().save(*args, **kwargs)
 
     def get_absolute_url(self):
+        """Return public information page url."""
         return reverse('scipost:contributor_info', args=(self.id,))
 
-    @property
-    def get_formal_display(self):
-        return '%s %s %s' % (self.get_title_display(), self.user.first_name, self.user.last_name)
-
     @property
     def is_currently_available(self):
+        """Check if Contributor is currently not marked as unavailable."""
         return not self.unavailability_periods.today().exists()
 
     def is_EdCol_Admin(self):
+        """Check if Contributor is an Editorial Administrator."""
         return (self.user.groups.filter(name='Editorial Administrators').exists()
                 or self.user.is_superuser)
 
     def is_SP_Admin(self):
+        """Check if Contributor is a SciPost Administrator."""
         return (self.user.groups.filter(name='SciPost Administrators').exists()
                 or self.user.is_superuser)
 
     def is_MEC(self):
+        """Check if Contributor is a member of the Editorial College."""
         return self.fellowships.active().exists() or self.user.is_superuser
 
     def is_VE(self):
+        """Check if Contributor is a Vetting Editor."""
         return (self.user.groups.filter(name='Vetting Editors').exists()
                 or self.user.is_superuser)
 
     def generate_key(self, feed=''):
-        """
-        Generate and save a new activation_key for the contributor, given a certain feed.
-        """
+        """Generate a new activation_key for the contributor, given a certain feed."""
         for i in range(5):
             feed += random.choice(string.ascii_letters)
         feed = feed.encode('utf8')
         salt = self.user.username.encode('utf8')
-        self.activation_key = hashlib.sha1(salt+salt).hexdigest()
+        self.activation_key = hashlib.sha1(salt + salt).hexdigest()
         self.key_expires = datetime.datetime.now() + datetime.timedelta(days=2)
 
     def expertises_as_string(self):
+        """Return joined expertises."""
         if self.expertises:
             return ', '.join([subject_areas_dict[exp].lower() for exp in self.expertises])
         return ''
diff --git a/scipost/templates/scipost/contributor_info.html b/scipost/templates/scipost/contributor_info.html
index 26423a9502582d3579c1ea106e3e1e9752c701fa..d65751ed7db2636235ff7754b3725448eed99fb8 100644
--- a/scipost/templates/scipost/contributor_info.html
+++ b/scipost/templates/scipost/contributor_info.html
@@ -4,7 +4,7 @@
 
 {% block content %}
 
-<h1 class="highlight mb-4">Contributor info: {{ contributor.get_formal_display }}</h1>
+<h1 class="highlight mb-4">Contributor info: {{ contributor.get_title_display }} {{ contributor.user.first_name }} {{ contributor.user.last_name }}</h1>
 
 {% include "scipost/_public_info_as_table.html" with contributor=contributor %}
 
diff --git a/scipost/views.py b/scipost/views.py
index 9c9ba8c786e6f0739759f8682df5cd4c3db2a51f..915df08f3fa3e0bfdc34fbef5b55fdda1e969eb5 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404, render
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import login, logout, update_session_auth_hash
-from django.contrib.auth.decorators import login_required, user_passes_test
+from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import Group
 from django.contrib.auth.views import password_reset, password_reset_confirm
 from django.core import mail
@@ -29,16 +29,16 @@ from django.views.static import serve
 from guardian.decorators import permission_required
 from haystack.generic_views import SearchView
 
-from .constants import SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict, SciPost_from_addresses_dict,\
-                       CONTRIBUTOR_NORMAL
-from .decorators import has_contributor
-from .models import Contributor, UnavailabilityPeriod,\
-                    AuthorshipClaim, EditorialCollege, EditorialCollegeFellowship
-from .forms import AuthenticationForm, UnavailabilityPeriodForm,\
-                   RegistrationForm, AuthorshipClaimForm,\
-                   SearchForm, VetRegistrationForm, reg_ref_dict,\
-                   UpdatePersonalDataForm, UpdateUserDataForm, PasswordChangeForm,\
-                   EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm
+from .constants import (
+    SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict, SciPost_from_addresses_dict, NORMAL_CONTRIBUTOR)
+from .decorators import has_contributor, is_contributor_user
+from .models import (
+    Contributor, UnavailabilityPeriod, AuthorshipClaim, EditorialCollege,
+    EditorialCollegeFellowship)
+from .forms import (
+    AuthenticationForm, UnavailabilityPeriodForm, RegistrationForm, AuthorshipClaimForm,
+    SearchForm, VetRegistrationForm, reg_ref_dict, UpdatePersonalDataForm, UpdateUserDataForm,
+    PasswordChangeForm, EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm)
 from .utils import Utils, EMAIL_FOOTER, SCIPOST_SUMMARY_FOOTER, SCIPOST_SUMMARY_FOOTER_HTML
 
 from affiliations.forms import AffiliationsFormset
@@ -49,8 +49,7 @@ from invitations.constants import STATUS_REGISTERED
 from invitations.models import RegistrationInvitation
 from journals.models import Publication, Journal, PublicationAuthorsTable
 from news.models import NewsItem
-from submissions.models import Submission, RefereeInvitation,\
-                               Report, EICRecommendation
+from submissions.models import Submission, RefereeInvitation, Report, EICRecommendation
 from partners.models import MembershipAgreement
 from theses.models import ThesisLink
 
@@ -60,18 +59,18 @@ from theses.models import ThesisLink
 ##############
 
 def is_registered(user):
-    """
-    This method checks if user is activated assuming an validated user
-    has at least one permission group (`Registered Contributor` or `Partner Accounts`).
-    """
+    """Check if user is a validated user; has at least one permission group."""
     return user.groups.exists()
 
 
 class SearchView(SearchView):
+    """Search CBV inherited from Haystack."""
+
     template_name = 'search/search.html'
     form_class = SearchForm
 
     def get_context_data(self, *args, **kwargs):
+        """Update context with some additional information."""
         ctx = super().get_context_data(*args, **kwargs)
         ctx['search_query'] = self.request.GET.get('q')
         ctx['results_count'] = kwargs['object_list'].count()
@@ -83,7 +82,7 @@ class SearchView(SearchView):
 #############
 
 def index(request):
-    '''Main page.'''
+    """Homepage view of SciPost."""
     context = {
         'latest_newsitem': NewsItem.objects.filter(on_homepage=True).order_by('-date').first(),
         'submissions': Submission.objects.public().order_by('-submission_date')[:3],
@@ -96,7 +95,8 @@ def index(request):
 
 
 def protected_serve(request, path, show_indexes=False):
-    """
+    """Serve media files from outside the public MEDIA_ROOT folder.
+
     Serve files that are saved outside the default MEDIA_ROOT folder for superusers only!
     This will be usefull eg. in the admin pages.
     """
@@ -112,6 +112,7 @@ def protected_serve(request, path, show_indexes=False):
 ###############
 
 def feeds(request):
+    """Information page for RSS and Atom feeds."""
     context = {'subject_areas_physics': SCIPOST_SUBJECT_AREAS[0][1]}
     return render(request, 'scipost/feeds.html', context)
 
@@ -121,7 +122,8 @@ def feeds(request):
 ################
 
 def register(request):
-    """
+    """Contributor registration form page.
+
     This public registration view shows and processes the form
     that will create new user account requests. After registration
     the Contributor will need to activate its account via the mail
@@ -155,7 +157,8 @@ def register(request):
 
 
 def invitation(request, key):
-    """
+    """Registration Invitation reception page.
+
     If a scientist has recieved an invitation (RegistrationInvitation)
     he/she will finish it's invitation via still view which will prefill
     the default registration form.
@@ -244,9 +247,8 @@ def unsubscribe(request, contributor_id, key):
 
 @permission_required('scipost.can_vet_registration_requests', return_403=True)
 def vet_registration_requests(request):
-    contributors_to_vet = (Contributor.objects
-                           .awaiting_vetting()
-                           .order_by('key_expires'))
+    """List of new Registration requests to vet."""
+    contributors_to_vet = Contributor.objects.awaiting_vetting().order_by('key_expires')
     form = VetRegistrationForm()
     context = {'contributors_to_vet': contributors_to_vet, 'form': form}
     return render(request, 'scipost/vet_registration_requests.html', context)
@@ -254,12 +256,12 @@ def vet_registration_requests(request):
 
 @permission_required('scipost.can_vet_registration_requests', return_403=True)
 def vet_registration_request_ack(request, contributor_id):
-    # process the form
+    """Form view to vet new Registration requests."""
     form = VetRegistrationForm(request.POST or None)
     contributor = Contributor.objects.get(pk=contributor_id)
     if form.is_valid():
         if form.promote_to_registered_contributor():
-            contributor.status = 1
+            contributor.status = NORMAL_CONTRIBUTOR
             contributor.vetted_by = request.user.contributor
             contributor.save()
             group = Group.objects.get(name='Registered Contributors')
@@ -274,11 +276,10 @@ def vet_registration_request_ack(request, contributor_id):
             except RefereeInvitation.DoesNotExist:
                 pending_ref_inv_exists = False
 
-            email_text = ('Dear ' + contributor.get_title_display() + ' '
-                          + contributor.user.last_name +
-                          ', \n\nYour registration to the SciPost publication portal '
-                          'has been accepted. '
-                          'You can now login at https://scipost.org and contribute. \n\n')
+            email_text = (
+                'Dear ' + contributor.get_title_display() + ' ' + contributor.user.last_name +
+                ', \n\nYour registration to the SciPost publication portal has been accepted. '
+                'You can now login at https://scipost.org and contribute. \n\n')
             if pending_ref_inv_exists:
                 email_text += (
                     'Note that you have pending refereeing invitations; please navigate to '
@@ -292,18 +293,16 @@ def vet_registration_request_ack(request, contributor_id):
                                         reply_to=['registration@scipost.org'])
             emailmessage.send(fail_silently=False)
         else:
-            ref_reason = int(form.cleaned_data['refusal_reason'])
-            email_text = ('Dear ' + contributor.get_title_display() + ' '
-                          + contributor.user.last_name +
-                          ', \n\nYour registration to the SciPost publication portal '
-                          'has been turned down, the reason being: '
-                          + reg_ref_dict[ref_reason] + '. You can however still view '
-                          'all SciPost contents, just not submit papers, '
-                          'comments or votes. We nonetheless thank you for your interest.'
-                          '\n\nThe SciPost Team.')
+            ref_reason = form.cleaned_data['refusal_reason']
+            email_text = (
+                'Dear ' + contributor.get_title_display() + ' ' + contributor.user.last_name +
+                ', \n\nYour registration to the SciPost publication portal has been turned down,'
+                ' the reason being: ' + reg_ref_dict[ref_reason] + '. You can however still view '
+                'all SciPost contents, just not submit papers, comments or votes. We nonetheless '
+                'thank you for your interest.\n\nThe SciPost Team.')
             if form.cleaned_data['email_response_field']:
-                email_text += ('\n\nFurther explanations: '
-                               + form.cleaned_data['email_response_field'])
+                email_text += (
+                    '\n\nFurther explanations: ' + form.cleaned_data['email_response_field'])
             emailmessage = EmailMessage('SciPost registration: unsuccessful',
                                         email_text,
                                         'SciPost registration <registration@scipost.org>',
@@ -337,9 +336,7 @@ def registration_requests(request):
 @require_POST
 @permission_required('scipost.can_resend_registration_requests', return_403=True)
 def registration_requests_reset(request, contributor_id):
-    '''
-    Reset specific activation_key for Contributor and resend activation mail.
-    '''
+    """Reset specific activation_key for Contributor and resend activation mail."""
     contributor = get_object_or_404(Contributor.objects.awaiting_validation(), id=contributor_id)
     contributor.generate_key()
     contributor.save()
@@ -351,15 +348,7 @@ def registration_requests_reset(request, contributor_id):
 
 
 def login_view(request):
-    """
-    This view shows and processes a user's login session.
-
-    The function based method login() is deprecated from
-    Django 1.11 and replaced by Class Based Views.
-
-    See:
-    https://docs.djangoproject.com/en/1.11/releases/1.11/#django-contrib-auth
-    """
+    """Login form page."""
     form = AuthenticationForm(request.POST or None, initial=request.GET)
     if form.is_valid():
         user = form.authenticate()
@@ -381,13 +370,7 @@ def login_view(request):
 
 
 def logout_view(request):
-    """
-    The function based method logout() is deprecated from
-    Django 1.11 and replaced by Class Based Views.
-
-    See:
-    https://docs.djangoproject.com/en/1.11/releases/1.11/#django-contrib-auth
-    """
+    """Logout form page."""
     logout(request)
     messages.success(request, ('<h3>Keep contributing!</h3>'
                                'You are now logged out of SciPost.'))
@@ -395,11 +378,9 @@ def logout_view(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def mark_unavailable_period(request):
-    '''
-    Mark period unavailable for Contributor using this view.
-    '''
+    """Form view to mark period unavailable for Contributor."""
     unav_form = UnavailabilityPeriodForm(request.POST or None)
     if unav_form.is_valid():
         unav = unav_form.save(commit=False)
@@ -415,11 +396,9 @@ def mark_unavailable_period(request):
 
 @require_POST
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def delete_unavailable_period(request, period_id):
-    '''
-    Delete period unavailable registered.
-    '''
+    """Delete period unavailable registered."""
     unav = get_object_or_404(UnavailabilityPeriod,
                              contributor=request.user.contributor, id=int(period_id))
     unav.delete()
@@ -428,11 +407,9 @@ def delete_unavailable_period(request, period_id):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_editorial_account(request):
-    """
-    The Personal Page tab: Account
-    """
+    """Personal Page tab: Account."""
     contributor = request.user.contributor
     context = {
         'contributor': contributor,
@@ -442,11 +419,9 @@ def _personal_page_editorial_account(request):
     return render(request, 'partials/scipost/personal_page/account.html', context)
 
 
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_editorial_actions(request):
-    """
-    The Personal Page tab: Editorial Actions
-    """
+    """Personal Page tab: Editorial Actions."""
     permission = request.user.groups.filter(name__in=[
         'Ambassadors',
         'Advisory Board',
@@ -491,11 +466,9 @@ def _personal_page_editorial_actions(request):
 
 
 @permission_required('scipost.can_referee', return_403=True)
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_refereeing(request):
-    """
-    The Personal Page tab: Refereeing
-    """
+    """Personal Page tab: Refereeing."""
     context = {
         'contributor': request.user.contributor
     }
@@ -503,11 +476,9 @@ def _personal_page_refereeing(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_publications(request):
-    """
-    The Personal Page tab: Publications
-    """
+    """Personal Page tab: Publications."""
     contributor = request.user.contributor
     context = {
         'contributor': contributor,
@@ -522,11 +493,9 @@ def _personal_page_publications(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_submissions(request):
-    """
-    The Personal Page tab: Submissions
-    """
+    """Personal Page tab: Submissions."""
     contributor = request.user.contributor
     context = {'contributor': contributor}
 
@@ -541,11 +510,9 @@ def _personal_page_submissions(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_commentaries(request):
-    """
-    The Personal Page tab: Commentaries
-    """
+    """Personal Page tab: Commentaries."""
     contributor = request.user.contributor
     context = {'contributor': contributor}
 
@@ -559,11 +526,9 @@ def _personal_page_commentaries(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_theses(request):
-    """
-    The Personal Page tab: Theses
-    """
+    """Personal Page tab: Theses."""
     contributor = request.user.contributor
     context = {'contributor': contributor}
 
@@ -577,11 +542,9 @@ def _personal_page_theses(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_comments(request):
-    """
-    The Personal Page tab: Comments
-    """
+    """Personal Page tab: Comments."""
     contributor = request.user.contributor
     context = {
         'contributor': contributor,
@@ -592,11 +555,9 @@ def _personal_page_comments(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def _personal_page_author_replies(request):
-    """
-    The Personal Page tab: Author Replies
-    """
+    """Personal Page tab: Author Replies."""
     contributor = request.user.contributor
     context = {
         'contributor': contributor,
@@ -608,9 +569,7 @@ def _personal_page_author_replies(request):
 
 @login_required
 def personal_page(request, tab='account'):
-    """
-    The Personal Page is the main view for accessing user functions.
-    """
+    """Personal Page is the main view for accessing user functions."""
     if request.is_ajax():
         if tab == 'account':
             return _personal_page_editorial_account(request)
@@ -640,7 +599,7 @@ def personal_page(request, tab='account'):
 
     try:
         contributor = Contributor.objects.select_related('user').get(user=request.user)
-        context['needs_validation'] = contributor.status != CONTRIBUTOR_NORMAL
+        context['needs_validation'] = contributor.status != NORMAL_CONTRIBUTOR
     except Contributor.DoesNotExist:
         contributor = None
 
@@ -659,6 +618,7 @@ def personal_page(request, tab='account'):
 
 @login_required
 def change_password(request):
+    """Change password form view."""
     form = PasswordChangeForm(request.POST or None, current_user=request.user)
     if form.is_valid():
         form.save_new_password()
@@ -729,7 +689,7 @@ def update_personal_data(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def claim_authorships(request):
     """
     The system auto-detects potential authorships (of submissions,
@@ -778,7 +738,7 @@ def claim_authorships(request):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def claim_pub_authorship(request, publication_id, claim):
     if request.method == 'POST':
         contributor = Contributor.objects.get(user=request.user)
@@ -794,7 +754,7 @@ def claim_pub_authorship(request, publication_id, claim):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def claim_sub_authorship(request, submission_id, claim):
     if request.method == 'POST':
         contributor = Contributor.objects.get(user=request.user)
@@ -810,7 +770,7 @@ def claim_sub_authorship(request, submission_id, claim):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def claim_com_authorship(request, commentary_id, claim):
     if request.method == 'POST':
         contributor = Contributor.objects.get(user=request.user)
@@ -826,7 +786,7 @@ def claim_com_authorship(request, commentary_id, claim):
 
 
 @login_required
-@user_passes_test(has_contributor)
+@is_contributor_user()
 def claim_thesis_authorship(request, thesis_id, claim):
     if request.method == 'POST':
         contributor = Contributor.objects.get(user=request.user)
@@ -908,10 +868,10 @@ def contributor_info(request, contributor_id):
     contributor_submissions = Submission.objects.public_unlisted().filter(authors=contributor)
     contributor_commentaries = Commentary.objects.filter(authors=contributor)
     contributor_theses = ThesisLink.objects.vetted().filter(author_as_cont=contributor)
-    contributor_comments = (Comment.objects.vetted()
+    contributor_comments = (Comment.objects.vetted().publicly_visible()
                             .filter(author=contributor, is_author_reply=False)
                             .order_by('-date_submitted'))
-    contributor_authorreplies = (Comment.objects.vetted()
+    contributor_authorreplies = (Comment.objects.vetted().publicly_visible()
                                  .filter(author=contributor, is_author_reply=True)
                                  .order_by('-date_submitted'))
     context = {'contributor': contributor,
diff --git a/submissions/models.py b/submissions/models.py
index 56a4b9d6789457653789014ed59bc3842b727fac..9c3bbcf7b50c7927ca7cf7f65379de024bcd27d0 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -71,6 +71,8 @@ class Submission(models.Model):
                                         default=CYCLE_DEFAULT)
     fellows = models.ManyToManyField('colleges.Fellowship', blank=True,
                                      related_name='pool')
+    # visible_pool = models.BooleanField(default=True)
+    # visible_public = models.BooleanField(default=False)
     subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS,
                                     verbose_name='Primary subject area', default='Phys:QP')
     submission_type = models.CharField(max_length=10, choices=SUBMISSION_TYPE)
diff --git a/submissions/views.py b/submissions/views.py
index 124890e11000fa6aa98f8f547503150b1dbd75ed..bac948e24945f6506ea4fb62dea10bf0309bdfd8 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -43,8 +43,6 @@ from .utils import SubmissionUtils
 
 from colleges.permissions import fellowship_required, fellowship_or_admin_required
 from comments.forms import CommentForm
-from invitations.constants import INVITATION_REFEREEING
-from invitations.models import RegistrationInvitation
 from journals.models import Journal
 from mails.views import MailEditingSubView
 from production.forms import ProofsDecisionForm
diff --git a/templates/search/indexes/comments/comment_text.txt b/templates/search/indexes/comments/comment_text.txt
index e187fa7fe26f1fc628c3190ea008c6eeec87dc31..896dec580c8f2d3fb36fbbbf840076ce88922a09 100644
--- a/templates/search/indexes/comments/comment_text.txt
+++ b/templates/search/indexes/comments/comment_text.txt
@@ -1,3 +1,3 @@
 {{object.comment_text}}
 {{object.date_submitted}}
-{{object.author}}
+{% if not object.anonymous %}{{ object.author }}{% endif %}