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 %}