diff --git a/affiliations/forms.py b/affiliations/forms.py index 088fb0125e21db45acb77e9a1f7d3f5008bd26ca..774a2e6a7895455701b1fbc7652c79cc6c4aa205 100644 --- a/affiliations/forms.py +++ b/affiliations/forms.py @@ -12,9 +12,9 @@ from .models import Affiliation, Institution class AffiliationForm(forms.ModelForm): - name = forms.CharField(label='* Affiliation', max_length=300) + name = forms.CharField(label='* Institution', max_length=300) country = LazyTypedChoiceField( - choices=countries, label='* Country', widget=CountrySelectWidget()) + choices=countries, label='* Country', widget=CountrySelectWidget(), initial='NL') class Meta: model = Affiliation diff --git a/affiliations/migrations/0006_auto_20171102_0843.py b/affiliations/migrations/0006_auto_20171102_0843.py index bf8745a366ed9df5067bb99ca24254415596790f..e17f39362988a0e0812cacdd5a7fc17e423d6638 100644 --- a/affiliations/migrations/0006_auto_20171102_0843.py +++ b/affiliations/migrations/0006_auto_20171102_0843.py @@ -21,6 +21,7 @@ class Migration(migrations.Migration): dependencies = [ ('affiliations', '0005_affiliation'), + ('scipost', '0069_auto_20171102_0840'), ] operations = [ diff --git a/affiliations/models.py b/affiliations/models.py index 3806e939d109324d9949964991677b78dd3ed181..33b31f455404ed1c67aa03743236234de981bcb9 100644 --- a/affiliations/models.py +++ b/affiliations/models.py @@ -3,6 +3,8 @@ from django.urls import reverse from django_countries.fields import CountryField +from scipost.models import Contributor + from .constants import INSTITUTION_TYPES, TYPE_UNIVERSITY from .managers import AffiliationQuerySet @@ -26,6 +28,9 @@ class Institution(models.Model): def get_absolute_url(self): return reverse('affiliations:institution_details', args=(self.id,)) + def contributors(self): + return Contributor.objects.filter(affiliations__institution=self) + class Affiliation(models.Model): """ diff --git a/affiliations/templates/affiliations/institute_form.html b/affiliations/templates/affiliations/institution_form.html similarity index 96% rename from affiliations/templates/affiliations/institute_form.html rename to affiliations/templates/affiliations/institution_form.html index 75463ad95d67dbf12022e197f8f015e798bcd97e..d6f6c8fb38c08b764e03fe5a85564daea0ce6d9a 100644 --- a/affiliations/templates/affiliations/institute_form.html +++ b/affiliations/templates/affiliations/institution_form.html @@ -47,7 +47,7 @@ <br> <h3>Contributors of {{ institution }}</h3> <ul> - {% for contributor in institution.contributors.all %} + {% for contributor in institution.contributors %} <li>{{ contributor }}</li> {% endfor %} </ul> diff --git a/affiliations/templates/affiliations/institute_list.html b/affiliations/templates/affiliations/institution_list.html similarity index 100% rename from affiliations/templates/affiliations/institute_list.html rename to affiliations/templates/affiliations/institution_list.html diff --git a/commentaries/forms.py b/commentaries/forms.py index e0df7db0437beb4251d5377eaeabbd71cea8b620..d92ccae78b24e3b2f62bda14953464a1f3f26514 100644 --- a/commentaries/forms.py +++ b/commentaries/forms.py @@ -9,6 +9,7 @@ from django.template.loader import get_template from .models import Commentary from .constants import COMMENTARY_PUBLISHED, COMMENTARY_PREPRINT +from comments.forms import CommentForm from scipost.services import DOICaller, ArxivCaller from scipost.models import Contributor @@ -101,7 +102,7 @@ class RequestCommentaryForm(forms.ModelForm): super().__init__(*args, **kwargs) def save(self, *args, **kwargs): - self.instance.parse_links_into_urls() + self.instance.parse_links_into_urls(commit=False) if self.requested_by: self.instance.requested_by = self.requested_by return super().save(*args, **kwargs) @@ -291,3 +292,66 @@ class CommentarySearchForm(forms.Form): title__icontains=self.cleaned_data['title'], pub_abstract__icontains=self.cleaned_data['abstract'], author_list__icontains=self.cleaned_data['author']).order_by('-pub_date') + + +class CommentSciPostPublication(CommentForm): + """ + This Form will let authors of an SciPost publication comment on their Publication + using the Commentary functionalities. It will create an Commentary page if it does not + exist yet. + + It inherits from ModelForm: CommentForm and thus will, by default, return a Comment! + """ + + def __init__(self, *args, **kwargs): + self.publication = kwargs.pop('publication') + self.current_user = kwargs.pop('current_user') + super().__init__(*args, **kwargs) + + def save(self, commit=True): + """ + Create (vetted) Commentary page if not exist and do save actions as + per original CommentForm. + """ + if not commit: + raise AssertionError('CommentSciPostPublication can only be used with commit=True') + + try: + commentary = self.publication.commentary + except Commentary.DoesNotExist: + submission = self.publication.accepted_submission + commentary = Commentary(**{ + # 'vetted_by': None, + 'requested_by': self.current_user.contributor, + 'vetted': True, + 'type': COMMENTARY_PUBLISHED, + 'discipline': self.publication.discipline, + 'domain': self.publication.domain, + 'subject_area': self.publication.subject_area, + 'title': self.publication.title, + 'arxiv_identifier': submission.arxiv_identifier_w_vn_nr, + 'arxiv_link': submission.arxiv_link, + 'pub_DOI': self.publication.doi_string, + 'metadata': self.publication.metadata, + 'scipost_publication': self.publication, + 'author_list': self.publication.author_list, + 'journal': self.publication.in_issue.in_volume.in_journal.get_name_display(), + 'pages': self.publication.in_issue.number, + 'volume': self.publication.in_issue.in_volume.number, + 'pub_date': self.publication.publication_date, + 'pub_abstract': self.publication.abstract, + }) + commentary.parse_links_into_urls(commit=False) + commentary.save() + commentary.authors.add(*self.publication.authors.all()) + commentary.authors_claims.add(*self.publication.authors_claims.all()) + commentary.authors_false_claims.add(*self.publication.authors_false_claims.all()) + + # Original saving steps + comment = super().save(commit=False) + comment.author = self.current_user.contributor + comment.is_author_reply = True + comment.content_object = commentary + comment.save() + comment.grant_permissions() + return comment diff --git a/commentaries/migrations/0020_auto_20171111_1153.py b/commentaries/migrations/0020_auto_20171111_1153.py new file mode 100644 index 0000000000000000000000000000000000000000..529c8c7d9e10a7d53155730031873343c9f4b68a --- /dev/null +++ b/commentaries/migrations/0020_auto_20171111_1153.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-11-11 10:53 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0052_auto_20171107_1354'), + ('commentaries', '0019_auto_20170925_2124'), + ] + + operations = [ + migrations.AddField( + model_name='commentary', + name='scipost_publication', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='commentary', to='journals.Publication'), + ), + migrations.AlterField( + model_name='commentary', + name='title', + field=models.CharField(max_length=300), + ), + ] diff --git a/commentaries/models.py b/commentaries/models.py index add1f6b8ca87771c4527dd64646ccb71cb405ff6..56e5f467efe795e6082410663202f94c427465d4 100644 --- a/commentaries/models.py +++ b/commentaries/models.py @@ -28,7 +28,9 @@ class Commentary(TimeStampedModel): subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS, default='Phys:QP') open_for_commenting = models.BooleanField(default=True) - title = models.CharField(max_length=300, verbose_name='title') + + # Article/publication data + title = models.CharField(max_length=300) arxiv_identifier = models.CharField(max_length=100, blank=True, verbose_name="arXiv identifier (including version nr)") arxiv_link = models.URLField(verbose_name='arXiv link (including version nr)', blank=True) @@ -41,9 +43,11 @@ class Commentary(TimeStampedModel): arxiv_or_DOI_string = models.CharField(max_length=100, verbose_name='string form of arxiv nr or' ' DOI for commentary url') - author_list = models.CharField(max_length=1000) + scipost_publication = models.OneToOneField('journals.Publication', null=True, blank=True, + related_name='commentary') # Authors which have been mapped to contributors: + author_list = models.CharField(max_length=1000) authors = models.ManyToManyField('scipost.Contributor', blank=True, related_name='commentaries') authors_claims = models.ManyToManyField('scipost.Contributor', blank=True, @@ -71,7 +75,7 @@ class Commentary(TimeStampedModel): def get_absolute_url(self): return reverse('commentaries:commentary', args=(self.arxiv_or_DOI_string,)) - def parse_links_into_urls(self, commit=False): + def parse_links_into_urls(self, commit=True): """ Takes the arXiv nr or DOI and turns it into the urls """ if self.pub_DOI: self.arxiv_or_DOI_string = self.pub_DOI diff --git a/commentaries/templates/commentaries/_commentary_summary.html b/commentaries/templates/commentaries/_commentary_summary.html index 914dd82eadc074a2b44a9ca2c396aa7837c2fbe3..7567fea0d99cbf2d19e9d17abae4c852be517e06 100644 --- a/commentaries/templates/commentaries/_commentary_summary.html +++ b/commentaries/templates/commentaries/_commentary_summary.html @@ -18,28 +18,33 @@ </td> </tr> {% if commentary.type == 'published' %} - <tr> - <td>Journal ref.:</td> - <td>{{commentary.journal}} {{commentary.volume}}, {{commentary.pages}}</td> - </tr> - <tr> - <td>DOI:</td> - <td> - <a href="{{commentary.pub_DOI_link}}" target="_blank">{{commentary.pub_DOI_link}}</a> - </td> - </tr> + <tr> + <td>Journal ref.:</td> + <td>{{commentary.journal}} {{commentary.volume}}{% if commentary.pages %}, {{commentary.pages}}{% endif %}</td> + </tr> + <tr> + <td>DOI:</td> + <td> + <a href="{{commentary.pub_DOI_link}}" target="_blank">{{commentary.pub_DOI_link}}</a> + </td> + </tr> {% elif commentary.type == 'preprint' %} - <tr> - <td>arxiv Link:</td> - <td> - <a href="{{commentary.arxiv_link}}" target="_blank">{{commentary.arxiv_link}}</a> - </td> - </tr> + <tr> + <td>arxiv Link:</td> + <td> + <a href="{{commentary.arxiv_link}}" target="_blank">{{commentary.arxiv_link}}</a> + </td> + </tr> {% endif %} {% if commentary.pub_date %} - <tr> - <td>Date:</td> - <td>{{commentary.pub_date}}</td> - </tr> + <tr> + <td>Date:</td> + <td>{{commentary.pub_date}}</td> + </tr> {% endif %} </table> + +{% if commentary.scipost_publication %} + <br> + <p class="my-0">Published in {{commentary.scipost_publication.in_issue.in_volume.in_journal.get_name_display}}: <a href="{{commentary.scipost_publication.get_absolute_url}}">{{commentary.scipost_publication.in_issue.in_volume.in_journal.get_abbreviation_citation}} <strong>{{commentary.scipost_publication.in_issue.in_volume.number}}</strong>, {{commentary.scipost_publication.get_paper_nr}} ({{commentary.scipost_publication.publication_date|date:'Y'}})</a></p> +{% endif %} diff --git a/commentaries/templates/commentaries/base.html b/commentaries/templates/commentaries/base.html new file mode 100644 index 0000000000000000000000000000000000000000..61d1457ba3b5e42318b32650a2adb8dff66d941b --- /dev/null +++ b/commentaries/templates/commentaries/base.html @@ -0,0 +1,11 @@ +{% extends 'scipost/base.html' %} + +{% block breadcrumb %} + <nav class="breadcrumb py-md-2 px-0"> + <div class="container"> + {% block breadcrumb_items %} + <a href="{% url 'commentaries:commentaries' %}" class="breadcrumb-item">Commentaries</a> + {% endblock %} + </div> + </nav> +{% endblock %} diff --git a/commentaries/templates/commentaries/comment_on_publication.html b/commentaries/templates/commentaries/comment_on_publication.html new file mode 100644 index 0000000000000000000000000000000000000000..2471db9caa1cca22efdaca2a182af16b85b65a3a --- /dev/null +++ b/commentaries/templates/commentaries/comment_on_publication.html @@ -0,0 +1,22 @@ +{% extends 'journals/base_detail_page.html' %} + +{% block pagetitle %}: Comment on your Publication{% endblock pagetitle %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">Comment on your Publication</span> +{% endblock %} + +{% block content %} + +<h1 class="highlight">Comment on your Publication</h1> + +{% include 'partials/journals/publication_li_content.html' with publication=publication %} + +<h3 class="mt-2">Abstract</h3> +<p>{{ publication.abstract|default:'No abstract found' }}</p> + +{% url 'comments:new_comment' object_id=object_id type_of_object=type_of_object as url %} +{% include 'comments/_add_comment_form.html' with form=form url=url %} + +{% endblock content %} diff --git a/commentaries/templates/commentaries/commentary_detail.html b/commentaries/templates/commentaries/commentary_detail.html index ad8cee7adb14d5cf208ef086a3dc79d3065cfe17..4d0160aa6a71bb8447f9fcb3fd1aed472e771f25 100644 --- a/commentaries/templates/commentaries/commentary_detail.html +++ b/commentaries/templates/commentaries/commentary_detail.html @@ -1,34 +1,25 @@ -{% extends 'scipost/base.html' %} +{% extends 'commentaries/base.html' %} + +{% load scipost_extras %} {% block pagetitle %}: Commentary detail{% endblock pagetitle %} -{% block headsup %} +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">{{ commentary }}</span> +{% endblock %} -{% load scipost_extras %} +{% block content %} -{% endblock headsup %} +<h1 class="highlight">SciPost Commentary Page</h1> -{% block content %} +<h3>Original publication:</h3> +{% include 'commentaries/_commentary_summary.html' with commentary=commentary %} + +<br> -<div class="row"> - <div class="col-12"> - <h1 class="highlight">SciPost Commentary Page (non-SciPost publications)</h1> - </div> -</div> - -<div class="row"> - <div class="col-12"> - <h2>Original publication: </h2> - {% include 'commentaries/_commentary_summary.html' with commentary=commentary %} - </div> -</div> - -<div class="row"> - <div class="col-12"> - <h3>Abstract:</h3> - <p>{{ commentary.pub_abstract }}</p> - </div> -</div> +<h3>Abstract:</h3> +<p>{{ commentary.pub_abstract }}</p> {% include 'scipost/comments_block.html' with comments=commentary.comments.vetted type_of_object='Commentary' %} diff --git a/commentaries/urls.py b/commentaries/urls.py index b3c4e999c7119ae5d1b063efd5c80c3820cffd71..0679f78bb7523a5e6f74f514d8c560ee6c526b63 100644 --- a/commentaries/urls.py +++ b/commentaries/urls.py @@ -35,4 +35,8 @@ urlpatterns = [ name='vet_commentary_requests_submit'), url(r'^vet_commentary_requests/(?P<commentary_id>[0-9]+)/modify$', views.modify_commentary_request, name='modify_commentary_request'), + + # Commentaries on SciPost Publications + url(r'^publications/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/comment$', + views.comment_on_publication, name='comment_on_publication') ] diff --git a/commentaries/views.py b/commentaries/views.py index dd60ecd04fe4c11fe0b4be93ae4f4db60f8d9a6b..015fb814d1a87c8739186b1c1001ecdad39df58c 100644 --- a/commentaries/views.py +++ b/commentaries/views.py @@ -3,6 +3,7 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.core.mail import EmailMessage from django.core.urlresolvers import reverse, reverse_lazy +from django.db import transaction from django.shortcuts import redirect from django.template.loader import render_to_string from django.views.generic.edit import CreateView @@ -12,10 +13,12 @@ from django.http import Http404 from .models import Commentary from .forms import DOIToQueryForm, ArxivQueryForm, VetCommentaryForm, RequestCommentaryForm,\ - CommentarySearchForm, RequestPublishedArticleForm, RequestArxivPreprintForm + CommentarySearchForm, RequestPublishedArticleForm, RequestArxivPreprintForm,\ + CommentSciPostPublication from comments.models import Comment from comments.forms import CommentForm +from journals.models import Publication from scipost.mixins import PaginationMixin import strings @@ -238,3 +241,26 @@ def commentary_detail(request, arxiv_or_DOI_string): context = {'commentary': commentary, 'author_replies': author_replies, 'form': form} return render(request, 'commentaries/commentary_detail.html', context) + + +@login_required +@permission_required('scipost.can_submit_comments', raise_exception=True) +@transaction.atomic +def comment_on_publication(request, doi_label): + """ + This will let authors of an SciPost publication comment on their Publication by + automatically creating a Commentary page if not exist already. + """ + publication = get_object_or_404(Publication.objects.published(), + doi_label=doi_label, authors=request.user.contributor) + form = CommentSciPostPublication(request.POST or None, request.FILES or None, + publication=publication, current_user=request.user) + if form.is_valid(): + comment = form.save() + messages.success(request, strings.acknowledge_request_commentary) + return redirect(comment.content_object.get_absolute_url()) + context = { + 'publication': publication, + 'form': form + } + return render(request, 'commentaries/comment_on_publication.html', context) diff --git a/comments/forms.py b/comments/forms.py index 94b6fa62fefff353346ba27926df9aaa7f7e1ebb..ac922bb92967199688a976cdcac70dae74b08689 100644 --- a/comments/forms.py +++ b/comments/forms.py @@ -9,15 +9,20 @@ class CommentForm(forms.ModelForm): model = Comment fields = ['is_cor', 'is_rem', 'is_que', 'is_ans', 'is_obj', 'is_rep', 'is_val', 'is_lit', 'is_sug', - 'comment_text', 'remarks_for_editors', 'file_attachment'] + 'comment_text', 'remarks_for_editors', 'file_attachment', + 'anonymous'] def __init__(self, *args, **kwargs): - super(CommentForm, self).__init__(*args, **kwargs) + self.is_report_comment = kwargs.pop('is_report_comment', False) + super().__init__(*args, **kwargs) self.fields['comment_text'].widget.attrs.update( {'placeholder': 'NOTE: only serious and meaningful Comments will be accepted.'}) self.fields['remarks_for_editors'].widget.attrs.update( {'rows': 3, 'placeholder': '(these remarks will not be publicly visible)'}) + if not self.is_report_comment: + del self.fields['anonymous'] + class VetCommentForm(forms.Form): action_option = forms.ChoiceField(widget=forms.RadioSelect, choices=COMMENT_ACTION_CHOICES, diff --git a/comments/templates/comments/_add_comment_form.html b/comments/templates/comments/_add_comment_form.html index 3a02aca5239ab9357756b563bca58cbb468351bb..8225e51af3c7c597a496901cf3505b347e02678c 100644 --- a/comments/templates/comments/_add_comment_form.html +++ b/comments/templates/comments/_add_comment_form.html @@ -14,7 +14,7 @@ </script> {% endblock %} -<form enctype="multipart/form-data" action="{{url}}" method="post"> +<form enctype="multipart/form-data" {% if url %}action="{{url}}" {% endif %}method="post"> {% csrf_token %} <div class="row"> <div class="col-md-9"> @@ -52,6 +52,7 @@ <div class="row"> <div class="col-12"> + {% if form.anonymous %}{{ form.anonymous|bootstrap }}{% endif %} <input type="submit" name="submit" value="Submit your Comment for vetting" class="btn btn-primary" id="submit-id-submit"> <p class="mt-2" id="goodCommenter"><i>By clicking on Submit, you agree with the <a target="_blank" href="{% url 'scipost:terms_and_conditions' %}">Terms and Conditions</a>, in particular that <span class="text-danger">your identity will be attached to the Comment</span>.</i></p> </div> diff --git a/comments/templates/comments/_comment_categories.html b/comments/templates/comments/_comment_categories.html index 60b7720e7c5cdee801c81eb7f2157d55e027fec9..07e3c1d53072e4723c8baf86098421399d0dd48c 100644 --- a/comments/templates/comments/_comment_categories.html +++ b/comments/templates/comments/_comment_categories.html @@ -31,4 +31,5 @@ <div>suggestion for further work</div> {% endif %} </div> + <br> {% endif %} diff --git a/comments/templates/comments/_comment_identifier.html b/comments/templates/comments/_comment_identifier.html index fa6faab3c4287f02ef037b74f9a324d506cf983e..2d518ecedeea4fe7dd83cdf87755bd40e115d019 100644 --- a/comments/templates/comments/_comment_identifier.html +++ b/comments/templates/comments/_comment_identifier.html @@ -1,25 +1,33 @@ {% load comment_extras %} +{% load user_groups %} +{% load submissions_extras %} -<div class="commentid" id="comment_id{{comment.id}}"> - <h3> - {% if comment.is_author_reply %}Author{% endif %} +{% is_edcol_admin request.user as is_edcol_admin %} - <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a> - on {{comment.date_submitted|date:'Y-m-d'}} +<div class="commentid" id="comment_id{{ comment.id }}"> + <h3> + {% if request.user.contributor and request.user.contributor == comment.core_content_object.editor_in_charge or is_edcol_admin and request.user|is_not_author_of_submission:comment.core_content_object.arxiv_identifier_w_vn_nr %} + <h3>{% if comment.anonymous %}(chose public anonymity) {% endif %}<a href="{{ comment.author.get_absolute_url }}">{{ comment.author.user.first_name }} {{ comment.author.user.last_name }}</a> + on {{ comment.date_submitted|date:'Y-m-d' }} + </h3> + {% elif comment.anonymous %} + Anonymous on {{comment.date_submitted|date:'Y-m-d'}} + {% else %} + {% if comment.is_author_reply %}Author{% endif %} + <a href="{{comment.author.get_absolute_url}}">{{comment.author.user.first_name}} {{comment.author.user.last_name}}</a> + on {{comment.date_submitted|date:'Y-m-d'}} + {% endif %} {% if comment.doi_string %} <small>doi: {{ comment.doi_string }}</small>{% endif %} </h3> {% if comment|is_reply_to_comment %} - (in reply to <a href="{{comment.content_object.get_absolute_url}}">{{comment.content_object.author.user.first_name}} {{comment.content_object.author.user.last_name}}</a> on {{comment.content_object.date_submitted|date:'Y-m-d'}}) + (in reply to <a href="{{comment.content_object.get_absolute_url}}">{% if comment.content_object.anonymous %}Anonymous Comment{% else %}{{comment.content_object.author.user.first_name}} {{comment.content_object.author.user.last_name}}{% endif %}</a> on {{comment.content_object.date_submitted|date:'Y-m-d'}}) {% elif comment|is_reply_to_report %} - (in reply to <a href="{{comment.content_object.get_absolute_url}}"> + (in reply to - Report {{comment.content_object.report_nr}} - {% if not comment.content_object.anonymous %} - by {{comment.content_object.author.user.first_name}} {{comment.content_object.author.user.last_name}} - {% endif %} + <a href="{{comment.content_object.get_absolute_url}}">Report {{comment.content_object.report_nr}}{% if not comment.content_object.anonymous %} by {{comment.content_object.author.user.first_name}} {{comment.content_object.author.user.last_name}}{% endif %}</a> - </a> on {{comment.content_object.date_submitted|date:'Y-m-d'}}) + on {{comment.content_object.date_submitted|date:'Y-m-d'}}) {% endif %} </div> diff --git a/comments/templates/comments/_single_comment.html b/comments/templates/comments/_single_comment.html index ee94826ec7b15950249cc4f51465cee07fb6b5a4..665503b06852f4686845577dde1c1356898f831a 100644 --- a/comments/templates/comments/_single_comment.html +++ b/comments/templates/comments/_single_comment.html @@ -1,6 +1,10 @@ {% load scipost_extras %} {% load filename %} {% load file_extentions %} +{% load user_groups %} + +{% is_edcol_admin request.user as is_edcol_admin %} +{% is_editorial_college request.user as is_editorial_college %} <div class="comment"> {% include 'comments/_comment_identifier.html' with comment=comment %} @@ -25,7 +29,7 @@ </p> {% endif %} </p> - {% if user|is_in_group:'Editorial College' or user|is_in_group:'Editorial Administrators' %} + {% if is_editorial_college or is_edcol_admin %} {% if comment.remarks_for_editors %} <h3>Remarks for editors:</h3> <p>{{ comment.remarks_for_editors|linebreaks }}</p> diff --git a/comments/views.py b/comments/views.py index 9011315784e62cfeebc24c2d865e38b8a195b3c0..2de6693126a45ac20b03a1e8186879949c06609b 100644 --- a/comments/views.py +++ b/comments/views.py @@ -181,7 +181,7 @@ def reply_to_report(request, report_id): # Verify if this is from an author: is_author = report.submission.authors.filter(user=request.user).exists() - form = CommentForm(request.POST or None, request.FILES or None) + form = CommentForm(request.POST or None, request.FILES or None, is_report_comment=True) if form.is_valid(): newcomment = form.save(commit=False) newcomment.content_object = report diff --git a/journals/constants.py b/journals/constants.py index bc5554b2b0dc43a9e9b1fb966da2d0f37c6b7af8..da5c678eae99d18964113ca71b204208c397769e 100644 --- a/journals/constants.py +++ b/journals/constants.py @@ -14,7 +14,6 @@ SCIPOST_JOURNALS_SUBMIT = ( # Journal closed for submission SCIPOST_JOURNALS_NO_SUBMIT = ( (SCIPOST_JOURNAL_PHYSICS_SELECT, 'SciPost Physics Select'), - (SCIPOST_JOURNAL_PHYSICS_PROC, 'SciPost Physics Proceedings'), ) # All allowed journals diff --git a/journals/managers.py b/journals/managers.py index 3d5c8e9168a1ca9800edae2e5e5a267efcc42670..0b058c1b3a78d47baca5c366cf9d94c8a71e60a3 100644 --- a/journals/managers.py +++ b/journals/managers.py @@ -39,7 +39,7 @@ class IssueManager(models.Manager): **kwargs).order_by('-until_date').first() -class PublicationManager(models.Manager): +class PublicationQuerySet(models.QuerySet): def get_published(self, *args, **kwargs): try: return self.published(*args, **kwargs)[0] diff --git a/journals/migrations/0051_auto_20171102_1307.py b/journals/migrations/0051_auto_20171102_1307.py index aa16b353181771fa0bd88287991f6ec99322d596..68cb00d96b9855a098ad7dc2e211d40359a88ba3 100644 --- a/journals/migrations/0051_auto_20171102_1307.py +++ b/journals/migrations/0051_auto_20171102_1307.py @@ -14,7 +14,7 @@ def fill_publications(apps, schema_editor): for publication in Publication.objects.all(): for author in publication.authors.all(): for affiliation in author.affiliations.all(): - publication.institutes.add(affiliation.institute) + publication.institutes.add(affiliation.institution) def return_none(*args, **kwargs): @@ -25,6 +25,7 @@ class Migration(migrations.Migration): dependencies = [ ('journals', '0050_publication_institutes'), + ('affiliations', '0008_auto_20171107_1354'), ] operations = [ diff --git a/journals/migrations/0053_auto_20171114_1255.py b/journals/migrations/0053_auto_20171114_1255.py new file mode 100644 index 0000000000000000000000000000000000000000..77b99bccdf9ad300a49f35f8dcd3389ae7dd4211 --- /dev/null +++ b/journals/migrations/0053_auto_20171114_1255.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-11-14 11:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0052_auto_20171107_1354'), + ] + + operations = [ + migrations.AlterField( + model_name='journal', + name='name', + field=models.CharField(choices=[('SciPostPhys', 'SciPost Physics'), ('SciPostPhysLectNotes', 'SciPost Physics Lecture Notes'), ('SciPostPhysProc', 'SciPost Physics Proceedings'), ('SciPostPhysSel', 'SciPost Physics Select')], max_length=100, unique=True), + ), + ] diff --git a/journals/models.py b/journals/models.py index d1ea12b9da0affde8039631ac717e3bfd8bb4364..de8c4ded3df04c91c56932294efde0f48bbfde03 100644 --- a/journals/models.py +++ b/journals/models.py @@ -11,7 +11,7 @@ from .constants import SCIPOST_JOURNALS, SCIPOST_JOURNALS_DOMAINS,\ STATUS_DRAFT, STATUS_PUBLISHED, ISSUE_STATUSES,\ CCBY4, CC_LICENSES, CC_LICENSES_URI from .helpers import paper_nr_string, journal_name_abbrev_citation -from .managers import IssueManager, PublicationManager, JournalManager +from .managers import IssueManager, PublicationQuerySet, JournalManager from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS from scipost.fields import ChoiceArrayField @@ -53,7 +53,7 @@ class Journal(models.Model): class Volume(models.Model): - in_journal = models.ForeignKey(Journal, on_delete=models.CASCADE) + in_journal = models.ForeignKey('journals.Journal', on_delete=models.CASCADE) number = models.PositiveSmallIntegerField() start_date = models.DateField(default=timezone.now) until_date = models.DateField(default=timezone.now) @@ -72,7 +72,7 @@ class Volume(models.Model): class Issue(models.Model): - in_volume = models.ForeignKey(Volume, on_delete=models.CASCADE) + in_volume = models.ForeignKey('journals.Volume', on_delete=models.CASCADE) number = models.PositiveSmallIntegerField() start_date = models.DateField(default=timezone.now) until_date = models.DateField(default=timezone.now) @@ -129,7 +129,7 @@ class Publication(models.Model): """ # Publication data accepted_submission = models.OneToOneField('submissions.Submission', on_delete=models.CASCADE) - in_issue = models.ForeignKey(Issue, on_delete=models.CASCADE) + in_issue = models.ForeignKey('journals.Issue', on_delete=models.CASCADE) paper_nr = models.PositiveSmallIntegerField() # Core fields @@ -187,7 +187,7 @@ class Publication(models.Model): latest_metadata_update = models.DateTimeField(blank=True, null=True) latest_activity = models.DateTimeField(default=timezone.now) - objects = PublicationManager() + objects = PublicationQuerySet.as_manager() def __str__(self): header = (self.citation() + ', ' diff --git a/journals/templates/journals/add_author.html b/journals/templates/journals/add_author.html index 89686e236c86c06fe66a52372e098f01f4922d0b..23e924b7ee3d0660bb95f4908c36055bbea3d0e6 100644 --- a/journals/templates/journals/add_author.html +++ b/journals/templates/journals/add_author.html @@ -19,94 +19,78 @@ {% block content %} -<div class="row"> - <div class="col-12"> - <h1 class="highlight">Add author to publication</h1> - </div> -</div> - -<div class="row"> - <div class="col-12"> - {% include 'journals/_publication_details.html' with publication=publication %} - </div> -</div> +<h1 class="highlight">Add author to publication</h1> +{% include 'partials/journals/publication_li_content.html' with publication=publication %} +<br> -<div class="row"> - <div class="col-12"> - <h2 class="highlight">Add an (unregistered) author</h2> - </div> -</div> - <div class="row"> <div class="col-12"> <h3>Current list of authors as contributors:</h3> - <ul class="list-group list-group-noborder"> + <ul> {% for author in publication.authors.all %} - <li class="list-group-item py-1 pl-2"> - <a href="{% url 'scipost:contributor_info' author.id %}">{{ author.user.first_name }} {{ author.user.last_name }}</a> - </li> + <li><a href="{% url 'scipost:contributor_info' author.id %}">{{ author.user.first_name }} {{ author.user.last_name }}</a></li> {% empty %} - No unregistered authors known. + <li>No unregistered authors known.</li> {% endfor %} </ul> <h3>Current list of additional authors (unregistered):</h3> - <ul class="list-group list-group-noborder"> + <ul> {% for author in publication.authors_unregistered.all %} - <li class="list-group-item py-1 pl-2">{{ author }}</li> + <li>{{ author }}</li> {% empty %} - No unregistered authors known. + <li>No unregistered authors known.</li> {% endfor %} </ul> - <hr class="small"> - - <div class="row"> - <div class="col-md-8"> - <h3>Search for missing author:</h3> - <form action="{% url 'journals:add_author' publication.id %}" method="post"> - {% csrf_token %} - {{form|bootstrap}} - <input class="btn btn-secondary" type="submit" value="Search"> - </form> - - {% if form.has_changed %} - <h3 class="mt-4">Identified as contributor:</h3> - <ul class="list-group"> - {% for contributor in contributors_found %} - <li class="list-group-item p-2"> - <div class="d-block w-100 font-weight-bold">{{ contributor.user.first_name }} {{ contributor.user.last_name }}</div> - <a class="d-block" href="{% url 'journals:add_author' publication_id=publication.id contributor_id=contributor.id %}">Add this Contributor as author of this Publication</a> - </li> - {% empty %} - <span class="text-danger">No Contributor with this name could be identified.</span> - {% endfor %} - </ul> - - <h3 class="mt-2">Identified as existing unregistered author:</h3> - <ul class="list-group"> - {% for unreg_auth in unregistered_authors_found %} - <li class="list-group-item"> - <div class="d-block w-100 font-weight-bold">{{ unreg_auth }} - <a class="d-block" href="{% url 'journals:add_unregistered_author' publication_id=publication.id unregistered_author_id=unreg_auth.id %}">Add this unregistered author as author of this Publication</a> - </li> - {% empty %} - <span class="text-danger">No UnregisteredAuthor with this name could be found in the database.</span> - {% endfor %} - </ul> - - <h3 class="mt-3">You can otherwise create an UnregisteredAuthor object instance and link it to this publication:</h3> - <form action="{% url 'journals:add_new_unreg_author' publication_id=publication.id %}" method="post"> - {% csrf_token %} - {{ new_unreg_author_form|bootstrap }} - <input class="btn btn-secondary" type="submit" value="Add"> - </form> - - {% endif %} - </div> - </div> + <br> + <h2 class="highlight">Add an (unregistered) author</h2> + + <h3>Search for missing author:</h3> + <form action="{% url 'journals:add_author' publication.id %}" method="post"> + {% csrf_token %} + {{form|bootstrap}} + <input class="btn btn-primary" type="submit" value="Search"> + </form> + + {% if form.has_changed %} + <br> + <h3 class="mt-2">Identified as contributor:</h3> + <ul class="list-group"> + {% for contributor in contributors_found %} + <li class="list-group-item p-2"> + <div class="d-block w-100 font-weight-bold">{{ contributor.user.first_name }} {{ contributor.user.last_name }}</div> + <a class="d-block" href="{% url 'journals:add_author' publication_id=publication.id contributor_id=contributor.id %}">Add this Contributor as author of this Publication</a> + </li> + {% empty %} + <span class="text-danger">No Contributor with this name could be identified.</span> + {% endfor %} + </ul> + + <h3 class="mt-2">Identified as existing unregistered author:</h3> + <ul class="list-group"> + {% for unreg_auth in unregistered_authors_found %} + <li class="list-group-item"> + <div class="d-block w-100 font-weight-bold">{{ unreg_auth }} + <a class="d-block" href="{% url 'journals:add_unregistered_author' publication_id=publication.id unregistered_author_id=unreg_auth.id %}">Add this unregistered author as author of this Publication</a> + </li> + {% empty %} + <span class="text-danger">No UnregisteredAuthor with this name could be found in the database.</span> + {% endfor %} + </ul> + + <h3 class="mt-3">You can otherwise create an UnregisteredAuthor object instance and link it to this publication:</h3> + <form action="{% url 'journals:add_new_unreg_author' publication_id=publication.id %}" method="post"> + {% csrf_token %} + {{ new_unreg_author_form|bootstrap }} + <input class="btn btn-primary" type="submit" value="Add"> + </form> + + {% endif %} + + <br> <h3> - <a href="{{publication.get_absolute_url}}">Return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a> + <a href="{{publication.get_absolute_url}}">Return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a>. </h3> </div> </div> diff --git a/journals/templates/journals/base_detail_page.html b/journals/templates/journals/base_detail_page.html new file mode 100644 index 0000000000000000000000000000000000000000..5881a76d967e44d57bdbbcadb0217a8ddd56b5ec --- /dev/null +++ b/journals/templates/journals/base_detail_page.html @@ -0,0 +1,17 @@ +{% extends 'scipost/base.html' %} + +{% load staticfiles %} + +{% block pagetitle %}: {{journal}}{% endblock pagetitle %} +{% block body_class %}{{block.super}} journals{% endblock %} + +{% block breadcrumb %} + <nav class="breadcrumb py-md-2 px-0 hidden-sm-down"> + <div class="container"> + {% block breadcrumb_items %} + <a href="{% url 'journals:journals' %}" class="breadcrumb-item">Journals</a> + <a href="{% url 'scipost:publication_detail' publication.doi_label %}" class="breadcrumb-item">{{ publication.doi_label }}</a> + {% endblock %} + </div> + </nav> +{% endblock %} diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html index b86f7d42920366287c78f855a41abf1eb8f13cbb..911fcf398478aacdb3a9b4e189cb7b9d969bd0c6 100644 --- a/journals/templates/journals/publication_detail.html +++ b/journals/templates/journals/publication_detail.html @@ -49,7 +49,14 @@ {% block content %} {% is_edcol_admin request.user as is_edcol_admin %} - {% include 'journals/_publication_details.html' with publication=publication %} + {% include 'partials/journals/publication_summary.html' with publication=publication %} + + {% if publication.commentary and publication.commentary.comments.vetted.exists %} + <h3>Post-publication commentaries</h3> + <p> + This Publication ({{ publication.commentary.comments.vetted.count }}) has been commented on, see <a href="{{ publication.commentary.get_absolute_url }}">this Publication's Commentary page</a> for details. + </p> + {% endif %} <hr> {% if publication.citedby|length >= 1 %} @@ -100,6 +107,13 @@ </div> </div> + {% if request.user and request.user.contributor in publication.authors.all %} + <h3>Author actions</h3> + <ul> + <li><a href="{% url 'commentaries:comment_on_publication' publication.doi_label %}">Place a comment on this publication</a></li> + </ul> + {% endif %} + {% if is_edcol_admin %} <hr> <div class="row"> diff --git a/journals/templates/journals/_publication_details_small.html b/journals/templates/partials/journals/publication_li_content.html similarity index 85% rename from journals/templates/journals/_publication_details_small.html rename to journals/templates/partials/journals/publication_li_content.html index a7a1376c2488114e9688d73059a4cb1fd1a3183d..2f8346ba21f8b2516f3cc1698d256f787030363d 100644 --- a/journals/templates/journals/_publication_details_small.html +++ b/journals/templates/partials/journals/publication_li_content.html @@ -1,7 +1,7 @@ <h5 class="pb-0">{{publication.get_subject_area_display}}</h5> <h3><a href="{{publication.get_absolute_url}}">{{publication.title}}</a></h3> -<p class="mt-0 mb-3">{{ publication.author_list }}</p> +<p class="mt-0 mb-2">{{ publication.author_list }}</p> <p class="text-muted mb-0"> {{ publication.citation }} · <span class="font-weight-light">published {{ publication.publication_date|date:'j F Y' }}</span> </p> diff --git a/journals/templates/journals/_publication_details.html b/journals/templates/partials/journals/publication_summary.html similarity index 100% rename from journals/templates/journals/_publication_details.html rename to journals/templates/partials/journals/publication_summary.html diff --git a/journals/views.py b/journals/views.py index 8f7b60053f6b2efb3fa50f35b6a5f1be360103fc..d1428b475ea226833e3b5015ccf790dc0c456af6 100644 --- a/journals/views.py +++ b/journals/views.py @@ -256,8 +256,8 @@ def validate_publication(request): # Add Institutions to the publication for author in publication.authors.all(): - for institution in author.affiliations.active(): - publication.institutions.add(institution) + for current_affiliation in author.affiliations.active(): + publication.institutions.add(current_affiliation.institution) # Save the beast publication.save() @@ -921,8 +921,11 @@ def harvest_citedby_links(request, doi_label): for link in response_deserialized.iter(prefix + 'forward_link'): doi = link.find(prefix + 'journal_cite').find(prefix + 'doi').text article_title = link.find(prefix + 'journal_cite').find(prefix + 'article_title').text - journal_abbreviation = link.find(prefix + 'journal_cite').find( - prefix + 'journal_abbreviation').text + try: + journal_abbreviation = link.find(prefix + 'journal_cite').find( + prefix + 'journal_abbreviation').text + except: + journal_abbreviation = None try: volume = link.find(prefix + 'journal_cite').find(prefix + 'volume').text except AttributeError: diff --git a/mails/forms.py b/mails/forms.py index fd1feb9cb95dcef96c1f321560be835f56c61633..a7a8b241a3b22343728d21d1e28536af52da02b4 100644 --- a/mails/forms.py +++ b/mails/forms.py @@ -62,7 +62,7 @@ class EmailTemplateForm(forms.Form): # Get recipients list. Try to send through BCC to prevent privacy issues! bcc_list = [] - if self.mail_data.get('bcc_to') and self.object: + if self.mail_data.get('bcc_to', False) and self.object: if re.match("[^@]+@[^@]+\.[^@]+", self.mail_data.get('bcc_to')): bcc_list = [self.mail_data.get('bcc_to')] else: @@ -74,7 +74,7 @@ class EmailTemplateForm(forms.Form): bcc_list = [bcc_to] else: bcc_list = bcc_to - elif re.match("[^@]+@[^@]+\.[^@]+", self.mail_data.get('bcc_to')): + elif re.match("[^@]+@[^@]+\.[^@]+", self.mail_data.get('bcc_to', '')): bcc_list = [self.mail_data.get('bcc_to')] if self.cleaned_data.get('extra_recipient') and self.recipient: diff --git a/mails/templates/mail_templates/registration_invitation_renewal.html b/mails/templates/mail_templates/registration_invitation_renewal.html new file mode 100644 index 0000000000000000000000000000000000000000..70cefdbf0c36128c5b7a928b3c32229871dd5813 --- /dev/null +++ b/mails/templates/mail_templates/registration_invitation_renewal.html @@ -0,0 +1,273 @@ +{% if invitation.nr_reminders > 0 %} + <p><strong>Reminder: Invitation to SciPost</strong></p> +{% endif %} + +{% if invitation.invitation_type == 'F' %} + <p><strong>RE: Invitation to join the Editorial College of SciPost</strong></p> +{% endif %} + + +<p> + Dear + {% if invitation.message_style == 'F' %} + {{ invitation.get_title_display }} {{ invitation.last_name }}, + {% else %} + {{ invitation.first_name }}, + {% endif %} +</p> + +{% if invitation.invitation_type == 'R' %} + <p> + We would hereby like to cordially invite you + to become a Contributor on SciPost + (this is required in order to deliver reports; + our records show that you are not yet registered); + for your convenience, we have prepared a pre-filled + <a href="https://scipost.org/invitation/{{ invitation.invitation_key }}">registration form</a> + for you. After activation of your registration, you will be allowed to contribute, + in particular by providing referee reports. + </p> + <p> + To ensure timely processing of the submission (out of respect for the authors), + we would appreciate a quick accept/decline + response from you, ideally within the next 2 days. + </p> + <p>If you are <strong>not</strong> able to provide a Report, you can let us know by simply + <a href="https://scipost.org/submissions/decline_ref_invitation/{{ invitation.invitation_key }}"> + clicking here</a>. + </p> + <p> + If you are able to provide a Report, you can confirm this after registering + and logging in (you will automatically be prompted for a confirmation). + </p> + <p>We very much hope that we can count on your expertise,</p> + <p>Many thanks in advance,</p> + <p>The SciPost Team</p> +{% elif invitation.invitation_type == 'ci' %} + <p> + Your work has been cited in a manuscript submitted to SciPost, {{ invitation.cited_in_submission.title }} <br>by {{ invitation.cited_in_submission.author_list }}, <br> + which you can find online at the + <a href="https://scipost.org/submission/{{ invitation.cited_in_submission.arxiv_identifier_w_vn_nr }}"> + submission's page</a>. + </p> + <p> + I would hereby like to use this opportunity to quickly introduce + you to the SciPost initiative, and to invite you to become an active + Contributor to the site. You might for example consider reporting or + commenting on the above submission before the refereeing deadline. + </p> + + + <p> + In summary, SciPost.org is a publication portal managed by + professional scientists, offering (among others) high-quality + Open Access journals with innovative forms of refereeing, and a + means of commenting on all existing literature. SciPost is established as + a not-for-profit foundation devoted to serving the interests of the + international scientific community. + </p> + <p> + The site is anchored at <a href="https://scipost.org">scipost.org</a>. + Many further details + about SciPost, its principles, ideals and implementation can be found at + the <a href="https://scipost.org/about">about</a> + and <a href="https://scipost.org/FAQ">FAQ</a> pages. + </p> + <p> + As a professional academic, you can register at the + <a href="https://scipost.org/register">registration page</a>, + enabling you to contribute to the site's + contents, for example by offering submissions, reports and comments. + </p> + <p> + For your convenience, a partly pre-filled + <a href="https://scipost.org/invitation/{{ invitation_key }}">registration form</a> + has been prepared for you (you can in any case still register at the + <a href="https://scipost.org/register">registration page</a>). + </p> + <p> + If you do develop sympathy for the initiative, besides participating in the + online platform, we would be very grateful if you considered submitting a + publication to one of the journals within the near future, in order to help + establish their reputation. We'll also be looking forward to your reaction, + comments and suggestions about the initiative, which we hope you will find + useful to your work as a professional scientist. + </p> + <p>Many thanks in advance for taking a few minutes to look into it,</p> + <p>On behalf of the SciPost Foundation,</p> + <p>{{ invitation.invited_by.get_title_display }} {{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</p> + +{% elif invitation.invitation_type == 'cp' %} + <p>Your work has been cited in a paper published by SciPost,</p> + <p> + {{ invitation.cited_in_publication.title }}</p> <p>by {{ invitation.cited_in_publication.author_list }} (published as <a href="https://scipost.org/{{ invitation.cited_in_publication.doi_label }}">{{ invitation.cited_in_publication.citation }}</a>). + </p> + <p> + I would hereby like to use this opportunity to quickly introduce + you to the SciPost initiative, and to invite you to become an active + Contributor to the site. + </p> + + + <p> + In summary, SciPost.org is a publication portal managed by + professional scientists, offering (among others) high-quality + Open Access journals with innovative forms of refereeing, and a + means of commenting on all existing literature. SciPost is established as + a not-for-profit foundation devoted to serving the interests of the + international scientific community. + </p> + <p> + The site is anchored at <a href="https://scipost.org">scipost.org</a>. + Many further details + about SciPost, its principles, ideals and implementation can be found at + the <a href="https://scipost.org/about">about</a> + and <a href="https://scipost.org/FAQ">FAQ</a> pages. + </p> + <p> + As a professional academic, you can register at the + <a href="https://scipost.org/register">registration page</a>, + enabling you to contribute to the site's + contents, for example by offering submissions, reports and comments. + </p> + <p> + For your convenience, a partly pre-filled + <a href="https://scipost.org/invitation/{{ invitation_key }}">registration form</a> + has been prepared for you (you can in any case still register at the + <a href="https://scipost.org/register">registration page</a>. + </p> + <p> + If you do develop sympathy for the initiative, besides participating in the + online platform, we would be very grateful if you considered submitting a + publication to one of the journals within the near future, in order to help + establish their reputation. We'll also be looking forward to your reaction, + comments and suggestions about the initiative, which we hope you will find + useful to your work as a professional scientist. + </p> + <p>Many thanks in advance for taking a few minutes to look into it,</p> + <p>On behalf of the SciPost Foundation,</p> + <p>{{ invitation.invited_by.get_title_display }} {{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</p> + +{% elif invitation.invitation_type == 'C' %} + <p> + I would hereby like to quickly introduce + you to a scientific publishing initiative + called SciPost, and to invite you to become an active Contributor. + </p> + + + <p> + In summary, SciPost.org is a publication portal managed by + professional scientists, offering (among others) high-quality + Open Access journals with innovative forms of refereeing, and a + means of commenting on all existing literature. SciPost is established as + a not-for-profit foundation devoted to serving the interests of the + international scientific community. + </p> + <p> + The site is anchored at <a href="https://scipost.org">scipost.org</a>. + Many further details + about SciPost, its principles, ideals and implementation can be found at + the <a href="https://scipost.org/about">about</a> + and <a href="https://scipost.org/FAQ">FAQ</a> pages. + </p> + <p> + As a professional academic, you can register at the + <a href="https://scipost.org/register">registration page</a>, + enabling you to contribute to the site's + contents, for example by offering submissions, reports and comments. + </p> + <p> + For your convenience, a partly pre-filled + <a href="https://scipost.org/invitation/{{ invitation_key }}">registration form</a> + has been prepared for you (you can in any case still register at the + <a href="https://scipost.org/register">registration page</a>. + </p> + <p> + If you do develop sympathy for the initiative, besides participating in the + online platform, we would be very grateful if you considered submitting a + publication to one of the journals within the near future, in order to help + establish their reputation. We'll also be looking forward to your reaction, + comments and suggestions about the initiative, which we hope you will find + useful to your work as a professional scientist. + </p> + <p>Many thanks in advance for taking a few minutes to look into it,</p> + <p>On behalf of the SciPost Foundation,</p> + <p>{{ invitation.invited_by.get_title_display }} {{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</p> + +{% elif invitation.invitation_type == 'F' %} + <p> + You will perhaps have already heard about SciPost, a publication + portal established by and for professional scientists. + </p> + <p> + SciPost.org is legally based on a not-for-profit foundation and will + operate in perpetuity as a non-commercial entity at the exclusive service + of the academic sector, bringing a cost-slashing alternative to existing + practices. + </p> + <p> + SciPost offers a collection of two-way open + access (no subscription fees, no author fees) journals with extremely + stringent (peer-witnessed) refereeing, overseen by + our Editorial College (exclusively composed + of established, professionally practising scientists). The whole process is + designed to ensure the highest achievable scientific quality while making the + editorial workflow as light and efficient as possible.</p> + <p> + To go straight to the point, on behalf of the SciPost Foundation + and in view of your professional expertise, I hereby would + ike to invite you to become an Editorial Fellow and thus join the + Editorial College of SciPost Physics. + </p> + <p> + Please note that only well-known and respected senior academics are + being contacted for this purpose. Academic reputation and involvement + in the community are the most important criteria guiding our + considerations of who should belong to the Editorial College. + </p> + <p> + To help you in considering this, it would be best if you were to take + the time to look at the website itself, which is anchored at scipost.org. + Besides looking around the site, you can also personally register + (to become a Contributor, without necessarily committing to membership + of the Editorial College, this to be discussed separately) by visiting + the following <a href="https://scipost.org/invitation/{{ invitation.invitation_key }}"> + single-use link</a>, containing a partly pre-filled form for + your convenience.</p> + <p> + Many details about the initiative + can then be found at <a href="https://scipost.org/about">SciPost About</a> and at <a href="https://scipost.org/FAQ">SciPost FAQ</a>. + Functioning of the College will proceed according to the by-laws set + out in <a href="https://scipost.org/EdCol_by-laws">the by-laws</a>. + </p> + <p> + Since the success of this initiative is dependent on the involvement of + the very people it is meant to serve, we'd be very grateful if you were + to give due consideration to this proposal. We would expect you to + commit just 2-4 hours per month to help perform Editorial duties; we will + constantly adjust the number of Editorial Fellows to ensure this is the case. You + could try it out for 6 months or a year, and of course you could quit + any time you wished. + </p> + <p> + I'd be happy to provide you with more information, should you require + it. In view of our development plans, I would be grateful if you could + react (by replying to this email) within the next two or three weeks, + if possible. I'll be looking forward to your reaction, your comments + and suggestions, be they positive or negative. If you need more time + to consider, that's also fine; just let me know. + </p> + + <p>On behalf of the SciPost Foundation, + <br/>Prof. dr Jean-Sébastien Caux + <br/>--------------------------------------------- + <br/>Institute for Theoretial Physics + <br/>University of Amsterdam + <br/>Science Park 904<br/>1098 XH Amsterdam<br/>The Netherlands + <br/>--------------------------------------------- + <br/>tel.: +31 (0)20 5255775 + <bf/>fax: +31 (0)20 5255778 + <br/>--------------------------------------------- + </p> +{% endif %} diff --git a/mails/templates/mail_templates/registration_invitation_renewal.json b/mails/templates/mail_templates/registration_invitation_renewal.json new file mode 100644 index 0000000000000000000000000000000000000000..9941b614623cf8f70ca83bcfbf4f934307d93b3f --- /dev/null +++ b/mails/templates/mail_templates/registration_invitation_renewal.json @@ -0,0 +1,8 @@ +{ + "subject": "SciPost: invitation", + "to_address": "email", + "bcc_to": "invited_by.user.email", + "from_address_name": "J-S Caux", + "from_address": "jscaux@scipost.org", + "context_object": "invitation" +} diff --git a/mails/widgets.py b/mails/widgets.py index d5f1aff3003b38e75cacf58fc5b279304e883491..1754950616856af0813450a14037a0cdc95760b2 100644 --- a/mails/widgets.py +++ b/mails/widgets.py @@ -3,9 +3,6 @@ import json from django.core.urlresolvers import reverse, NoReverseMatch from django.forms import widgets, Media from django.utils.safestring import mark_safe -# from django.conf import settings - -# from . import PLUGINS, PLUGINS_WITH_CSS class SummernoteEditor(widgets.Textarea): @@ -23,6 +20,7 @@ class SummernoteEditor(widgets.Textarea): ['font', ['strikethrough', 'superscript', 'subscript']], ['fontsize', ['fontsize']], ['para', ['ul', 'ol', 'paragraph']], + ['insert', ['link', 'hr']] ], } @@ -34,10 +32,7 @@ class SummernoteEditor(widgets.Textarea): except NoReverseMatch: default_options['fileUpload'] = False - # settings_options = getattr(settings, 'FROALA_EDITOR_OPTIONS', {}) - # options = dict(default_options.items() + settings_options.items() + self.options.items()) options = dict(default_options.items()).copy() - # options.update(settings_options.items()) options.update(self.options.items()) json_options = json.dumps(options) diff --git a/petitions/views.py b/petitions/views.py index 86c527ccc405f6270909f55622607d7cfff08f70..662dc0774df2d0f75b77760951480c9522eafdc0 100644 --- a/petitions/views.py +++ b/petitions/views.py @@ -20,14 +20,17 @@ def petition(request, slug): if request.user.is_authenticated: is_signed = petition.petition_signatories.verified().filter( signatory=request.user.contributor).exists() + affiliation = request.user.contributor.affiliations.first() or {} + institition = affiliation.institution.name if affiliation else '' + country = affiliation.institution.country if affiliation else '' initial = { 'petition': petition, 'title': request.user.contributor.title, 'first_name': request.user.first_name, 'last_name': request.user.last_name, 'email': request.user.email, - 'country_of_employment': request.user.contributor.affiliation.country_of_employment, - 'affiliation': request.user.contributor.affiliation.name, + 'country_of_employment': country, + 'affiliation': institition, } form = SignPetitionForm(request.POST or None, initial=initial, petition=petition, diff --git a/production/admin.py b/production/admin.py index 2880659cd81d1d9efe90260d12ff1c7c9bbc23b5..e0452b589e599e8d32bf05776fe9668fa0c5bf92 100644 --- a/production/admin.py +++ b/production/admin.py @@ -2,7 +2,8 @@ from django.contrib import admin from guardian.admin import GuardedModelAdmin -from .models import ProductionStream, ProductionEvent, ProductionUser, Proofs +from .models import ProductionStream, ProductionEvent, ProductionUser, Proofs,\ + ProductionEventAttachment def event_count(obj): @@ -38,4 +39,5 @@ class ProductionProofsAdmin(admin.ModelAdmin): admin.site.register(Proofs, ProductionProofsAdmin) admin.site.register(ProductionUser) admin.site.register(ProductionEvent) +admin.site.register(ProductionEventAttachment) admin.site.register(ProductionStream, ProductionStreamAdmin) diff --git a/production/forms.py b/production/forms.py index 1768d6d12d07e8e59cf64f72db7e501dea1d64ae..dabd1d19de44952673b90a7206f8439a361b0a91 100644 --- a/production/forms.py +++ b/production/forms.py @@ -3,7 +3,8 @@ import datetime from django import forms from . import constants -from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs +from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs,\ + ProductionEventAttachment from .signals import notify_stream_status_change today = datetime.datetime.today() @@ -144,6 +145,7 @@ class ProofsDecisionForm(forms.ModelForm): decision = forms.ChoiceField(choices=[(True, 'Accept Proofs for publication'), (False, 'Decline Proofs for publication')]) feedback = forms.CharField(required=False, widget=forms.Textarea) + feedback_attachment = forms.FileField(required=False) class Meta: model = Proofs @@ -173,9 +175,14 @@ class ProofsDecisionForm(forms.ModelForm): prodevent = ProductionEvent( stream=proofs.stream, event='status', - comments='Received feedback from the authors: {comments}'.format( + comments='<em>Received feedback from the authors:</em><br>{comments}'.format( comments=comments), noted_by=proofs.stream.supervisor ) prodevent.save() + if self.cleaned_data.get('feedback_attachment'): + attachment = ProductionEventAttachment( + attachment=self.cleaned_data['feedback_attachment'], + production_event=prodevent) + attachment.save() return proofs diff --git a/production/migrations/0033_productioneventattachment.py b/production/migrations/0033_productioneventattachment.py new file mode 100644 index 0000000000000000000000000000000000000000..a9a7fdaa7381c413eb64499b7b856aca5f8061c9 --- /dev/null +++ b/production/migrations/0033_productioneventattachment.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-11-12 16:05 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import production.models +import scipost.storage + + +class Migration(migrations.Migration): + + dependencies = [ + ('production', '0032_auto_20171010_1008'), + ] + + operations = [ + migrations.CreateModel( + name='ProductionEventAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(storage=scipost.storage.SecureFileStorage(), upload_to=production.models.production_event_upload_location)), + ('production_event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='production.ProductionEvent')), + ], + ), + ] diff --git a/production/models.py b/production/models.py index 65ace229bff3e4de8987e7199005eeb600c4b2f6..12c461b7d0523b84404f1f3fef6b112779ec9eb8 100644 --- a/production/models.py +++ b/production/models.py @@ -106,6 +106,29 @@ class ProductionEvent(models.Model): return self.stream.notification_name +def production_event_upload_location(instance, filename): + submission = instance.production_event.stream.submission + return 'UPLOADS/PRODSTREAMS/{year}/{arxiv}/{filename}'.format( + year=submission.submission_date.year, + arxiv=submission.arxiv_identifier_wo_vn_nr, + filename=filename) + + +class ProductionEventAttachment(models.Model): + """ + An ProductionEventAttachment is in general used by authors to reply to an Proofs version + with their version of the Proofs with comments. + """ + production_event = models.ForeignKey('production.ProductionEvent', on_delete=models.CASCADE, + related_name='attachments') + attachment = models.FileField(upload_to=production_event_upload_location, + storage=SecureFileStorage()) + + def get_absolute_url(self): + return reverse('production:production_event_attachment_pdf', + args=(self.production_event.stream.id, self.id,)) + + def proofs_upload_location(instance, filename): submission = instance.stream.submission return 'UPLOADS/PROOFS/{year}/{arxiv}/{filename}'.format( diff --git a/production/templates/production/partials/production_events.html b/production/templates/production/partials/production_events.html index 653f9c3df4bbf1999172d6fd62d320206a720b41..74fbd57a54ee493a522c938c6f1e6fd311ae9857 100644 --- a/production/templates/production/partials/production_events.html +++ b/production/templates/production/partials/production_events.html @@ -34,10 +34,18 @@ {% if event.noted_to %} {{ event.noted_by.user.first_name }} {{ event.noted_by.user.last_name }} {{ event.comments|linebreaksbr }} {{ event.noted_to.user.first_name }} {{ event.noted_to.user.last_name }}. {% else %} - {{ event.comments|linebreaksbr }} + {{ event.comments|safe|linebreaksbr }} {% endif %} </p> {% endif %} + + {% if event.attachments.exists %} + <ul> + {% for attachment in event.attachments.all %} + <li><a href="{{ attachment.get_absolute_url }}" target="_blank">Download Attachment {{ forloop.counter }}</a></li> + {% endfor %} + </ul> + {% endif %} </li> {% empty %} <li>No events were found.</li> diff --git a/production/templates/production/partials/production_stream_card_completed.html b/production/templates/production/partials/production_stream_card_completed.html index e938b1d37d4c21929b5156d3595f5f10fd54976b..38f1c7fa637e5fafee9117caf225492c8dad3abb 100644 --- a/production/templates/production/partials/production_stream_card_completed.html +++ b/production/templates/production/partials/production_stream_card_completed.html @@ -54,8 +54,8 @@ <ul> {% for proofs in stream.proofs.all %} <li class="py-1"> - <a href="{% url 'production:proofs' stream_id=stream.id version=proofs.version %}">Version {{ proofs.version }}</a><br> - Uploaded by {{ proofs.uploaded_by.user.first_name }} {{ proofs.uploaded_by.user.last_name }}<br> + <a href="{% url 'production:proofs' stream_id=stream.id version=proofs.version %}">Version {{ proofs.version }}</a> · <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span><br> + Uploaded by: {{ proofs.uploaded_by.user.first_name }} {{ proofs.uploaded_by.user.last_name }}<br> Accessible for authors: {{ proofs.accessible_for_authors|yesno:'<strong>Yes</strong>,No'|safe }}<br> {% if perms.scipost.can_run_proofs_by_authors %} @@ -71,8 +71,6 @@ </ul> {% endif %} {% endif %} - - <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span> </li> {% empty %} <li>No Proofs found.</li> diff --git a/production/templates/production/proofs.html b/production/templates/production/proofs.html index bfbaa7937fdd75a7a6d0a230882174e77609428c..b939acd9743c71e4413761dc6d3ce4007bb1ea30 100644 --- a/production/templates/production/proofs.html +++ b/production/templates/production/proofs.html @@ -38,8 +38,7 @@ </li> {% elif proofs.status == 'accepted_sup' %} <li><a href="{% url 'production:send_proofs' proofs.stream.id proofs.version %}">Send proofs to authors</a></li> - {% endif %} - {% if proofs.status != 'uploaded' %} + {% else %} <li><a href="{% url 'production:toggle_accessibility' proofs.stream.id proofs.version %}">{{ proofs.accessible_for_authors|yesno:'Hide,Make accessible' }} for authors</a></li> {% endif %} {% endif %} diff --git a/production/urls.py b/production/urls.py index c44ef6554601b294b125872607bb94f85bd7dcc5..e0ecebf5d00fe77d42d40e79c8533a40fdeea3d1 100644 --- a/production/urls.py +++ b/production/urls.py @@ -21,6 +21,8 @@ urlpatterns = [ production_views.send_proofs, name='send_proofs'), url(r'^streams/(?P<stream_id>[0-9]+)/proofs/(?P<version>[0-9]+)/toggle_access$', production_views.toggle_accessibility, name='toggle_accessibility'), + url(r'^streams/(?P<stream_id>[0-9]+)/proofs/(?P<attachment_id>[0-9]+)/reply/pdf$', + production_views.production_event_attachment_pdf, name='production_event_attachment_pdf'), url(r'^streams/(?P<stream_id>[0-9]+)/events/add$', production_views.add_event, name='add_event'), url(r'^streams/(?P<stream_id>[0-9]+)/logs/add$', diff --git a/production/views.py b/production/views.py index cc7e0d4063e42b191d0b4074367e3d58ee4b259e..f368999aa87719819176a8ef2b3a22073c402cc1 100644 --- a/production/views.py +++ b/production/views.py @@ -18,7 +18,8 @@ from finances.forms import WorkLogForm from mails.views import MailEditingSubView from . import constants -from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs +from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs,\ + ProductionEventAttachment from .forms import ProductionEventForm, AssignOfficerForm, UserToOfficerForm,\ AssignSupervisorForm, StreamStatusForm, ProofsUploadForm, ProofsDecisionForm,\ AssignInvitationsOfficerForm @@ -492,7 +493,7 @@ def proofs_pdf(request, slug): # because now it will return 404 instead of a redirect to the login page. raise Http404 - proofs = Proofs.objects.get(id=proofs_slug_to_id(slug)) + proofs = get_object_or_404(Proofs, id=proofs_slug_to_id(slug)) stream = proofs.stream # Check if user has access! @@ -511,6 +512,34 @@ def proofs_pdf(request, slug): return response +def production_event_attachment_pdf(request, stream_id, attachment_id): + """ Open ProductionEventAttachment pdf. """ + if not request.user.is_authenticated: + # Don't use the decorator but this strategy, + # because now it will return 404 instead of a redirect to the login page. + raise Http404 + + stream = get_object_or_404(ProductionStream, id=stream_id) + attachment = get_object_or_404( + ProductionEventAttachment.objects.filter(production_event__stream=stream), + id=attachment_id) + + # Check if user has access! + checker = ObjectPermissionChecker(request.user) + access = checker.has_perm('can_work_for_stream', stream) and request.user.has_perm('scipost.can_view_production') + if not access and request.user.contributor: + access = request.user.contributor in stream.submission.authors.all() + if not access: + raise Http404 + + # Passed the test! The user may see the file! + content_type, encoding = mimetypes.guess_type(attachment.attachment.path) + content_type = content_type or 'application/octet-stream' + response = HttpResponse(attachment.attachment.read(), content_type=content_type) + response["Content-Encoding"] = encoding + return response + + @login_required @transaction.atomic def author_decision(request, slug): @@ -526,7 +555,7 @@ def author_decision(request, slug): if request.user.contributor not in proofs.stream.submission.authors.all(): raise Http404 - form = ProofsDecisionForm(request.POST or None, instance=proofs) + form = ProofsDecisionForm(request.POST or None, request.FILES or None, instance=proofs) if form.is_valid(): proofs = form.save() notify_stream_status_change(request.user, stream, False) @@ -595,7 +624,7 @@ def decision(request, stream_id, version, decision): ) prodevent.save() messages.success(request, 'Proofs have been {decision}.'.format(decision=decision)) - return redirect(stream.get_absolute_url()) + return redirect(reverse('production:proofs', args=(stream.id, proofs.version))) @is_production_user() diff --git a/scipost/forms.py b/scipost/forms.py index 27a14be3cae1827e539385fc03715a6be18631c3..dae0ae808fea8310bd616fa35ae3488777d84aeb 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -272,7 +272,8 @@ class UpdatePersonalDataForm(forms.ModelForm): 'expertises', 'orcid_id', 'address', - 'personalwebpage' + 'personalwebpage', + 'accepts_SciPost_emails', ] def sync_lists(self): diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py index 1b99ccf50734665e25491a3b6b0e8934057d33d0..35c279649c84fc40da52a185334e9ecacea13701 100644 --- a/scipost/management/commands/add_groups_and_permissions.py +++ b/scipost/management/commands/add_groups_and_permissions.py @@ -314,6 +314,7 @@ class Command(BaseCommand): EditorialAdmin.permissions.set([ can_view_pool, + can_invite_Fellows, can_assign_submissions, can_do_plagiarism_checks, can_oversee_refereeing, diff --git a/scipost/migrations/0067_auto_20171101_2132.py b/scipost/migrations/0067_auto_20171101_2132.py index ef5d86831a6913c851880a26364f6ec357ee8e38..0df9c7605398e0c39d2bf9c08750cd3c7c13f0a7 100644 --- a/scipost/migrations/0067_auto_20171101_2132.py +++ b/scipost/migrations/0067_auto_20171101_2132.py @@ -10,6 +10,7 @@ class Migration(migrations.Migration): dependencies = [ ('scipost', '0066_contributor__affiliation'), + ('affiliations', '0003_auto_20171101_2022'), ] operations = [ diff --git a/scipost/models.py b/scipost/models.py index cd56f991ddf98372a1e4323676f50f879de20ed9..c289ded4c241c022a29d3a73408cf48fef8e0055 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -219,6 +219,18 @@ class RegistrationInvitation(models.Model): return (self.first_name + ' ' + self.last_name + ' on ' + self.date_sent.strftime("%Y-%m-%d")) + def refresh_keys(self, force_new_key=False): + # Generate email activation key and link + if not self.invitation_key or force_new_key: + salt = "" + for i in range(5): + salt = salt + random.choice(string.ascii_letters) + salt = salt.encode('utf8') + invitationsalt = self.last_name.encode('utf8') + self.invitation_key = hashlib.sha1(salt + invitationsalt).hexdigest() + self.key_expires = timezone.now() + datetime.timedelta(days=365) + self.save() + class CitationNotification(models.Model): contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE) diff --git a/scipost/static/scipost/assets/css/_buttons.scss b/scipost/static/scipost/assets/css/_buttons.scss index 4c1d9d4bbd2619b9bc50d7eed5980df0e7b3671a..6abdc7c02b81ac4c4a9c4d8ce9cc82c87f0ef977 100644 --- a/scipost/static/scipost/assets/css/_buttons.scss +++ b/scipost/static/scipost/assets/css/_buttons.scss @@ -39,13 +39,13 @@ .voting-group { border-radius: $card-border-radius; color: $scipost-darkblue; - box-shadow: 0 1px 0 0 $gray-600; + border: 1px solid $gray-600; > div { padding: 0.25rem 0.5rem; font-size: 0.8rem; border-radius: 0; - line-height: 1.25; + line-height: 1; margin: 0; display: inline-block; color: #002b49; @@ -98,3 +98,12 @@ } } } + +.category-group { + display: inline-block; + margin-bottom: 0.5rem; + + > div { + line-height: 1.3; + } +} diff --git a/scipost/templates/scipost/_draft_registration_tables.html b/scipost/templates/scipost/_draft_registration_tables.html index d0909ac9068a93196bdaadb663b869ec96fdb0b9..76765d3f3e4b824531631a0ed6349fc29c29c4aa 100644 --- a/scipost/templates/scipost/_draft_registration_tables.html +++ b/scipost/templates/scipost/_draft_registration_tables.html @@ -1,357 +1,425 @@ -<div class="row"> - <div class="col-12"> - <h2 class="highlight">Invitations sent (response pending)</h2> - <h3>Editorial Fellows ({{sent_reg_inv_fellows|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_sent_reg_inv_fellows">view/hide</a></h3> +<h2 class="highlight">Invitations sent (response pending)</h2> - <table class="table" id="table_sent_reg_inv_fellows" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for fellow in sent_reg_inv_fellows %} - <tr> - <td>{{ fellow.last_name }}</td> - <td>{{ fellow.first_name }}</td> - <td>{{ fellow.email }}</td> - <td>{{ fellow.date_sent }} </td> - <td>{{ fellow.get_invitation_type_display }}</td> - <td>{{ fellow.invited_by.user.first_name }} {{ fellow.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<h3>Editorial Fellows ({{sent_reg_inv_fellows|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_sent_reg_inv_fellows">view/hide +</a> - <h3>Normal Contributors ({{sent_reg_inv_contrib|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_sent_reg_inv_contrib">view/hide</a></h3> - <table class="table" id="table_sent_reg_inv_contrib" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in sent_reg_inv_contrib %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<table class="table" id="table_sent_reg_inv_fellows" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + <th></th> + </tr> + </thead> + <tbody> + {% for fellow in sent_reg_inv_fellows %} + <tr> + <td>{{ fellow.last_name }}</td> + <td>{{ fellow.first_name }}</td> + <td>{{ fellow.get_title_display }}</td> + <td>{{ fellow.email }}</td> + <td>{{ fellow.date_sent }} </td> + <td>{{ fellow.get_invitation_type_display }}</td> + <td>{{ fellow.invited_by.user.first_name }} {{ fellow.invited_by.user.last_name }}</td> + <td> + {% if perms.scipost.can_invite_Fellows %} + <a href="{% url 'scipost:renew_registration_invitation' invitation_id=fellow.id %}">Renew</a> ({{ fellow.nr_reminders }}) {% if fellow.date_last_reminded %}(last: {{ fellow.date_last_reminded|date:"Y-m-d" }}){% endif %} + · + <a href="{% url 'scipost:mark_reg_inv_as_declined' invitation_id=fellow.id %}">Declined</a> + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="7">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> - <h3>Referees ({{sent_reg_inv_ref|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_sent_reg_inv_ref">view/hide</a></h3> - <table class="table" id="table_sent_reg_inv_ref" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in sent_reg_inv_ref %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<h3>Normal Contributors ({{sent_reg_inv_contrib|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_sent_reg_inv_contrib">view/hide +</a> - <h3>Cited in sub ({{sent_reg_inv_cited_sub|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_sent_reg_inv_cited_sub">view/hide</a></h3> - <table class="table" id="table_sent_reg_inv_cited_sub" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in sent_reg_inv_cited_sub %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<table class="table" id="table_sent_reg_inv_contrib" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + <th></th> + </tr> + </thead> + <tbody> + {% for invitation in sent_reg_inv_contrib %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + <td> + {% if perms.scipost.can_invite_Fellows %} + <a href="{% url 'scipost:renew_registration_invitation' invitation_id=invitation.id %}">Renew</a> ({{ invitation.nr_reminders }}) {% if invitation.date_last_reminded %}(last: {{ invitation.date_last_reminded|date:"Y-m-d" }}){% endif %} + · + <a href="{% url 'scipost:mark_reg_inv_as_declined' invitation_id=invitation.id %}">Declined</a> + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="7">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> - <h3>Cited in pub ({{sent_reg_inv_cited_pub|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_sent_reg_inv_cited_pub">view/hide</a></h3> - <table class="table" id="table_sent_reg_inv_cited_pub" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in sent_reg_inv_cited_pub %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> - </div> -</div> +<h3>Referees ({{sent_reg_inv_ref|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_sent_reg_inv_ref">view/hide +</a> -<div class="row"> - <div class="col-12"> - <h2 class="highlight">Invitations sent (responded)</h2> +<table class="table" id="table_sent_reg_inv_ref" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + <th></th> + </tr> + </thead> + <tbody> + {% for invitation in sent_reg_inv_ref %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + <td> + {% if perms.scipost.can_invite_Fellows %} + <a href="{% url 'scipost:renew_registration_invitation' invitation_id=invitation.id %}">Renew</a> ({{ invitation.nr_reminders }}) {% if invitation.date_last_reminded %}(last: {{ invitation.date_last_reminded|date:"Y-m-d" }}){% endif %} + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="7">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> - <h3>Editorial Fellows ({{resp_reg_inv_fellow|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_resp_reg_inv_fellow">view/hide</a></h3> - <table class="table" id="table_resp_reg_inv_fellow" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in resp_reg_inv_fellow %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<h3>Cited in sub ({{sent_reg_inv_cited_sub|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_sent_reg_inv_cited_sub">view/hide +</a> - <h3>Normal Contributors ({{resp_reg_inv_contrib|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_resp_reg_inv_contrib">view/hide</a></h3> - <table class="table" id="table_resp_reg_inv_contrib" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in resp_reg_inv_contrib %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<table class="table" id="table_sent_reg_inv_cited_sub" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + <th></th> + </tr> + </thead> + <tbody> + {% for invitation in sent_reg_inv_cited_sub %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + <td> + {% if perms.scipost.can_invite_Fellows %} + <a href="{% url 'scipost:renew_registration_invitation' invitation_id=invitation.id %}">Renew</a> ({{ invitation.nr_reminders }}) {% if invitation.date_last_reminded %}(last: {{ invitation.date_last_reminded|date:"Y-m-d" }}){% endif %} + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="7">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> - <h3>Referees ({{resp_reg_inv_ref|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_resp_reg_inv_ref">view/hide</a></h3> - <table class="table" id="table_resp_reg_inv_ref" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in resp_reg_inv_ref %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> - <h3>Cited in sub ({{resp_reg_inv_cited_sub|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_resp_reg_inv_cited_sub">view/hide</a></h3> - <table class="table" id="table_resp_reg_inv_cited_sub" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in resp_reg_inv_cited_sub %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<h3>Cited in pub ({{sent_reg_inv_cited_pub|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_sent_reg_inv_cited_pub">view/hide +</a> - <h3>Cited in pub ({{resp_reg_inv_cited_pub|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_resp_reg_inv_cited_pub">view/hide</a></h3> - <table class="table" id="table_resp_reg_inv_cited_pub" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in resp_reg_inv_cited_pub %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> +<table class="table" id="table_sent_reg_inv_cited_pub" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + <th></th> + </tr> + </thead> + <tbody> + {% for invitation in sent_reg_inv_cited_pub %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + <td> + {% if perms.scipost.can_invite_Fellows %} + <a href="{% url 'scipost:renew_registration_invitation' invitation_id=invitation.id %}">Renew</a> ({{ invitation.nr_reminders }}) {% if invitation.date_last_reminded %}(last: {{ invitation.date_last_reminded|date:"Y-m-d" }}){% endif %} + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="7">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> - <h3>Declined ({{decl_reg_inv|length}}) - <a href="javascript:void(0)" data-toggle="toggle" data-target="#table_decl_reg_inv">view/hide</a></h3> - <table class="table" id="table_decl_reg_inv" style="display: none;"> - <thead> - <tr> - <th>Last name</th> - <th>First name</th> - <th>Email</th> - <th>Date sent</th> - <th>Type</th> - <th>Invited by</th> - </tr> - </thead> - <tbody> - {% for invitation in decl_reg_inv %} - <tr> - <td>{{ invitation.last_name }}</td> - <td>{{ invitation.first_name }}</td> - <td>{{ invitation.email }}</td> - <td>{{ invitation.date_sent }} </td> - <td>{{ invitation.get_invitation_type_display }}</td> - <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> - </tr> - {% empty %} - <tr> - <td colspan="6">No invitations found.</td> - </tr> - {% endfor %} - </tbody> - </table> - </div> -</div> +<h2 class="highlight">Invitations sent (responded)</h2> + +<h3>Editorial Fellows ({{resp_reg_inv_fellow|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_resp_reg_inv_fellow">view/hide +</a> + +<table class="table" id="table_resp_reg_inv_fellow" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in resp_reg_inv_fellow %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h3>Normal Contributors ({{resp_reg_inv_contrib|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_resp_reg_inv_contrib">view/hide +</a> + +<table class="table" id="table_resp_reg_inv_contrib" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in resp_reg_inv_contrib %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h3>Referees ({{resp_reg_inv_ref|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_resp_reg_inv_ref">view/hide +</a> + +<table class="table" id="table_resp_reg_inv_ref" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in resp_reg_inv_ref %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h3>Cited in sub ({{resp_reg_inv_cited_sub|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_resp_reg_inv_cited_sub">view/hide +</a> + +<table class="table" id="table_resp_reg_inv_cited_sub" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in resp_reg_inv_cited_sub %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h3>Cited in pub ({{resp_reg_inv_cited_pub|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_resp_reg_inv_cited_pub">view/hide +</a> + +<table class="table" id="table_resp_reg_inv_cited_pub" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in resp_reg_inv_cited_pub %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h3>Declined ({{decl_reg_inv|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_decl_reg_inv">view/hide +</a> + +<table class="table" id="table_decl_reg_inv" style="display: none;"> + <thead> + <tr> + <th>Last name</th> + <th>First name</th> + <th>Title</th> + <th>Email</th> + <th>Date sent</th> + <th>Type</th> + <th>Invited by</th> + </tr> + </thead> + <tbody> + {% for invitation in decl_reg_inv %} + <tr> + <td>{{ invitation.last_name }}</td> + <td>{{ invitation.first_name }}</td> + <td>{{ invitation.get_title_display }}</td> + <td>{{ invitation.email }}</td> + <td>{{ invitation.date_sent }} </td> + <td>{{ invitation.get_invitation_type_display }}</td> + <td>{{ invitation.invited_by.user.first_name }} {{ invitation.invited_by.user.last_name }}</td> + </tr> + {% empty %} + <tr> + <td colspan="6">No invitations found.</td> + </tr> + {% endfor %} + </tbody> +</table> + +<h2 class="highlight">List of already-registered contributors ({{names_reg_contributors|length}})</h3> +<a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#registered_contributors">view/hide +</a> -<div class="row"> - <div class="col-12"> - <h2 class="highlight">List of already-registered contributors ({{names_reg_contributors|length}}) <small><a href="javascript:void(0)" data-toggle="toggle" data-target="#registered_contributors">view/hide</a></small></h3> - <div class="card-columns" id="registered_contributors" style="display: none;"> - {% for first_name, last_name in names_reg_contributors %} - <div class="card border-0"> - {{ last_name }}, {{ first_name }} - </div> - {% endfor %} +<div class="card-columns" id="registered_contributors" style="display: none;"> + {% for first_name, last_name in names_reg_contributors %} + <div class="card border-0"> + {{ last_name }}, {{ first_name }} </div> - </div> + {% endfor %} </div> diff --git a/scipost/templates/scipost/draft_registration_invitation.html b/scipost/templates/scipost/draft_registration_invitation.html index 7c81a644bbb4352521510044e6030b9a4e8612b6..6724952a0e9522b0c91b8df0f3c6f37d66b4777f 100644 --- a/scipost/templates/scipost/draft_registration_invitation.html +++ b/scipost/templates/scipost/draft_registration_invitation.html @@ -56,8 +56,10 @@ $(document).ready(function(){ <div class="row"> <div class="col-12"> - <h2 class="highlight">Existing drafts (to be processed by Admin) ({{existing_drafts|length}}) <small><a href="javascript:void(0)" data-toggle="toggle" data-target="#table_existing_drafts">view/hide</a></small></h2> - <table class="table" id="table_existing_drafts" style="display: none;"> + <h2 class="highlight">Existing drafts (to be processed by Admin) ({{existing_drafts|length}})</h2> + <a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_existing_drafts">view/hide +</a> + + <table class="table" id="table_existing_drafts"> <thead> <tr> <th>Last name</th> diff --git a/scipost/templates/scipost/index.html b/scipost/templates/scipost/index.html index 36cd4a79dc0af6b6ad7844469e2327b0bf9d26b1..59862ebd0256875d6e3b27ead7f7c84647c147d6 100644 --- a/scipost/templates/scipost/index.html +++ b/scipost/templates/scipost/index.html @@ -35,7 +35,7 @@ {% for publication in publications %} <li class="list-group-item"> <div class="card-body px-0"> - {% include 'journals/_publication_details_small.html' with publication=publication %} + {% include 'partials/journals/publication_li_content.html' with publication=publication %} </div> </li> {% endfor %} diff --git a/scipost/templates/scipost/registration_invitations.html b/scipost/templates/scipost/registration_invitations.html index a495c7ce7be80a89ee415f1daa8428276ec7e04e..cc5a6922d58967980a3431416ae490513f42b2b7 100644 --- a/scipost/templates/scipost/registration_invitations.html +++ b/scipost/templates/scipost/registration_invitations.html @@ -64,8 +64,10 @@ $(document).ready(function(){ <div class="row"> <div class="col-12"> - <h2 class="highlight">Existing drafts (to be processed by Admin) ({{existing_drafts|length}}) <small><a href="javascript:void(0)" data-toggle="toggle" data-target="#table_existing_drafts">view/hide</a></small></h2> - <table class="table" id="table_existing_drafts"> + <h2 class="highlight">Existing drafts (to be processed by Admin)</h2> + <a href="javascript:void(0)" class="btn mb-2" data-toggle="toggle" data-target="#table_existing_drafts">view/hide ({{existing_drafts|length}}) +</a> + + <table class="table" id="table_existing_drafts" style="display: none;"> <thead> <tr> <th>Last name</th> diff --git a/scipost/templates/scipost/update_personal_data.html b/scipost/templates/scipost/update_personal_data.html index d4a41b47966f1c8f0105c0085b78c4ab29f8e2fc..bf638ee1511c87ace3d42a6608f795ce215c41dc 100644 --- a/scipost/templates/scipost/update_personal_data.html +++ b/scipost/templates/scipost/update_personal_data.html @@ -58,7 +58,7 @@ {% if institution_formset %} <div class="col-lg-6"> <div id="institutions" class="formset-group"> - <h1 class="mb-3">Your Institutions</h1> + <h1 class="mb-3">Your Affiliations</h1> {{ institution_formset.media }} {{ institution_formset|bootstrap }} </div> diff --git a/scipost/views.py b/scipost/views.py index 3269094efa55060fc8723935289b4c863e2f14eb..858b004e79829bd2f4ae61483d1714def50eebdb 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -39,6 +39,7 @@ from affiliations.forms import AffiliationsFormset from commentaries.models import Commentary from comments.models import Comment from journals.models import Publication, Journal +from mails.views import MailEditingSubView from news.models import NewsItem from submissions.models import Submission, RefereeInvitation,\ Report, EICRecommendation @@ -440,7 +441,7 @@ def map_draft_reg_inv_to_contributor(request, draft_id, contributor_id): return redirect(reverse('scipost:registration_invitations')) -@permission_required('scipost.can_manage_registration_invitations', return_403=True) +@permission_required('scipost.can_invite_Fellows', return_403=True) def registration_invitations(request, draft_id=None): """ Overview and tools for administrators """ # List invitations sent; send new ones @@ -468,6 +469,7 @@ def registration_invitations(request, draft_id=None): invitation = reg_inv_form.save(commit=False) invitation.invited_by = request.user.contributor invitation.save() + invitation.refresh_keys() Utils.load({'invitation': invitation}) Utils.send_registration_invitation_email() @@ -568,26 +570,27 @@ def edit_invitation_personal_message(request, invitation_id): return render(request, 'scipost/edit_invitation_personal_message.html', context) -@permission_required('scipost.can_manage_registration_invitations', return_403=True) +@permission_required('scipost.can_invite_Fellows', return_403=True) def renew_registration_invitation(request, invitation_id): """ Renew an invitation (called from registration_invitations). """ invitation = get_object_or_404(RegistrationInvitation, pk=invitation_id) - errormessage = None - if(invitation.invitation_type == 'F' - and not request.user.has_perm('scipost.can_invite_Fellows')): - errormessage = ('You do not have the authorization to send a Fellow-type ' - 'invitation. Consider Contributor, or cited (sub/pub). ') - elif invitation.invitation_type == 'R': - errormessage = ('Referee-type invitations must be made by the Editor-in-charge ' - 'at the relevant Submission\'s Editorial Page. ') - if errormessage is not None: - return render(request, 'scipost/error.html', context={'errormessage': errormessage}) - - Utils.load({'invitation': invitation}) - Utils.send_registration_invitation_email(True) - return redirect(reverse('scipost:registration_invitations')) + + # Utils.load({'invitation': invitation}) + # Utils.send_registration_invitation_email(True) + mail_request = MailEditingSubView(request, mail_code='registration_invitation_renewal', + invitation=invitation) + if mail_request.is_valid(): + invitation.nr_reminders += 1 + invitation.date_last_reminded = timezone.now() + invitation.save() + invitation.refresh_keys() + messages.success(request, 'Registration invitation have been sent.') + mail_request.send() + return redirect('scipost:registration_invitations') + else: + return mail_request.return_render() @permission_required('scipost.can_manage_registration_invitations', return_403=True) diff --git a/submissions/templates/submissions/_single_public_report.html b/submissions/templates/submissions/_single_public_report.html index efdc5c5144fb7f7dde0959338e2d3c9b751582b4..c39fcdc22220cb2a003b96a637c94a49f6f19fca 100644 --- a/submissions/templates/submissions/_single_public_report.html +++ b/submissions/templates/submissions/_single_public_report.html @@ -6,6 +6,10 @@ <h3><a href="{% url 'comments:reply_to_report' report_id=report.id %}">Reply to this Report</a> (authors only)</h3> {% endif %} + {% if report.comments.vetted.exists %} + <br> + {% endif %} + {% for reply in report.comments.vetted %} {% include 'comments/_single_comment_with_link.html' with comment=reply perms=perms user=user %} {% endfor %} diff --git a/submissions/templates/submissions/pool/pool.html b/submissions/templates/submissions/pool/pool.html index 1607a04b15df42d6a004b7d1b9cc40f7413ee628..065b4f6f0ca9a687484f69e73cab75b6166c264b 100644 --- a/submissions/templates/submissions/pool/pool.html +++ b/submissions/templates/submissions/pool/pool.html @@ -27,7 +27,7 @@ <h1>SciPost Submissions Pool</h1> {% if is_ECAdmin %} - {% if recommendations.voting_in_preparation.exists or recommendations.put_to_voting.exists %} + {% if recommendations.voting_in_preparation.exists or recommendations.put_to_voting.exists or latest_submission_events %} <div class="quote-border"> <h2 class="text-primary">Administrative Tasks</h2> @@ -53,7 +53,14 @@ {% endfor %} </ul> {% endif %} + {% if latest_submission_events %} + <a href="javascript:void(0)" class="btn" data-toggle="toggle" data-target="#lastest_events_list"><i class="fa fa-comments-o"></i> View/hide latest events ({{ latest_submission_events|length }}) in the last 24 hours</a> + <div id="lastest_events_list" style="display: none;"> + {% include 'submissions/submission_event_list_general.html' with events=latest_submission_events %} + </div> + {% endif %} </div> + <br> {% endif %} {% endif %} diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html index 0010c1e89e760112bcce533f491945214cd63edc..1e66d5504f90db0c682f1200c7ca359b3e341c83 100644 --- a/submissions/templates/submissions/submission_detail.html +++ b/submissions/templates/submissions/submission_detail.html @@ -105,13 +105,15 @@ <li> <a href="{{ proofs.get_absolute_url }}" target="_blank">Download version {{ proofs.version }}</a> · uploaded: {{ proofs.created|date:"DATE_FORMAT" }} · status: <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span> - {% if proofs.status == 'accepted_sup' and proofs_decision_form and is_author %} - <h3 class="mb-0 mt-2">Please advise the Production Team on your findings on Proofs version {{ proof.version }}</h3> - <form method="post" action="{% url 'production:author_decision' proofs.slug %}" class="my-2"> - {% csrf_token %} - {{ proofs_decision_form|bootstrap }} - <input class="btn btn-primary btn-sm" type="submit" value="Submit"> - </form> + {% if proofs.status == 'accepted_sup' or proofs.status == 'sent' %} + {% if proofs_decision_form and is_author %} + <h3 class="mb-0 mt-2">Please advise the Production Team on your findings on Proofs version {{ proofs.version }}</h3> + <form method="post" enctype="multipart/form-data" action="{% url 'production:author_decision' proofs.slug %}" class="my-2"> + {% csrf_token %} + {{ proofs_decision_form|bootstrap }} + <input class="btn btn-primary btn-sm" type="submit" value="Submit"> + </form> + {% endif %} {% endif %} </li> {% endfor %} diff --git a/submissions/templatetags/submissions_extras.py b/submissions/templatetags/submissions_extras.py index 9cb66171d072b47b057a2a2b6e79817614db882c..d98be57a13f124f628b54f7e016404de300fd475 100644 --- a/submissions/templatetags/submissions_extras.py +++ b/submissions/templatetags/submissions_extras.py @@ -21,6 +21,7 @@ def is_viewable_by_authors(recommendation): 'accepted', 'rejected', 'published', 'withdrawn'] + @register.filter def user_is_referee(submission, user): return submission.referee_invitations.filter(referee__user=user).exists() diff --git a/submissions/views.py b/submissions/views.py index a8d12ce41147f6943d1532d5f21429b55c5bcb8c..24564a1a2ace125c92c93abc6596a82ddb05e2e0 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -387,7 +387,7 @@ def pool(request, arxiv_identifier_w_vn_nr=None): # EdColAdmin related variables if request.user.has_perm('scipost.can_oversee_refereeing'): - context['latest_events'] = SubmissionEvent.objects.for_eic().last_hours() + context['latest_submission_events'] = SubmissionEvent.objects.for_eic().last_hours() # Temporary test logic: only testers see the new Pool if context['submission'] and request.is_ajax(): diff --git a/templates/500.html b/templates/500.html index 91403510f7c56414f89d263243fe1c4bbebce003..fea9d3baa473f2a2d2bda5ccd4cc497e8f613646 100644 --- a/templates/500.html +++ b/templates/500.html @@ -6,9 +6,9 @@ <div style="text-align: center;"> <img src="//scipost.org/static/scipost/images/logo_scipost_RGB_HTML_groot.png" alt="SciPost logo" width="240" style="margin-top: 20px; margin-bottom: 20px" /> - <h1>The server responded with an error.</h1> + <h1>We are sorry, something went wrong.</h1> <h2>500</h2> - <h3>We are sorry, something went wrong. The SciPost administrators have been informed.</h3> + <h3>The SciPost administrators have been informed.</h3> <p style="margin-top: 20px;">Go back to <a href="//scipost.org">the homepage</a>.</p> </div>