diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py index 7121c9a6f2909f450bd3b06d8375248fd894c1a3..669f3bc7ed10183e7bcea091ff22551cd217818e 100644 --- a/SciPost_v1/settings/base.py +++ b/SciPost_v1/settings/base.py @@ -87,12 +87,13 @@ INSTALLED_APPS = ( 'commentaries', 'comments', 'journals', + 'mailing_lists', 'news', 'scipost', 'submissions', 'theses', 'virtualmeetings', - 'webpack_loader' + 'webpack_loader', ) HAYSTACK_CONNECTIONS = { @@ -227,6 +228,9 @@ WEBPACK_LOADER = { # Email EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' EMAIL_FILE_PATH = 'local_files/email/' +MAILCHIMP_DATABASE_CODE = 'us6' +MAILCHIMP_API_USER = 'test_API-user' +MAILCHIMP_API_KEY = 'test_API-key' # Own settings JOURNALS_DIR = 'journals' diff --git a/SciPost_v1/settings/local_jorran.py b/SciPost_v1/settings/local_jorran.py index 12dcdcfa68b6054fae82ccda1936723bab419563..1d47922efff81ddbc6b2157ce74b9fee93f697a6 100644 --- a/SciPost_v1/settings/local_jorran.py +++ b/SciPost_v1/settings/local_jorran.py @@ -17,3 +17,6 @@ STATIC_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/' MEDIA_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/media/' WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] =\ '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/bundles/' + +MAILCHIMP_API_USER = get_secret("MAILCHIMP_API_USER") +MAILCHIMP_API_KEY = get_secret("MAILCHIMP_API_KEY") diff --git a/SciPost_v1/settings/production.py b/SciPost_v1/settings/production.py index dbf8b348bdacf0fb1935dffaee118962d3ce1103..b6f220c85893695023746f93a09ea40c130f7c14 100644 --- a/SciPost_v1/settings/production.py +++ b/SciPost_v1/settings/production.py @@ -39,3 +39,5 @@ SERVER_EMAIL = get_secret("SERVER_EMAIL") CROSSREF_LOGIN_ID = get_secret("CROSSREF_LOGIN_ID") CROSSREF_LOGIN_PASSWORD = get_secret("CROSSREF_LOGIN_PASSWORD") HAYSTACK_CONNECTIONS['default']['PATH'] = '/home/jscaux/webapps/scipost/SciPost_v1/whoosh_index' +MAILCHIMP_API_USER = get_secret("MAILCHIMP_API_USER") +MAILCHIMP_API_KEY = get_secret("MAILCHIMP_API_KEY") diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py index c9de03f6f38e5bdc3e56cdc88761d65cf3717e5a..f930e4a9312d8e0a5326bac188cb63824c27a412 100644 --- a/SciPost_v1/urls.py +++ b/SciPost_v1/urls.py @@ -35,7 +35,7 @@ urlpatterns = [ url(r'^commentary/', include('commentaries.urls', namespace="commentaries")), url(r'^comments/', include('comments.urls', namespace="comments")), url(r'^journals/', include('journals.urls.general', namespace="journals")), - + url(r'^mailing_list/', include('mailing_lists.urls', namespace="mailing_lists")), url(r'^submissions/', include('submissions.urls', namespace="submissions")), url(r'^submission/', include('submissions.urls', namespace="submissions")), url(r'^theses/', include('theses.urls', namespace="theses")), diff --git a/journals/templates/journals/_publication_single_short_summary.html b/journals/templates/journals/_publication_single_short_summary.html index 7ca8c2165167f03f4664ae6631e2b138643922cc..ddc8caa6747557a43b2362575ea221038bb623e3 100644 --- a/journals/templates/journals/_publication_single_short_summary.html +++ b/journals/templates/journals/_publication_single_short_summary.html @@ -1,3 +1,3 @@ <h3 class="pb-0"><a href="{{publication.get_absolute_url}}">{{ publication.title }}</a></h3> -<div class="text-muted mb-1"><span class="d-inline-block">published {{ publication.publication_date }}</span> <span class="d-inline-block">as {{ publication.doi_string }}</span></div> +<div class="text-muted mb-1"><span class="d-inline-block">published {{ publication.publication_date }}</span> <span class="d-inline-block">as {{ publication.citation }}</span></div> <div>by {{ publication.author_list|truncatechars:30 }}</div> diff --git a/journals/templatetags/lookup.py b/journals/templatetags/lookup.py index e262a10bf1911590174b222822388bf899d160bd..bdfeb24b0ce148daab9e22cc133a37abf5e7cc2a 100644 --- a/journals/templatetags/lookup.py +++ b/journals/templatetags/lookup.py @@ -21,3 +21,11 @@ class PublicationLookup(LookupChannel): return u"%s (%s)<br><span class='text-muted'>by %s</span>" % (item.title, item.doi_string, item.author_list) + + def check_auth(self, request): + """ + Check if current user has required permissions. + Right now only used for draft registration invitations. May be extended in the + future for other purposes as well. + """ + return request.user.has_perm('can_draft_registration_invitations') diff --git a/journals/utils.py b/journals/utils.py index ccb5852064de8ae78db09de828d791749e7b5b0b..bda96d34647ef5620842afed9113749834afe348 100644 --- a/journals/utils.py +++ b/journals/utils.py @@ -21,7 +21,7 @@ class JournalUtils(object): '\n\nhas been published online with reference ' + cls.publication.citation() + '.' '\n\nThe publication page is located at the permanent link ' - 'https://scipost.org/' + cls.publication.doi_string + '.' + 'https://scipost.org/' + cls.publication.doi_label + '.' '\n\nTo facilitate dissemination of your paper, we greatly encourage ' 'you to update the arXiv Journal-ref with this information.' '\n\nWe warmly congratulate you on this achievement and thank you ' diff --git a/mailing_lists/__init__.py b/mailing_lists/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mailing_lists/admin.py b/mailing_lists/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..0730bce3ef54291b105d1561d7938594db162021 --- /dev/null +++ b/mailing_lists/admin.py @@ -0,0 +1,14 @@ +from django.contrib import admin + +from .models import MailchimpList + + +class MailchimpListAdmin(admin.ModelAdmin): + list_display = ['__str__', 'mailchimp_list_id', 'status'] + list_filter = ['status'] + + def has_add_permission(self, *args): + return False + + +admin.site.register(MailchimpList, MailchimpListAdmin) diff --git a/mailing_lists/apps.py b/mailing_lists/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..7b4422d51ad5206634f7e010a8d74f96dba289b5 --- /dev/null +++ b/mailing_lists/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MailchimpConfig(AppConfig): + name = 'mailing_lists' diff --git a/mailing_lists/constants.py b/mailing_lists/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..f0146707df801221665e3497740e8043b11469fa --- /dev/null +++ b/mailing_lists/constants.py @@ -0,0 +1,12 @@ +MAIL_LIST_STATUS_ACTIVE = 'active' +MAIL_LIST_STATUS_DEACTIVATED = 'deactivated' +MAIL_LIST_STATUSES = ( + (MAIL_LIST_STATUS_ACTIVE, 'Active'), + (MAIL_LIST_STATUS_DEACTIVATED, 'Deactivated'), +) + +MAILCHIMP_SUBSCRIBED = 'subscribed' +MAILCHIMP_STATUSES = ( + (MAILCHIMP_SUBSCRIBED, 'Subscribed'), + ('unsubscribed', 'Unsubscribed'), +) diff --git a/mailing_lists/forms.py b/mailing_lists/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..b8b73d6efffc49c37895448538631fac0282a3c8 --- /dev/null +++ b/mailing_lists/forms.py @@ -0,0 +1,41 @@ +from django import forms +from django.conf import settings + +from mailchimp3 import MailChimp + +from .constants import MAIL_LIST_STATUS_ACTIVE, MAIL_LIST_STATUS_DEACTIVATED +from .models import MailchimpList + +from scipost.models import Contributor + + +class MailchimpUpdateForm(forms.Form): + """ + This form does the synchronizing of mailing lists in the database. + """ + def __init__(self): + self.client = MailChimp(settings.MAILCHIMP_API_USER, settings.MAILCHIMP_API_KEY) + self.lists = MailchimpList.objects.active() + + def sync(self): + # Deactivate all mailing lists by default + MailchimpList.objects.active().update(status=MAIL_LIST_STATUS_DEACTIVATED) + + # Connect the client to Mailchimp API + client = MailChimp(settings.MAILCHIMP_API_USER, settings.MAILCHIMP_API_KEY) + response = client.lists.all(get_all=True, fields="lists.name,lists.id") + + # Do the update for all Lists recieved + count = 0 + while response['lists']: + _list = response['lists'].pop() + chimplist, created = MailchimpList.objects.get_or_create(mailchimp_list_id=_list['id']) + chimplist.name = _list['name'] + chimplist.status = MAIL_LIST_STATUS_ACTIVE + chimplist.save() + count += 1 + return count + + def sync_members(self, _list): + contributors = Contributor.objects.active().filter(accepts_SciPost_emails=True) + return _list.update_membership(contributors) diff --git a/mailing_lists/managers.py b/mailing_lists/managers.py new file mode 100644 index 0000000000000000000000000000000000000000..c58c92de26405a91950d440684c18b206a59f4d9 --- /dev/null +++ b/mailing_lists/managers.py @@ -0,0 +1,24 @@ +from django.db import models + +from .constants import MAIL_LIST_STATUS_ACTIVE + + +class MailListManager(models.Manager): + def active(self): + """ + Return Lists active in the Mailchimp account. + + To be used on Admin pages. + """ + return self.filter(status=MAIL_LIST_STATUS_ACTIVE) + + def open_to_subscribe(self, contributor): + """ + Return Lists active in the Mailchimp account and open for + subscription for Contributors in the settings page. + + + """ + return self.filter(status=MAIL_LIST_STATUS_ACTIVE, + open_for_subscription=True, + allowed_groups__in=contributor.user.groups.all()) diff --git a/mailing_lists/migrations/0001_initial.py b/mailing_lists/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..1c909a2c13f0f9833d0d0d04d6d18ed151d2cc0e --- /dev/null +++ b/mailing_lists/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-04-23 14:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import scipost.db.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('scipost', '0050_auto_20170416_2152'), + ('auth', '0008_alter_user_username_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='ActiveMailchimpSubscription', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('latest_activity', scipost.db.fields.AutoDateTimeField(blank=True, default=django.utils.timezone.now, editable=False)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='MailchimpList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('latest_activity', scipost.db.fields.AutoDateTimeField(blank=True, default=django.utils.timezone.now, editable=False)), + ('name', models.CharField(max_length=255)), + ('internal_name', models.CharField(blank=True, max_length=255)), + ('supporting_text', models.TextField(blank=True)), + ('mailchimp_list_id', models.CharField(max_length=255)), + ('status', models.CharField(choices=[('active', 'Active'), ('deactivated', 'Deactivated')], default='active', max_length=255)), + ('allowed_groups', models.ManyToManyField(related_name='allowed_mailchimp_lists', to='auth.Group')), + ], + options={ + 'default_permissions': ('delete',), + }, + ), + migrations.AddField( + model_name='activemailchimpsubscription', + name='active_list', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mailing_lists.MailchimpList'), + ), + migrations.AddField( + model_name='activemailchimpsubscription', + name='contributor', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor'), + ), + ] diff --git a/mailing_lists/migrations/0002_auto_20170423_1943.py b/mailing_lists/migrations/0002_auto_20170423_1943.py new file mode 100644 index 0000000000000000000000000000000000000000..c39c7793b8f3120021458108d6eea1e429976402 --- /dev/null +++ b/mailing_lists/migrations/0002_auto_20170423_1943.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-04-23 17:43 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0050_auto_20170416_2152'), + ('mailing_lists', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='mailchimplist', + options={'ordering': ['status', 'internal_name', 'name']}, + ), + migrations.AddField( + model_name='mailchimplist', + name='open_for_subscription', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='activemailchimpsubscription', + name='contributor', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mail_subscription', to='scipost.Contributor'), + ), + migrations.AlterField( + model_name='mailchimplist', + name='mailchimp_list_id', + field=models.CharField(max_length=255, unique=True), + ), + migrations.AlterUniqueTogether( + name='activemailchimpsubscription', + unique_together=set([('active_list', 'contributor')]), + ), + ] diff --git a/mailing_lists/migrations/0003_activemailchimpsubscription_status.py b/mailing_lists/migrations/0003_activemailchimpsubscription_status.py new file mode 100644 index 0000000000000000000000000000000000000000..5f37e18cf2c96bac71543ce2232adb4d9035efc0 --- /dev/null +++ b/mailing_lists/migrations/0003_activemailchimpsubscription_status.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-04-23 20:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailing_lists', '0002_auto_20170423_1943'), + ] + + operations = [ + migrations.AddField( + model_name='activemailchimpsubscription', + name='status', + field=models.CharField(choices=[('subscribed', 'Subscribed'), ('unsubscribed', 'Unsubscribed')], default='subscribed', max_length=255), + ), + ] diff --git a/mailing_lists/migrations/0004_auto_20170423_2238.py b/mailing_lists/migrations/0004_auto_20170423_2238.py new file mode 100644 index 0000000000000000000000000000000000000000..320ca406e5b69fa4b925ac9f69540f11bb02dd13 --- /dev/null +++ b/mailing_lists/migrations/0004_auto_20170423_2238.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-04-23 20:38 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailing_lists', '0003_activemailchimpsubscription_status'), + ] + + operations = [ + migrations.RenameModel( + old_name='ActiveMailchimpSubscription', + new_name='MailchimpSubscription', + ), + ] diff --git a/mailing_lists/migrations/__init__.py b/mailing_lists/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mailing_lists/models.py b/mailing_lists/models.py new file mode 100644 index 0000000000000000000000000000000000000000..262f50eb40ad059683aa59cd92928ad292bad0d2 --- /dev/null +++ b/mailing_lists/models.py @@ -0,0 +1,73 @@ +from django.db import models +from django.conf import settings +from django.contrib.auth.models import Group +from django.urls import reverse + +from mailchimp3 import MailChimp + +from .constants import MAIL_LIST_STATUSES, MAIL_LIST_STATUS_ACTIVE,\ + MAILCHIMP_STATUSES, MAILCHIMP_SUBSCRIBED +from .managers import MailListManager + +from scipost.behaviors import TimeStampedModel + + +class MailchimpList(TimeStampedModel): + """ + This model is a copy of the current lists in the Mailchimp account. + It will be used to map the Contributor's preferences to the Mailchimp's lists + and keeping both up to date. + """ + name = models.CharField(max_length=255) + internal_name = models.CharField(max_length=255, blank=True) + supporting_text = models.TextField(blank=True) + mailchimp_list_id = models.CharField(max_length=255, unique=True) + status = models.CharField(max_length=255, choices=MAIL_LIST_STATUSES, + default=MAIL_LIST_STATUS_ACTIVE) + open_for_subscription = models.BooleanField(default=False) + allowed_groups = models.ManyToManyField(Group, related_name='allowed_mailchimp_lists') + + objects = MailListManager() + + class Meta: + ordering = ['status', 'internal_name', 'name'] + + def __str__(self): + if self.internal_name: + return self.internal_name + return self.name + + def get_absolute_url(self): + return reverse('mailing_lists:list_detail', args=[self.mailchimp_list_id]) + + def update_membership(self, contributors, status='subscribed'): + client = MailChimp(settings.MAILCHIMP_API_USER, settings.MAILCHIMP_API_KEY) + for contributor in contributors: + if self.allowed_groups.filter(user__contributor=contributor).exists(): + payload = { + 'email_address': contributor.user.email, + 'status': status, + 'status_if_new': status, + 'merge_fields': { + 'FNAME': contributor.user.first_name, + 'LNAME': contributor.user.last_name, + }, + } + client.lists.members.create_or_update(self.mailchimp_list_id, + payload['email_address'], + payload) + return True + + +class MailchimpSubscription(TimeStampedModel): + """ + Track the Contributors' settings on wheter he/she wants to have an + active subscription to a specific (public) list. + """ + active_list = models.ForeignKey('mailing_lists.MailchimpList') + contributor = models.ForeignKey('scipost.Contributor', related_name='mail_subscription') + status = models.CharField(max_length=255, choices=MAILCHIMP_STATUSES, + default=MAILCHIMP_SUBSCRIBED) + + class Meta: + unique_together = ('active_list', 'contributor',) diff --git a/mailing_lists/templates/mailing_lists/mailchimplist_form.html b/mailing_lists/templates/mailing_lists/mailchimplist_form.html new file mode 100644 index 0000000000000000000000000000000000000000..88fd7fface6bf3186f8ff11edb3cb6d56d0f28a6 --- /dev/null +++ b/mailing_lists/templates/mailing_lists/mailchimplist_form.html @@ -0,0 +1,38 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% block pagetitle %}: mailing lists{% endblock pagetitle %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{block.super}} + <a href="{% url 'mailing_lists:overview' %}" class="breadcrumb-item">Mailing lists</a> + <span class="breadcrumb-item">{{object}}</span> +{% endblock %} + +{% block content %} + + +<div class="row"> + <div class="col-12"> + <h1>Edit the mailing list <i>{{object}}</i></h1> + <h3>Mailchimp configuration:</h3> + <pre><code>ID: {{object.mailchimp_list_id}}<br>name: {{object.name}}<br>Status: {{object.get_status_display}}</code></pre> + <h3 class="mt-2">Actions:</h3> + <ul class="actions"> + <li><a href="{% url 'mailing_lists:sync_members' object.mailchimp_list_id %}">Syncronize members of the list</a></li> + </ul> + </div> +</div> + +<div class="row"> + <div class="col-12"> + <form method="post"> + {% csrf_token %} + {{form|bootstrap}} + <input type="submit" value="Update" class="btn btn-secondary" /> + </form> + </div> +</div> + +{% endblock content %} diff --git a/mailing_lists/templates/mailing_lists/overview.html b/mailing_lists/templates/mailing_lists/overview.html new file mode 100644 index 0000000000000000000000000000000000000000..06df462e1e1d9d1c0c927b56c2cc5ebf2dc03a50 --- /dev/null +++ b/mailing_lists/templates/mailing_lists/overview.html @@ -0,0 +1,45 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% block pagetitle %}: mailing lists{% endblock pagetitle %} + +{% block breadcrumb_items %} + {{block.super}} + <span class="breadcrumb-item">Mailing lists</span> +{% endblock %} + +{% block content %} + + +<div class="row"> + <div class="col-12"> + <h1>All mailing lists known to SciPost</h1> + <h3>Actions:</h3> + <ul class="actions"> + <li><a href="{% url 'mailing_lists:sync_lists' %}">Syncronize lists with the mail server</a></li> + </ul> + </div> +</div> + +<div class="row"> + <div class="col-12"> + <div class="card border-0"> + <h3 class="card-title">All lists in the database:</h3> + <ul class="list-group list-group-flush"> + {% for mail_list in object_list %} + <li class="list-group-item"> + <div class="py-1"> + <h3><a href="{{mail_list.get_absolute_url}}">{{mail_list}}</a> <small><code>ID: {{mail_list.mailchimp_list_id}}</code></small> {% if mail_list.open_for_subscription %}<span class="label label-sm label-outline-info">Open for subscription</span>{% endif %}</h3> + <p class="text-muted mb-0">{{mail_list.get_status_display}} | {{mail_list.allowed_groups.count}} group{{mail_list.allowed_groups.count|pluralize}} | last update: {{mail_list.latest_activity}}</p> + </div> + </li> + {% empty %} + <li class="list-group-item"> + <p class="mb-0">No mailing lists known. Please <a href="{% url 'mailing_lists:sync_lists' %}">update the list</a>.</p> + </li> + {% endfor %} + </ul> + </div> + </div> +</div> + +{% endblock content %} diff --git a/mailing_lists/templates/mailing_lists/widget_checkbox_option.html b/mailing_lists/templates/mailing_lists/widget_checkbox_option.html new file mode 100644 index 0000000000000000000000000000000000000000..e428f69f4aab4f7d5fae3cb6a4dd46df6313ac9a --- /dev/null +++ b/mailing_lists/templates/mailing_lists/widget_checkbox_option.html @@ -0,0 +1 @@ +ecx diff --git a/mailing_lists/tests.py b/mailing_lists/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/mailing_lists/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/mailing_lists/urls.py b/mailing_lists/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..2096c1591a9b5077b2971c8a1d857ece17262466 --- /dev/null +++ b/mailing_lists/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + # Mailchimp + url(r'^$', views.MailchimpListView.as_view(), name='overview'), + url(r'^sync$', views.syncronize_lists, name='sync_lists'), + url(r'^sync/(?P<list_id>[0-9a-zA-Z]+)/members$', views.syncronize_members, name='sync_members'), + url(r'^(?P<list_id>[0-9a-zA-Z]+)/$', views.ListDetailView.as_view(), name='list_detail'), +] diff --git a/mailing_lists/views.py b/mailing_lists/views.py new file mode 100644 index 0000000000000000000000000000000000000000..85396f413f8f982cd0683d587c1e1abc92929f05 --- /dev/null +++ b/mailing_lists/views.py @@ -0,0 +1,68 @@ +from django.contrib import messages +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin +from django.views.generic import UpdateView +from django.views.generic.list import ListView +from django.core.urlresolvers import reverse +from django.shortcuts import redirect, get_object_or_404 + +from .forms import MailchimpUpdateForm +from .models import MailchimpList + + +class MailchimpMixin(LoginRequiredMixin, PermissionRequiredMixin): + permission_required = 'scipost.can_manage_mailchimp' + raise_exception = True + + +class MailchimpListView(MailchimpMixin, ListView): + """ + List all lists of Mailchimp known to the current database. + This is part of the editorial actions for SciPost Administrators. + It should act as a main page from which the admin can to action to update + some general mailchimp settings. + """ + template_name = 'mailing_lists/overview.html' + model = MailchimpList + + +@login_required +@permission_required('scipost.can_manage_mailchimp', raise_exception=True) +def syncronize_lists(request): + """ + Syncronize the Mailchimp lists in the database with the lists known in + the mailchimp account which is related to the API_KEY. + """ + form = MailchimpUpdateForm() + updated = form.sync() + messages.success(request, '%i mailing lists have succesfully been updated.' % updated) + return redirect(reverse('mailing_lists:overview')) + + +@login_required +@permission_required('scipost.can_manage_mailchimp', raise_exception=True) +def syncronize_members(request, list_id): + """ + Syncronize the Mailchimp lists in the database with the lists known in + the mailchimp account which is related to the API_KEY. + """ + _list = get_object_or_404(MailchimpList, mailchimp_list_id=list_id) + form = MailchimpUpdateForm() + updated = form.sync_members(_list) + messages.success(request, '%i members have succesfully been updated.' % updated) + return redirect(_list.get_absolute_url()) + + +class ListDetailView(MailchimpMixin, UpdateView): + """ + The detail view of a certain Mailchimp list. This allows the admin to i.e. manage group + permissions to the group. + """ + slug_field = 'mailchimp_list_id' + slug_url_kwarg = 'list_id' + fields = ('allowed_groups', 'internal_name', 'open_for_subscription') + model = MailchimpList + + def form_valid(self, form): + messages.success(self.request, 'List succesfully updated') + return super().form_valid(form) diff --git a/requirements.txt b/requirements.txt index ab8676cb6d7f18193124c876281fe70288093f81..ba25dba1447cf01d84a54298194168b368fbd502 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ fake-factory==0.7.2 feedparser==5.2.1 imagesize==0.7.1 Jinja2==2.8 +mailchimp3==2.0.11 Markdown==2.6.7 MarkupSafe==0.23 pep8==1.7.0 diff --git a/scipost/forms.py b/scipost/forms.py index 3dac8846cc2988ef958cb3a4628e3cc47af9d5fd..0b7bb73ef229655d7483e8dd890d0126d42c6c58 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -20,6 +20,7 @@ from .models import Contributor, DraftInvitation, RegistrationInvitation,\ UnavailabilityPeriod, PrecookedEmail from journals.models import Publication +from mailing_lists.models import MailchimpList, MailchimpSubscription REGISTRATION_REFUSAL_CHOICES = ( @@ -174,11 +175,37 @@ class UpdatePersonalDataForm(forms.ModelForm): class Meta: model = Contributor fields = ['title', 'discipline', 'expertises', 'orcid_id', 'country_of_employment', - 'affiliation', 'address', 'personalwebpage', - 'accepts_SciPost_emails' + 'affiliation', 'address', 'personalwebpage' ] widgets = {'country_of_employment': CountrySelectWidget()} + # def __init__(self, *args, **kwargs): + # super().__init__(*args, **kwargs) + # self.fields['mail_subscription'] = forms.ModelMultipleChoiceField( + # queryset=MailchimpList.objects.open_to_subscribe(kwargs['instance']).distinct(), + # widget=forms.CheckboxSelectMultiple(), + # label='Subscribe to the following mailing lists:', + # required=False) + # self.fields['mailing_lists'] = forms.ModelMultipleChoiceField( + # queryset=MailchimpList.objects.open_to_subscribe(kwargs['instance']).distinct(), + # widget=forms.CheckboxSelectMultiple(), + # label='Subscribe to the following mailing lists:', + # required=False) + + def sync_lists(self): + return + # contributor = self.instance + # original_lists = list(self.fields['mailing_lists'].queryset) + # + # # Subscribe to lists + # for _list in self.cleaned_data['mailing_lists']: + # _list.update_membership([contributor]) + # original_lists.remove(_list) + # + # # Unsubscribe from the leftovers + # for _list in original_lists: + # _list.update_membership([contributor], status='unsubscribed') + class VetRegistrationForm(forms.Form): decision = forms.ChoiceField(widget=forms.RadioSelect, @@ -188,7 +215,7 @@ class VetRegistrationForm(forms.Form): label='Justification (optional)', required=False) def promote_to_registered_contributor(self): - return bool(self.cleaned_data.get('decision', False)) + return self.cleaned_data.get('decision') == 'True' class AuthenticationForm(forms.Form): diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py index 6ee4e935c17da5f72672d5d405f465f4374c4faa..b661bab5b80ec3002a089da3bdd0639e4484c571 100644 --- a/scipost/management/commands/add_groups_and_permissions.py +++ b/scipost/management/commands/add_groups_and_permissions.py @@ -159,6 +159,12 @@ class Command(BaseCommand): name='Can view docs: scipost', content_type=content_type) + # Mailchimp + can_manage_mailchimp, created = Permission.objects.get_or_create( + codename='can_manage_mailchimp', + name='Can manage Mailchimp settings', + content_type=content_type) + # Assign permissions to groups SciPostAdmin.permissions.add( can_manage_registration_invitations, @@ -174,6 +180,7 @@ class Command(BaseCommand): can_prepare_recommendations_for_voting, can_fix_College_decision, can_attend_VGMs, + can_manage_mailchimp, ) AdvisoryBoard.permissions.add( can_manage_registration_invitations, diff --git a/scipost/managers.py b/scipost/managers.py index 1572535c3b5175afef919a21d38e43a628ff7941..518ca5aaf34d411d1ab3cde65c4a5023b9a7b1df 100644 --- a/scipost/managers.py +++ b/scipost/managers.py @@ -3,6 +3,8 @@ import datetime from django.db import models from django.db.models import Q +from .constants import CONTRIBUTOR_NORMAL + class FellowManager(models.Manager): def active(self, *args, **kwargs): @@ -13,3 +15,8 @@ class FellowManager(models.Manager): Q(start_date__lte=today, until_date__gte=today) | Q(start_date__isnull=True, until_date__isnull=True), **kwargs).order_by('contributor__user__last_name') + + +class ContributorManager(models.Manager): + def active(self): + return self.filter(user__is_active=True, status=CONTRIBUTOR_NORMAL) diff --git a/scipost/migrations/0039_auto_20170306_0804.py b/scipost/migrations/0039_auto_20170306_0804.py index 49894a205825fdbe083d86f688b835138d7516ce..5be053234821f984b1ce69eb6092d7f2c324d784 100644 --- a/scipost/migrations/0039_auto_20170306_0804.py +++ b/scipost/migrations/0039_auto_20170306_0804.py @@ -12,87 +12,7 @@ class Migration(migrations.Migration): ('scipost', '0038_nomination_webpage'), ] - state_operations = [ - migrations.RemoveField( - model_name='feedback', - name='VGM', - ), - migrations.RemoveField( - model_name='feedback', - name='by', - ), - migrations.RemoveField( - model_name='motion', - name='VGM', - ), - migrations.RemoveField( - model_name='motion', - name='in_agreement', - ), - migrations.RemoveField( - model_name='motion', - name='in_disagreement', - ), - migrations.RemoveField( - model_name='motion', - name='in_notsure', - ), - migrations.RemoveField( - model_name='motion', - name='put_forward_by', - ), - migrations.DeleteModel( - name='NewsItem', - ), - migrations.RemoveField( - model_name='nomination', - name='VGM', - ), - migrations.RemoveField( - model_name='nomination', - name='by', - ), - migrations.RemoveField( - model_name='nomination', - name='in_agreement', - ), - migrations.RemoveField( - model_name='nomination', - name='in_disagreement', - ), - migrations.RemoveField( - model_name='nomination', - name='in_notsure', - ), - migrations.AlterField( - model_name='remark', - name='feedback', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Feedback'), - ), - migrations.AlterField( - model_name='remark', - name='motion', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Motion'), - ), - migrations.AlterField( - model_name='remark', - name='nomination', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Nomination'), - ), - migrations.DeleteModel( - name='Feedback', - ), - migrations.DeleteModel( - name='Motion', - ), - migrations.DeleteModel( - name='Nomination', - ), - migrations.DeleteModel( - name='VGM', - ), - ] - operations = [ - migrations.SeparateDatabaseAndState(state_operations=state_operations) + # I'm in a vacuum state. + # Please don't remove me though... ] diff --git a/scipost/migrations/0051_auto_20170504_1359.py b/scipost/migrations/0051_auto_20170504_1359.py new file mode 100644 index 0000000000000000000000000000000000000000..93ec6c4eec19c94aa37fb011b5ae9955b2a4a640 --- /dev/null +++ b/scipost/migrations/0051_auto_20170504_1359.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-05-04 11:59 +from __future__ import unicode_literals + +from django.db import migrations +from django.db.migrations.exceptions import InvalidMigrationPlan + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0050_auto_20170416_2152'), + ] + + operations = [ + migrations.AlterModelTable( + name='Feedback', + table='scipost_feedback', + ), + migrations.AlterModelTable( + name='Motion', + table='scipost_motion', + ), + migrations.AlterModelTable( + name='Nomination', + table='scipost_nomination', + ), + migrations.AlterModelTable( + name='VGM', + table='scipost_vgm', + ), + migrations.RemoveField( + model_name='remark', + name='feedback', + ), + migrations.RemoveField( + model_name='remark', + name='motion', + ), + migrations.RemoveField( + model_name='remark', + name='nomination', + ), + ] + + def apply(self, *args, **kwargs): + '''Forward apply the migration only if user explictly accepts the migration.''' + print('\n\nDue to earlier migration issues, this migration will remove the Remark foreignkey' + ' data to `Feedback`, `Motion` and `Nomination`. Please make a backup to' + ' be able to *manually* fix the relations afterwards.') + go_ahead = input(' Do you want to continue? [y/N] ') + if go_ahead == 'y': + return super().apply(*args, **kwargs) + else: + raise InvalidMigrationPlan diff --git a/scipost/migrations/0052_auto_20170504_1404.py b/scipost/migrations/0052_auto_20170504_1404.py new file mode 100644 index 0000000000000000000000000000000000000000..26c68461e6c393076e79a580f481ec0d7395c420 --- /dev/null +++ b/scipost/migrations/0052_auto_20170504_1404.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-05-04 12:04 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0051_auto_20170504_1359'), + ] + + database_operations = [ + migrations.AlterModelTable('Motion', 'virtualmeetings_motion'), + migrations.AlterModelTable('Feedback', 'virtualmeetings_feedback'), + migrations.AlterModelTable('Nomination', 'virtualmeetings_nomination'), + migrations.AlterModelTable('VGM', 'virtualmeetings_vgm'), + migrations.AlterField( + model_name='motion', + name='VGM', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'), + ), + migrations.AlterField( + model_name='feedback', + name='VGM', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'), + ), + migrations.AlterField( + model_name='nomination', + name='VGM', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'), + ), + ] + + state_operations = [ + migrations.RemoveField( + model_name='feedback', + name='VGM', + ), + migrations.RemoveField( + model_name='feedback', + name='by', + ), + migrations.RemoveField( + model_name='motion', + name='VGM', + ), + migrations.RemoveField( + model_name='motion', + name='in_agreement', + ), + migrations.RemoveField( + model_name='motion', + name='in_disagreement', + ), + migrations.RemoveField( + model_name='motion', + name='in_notsure', + ), + migrations.RemoveField( + model_name='motion', + name='put_forward_by', + ), + migrations.RemoveField( + model_name='nomination', + name='VGM', + ), + migrations.RemoveField( + model_name='nomination', + name='by', + ), + migrations.RemoveField( + model_name='nomination', + name='in_agreement', + ), + migrations.RemoveField( + model_name='nomination', + name='in_disagreement', + ), + migrations.RemoveField( + model_name='nomination', + name='in_notsure', + ), + migrations.DeleteModel('Motion'), + migrations.DeleteModel('Feedback'), + migrations.DeleteModel('Nomination'), + migrations.DeleteModel('VGM') + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=database_operations, + state_operations=state_operations) + ] diff --git a/scipost/migrations/0053_auto_20170504_1538.py b/scipost/migrations/0053_auto_20170504_1538.py new file mode 100644 index 0000000000000000000000000000000000000000..91b9c57bbbc911cce5f2bb81ec9e8b7710e2cda9 --- /dev/null +++ b/scipost/migrations/0053_auto_20170504_1538.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-05-04 13:38 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualmeetings', '0001_initial'), + ('scipost', '0052_auto_20170504_1404'), + ] + + operations = [ + migrations.AddField( + model_name='remark', + name='feedback', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Feedback'), + ), + migrations.AddField( + model_name='remark', + name='motion', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Motion'), + ), + migrations.AddField( + model_name='remark', + name='nomination', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Nomination'), + ), + ] diff --git a/scipost/migrations/0054_delete_newsitem.py b/scipost/migrations/0054_delete_newsitem.py new file mode 100644 index 0000000000000000000000000000000000000000..83bd9bc583e54abc883795124ef5c8bb129a0a01 --- /dev/null +++ b/scipost/migrations/0054_delete_newsitem.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-05-04 19:04 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0053_auto_20170504_1538'), + ] + + state_operations = [ + migrations.DeleteModel( + name='NewsItem', + ), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + state_operations=state_operations) + ] diff --git a/scipost/models.py b/scipost/models.py index ceb2aa42f7646fed59d693fb2d30847429bafc88..ecc4dcd821ed8ebb96a74b2c849839b69f28b95a 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -21,7 +21,7 @@ from .constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\ PARTNER_TYPES, PARTNER_STATUS,\ SPB_MEMBERSHIP_AGREEMENT_STATUS, SPB_MEMBERSHIP_DURATION from .fields import ChoiceArrayField -from .managers import FellowManager +from .managers import FellowManager, ContributorManager def get_sentinel_user(): @@ -66,6 +66,8 @@ class Contributor(models.Model): default=True, verbose_name="I accept to receive SciPost emails") + objects = ContributorManager() + def __str__(self): return '%s, %s' % (self.user.last_name, self.user.first_name) diff --git a/scipost/templates/scipost/index.html b/scipost/templates/scipost/index.html index 696aff8f4f84a389bb1032b836ccb30793721c4c..6ae0ba1ba7ba16059cff84e593d3c10f10501349 100644 --- a/scipost/templates/scipost/index.html +++ b/scipost/templates/scipost/index.html @@ -70,7 +70,6 @@ <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'journals:journals' %}">Latest Submissions</a></h1> - <h4 class="card-subtitle text-muted">A selection of the last submissions</h4> <ul class="list-group list-group-flush"> {% for submission in submissions %} <li class="list-group-item"> @@ -85,7 +84,6 @@ <div class="card card-grey"> <div class="card-block"> <h1 class="card-title mb-0"><a href="{% url 'journals:journals' %}">Latest Publications</a></h1> - <h4 class="card-subtitle text-muted">A selection of the last publications</h4> <ul class="list-group list-group-flush"> {% for publication in publications %} <li class="list-group-item"> diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index 41a53da7087a1226f810800ce475a2d7140335ac..0fdefd67db08f4abf334fe25cb86c9b37b9239c1 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -221,6 +221,9 @@ <li><a href="{% url 'scipost:send_precooked_email' %}">Send a precooked email</a></li> <li><a href="{% url 'scipost:email_particular' %}">Email a particular individual/address</a></li> {% endif %} + {% if perms.scipost.can_manage_mailchimp %} + <li><a href="{% url 'mailing_lists:overview' %}">Manage mailing lists</a></li> + {% endif %} </ul> </div> {% endif %} @@ -334,7 +337,7 @@ {% include 'submissions/_refereeing_invitation_card_content.html' with invitation=task %} </li> {% empty %} - <li class="list-group-item"><em>You do not have pending Refereering Tasks</em></li> + <li class="list-group-item"><em>You do not have any pending refereeing task</em></li> {% endfor %} </ul> </div> diff --git a/scipost/utils.py b/scipost/utils.py index 4e8236490f9f1dad43085a23b5d069b389b000f6..3af0f72d92b19ed5e9ebedee46971ff3d681ffeb 100644 --- a/scipost/utils.py +++ b/scipost/utils.py @@ -3,6 +3,8 @@ import hashlib import random import string +from django.contrib.auth.models import User + from django.core.mail import EmailMultiAlternatives from django.core.urlresolvers import reverse from django.template import Context, Template @@ -328,7 +330,7 @@ class Utils(BaseMailUtil): '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_key }}>' + '<a href="https://scipost.org/submissions/decline_ref_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>' @@ -343,11 +345,11 @@ class Utils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: refereeing request (and registration invitation)', email_text, - 'SciPost Registration <registration@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.invitation.email], cc=[cls.invitation.invited_by.user.email], - bcc=['registration@scipost.org'], - reply_to=['registration@scipost.org']) + bcc=['refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') elif cls.invitation.invitation_type == 'ci': diff --git a/scipost/views.py b/scipost/views.py index 3ebfee4ad98394fa61eb6e0710b391eae1147eec..babdffec078b6a3bf8f27e6cc5902acdd632425d 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -930,6 +930,7 @@ def update_personal_data(request): if user_form.is_valid() and cont_form.is_valid(): user_form.save() cont_form.save() + cont_form.sync_lists() messages.success(request, 'Your personal data has been updated.') return redirect(reverse('scipost:personal_page')) else: diff --git a/submissions/admin.py b/submissions/admin.py index a78b839e3b17109a1c12713edec35f47591f4e87..c0d1156527056afdba29f1656ff3e712246e7afd 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -33,7 +33,7 @@ class RefereeInvitationAdmin(admin.ModelAdmin): search_fields = ['submission__title', 'submission__author_list', 'referee__user__last_name', 'first_name', 'last_name', 'email_address'] - list_display = ('referee', submission_short_title, 'accepted', ) + list_display = ('__str__', 'accepted', ) list_filter = ('accepted', 'fulfilled', 'cancelled',) date_hierarchy = 'date_invited' diff --git a/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html b/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html index d56b1216c9e950ce47ae6c410e004df08e659693..aafb2c0c240ea9b1ea250722777448fdb5f97841 100644 --- a/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html +++ b/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html @@ -6,7 +6,7 @@ <section> {% if invitation.accepted == True %} - <h1>Thank you for agree to referee this Submission.</h1> + <h2>Thank you for agreeing to referee this Submission.</h2> <p>When you are ready, please go to the <a href="{% url 'submissions:submission' arxiv_identifier_w_vn_nr=invitation.submission.arxiv_identifier_w_vn_nr %}">Submission's page</a> to submit your Report.</p> {% else %} <h1>You have declined to contribute a Report.</h1> diff --git a/submissions/templates/submissions/decline_ref_invitation.html b/submissions/templates/submissions/decline_ref_invitation.html index 9efedb177f5058eb3fbe73a51397de573d09391d..8923b77afa84d2f41305e5d473a3b6698b578526 100644 --- a/submissions/templates/submissions/decline_ref_invitation.html +++ b/submissions/templates/submissions/decline_ref_invitation.html @@ -24,7 +24,8 @@ $(document).ready(function(){ <div class="row"> <div class="col-12"> <h1 class="highlight">SciPost Submission which you have been asked to Referee:</h1> - {% include 'submissions/_submission_summary.html' with submission=invitation.submission %} + <h3><a href="{{ invitation.submission.get_absolute_url }}">{{ invitation.submission.title }}</a></h3> + <p>{{ invitation.submission.author_list }}</p> </div> </div> <div class="row"> diff --git a/submissions/templates/submissions/refereeing_overview.html b/submissions/templates/submissions/refereeing_overview.html index a7e09c540820a90a020a229f3f8bad204736186a..fcf452bd6193f3df996a4b2a08ccc0f1db01281b 100644 --- a/submissions/templates/submissions/refereeing_overview.html +++ b/submissions/templates/submissions/refereeing_overview.html @@ -31,12 +31,13 @@ <div class="card-block"> <h3><a href="{{ submission.get_absolute_url }}">{{ submission.title }}</a></h3> <p>{{ submission.author_list }}</p> + <h4>Editor-in-charge: {{ submission.editor_in_charge }}</h4> <h4>Refereeing deadline: {{ submission.reporting_deadline }}</h4> <h4>Refereeing status summary:</h4> {% include 'submissions/_submission_refereeing_status.html' with submission=submission %} <h3 class="mb-2">Detail of refereeing invitations:</h3> {% include 'submissions/_submission_refereeing_invitations.html' with submission=submission invitations=submission.referee_invitations.all %} - <a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype='StoE' %}">Send a communication to the Editor-in-charge</a> + <a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype='StoE' %}" target="_blank">Send a communication to the Editor-in-charge</a> </div> </div> </div> diff --git a/submissions/templatetags/lookup.py b/submissions/templatetags/lookup.py index 0c73d76071d7ac88453dc094807492ff4f6347f6..803edd8acbd338d2202c2c1666955ac082787673 100644 --- a/submissions/templatetags/lookup.py +++ b/submissions/templatetags/lookup.py @@ -20,3 +20,11 @@ class SubmissionLookup(LookupChannel): def format_match(self, item): '''(HTML) Format item for displaying in the dropdown.''' return u"%s<br><span class='text-muted'>by %s</span>" % (item.title, item.author_list) + + def check_auth(self, request): + """ + Check if current user has required permissions. + Right now only used for draft registration invitations. May be extended in the + future for other purposes as well. + """ + return request.user.has_perm('can_draft_registration_invitations') diff --git a/submissions/utils.py b/submissions/utils.py index df19145f93f6a1e2db97d190fdb2e5a59d658fd0..d713236f3f2b66dab3fd04300962da4ece33e9a6 100644 --- a/submissions/utils.py +++ b/submissions/utils.py @@ -694,11 +694,11 @@ class SubmissionUtils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: refereeing request', email_text, - 'SciPost Editorial Admin <submissions@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.invitation.referee.user.email], bcc=[cls.invitation.submission.editor_in_charge.user.email, - 'submissions@scipost.org'], - reply_to=['submissions@scipost.org']) + 'refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') emailmessage.send(fail_silently=False) @@ -710,7 +710,7 @@ class SubmissionUtils(BaseMailUtil): It is called from the ref_invitation_reminder method in submissions/views.py. """ email_text = ( - 'Dear ' + cls.invitation.title + ' ' + 'Dear ' + cls.invitation.get_title_display() + ' ' + cls.invitation.last_name + ',\n\n' 'On behalf of the Editor-in-charge ' + cls.invitation.submission.editor_in_charge.get_title_display() + ' ' @@ -768,7 +768,7 @@ class SubmissionUtils(BaseMailUtil): 'ideally within the next 2 days.</p>' '<p>If you are <strong>not</strong> able to provide a Report, ' 'you can quickly let us know by simply ' - '<a href="https://scipost.org/decline_ref_invitation/{{ invitation_key }}>' + '<a href="https://scipost.org/decline_ref_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). ' @@ -786,7 +786,7 @@ class SubmissionUtils(BaseMailUtil): '<p>Many thanks in advance,</p>' '<p>The SciPost Team</p>') email_context = Context({ - 'title': cls.invitation.title, + 'title': cls.invitation.get_title_display(), 'last_name': cls.invitation.last_name, 'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(), 'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name, @@ -802,11 +802,11 @@ class SubmissionUtils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: reminder (refereeing request and registration invitation)', email_text, - 'SciPost Submissions <submissions@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.invitation.email_address], bcc=[cls.invitation.submission.editor_in_charge.user.email, - 'submissions@scipost.org'], - reply_to=['submissions@scipost.org']) + 'refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') emailmessage.send(fail_silently=False) @@ -889,11 +889,11 @@ class SubmissionUtils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: reminder (refereeing request and registration invitation)', email_text, - 'SciPost Submissions <submissions@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.invitation.email_address], bcc=[cls.invitation.submission.editor_in_charge.user.email, - 'submissions@scipost.org'], - reply_to=['submissions@scipost.org']) + 'refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') emailmessage.send(fail_silently=False) @@ -958,11 +958,11 @@ class SubmissionUtils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: report no longer needed', email_text, - 'SciPost Submissions <submissions@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.invitation.email_address], bcc=[cls.invitation.submission.editor_in_charge.user.email, - 'submissions@scipost.org'], - reply_to=['submissions@scipost.org']) + 'refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') emailmessage.send(fail_silently=False) @@ -995,6 +995,13 @@ class SubmissionUtils(BaseMailUtil): [cls._context['report'].submission.editor_in_charge.user.email], 'Report delivered') + @classmethod + def email_referee_report_delivered(cls): + """ Requires loading 'report' attribute. """ + cls._send_mail(cls, 'report_delivered_referee', + [cls._context['report'].author.user.email], + 'Report delivered') + @classmethod def acknowledge_report_email(cls): """ Requires loading 'report' attribute. """ @@ -1069,11 +1076,11 @@ class SubmissionUtils(BaseMailUtil): html_version = html_template.render(email_context) emailmessage = EmailMultiAlternatives( 'SciPost: Report acknowledgement', email_text, - 'SciPost Editorial Admin <submissions@scipost.org>', + 'SciPost Refereeing <refereeing@scipost.org>', [cls.report.author.user.email], bcc=[cls.report.submission.editor_in_charge.user.email, - 'submissions@scipost.org'], - reply_to=['submissions@scipost.org']) + 'refereeing@scipost.org'], + reply_to=['refereeing@scipost.org']) emailmessage.attach_alternative(html_version, 'text/html') emailmessage.send(fail_silently=False) diff --git a/submissions/views.py b/submissions/views.py index 387ac0debb92a8bfbbf80734ef0d87d1e22dd4e3..ce94d8f6cecd0b88133cd1c32a4914b707cbaeff 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -1126,6 +1126,7 @@ def submit_report(request, arxiv_identifier_w_vn_nr): author.save() SubmissionUtils.load({'report': newreport}, request) SubmissionUtils.email_EIC_report_delivered() + SubmissionUtils.email_referee_report_delivered() # Why is this session update? request.session['arxiv_identifier_w_vn_nr'] = arxiv_identifier_w_vn_nr diff --git a/templates/email/_footer.html b/templates/email/_footer.html index 8dbca35955bcdbb9777d99b3f11f28b6b52ea7ed..0ddecd32c06c5cc829db01b92d3157d572442ede 100644 --- a/templates/email/_footer.html +++ b/templates/email/_footer.html @@ -1,21 +1,21 @@ {% load staticfiles %} -<a href="https://scipost.org"><img src="//scipost.org{% static 'scipost/images/logo_scipost_with_bgd_small.png' %}" width="64px"></a> +<a href="https://scipost.org"><img src="https://scipost.org/static/scipost/images/logo_scipost_with_bgd_small.png" width="64px"></a> <br/> <div style="background-color: #f0f0f0; color: #002B49; align-items: center;"> <div style="display: inline-block; padding: 8px;"> - <a href="{{request.get_host}}{% url 'journals:journals' %}">Journals</a> + <a href="https://scipost.org/journals/">Journals</a> </div> - <div style="display: inline-block; padding: 8px;">' - <a href="{{request.get_host}}{% url 'submissions:submissions' %}">Submissions</a> + <div style="display: inline-block; padding: 8px;"> + <a href="https://scipost.org/submissions/">Submissions</a> </div> <div style="display: inline-block; padding: 8px;"> - <a href="{{request.get_host}}{% url 'commentaries:commentaries' %}">Commentaries</a> + <a href="https://scipost.org/commentaries/">Commentaries</a> </div> <div style="display: inline-block; padding: 8px;"> - <a href="{{request.get_host}}{% url 'theses:theses' %}">Theses</a> + <a href="https://scipost.org/theses/">Theses</a> </div> <div style="display: inline-block; padding: 8px;"> - <a href="{{request.get_host}}{% url 'scipost:login' %}">Login</a> + <a href="https://scipost.org/login/">Login</a> </div> </div> diff --git a/templates/email/new_activation_link.html b/templates/email/new_activation_link.html index 755aaddd9f0aae366ba9884b84bbc11624b4746f..85df025ad6f2933dbd91318177a87baf192e0912 100644 --- a/templates/email/new_activation_link.html +++ b/templates/email/new_activation_link.html @@ -2,7 +2,7 @@ Dear {{contributor.get_title_display}} {{contributor.user.last_name}},\n\n Your request for a new email activation link for registration to the SciPost publication portal has been received. You now need to visit this link within the next 48 hours: \n\n -{{request.get_host}}{% url 'scipost:activation' contributor.id contributor.activation_key %} +https://scipost.org{% url 'scipost:activation' contributor.id contributor.activation_key %} \n\n Your registration will thereafter be vetted. Many thanks for your interest.\n diff --git a/templates/email/new_activation_link_html.html b/templates/email/new_activation_link_html.html index 48a6e019acecf05d6122fb79e8ddf6ce99525fae..0098af24f11521c6001aa11120b557c0c0b95487 100644 --- a/templates/email/new_activation_link_html.html +++ b/templates/email/new_activation_link_html.html @@ -1,14 +1,14 @@ -<h3>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</h3> +<p>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</p> <p> Your request for a new email activation link for registration to the SciPost publication portal has been received. You now need to visit this link within the next 48 hours: </p> <p> - <a href="{{request.get_host}}{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a> + <a href="https://scipost.org{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a> </p> <p> - Your registration will thereafter be vetted. Many thanks for your interest. + Your registration will thereafter be vetted. Many thanks for your interest.<br> The SciPost Team. </p> diff --git a/templates/email/referee_in_response_to_decision.html b/templates/email/referee_in_response_to_decision.html index 4e3b8502223fa7402894ea9aa1f935c1923eeae8..bda9d8047d9b26663921017842aba421d7b75274 100644 --- a/templates/email/referee_in_response_to_decision.html +++ b/templates/email/referee_in_response_to_decision.html @@ -1,8 +1,9 @@ -Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}},\n\n +Dear {{ invitation.referee.get_title_display }} {{ invitation.referee.user.last_name }},\n\n -We hereby confirm your choice to {% if invitation.accepted %}accept{% else %}decline (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission\n\n +We hereby confirm your choice to {% if invitation.accepted %}accept{% else %}decline (due to reason: {{ invitation.get_refusal_reason_display }}){% endif %} to referee Submission\n\n -{{invitation.submission.title}} by {{invitation.submission.author_list}}\n\n +{{invitation.submission.title}}\n +by {{ invitation.submission.author_list }}\n\n {% if invitation.accepted %} We will look forward to receiving your Report by the reporting deadline {{ invitation.submission.reporting_deadline|date:'Y-m-d' }}.\n\n diff --git a/templates/email/referee_in_response_to_decision_html.html b/templates/email/referee_in_response_to_decision_html.html index d9a46fe44544f8ea7b72de0da038852df7d505fc..b8363b5b524d6dae43ea5b8ac9d27cbc4325bff1 100644 --- a/templates/email/referee_in_response_to_decision_html.html +++ b/templates/email/referee_in_response_to_decision_html.html @@ -1,20 +1,23 @@ -<h3>Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}},</h3> +<p>Dear {{ invitation.referee.get_title_display }} {{ invitation.referee.user.last_name }},</p> <p> - We hereby confirm your choice to {% if invitation.accepted %}accept{% else %}decline (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission + We hereby confirm your choice to {% if invitation.accepted %}accept{% else %}decline (due to reason: {{ invitation.get_refusal_reason_display }}){% endif %} to referee Submission </p> <p> - "<span style="color: grey;">{{invitation.submission.title}} by {{invitation.submission.author_list}}</span>". + {{ invitation.submission.title }} + <br/> + by {{ invitation.submission.author_list }}. </p> <p> {% if invitation.accepted %} We will look forward to receiving your Report by the reporting deadline {{ invitation.submission.reporting_deadline|date:'Y-m-d' }}. + <br/> Many thanks for your collaboration, {% else %} Nonetheless, we thank you very much for considering this refereeing invitation, {% endif %} - The SciPost Team. + <br/>The SciPost Team. </p> {% include 'email/_footer.html' %} diff --git a/templates/email/referee_response_to_EIC.html b/templates/email/referee_response_to_EIC.html index e73f226ccf6c68346f1d879ba008e59d5d828fcf..fdc5c1dc553aaf855fbb959bc1d9d1b62b861ede 100644 --- a/templates/email/referee_response_to_EIC.html +++ b/templates/email/referee_response_to_EIC.html @@ -1,11 +1,11 @@ -Dear {{invitation.submission.editor_in_charge.get_title_display}} {{invitation.submission.editor_in_charge.user.last_name}},\n\n +Dear {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }},\n\n -Referee {% if invitation.referee %}{{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}}{% else %}{{ invitation.title }} {{ invitation.first_name }} {{ invitation.last_name }}{% endif %} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission\n\n +Referee {% if invitation.referee %}{{ invitation.referee.get_title_display }} {{ invitation.referee.user.last_name }}{% else %}{{ invitation.title }} {{ invitation.first_name }} {{ invitation.last_name }}{% endif %} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{ invitation.get_refusal_reason_display }}){% endif %} to referee Submission\n\n -{{invitation.submission.title}} by {{invitation.submission.author_list}}\n\n +{{ invitation.submission.title }}\n by {{ invitation.submission.author_list }}\n\n {% if not invitation.accepted %} -Please invite another referee from the Submission\'s editorial page at {{request.get_host}}{% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}.\n\n +Please invite another referee from the Submission\'s editorial page at https://scipost.org{% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}.\n\n {% endif %} Many thanks for your collaboration,\n diff --git a/templates/email/referee_response_to_EIC_html.html b/templates/email/referee_response_to_EIC_html.html index e7bf2687d608b4f65977260e012dbcaa7a61d30b..f932f38e7a200cd79e0d2ee29674c205d365acd1 100644 --- a/templates/email/referee_response_to_EIC_html.html +++ b/templates/email/referee_response_to_EIC_html.html @@ -1,20 +1,22 @@ -<h3>Dear {{invitation.submission.editor_in_charge.get_title_display}} {{invitation.submission.editor_in_charge.user.last_name}},</h3> +<p>Dear {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }},</p> <p> - Referee {% if invitation.referee %}{{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}}{% else %}{{ invitation.title }} {{ invitation.first_name }} {{ invitation.last_name }}{% endif %} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission + Referee {% if invitation.referee %}{{ invitation.referee.get_title_display }} {{ invitation.referee.user.last_name }}{% else %}{{ invitation.title }} {{ invitation.first_name }} {{ invitation.last_name }}{% endif %} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{ invitation.get_refusal_reason_display }}){% endif %} to referee Submission </p> <p> - "<span style="color: grey;">{{invitation.submission.title}} by {{invitation.submission.author_list}}</span>". + {{ invitation.submission.title }} + <br/> + by {{ invitation.submission.author_list }}. </p> {% if not invitation.accepted %} <p> - Please invite another referee from the Submission's <a href="{{request.get_host}}{% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}">editorial page</a>. + Please invite another referee from the Submission's <a href="https://scipost.org{% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}">editorial page</a>. </p> {% endif %} <p> - Many thanks for your collaboration, + Many thanks for your collaboration,<br/> The SciPost Team. </p> diff --git a/templates/email/registration_request_received.html b/templates/email/registration_request_received.html index 47d9124523bc8588a5b54536a7661618ebef5029..88264d5ff1c2d459d8f79606ffa73850487b877e 100644 --- a/templates/email/registration_request_received.html +++ b/templates/email/registration_request_received.html @@ -2,7 +2,7 @@ Dear {{contributor.get_title_display}} {{contributor.user.last_name}},\n\n Your request for registration to the SciPost publication portal has been received. You now need to validate your email by visiting this link within the next 48 hours: \n\n -{{request.get_host}}{% url 'scipost:activation' contributor.id contributor.activation_key %} +https://scipost.org{% url 'scipost:activation' contributor.id contributor.activation_key %} \n\n Your registration will thereafter be vetted. Many thanks for your interest.\n diff --git a/templates/email/registration_request_received_html.html b/templates/email/registration_request_received_html.html index c9eb3ff312a5e7e7f9525017f935cb9c4845ed51..f18064bf55b004ce93676b6b561b8d4ca632428b 100644 --- a/templates/email/registration_request_received_html.html +++ b/templates/email/registration_request_received_html.html @@ -1,14 +1,14 @@ -<h3>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</h3> +<p>Dear {{contributor.get_title_display}} {{contributor.user.last_name}},</p> <p> Your request for registration to the SciPost publication portal has been received. You now need to validate your email by visiting this link within the next 48 hours: </p> <p> - <a href="{{request.get_host}}{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a> + <a href="https://scipost.org{% url 'scipost:activation' contributor.id contributor.activation_key %}">Activate your account</a> </p> <p> - Your registration will thereafter be vetted. Many thanks for your interest. + Your registration will thereafter be vetted. Many thanks for your interest.<br> The SciPost Team. </p> diff --git a/templates/email/report_delivered_eic.html b/templates/email/report_delivered_eic.html index 4d326dd5d8105f4b24be2dca147eac53a36b1b5a..fe01130b3d6c39bc41a02242ebecfe67d395da0a 100644 --- a/templates/email/report_delivered_eic.html +++ b/templates/email/report_delivered_eic.html @@ -1,11 +1,13 @@ -Dear {{report.submission.editor_in_charge.get_title_display}} {{report.submission.editor_in_charge.user.last_name}},\n\n +Dear {{ report.submission.editor_in_charge.get_title_display }} {{ report.submission.editor_in_charge.user.last_name }},\n\n -Referee {{report.author.get_title_display}} {{report.author.user.last_name}} has delivered a Report for Submission "{{report.submission.title}} by {{report.submission.author_list}}". +Referee {{ report.author.get_title_display }} {{ report.author.user.last_name }} has delivered a Report for Submission\n\n +{{ report.submission.title }}\n +by {{ report.submission.author_list }}. \n\n -Please vet this Report via your personal page under the Editorial Actions tab.\n -{{request.get_host}}{% url 'scipost:personal_page' %} -\n\n\n +Please vet this Report via your personal page at +https://scipost.org{% url 'scipost:personal_page' %}, under the Editorial Actions tab.\n +\n\n Many thanks in advance for your collaboration,\n The SciPost Team. diff --git a/templates/email/report_delivered_eic_html.html b/templates/email/report_delivered_eic_html.html index 0a94697c09c2a8c7d68da68728a0b22b3b2111d2..11d7d9f82e48e1ca640829b453dd495d531c386f 100644 --- a/templates/email/report_delivered_eic_html.html +++ b/templates/email/report_delivered_eic_html.html @@ -1,13 +1,18 @@ -<h3>Dear {{report.submission.editor_in_charge.get_title_display}} {{report.submission.editor_in_charge.user.last_name}},</h3> +<p>Dear {{ report.submission.editor_in_charge.get_title_display }} {{ report.submission.editor_in_charge.user.last_name }},</p> <p> - Referee {{report.author.get_title_display}} {{report.author.user.last_name}} has delivered a Report for Submission "<span style="color: grey;">{{report.submission.title}} by {{report.submission.author_list}}</span>". + Referee {{ report.author.get_title_display }} {{ report.author.user.last_name }} has delivered a Report for Submission: </p> <p> - Please vet this Report via your <a href="{{request.get_host}}{% url 'scipost:personal_page' %}">personal page</a> under the Editorial Actions tab. + {{report.submission.title}} + <br/> + by {{ report.submission.author_list }}. </p> <p> - Many thanks in advance for your collaboration, + Please vet this Report via your <a href="https://scipost.org{% url 'scipost:personal_page' %}">personal page</a> under the Editorial Actions tab. +</p> +<p> + Many thanks in advance for your collaboration,<br> The SciPost Team. </p> diff --git a/templates/email/report_delivered_referee.html b/templates/email/report_delivered_referee.html new file mode 100644 index 0000000000000000000000000000000000000000..ecff9be7a20b5597ccc9684b7d849915b8e7a610 --- /dev/null +++ b/templates/email/report_delivered_referee.html @@ -0,0 +1,11 @@ +Dear {{ report.author.get_title_display }} {{ report.author.user.last_name }},\n\n + +We hereby confirm reception of your Report on Submission\n\n + +{{ report.submission.title }}\n +by {{ report.submission.author_list }}.\n\n + +We are immensely grateful for your time and effort. Your Report will soon be vetted by the Submission's Editor-in-charge, at which point you will receive an email update from us.\n\n + +Many thanks again,\n +The SciPost Team. diff --git a/templates/email/report_delivered_referee_html.html b/templates/email/report_delivered_referee_html.html new file mode 100644 index 0000000000000000000000000000000000000000..9e50b5f4b9c9063c83433de22bd000034faa27fd --- /dev/null +++ b/templates/email/report_delivered_referee_html.html @@ -0,0 +1,21 @@ +<p> + Dear {{ report.author.get_title_display }} {{ report.author.user.last_name }}, +</p> + +<p>We hereby confirm reception of your Report on Submission</p> + +<p> + {{ report.submission.title }} + <br/> + by {{ report.submission.author_list }}. +</p> + +<p> + We are immensely grateful for your time and effort. Your Report will soon be vetted by the Submission's Editor-in-charge, at which point you will receive an email update from us. +</p> + +<p> + Many thanks again, + <br/> + The SciPost Team. +</p> diff --git a/templates/email/submission_cycle_reinvite_referee_html.html b/templates/email/submission_cycle_reinvite_referee_html.html index ec0eef3e764fb7e23b78723b275554efa664370b..84ceeb3ff14ee0d82279da763c9fd7ff30a5cc53 100644 --- a/templates/email/submission_cycle_reinvite_referee_html.html +++ b/templates/email/submission_cycle_reinvite_referee_html.html @@ -1,4 +1,4 @@ -<h3>Dear {{invitation.get_title_display}} {{invitation.last_name}},</h3> +<p>Dear {{invitation.get_title_display}} {{invitation.last_name}},</p> <p> The authors of submission @@ -9,7 +9,7 @@ <br> by {{invitation.submission.author_list}} <br> - (<a href="{{request.get_host}}{{invitation.submission.get_absolute_url}}">see on SciPost.org</a>) + (<a href="https://scipost.org{{invitation.submission.get_absolute_url}}">see on SciPost.org</a>) <p> have resubmitted their manuscript to SciPost. On behalf of the Editor-in-charge {{invitation.submission.editor_in_charge.get_title_display}} {{invitation.submission.editor_in_charge.last_name}}, we would like to invite you to quickly review this new version. Please accept or decline the invitation (login required) as soon as possible (ideally within the next 2 days). diff --git a/templates/email/submission_eic_reappointment.html b/templates/email/submission_eic_reappointment.html index f5429e97b1fd9f0b19d1e566875bda11ae5b9540..a4b8eba72d81a8b39c1a0e0a21958651d760dc7e 100644 --- a/templates/email/submission_eic_reappointment.html +++ b/templates/email/submission_eic_reappointment.html @@ -7,10 +7,10 @@ by {{submission.author_list}} \n\n have resubmitted their manuscript.\n\n -As Editor-in-charge, you can take your editorial actions from the submission\'s editorial page: {{request.get_host}}{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}.\n -(also accessible from your personal page under the Editorial Actions tab), see {{request.get_host}}{% url 'scipost:personal_page' %}. \n\n +As Editor-in-charge, you can take your editorial actions from the submission\'s editorial page: https://scipost.org{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}.\n +(also accessible from your personal page under the Editorial Actions tab), see https://scipost.org{% url 'scipost:personal_page' %}. \n\n -You can either take an immediate acceptance/rejection decision, quickly consult previous referees or run a new refereeing round, in which case you should now invite at least 3 referees; you might want to make sure you are aware of the detailed procedure described in the Editorial College by-laws. See {{request.get_host}}{% url 'scipost:EdCol_by-laws' %}. +You can either take an immediate acceptance/rejection decision, quickly consult previous referees or run a new refereeing round, in which case you should now invite at least 3 referees; you might want to make sure you are aware of the detailed procedure described in the Editorial College by-laws. See https://scipost.org{% url 'scipost:EdCol_by-laws' %}. \n\n Many thanks in advance for your collaboration,\n diff --git a/templates/email/submission_eic_reappointment_html.html b/templates/email/submission_eic_reappointment_html.html index 41b1ff0ce9ad308359476785e9c7dc94c6c1d30c..9603dd70a5cf2d9f2698bea43584834b15acd6a2 100644 --- a/templates/email/submission_eic_reappointment_html.html +++ b/templates/email/submission_eic_reappointment_html.html @@ -1,17 +1,17 @@ -<h3>Dear {{submission.editor_in_charge.get_title_display}} {{submission.editor_in_charge.user.last_name}},</h3> +<p>Dear {{submission.editor_in_charge.get_title_display}} {{submission.editor_in_charge.user.last_name}},</p> <p> - The authors of the SciPost Submission "<span style="color: grey;">{{submission.title}} by {{submission.author_list}}</span>" have resubmitted their manuscript. + The authors of the SciPost Submission "{{submission.title}} by {{submission.author_list}}" have resubmitted their manuscript. </p> <p> - As Editor-in-charge, you can take your editorial actions from the submission\'s <a href="{{request.get_host}}{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}">editorial page</a>. - (also accessible from your <a href="{{request.get_host}}{% url 'scipost:personal_page' %}">personal page</a> under the Editorial Actions tab). + As Editor-in-charge, you can take your editorial actions from the submission\'s <a href="https://scipost.org{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}">editorial page</a>. + (also accessible from your <a href="https://scipost.org{% url 'scipost:personal_page' %}">personal page</a> under the Editorial Actions tab). </p> <p> - You can either take an immediate acceptance/rejection decision, quickly consult previous referees or run a new refereeing round, in which case you should now invite at least 3 referees; you might want to make sure you are aware of the detailed procedure described in the <a href="{{request.get_host}}{% url 'scipost:EdCol_by-laws' %}">Editorial College by-laws</a>. + You can either take an immediate acceptance/rejection decision, quickly consult previous referees or run a new refereeing round, in which case you should now invite at least 3 referees; you might want to make sure you are aware of the detailed procedure described in the <a href="https://scipost.org{% url 'scipost:EdCol_by-laws' %}">Editorial College by-laws</a>. </p> <p> - Many thanks in advance for your collaboration, + Many thanks in advance for your collaboration,<br> The SciPost Team. </p> diff --git a/virtualmeetings/migrations/0001_initial.py b/virtualmeetings/migrations/0001_initial.py index 492b8e80ef3a4adc0541c026e15e68618489c93c..cbfb62cfed8e40c068277a0a09361d18fc706735 100644 --- a/virtualmeetings/migrations/0001_initial.py +++ b/virtualmeetings/migrations/0001_initial.py @@ -24,10 +24,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateField()), ('feedback', models.TextField()), - ], - options={ - 'db_table': 'scipost_feedback', - }, + ] ), migrations.CreateModel( name='Motion', @@ -42,10 +39,7 @@ class Migration(migrations.Migration): ('nr_D', models.PositiveIntegerField(default=0)), ('voting_deadline', models.DateTimeField(default=django.utils.timezone.now, verbose_name='voting deadline')), ('accepted', models.NullBooleanField()), - ], - options={ - 'db_table': 'scipost_motion', - }, + ] ), migrations.CreateModel( name='Nomination', @@ -62,10 +56,7 @@ class Migration(migrations.Migration): ('nr_D', models.PositiveIntegerField(default=0)), ('voting_deadline', models.DateTimeField(default=django.utils.timezone.now, verbose_name='voting deadline')), ('accepted', models.NullBooleanField()), - ], - options={ - 'db_table': 'scipost_nomination', - }, + ] ), migrations.CreateModel( name='VGM', @@ -74,10 +65,7 @@ class Migration(migrations.Migration): ('start_date', models.DateField()), ('end_date', models.DateField()), ('information', models.TextField(default='')), - ], - options={ - 'db_table': 'scipost_vgm', - }, + ] ), migrations.AddField( model_name='nomination', diff --git a/virtualmeetings/models.py b/virtualmeetings/models.py index f78744fc9114cd35791da721d09d57adcbad9778..7e6d9ffcba0993aa1d2ea76f0a79119300b144ce 100644 --- a/virtualmeetings/models.py +++ b/virtualmeetings/models.py @@ -21,9 +21,6 @@ class VGM(models.Model): end_date = models.DateField() information = models.TextField(default='') - class Meta: - db_table = 'scipost_vgm' - def __str__(self): return 'From %s to %s' % (self.start_date.strftime('%Y-%m-%d'), self.end_date.strftime('%Y-%m-%d')) @@ -33,14 +30,11 @@ class Feedback(models.Model): """ Feedback, suggestion or criticism on any aspect of SciPost. """ - VGM = models.ForeignKey(VGM, blank=True, null=True) - by = models.ForeignKey(Contributor) + VGM = models.ForeignKey('virtualmeetings.VGM', blank=True, null=True) + by = models.ForeignKey('scipost.Contributor') date = models.DateField() feedback = models.TextField() - class Meta: - db_table = 'scipost_feedback' - def __str__(self): return '%s: %s' % (self.by, self.feedback[:50]) @@ -61,8 +55,8 @@ class Nomination(models.Model): """ Nomination to an Editorial Fellowship. """ - VGM = models.ForeignKey(VGM, blank=True, null=True) - by = models.ForeignKey(Contributor) + VGM = models.ForeignKey('virtualmeetings.VGM', blank=True, null=True) + by = models.ForeignKey('scipost.Contributor') date = models.DateField() first_name = models.CharField(max_length=30, default='') last_name = models.CharField(max_length=30, default='') @@ -85,9 +79,6 @@ class Nomination(models.Model): voting_deadline = models.DateTimeField('voting deadline', default=timezone.now) accepted = models.NullBooleanField() - class Meta: - db_table = 'scipost_nomination' - def __str__(self): return '%s %s (nominated by %s)' % (self.first_name, self.last_name, @@ -154,27 +145,24 @@ class Motion(models.Model): They are meant to be voted on at the annual VGM. """ category = models.CharField(max_length=10, choices=MOTION_CATEGORIES, default='General') - VGM = models.ForeignKey(VGM, blank=True, null=True) + VGM = models.ForeignKey('virtualmeetings.VGM', blank=True, null=True) background = models.TextField() motion = models.TextField() - put_forward_by = models.ForeignKey(Contributor) + put_forward_by = models.ForeignKey('scipost.Contributor') date = models.DateField() nr_A = models.PositiveIntegerField(default=0) - in_agreement = models.ManyToManyField(Contributor, + in_agreement = models.ManyToManyField('scipost.Contributor', related_name='in_agreement_with_motion', blank=True) nr_N = models.PositiveIntegerField(default=0) - in_notsure = models.ManyToManyField(Contributor, + in_notsure = models.ManyToManyField('scipost.Contributor', related_name='in_notsure_with_motion', blank=True) nr_D = models.PositiveIntegerField(default=0) - in_disagreement = models.ManyToManyField(Contributor, + in_disagreement = models.ManyToManyField('scipost.Contributor', related_name='in_disagreement_with_motion', blank=True) voting_deadline = models.DateTimeField('voting deadline', default=timezone.now) accepted = models.NullBooleanField() - class Meta: - db_table = 'scipost_motion' - def __str__(self): return self.motion[:32]