From f6969a276c1ac616fdb1c9fb6dc3b1edf380527c Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 13 Sep 2024 11:07:47 +0100 Subject: [PATCH 01/49] Fixes the copyright notice label. --- src/templates/admin/submission/start.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/admin/submission/start.html b/src/templates/admin/submission/start.html index 41051192b8..af94db3b09 100644 --- a/src/templates/admin/submission/start.html +++ b/src/templates/admin/submission/start.html @@ -62,7 +62,7 @@

{% trans "Copyright Notice" %}

{{ journal_settings.general.copyright_notice|safe }}
{{ form.copyright_notice }}   + for="id_copyright_notice">{{ form.copyright_notice.label|safe }} {% endif %} From d75ddeffcd79627a1c43efd71d9c5827233bbfc5 Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Mon, 16 Sep 2024 17:16:29 +0100 Subject: [PATCH 02/49] Add Dutch translation for copyright help text #4409 --- src/core/locales/nl/LC_MESSAGES/django.po | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/locales/nl/LC_MESSAGES/django.po b/src/core/locales/nl/LC_MESSAGES/django.po index 8a87880e6c..5991d5d07b 100644 --- a/src/core/locales/nl/LC_MESSAGES/django.po +++ b/src/core/locales/nl/LC_MESSAGES/django.po @@ -2435,13 +2435,15 @@ msgstr "" msgid "" "All authors must agree to the below statements in order to submit an article " "to" -msgstr "" +msgstr "Alle auteurs moeten zich akkoord verklaren met onderstaande stellingen " +"om een artikel te publiceren in" #: src/templates/admin/submission/start.html:24 msgid "" "If you do not agree with these terms you will be unable to proceed with your " "submission." -msgstr "" +msgstr "Als je je niet akkoord verklaart met deze voorwaarden " +"kan je niet verdergaan met deze inzending." #: src/templates/admin/submission/start.html:29 msgid "Publication Fees" From 81020aed55b2d3ab8708f307ae907baeed39c4d9 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 13 Sep 2024 10:57:04 +0100 Subject: [PATCH 03/49] Reverts account and frozen author fields to charfields. --- .../0096_alter_account_activation_code_and_more.py | 14 +++++++------- src/core/models.py | 14 +++++++------- .../0080_frozen_author_bleach_20240507_1350.py | 14 +++++++------- src/submission/models.py | 14 +++++++------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/core/migrations/0096_alter_account_activation_code_and_more.py b/src/core/migrations/0096_alter_account_activation_code_and_more.py index 424abfd9c7..d8281c00f4 100644 --- a/src/core/migrations/0096_alter_account_activation_code_and_more.py +++ b/src/core/migrations/0096_alter_account_activation_code_and_more.py @@ -19,32 +19,32 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='account', name='department', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300, verbose_name='Department'), + field=models.CharField(blank=True, max_length=300, verbose_name='Department'), ), migrations.AlterField( model_name='account', name='first_name', - field=core.model_utils.JanewayBleachCharField(max_length=300, verbose_name='First name'), + field=models.CharField(max_length=300, verbose_name='First name'), ), migrations.AlterField( model_name='account', name='institution', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=1000, verbose_name='Institution'), + field=models.CharField(blank=True, max_length=1000, verbose_name='Institution'), ), migrations.AlterField( model_name='account', name='last_name', - field=core.model_utils.JanewayBleachCharField(max_length=300, verbose_name='Last name'), + field=models.CharField(max_length=300, verbose_name='Last name'), ), migrations.AlterField( model_name='account', name='middle_name', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300, verbose_name='Middle name'), + field=models.CharField(blank=True, max_length=300, verbose_name='Middle name'), ), migrations.AlterField( model_name='account', name='salutation', - field=core.model_utils.JanewayBleachCharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, verbose_name='Salutation'), + field=models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, verbose_name='Salutation'), ), migrations.AlterField( model_name='account', @@ -54,6 +54,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='account', name='suffix', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300, verbose_name='Name suffix'), + field=models.CharField(blank=True, max_length=300, verbose_name='Name suffix'), ), ] diff --git a/src/core/models.py b/src/core/models.py index 9a3661a4ba..c496d5dc9c 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -235,30 +235,30 @@ class Account(AbstractBaseUser, PermissionsMixin): username = models.CharField(max_length=254, unique=True, verbose_name=_('Username')) name_prefix = models.CharField(max_length=10, blank=True) - first_name = JanewayBleachCharField( + first_name = models.CharField( max_length=300, blank=False, verbose_name=_('First name'), ) - middle_name = JanewayBleachCharField( + middle_name = models.CharField( max_length=300, blank=True, verbose_name=_('Middle name'), ) - last_name = JanewayBleachCharField( + last_name = models.CharField( max_length=300, blank=False, verbose_name=_('Last name'), ) activation_code = models.CharField(max_length=100, null=True, blank=True) - salutation = JanewayBleachCharField( + salutation = models.CharField( max_length=10, choices=SALUTATION_CHOICES, blank=True, verbose_name=_('Salutation'), ) - suffix = JanewayBleachCharField( + suffix = models.CharField( max_length=300, blank=True, verbose_name=_('Name suffix'), @@ -268,12 +268,12 @@ class Account(AbstractBaseUser, PermissionsMixin): verbose_name=_('Biography'), ) orcid = models.CharField(max_length=40, null=True, blank=True, verbose_name=_('ORCiD')) - institution = JanewayBleachCharField( + institution = models.CharField( max_length=1000, blank=True, verbose_name=_('Institution'), ) - department = JanewayBleachCharField( + department = models.CharField( max_length=300, blank=True, verbose_name=_('Department'), diff --git a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py index 2cc7ae6838..114c9153de 100644 --- a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py +++ b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py @@ -15,12 +15,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='frozenauthor', name='department', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300), ), migrations.AlterField( model_name='frozenauthor', name='first_name', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300), ), migrations.AlterField( model_name='frozenauthor', @@ -40,26 +40,26 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='frozenauthor', name='institution', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=1000), + field=models.CharField(blank=True, max_length=1000), ), migrations.AlterField( model_name='frozenauthor', name='last_name', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300), ), migrations.AlterField( model_name='frozenauthor', name='middle_name', - field=core.model_utils.JanewayBleachCharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300), ), migrations.AlterField( model_name='frozenauthor', name='name_prefix', - field=core.model_utils.JanewayBleachCharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300), + field=models.CharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300), ), migrations.AlterField( model_name='frozenauthor', name='name_suffix', - field=core.model_utils.JanewayBleachCharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300), + field=models.CharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300), ), ] diff --git a/src/submission/models.py b/src/submission/models.py index a9b6d80c9b..3623f4d869 100755 --- a/src/submission/models.py +++ b/src/submission/models.py @@ -1911,23 +1911,23 @@ class FrozenAuthor(AbstractLastModifiedModel): on_delete=models.SET_NULL, ) - name_prefix = JanewayBleachCharField( + name_prefix = models.CharField( max_length=300, blank=True, help_text=_("Optional name prefix (e.g: Prof or Dr)") ) - name_suffix = JanewayBleachCharField( + name_suffix = models.CharField( max_length=300, blank=True, help_text=_("Optional name suffix (e.g.: Jr or III)") ) - first_name = JanewayBleachCharField(max_length=300, blank=True) - middle_name = JanewayBleachCharField(max_length=300, blank=True) - last_name = JanewayBleachCharField(max_length=300, blank=True) + first_name = models.CharField(max_length=300, blank=True) + middle_name = models.CharField(max_length=300, blank=True) + last_name = models.CharField(max_length=300, blank=True) - institution = JanewayBleachCharField(max_length=1000, blank=True) - department = JanewayBleachCharField(max_length=300, blank=True) + institution = models.CharField(max_length=1000, blank=True) + department = models.CharField(max_length=300, blank=True) frozen_biography = JanewayBleachField( blank=True, verbose_name=_('Frozen Biography'), From 41230035243bcf82cd2c1bb17096a136d97ae120 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 13 Sep 2024 11:18:18 +0100 Subject: [PATCH 04/49] Adds a button_classes var to style ORCID reg btn --- src/templates/common/elements/orcid_registration.html | 6 +++++- src/themes/OLH/templates/core/accounts/register.html | 2 +- src/themes/clean/templates/core/accounts/register.html | 2 +- src/themes/material/templates/core/accounts/register.html | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/templates/common/elements/orcid_registration.html b/src/templates/common/elements/orcid_registration.html index ee83a4b710..ee13576992 100644 --- a/src/templates/common/elements/orcid_registration.html +++ b/src/templates/common/elements/orcid_registration.html @@ -9,6 +9,10 @@ [remove]

{% else %} - {% trans "Register with ORCiD" %} + + {% trans "Register with ORCiD" %} + {% endif %} {% endif %} diff --git a/src/themes/OLH/templates/core/accounts/register.html b/src/themes/OLH/templates/core/accounts/register.html index 5a3d372c4f..3898ca57e5 100644 --- a/src/themes/OLH/templates/core/accounts/register.html +++ b/src/themes/OLH/templates/core/accounts/register.html @@ -30,7 +30,7 @@
{% trans "Register for an account with" %} {{ request.press.name }}.

{% blocktrans %}For more information read our password guide.{% endblocktrans %}

- {% include "common/elements/orcid_registration.html" %} + {% include "common/elements/orcid_registration.html" with button_classes="button expanded orcid-button" %} {% include "elements/forms/errors.html" with form=form %} diff --git a/src/themes/clean/templates/core/accounts/register.html b/src/themes/clean/templates/core/accounts/register.html index 2511eafdfd..be56f96d8d 100644 --- a/src/themes/clean/templates/core/accounts/register.html +++ b/src/themes/clean/templates/core/accounts/register.html @@ -20,7 +20,7 @@
{% trans "Register for an account with" %} {{ request.press.name }}.
{% include "common/elements/password_rules.html" %}

{% blocktrans %}For more information read our password guide.{% endblocktrans %}

- {% include "common/elements/orcid_registration.html" %} + {% include "common/elements/orcid_registration.html" with button_classes="btn orcid-button btn-block" %}
{% bootstrap_form form %}

diff --git a/src/themes/material/templates/core/accounts/register.html b/src/themes/material/templates/core/accounts/register.html index 40c25933b7..9599b9345d 100644 --- a/src/themes/material/templates/core/accounts/register.html +++ b/src/themes/material/templates/core/accounts/register.html @@ -24,7 +24,7 @@

{% blocktrans trimmed %}For more information read our password guide.{% endblocktrans %}

- {% include "common/elements/orcid_registration.html" %} + {% include "common/elements/orcid_registration.html" with button_classes="btn wide-button orcid-button" %}
{% include "elements/forms/errors.html" %} {% csrf_token %} From 5a26ba1e25590b8371dad65b396a728f0948beb4 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:38:26 +0100 Subject: [PATCH 05/49] #4405: Adds plain text sanitizer for model/forms --- src/utils/forms.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/utils/forms.py b/src/utils/forms.py index 1b6bb5eaff..e449881800 100644 --- a/src/utils/forms.py +++ b/src/utils/forms.py @@ -1,3 +1,4 @@ +import bleach from django.forms import ( CharField, CheckboxInput, @@ -8,6 +9,7 @@ ) from django.utils.translation import gettext_lazy as _ from django.conf import settings +from django.core.exceptions import ValidationError from modeltranslation import forms as mt_forms, translator from captcha.fields import ReCaptchaField @@ -18,6 +20,9 @@ from submission import models as submission_models +ENTITIES_MAP = (("&", "&"), (">", ">"), ("<", "<")) + + class JanewayTranslationModelForm(mt_forms.TranslationModelForm): def __init__(self, *args, **kwargs): super(JanewayTranslationModelForm, self).__init__(*args, **kwargs) @@ -109,3 +114,38 @@ def __init__(self, *args, **kwargs): captcha = CharField(widget=HiddenInput, required=False) self.fields["captcha"] = captcha + + +def text_sanitizer(text_value, tags=None, attrs=None, excl=ENTITIES_MAP): + """ A sanitizer for clearing potential harmful html/css/js from the input + :param text_value: the string to sanitize + :param tags: A list of allowed html tags + :param attrs: A dict of allowed html attributes + :param excl: A list of pairs of allowed items and their replacement + :return: Sanitized string + """ + tags = tags or [] + attrs = attrs or {} + excl = excl or {} + + cleaned = bleach.clean( + text_value, + tags=tags, + attributes=attrs, + strip=True, + ) + # Allow certain entities that bleach won't whitelist + # https://github.com/mozilla/bleach/issues/192#issuecomment-2304545475 + for escaped, raw in excl: + cleaned = cleaned.replace(escaped, raw) + + return cleaned + + +def plain_text_validator(value): + """ A field validator that ensures a textual input has no harmful code""" + sanitized = text_sanitizer(value) + if value != sanitized: + raise ValidationError( + _("HTML is not allowed in this field") + ) From 8dc372dd07ecf3377ccf69d180040a8058f2d465 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:38:59 +0100 Subject: [PATCH 06/49] #4405: Flag models to use new HTML sanitizer --- src/core/models.py | 8 ++++++++ src/submission/models.py | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/core/models.py b/src/core/models.py index c496d5dc9c..f3ba5e03ae 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -53,6 +53,7 @@ from submission import models as submission_models from utils.logger import get_logger from utils import logic as utils_logic +from utils.forms import plain_text_validator from production import logic as production_logic fs = JanewayFileSystemStorage() @@ -239,16 +240,19 @@ class Account(AbstractBaseUser, PermissionsMixin): max_length=300, blank=False, verbose_name=_('First name'), + validators=[plain_text_validator], ) middle_name = models.CharField( max_length=300, blank=True, verbose_name=_('Middle name'), + validators=[plain_text_validator], ) last_name = models.CharField( max_length=300, blank=False, verbose_name=_('Last name'), + validators=[plain_text_validator], ) activation_code = models.CharField(max_length=100, null=True, blank=True) @@ -257,11 +261,13 @@ class Account(AbstractBaseUser, PermissionsMixin): choices=SALUTATION_CHOICES, blank=True, verbose_name=_('Salutation'), + validators=[plain_text_validator], ) suffix = models.CharField( max_length=300, blank=True, verbose_name=_('Name suffix'), + validators=[plain_text_validator], ) biography = JanewayBleachField( blank=True, @@ -272,11 +278,13 @@ class Account(AbstractBaseUser, PermissionsMixin): max_length=1000, blank=True, verbose_name=_('Institution'), + validators=[plain_text_validator], ) department = models.CharField( max_length=300, blank=True, verbose_name=_('Department'), + validators=[plain_text_validator], ) twitter = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Twitter Handle')) facebook = models.CharField(max_length=300, null=True, blank=True, verbose_name=_('Facebook Handle')) diff --git a/src/submission/models.py b/src/submission/models.py index 3623f4d869..80e9537765 100755 --- a/src/submission/models.py +++ b/src/submission/models.py @@ -54,6 +54,7 @@ from review import models as review_models from utils.function_cache import cache from utils.logger import get_logger +from utils.forms import plain_text_validator from journal import models as journal_models from review.const import ( ReviewerDecisions as RD, @@ -1914,20 +1915,41 @@ class FrozenAuthor(AbstractLastModifiedModel): name_prefix = models.CharField( max_length=300, blank=True, - help_text=_("Optional name prefix (e.g: Prof or Dr)") - - ) + help_text=_("Optional name prefix (e.g: Prof or Dr)"), + validators=[plain_text_validator], + ) name_suffix = models.CharField( max_length=300, blank=True, - help_text=_("Optional name suffix (e.g.: Jr or III)") + help_text=_("Optional name suffix (e.g.: Jr or III)"), + validators=[plain_text_validator], + ) + first_name = models.CharField( + max_length=300, + blank=True, + validators=[plain_text_validator], + ) + middle_name = models.CharField( + max_length=300, + blank=True, + validators=[plain_text_validator], ) - first_name = models.CharField(max_length=300, blank=True) - middle_name = models.CharField(max_length=300, blank=True) - last_name = models.CharField(max_length=300, blank=True) + last_name = models.CharField( + max_length=300, + blank=True, + validators=[plain_text_validator], +) - institution = models.CharField(max_length=1000, blank=True) - department = models.CharField(max_length=300, blank=True) + institution = models.CharField( + max_length=1000, + blank=True, + validators=[plain_text_validator], +) + department = models.CharField( + max_length=300, + blank=True, + validators=[plain_text_validator], + ) frozen_biography = JanewayBleachField( blank=True, verbose_name=_('Frozen Biography'), From 8fc72f4a4d0436d6e621368f3c2d4930c58228b7 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:39:46 +0100 Subject: [PATCH 07/49] #4405: Update validators on migrations --- ...096_alter_account_activation_code_and_more.py | 16 +++++++++------- .../0080_frozen_author_bleach_20240507_1350.py | 15 ++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/core/migrations/0096_alter_account_activation_code_and_more.py b/src/core/migrations/0096_alter_account_activation_code_and_more.py index d8281c00f4..e9221a26f0 100644 --- a/src/core/migrations/0096_alter_account_activation_code_and_more.py +++ b/src/core/migrations/0096_alter_account_activation_code_and_more.py @@ -3,6 +3,8 @@ import core.model_utils from django.db import migrations, models +import utils + class Migration(migrations.Migration): @@ -19,32 +21,32 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='account', name='department', - field=models.CharField(blank=True, max_length=300, verbose_name='Department'), + field=models.CharField(blank=True, max_length=300, verbose_name='Department', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', name='first_name', - field=models.CharField(max_length=300, verbose_name='First name'), + field=models.CharField(max_length=300, verbose_name='First name', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', name='institution', - field=models.CharField(blank=True, max_length=1000, verbose_name='Institution'), + field=models.CharField(blank=True, max_length=1000, verbose_name='Institution', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', name='last_name', - field=models.CharField(max_length=300, verbose_name='Last name'), + field=models.CharField(max_length=300, verbose_name='Last name', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', name='middle_name', - field=models.CharField(blank=True, max_length=300, verbose_name='Middle name'), + field=models.CharField(blank=True, max_length=300, verbose_name='Middle name', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', name='salutation', - field=models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, verbose_name='Salutation'), + field=models.CharField(blank=True, choices=[('Miss', 'Miss'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Mr', 'Mr'), ('Mx', 'Mx'), ('Dr', 'Dr'), ('Prof.', 'Prof.')], max_length=10, verbose_name='Salutation', validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='account', @@ -54,6 +56,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='account', name='suffix', - field=models.CharField(blank=True, max_length=300, verbose_name='Name suffix'), + field=models.CharField(blank=True, max_length=300, verbose_name='Name suffix', validators=[utils.forms.plain_text_validator]), ), ] diff --git a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py index 114c9153de..d49a0fb616 100644 --- a/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py +++ b/src/submission/migrations/0080_frozen_author_bleach_20240507_1350.py @@ -2,6 +2,7 @@ import core.model_utils from django.db import migrations, models +import utils.forms class Migration(migrations.Migration): @@ -15,12 +16,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='frozenauthor', name='department', - field=models.CharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', name='first_name', - field=models.CharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', @@ -40,26 +41,26 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='frozenauthor', name='institution', - field=models.CharField(blank=True, max_length=1000), + field=models.CharField(blank=True, max_length=1000, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', name='last_name', - field=models.CharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', name='middle_name', - field=models.CharField(blank=True, max_length=300), + field=models.CharField(blank=True, max_length=300, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', name='name_prefix', - field=models.CharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300), + field=models.CharField(blank=True, help_text='Optional name prefix (e.g: Prof or Dr)', max_length=300, validators=[utils.forms.plain_text_validator]), ), migrations.AlterField( model_name='frozenauthor', name='name_suffix', - field=models.CharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300), + field=models.CharField(blank=True, help_text='Optional name suffix (e.g.: Jr or III)', max_length=300, validators=[utils.forms.plain_text_validator]), ), ] From 99b66a344a9095993795566f82d3e9f550b6fed1 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:39:57 +0100 Subject: [PATCH 08/49] #4405: Adds unit tests --- src/utils/tests.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/utils/tests.py b/src/utils/tests.py index 64ab1153be..5ce3bc1616 100644 --- a/src/utils/tests.py +++ b/src/utils/tests.py @@ -10,6 +10,7 @@ from django.test import TestCase, override_settings from django.utils import timezone, translation from django.core import mail +from django.core.exceptions import ValidationError from django.core.management import call_command from django.contrib.admin.sites import site from django.contrib.auth import get_user_model @@ -29,7 +30,11 @@ from utils import install from utils.transactional_emails import * -from utils.forms import FakeModelForm, KeywordModelForm +from utils.forms import ( + FakeModelForm, + KeywordModelForm, + plain_text_validator, +) from utils.logic import generate_sitemap from utils.testing import helpers from utils.shared import clear_cache @@ -792,6 +797,23 @@ class Meta: self.assertFalse(journal.keywords.exists()) +class TestPlainTextValidator(TestCase): + + def test_plain_text_validator_valid(self): + name_test = "Kathryn Janeway" + ampersand_test = "Voyager & co" + try: + plain_text_validator(name_test) + plain_text_validator(ampersand_test) + except ValidationError: + self.fail("Valid plain text input raised a ValidationError") + + def test_plain_text_validator_invalid(self): + rogue_input = 'Borg Queen' + with self.assertRaises(ValidationError): + plain_text_validator(rogue_input) + + class TestModels(TestCase): @classmethod From dc7a7a78f19c028d055411556a3c3c3181add764 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:40:13 +0100 Subject: [PATCH 09/49] 4405: Adds acceptance tests --- src/submission/tests.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/submission/tests.py b/src/submission/tests.py index 6b4f57a2ea..d3e490d575 100644 --- a/src/submission/tests.py +++ b/src/submission/tests.py @@ -13,6 +13,7 @@ from django.utils import translation, timezone from django.urls.base import clear_script_prefix from django.conf import settings +from django.core.exceptions import ValidationError from django.shortcuts import reverse from django.test.utils import override_settings @@ -567,6 +568,33 @@ def test_author_form_with_good_orcid(self): '0000-0003-2126-266X', ) + def test_author_form_harmful_inputs(self): + harmful_string = ' This are not the droids you are looking for ' + for i, attr in enumerate({ + "first_name", + "last_name", + "middle_name", + "name_prefix", + "suffix", + "institution", + "department", + }): + form = forms.AuthorForm( + { + 'first_name': 'Andy', + 'last_name': 'Byers', + 'biography': 'Andy', + 'institution': 'Birkbeck, University of London', + 'email': f'andy{i}@janeway.systems', + 'orcid': 'https://orcid.org/0000-0003-2126-266X', + **{attr: harmful_string}, + } + ) + self.assertFalse( + form.is_valid(), + f"Harmful code injected into field '{attr}'" + ) + @override_settings(URL_CONFIG='domain') def test_article_encoding_bibtex(self): article = helpers.create_article( From 4d5dec8183151e6b212ca4462f63a0f890cd6964 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Wed, 18 Sep 2024 11:53:23 +0100 Subject: [PATCH 10/49] Sets default class for orcid reg button. --- src/templates/common/elements/orcid_registration.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/common/elements/orcid_registration.html b/src/templates/common/elements/orcid_registration.html index ee13576992..f16882b260 100644 --- a/src/templates/common/elements/orcid_registration.html +++ b/src/templates/common/elements/orcid_registration.html @@ -11,7 +11,7 @@ {% else %} + class="{{ button_classes|default:"orcid-button" }}"> {% trans "Register with ORCiD" %} {% endif %} From dae095ca8fc38e19e74e7fcc7a20b22e9dacfd35 Mon Sep 17 00:00:00 2001 From: Mauro MSL Date: Tue, 17 Sep 2024 19:41:54 +0100 Subject: [PATCH 11/49] #3337: Undo orcid login template breaking change --- src/core/templatetags/orcid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/templatetags/orcid.py b/src/core/templatetags/orcid.py index 69f5bb016f..a44a11ea3f 100644 --- a/src/core/templatetags/orcid.py +++ b/src/core/templatetags/orcid.py @@ -6,7 +6,7 @@ @register.simple_tag(takes_context=True) -def orcid_redirect_uri(context, action): +def orcid_redirect_uri(context, action="login"): request = context.get('request') if request: return build_redirect_uri(request.site_type, action=action) From cce717955d3619ffa9a9a4ba4661020f73c537c7 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Wed, 18 Sep 2024 13:04:05 +0100 Subject: [PATCH 12/49] #4412 Combine recommendation display checks into one. --- src/templates/admin/elements/review/review_block.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/templates/admin/elements/review/review_block.html b/src/templates/admin/elements/review/review_block.html index 3a82e9a550..6aff9e6122 100644 --- a/src/templates/admin/elements/review/review_block.html +++ b/src/templates/admin/elements/review/review_block.html @@ -36,12 +36,10 @@

#{{ review.pk }} - {{ review.reviewer.full_name }} {% else %}Awaiting acknowledgement{% endif %} {{ review.date_due|date:"Y-m-d" }} - {% if review.decision %} + {% if review.decision and review.date_complete %} {{ review.get_decision_display|capfirst }}
- {% if review.date_complete %} - {{ review.date_complete|date:"Y-m-d H:i" }} - {% endif %} + {{ review.date_complete|date:"Y-m-d H:i" }} {% else %} -- {% endif %} From ac633743bdbef506e4bfbf4c2c10dc13ff3d611b Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Wed, 18 Sep 2024 13:04:26 +0100 Subject: [PATCH 13/49] #4412 include reviews_small in previous change. --- src/templates/admin/elements/review/reviews_small.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/templates/admin/elements/review/reviews_small.html b/src/templates/admin/elements/review/reviews_small.html index d5203a53ad..73c4b13267 100644 --- a/src/templates/admin/elements/review/reviews_small.html +++ b/src/templates/admin/elements/review/reviews_small.html @@ -26,12 +26,10 @@ {{ review.request_decision_status }} {{ review.date_due|date:"Y-m-d" }} - {% if review.decision %} + {% if review.decision and review.date_complete %} {{ review.get_decision_display|capfirst }}
- {% if review.date_complete %} - {{ review.date_complete|date:"Y-m-d H:i" }} - {% endif %} + {{ review.date_complete|date:"Y-m-d H:i" }} {% else %} -- {% endif %} From a2527d7e0f97ee59baf39b384afde3464a768152 Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Mon, 23 Sep 2024 16:48:52 +0100 Subject: [PATCH 14/49] Update default typesetting guide openlibhums/typesetting#31 --- src/utils/install/journal_defaults.json | 2 +- .../migrations/0036_typesetting_guide.py | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/utils/migrations/0036_typesetting_guide.py diff --git a/src/utils/install/journal_defaults.json b/src/utils/install/journal_defaults.json index 4475d5ffd9..e6f84d89f5 100644 --- a/src/utils/install/journal_defaults.json +++ b/src/utils/install/journal_defaults.json @@ -1092,7 +1092,7 @@ "type": "rich-text" }, "value": { - "default": "Here are some guidelines." + "default": "

When uploading galleys, please label them by the format, such as “XML” or “PDF”.

XML and HTML galleys can be previewed after uploading to check how they will be rendered when published.

The Galleys section should only contain one representative file for each format. If you are uploading the first version of a new format, please upload a new Galley (typeset file). If you are making revisions or corrections to an existing format, please use select Edit and then upload the new version, rather than adding a new Galley.

" }, "editable_by": [ "editor", diff --git a/src/utils/migrations/0036_typesetting_guide.py b/src/utils/migrations/0036_typesetting_guide.py new file mode 100644 index 0000000000..220ed6d773 --- /dev/null +++ b/src/utils/migrations/0036_typesetting_guide.py @@ -0,0 +1,32 @@ +from django.db import migrations +from utils import migration_utils + + +def update_typesetting_guide(apps, schema_editor): + values_to_replace = [ + 'Here are some guidelines.', + ] + + replacement_value = '

When uploading galleys, please label them by the format, such as “XML” or “PDF”.

XML and HTML galleys can be previewed after uploading to check how they will be rendered when published.

The Galleys section should only contain one representative file for each format. If you are uploading the first version of a new format, please upload a new Galley (typeset file). If you are making revisions or corrections to an existing format, please use select Edit and then upload the new version, rather than adding a new Galley.

' + + migration_utils.update_default_setting_values( + apps, + setting_name='typesetting_guide', + group_name='general', + values_to_replace=values_to_replace, + replacement_value=replacement_value, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('utils', '0035_upgrade_1_7_0'), + ] + + operations = [ + migrations.RunPython( + update_typesetting_guide, + reverse_code=migrations.RunPython.noop, + ), + ] From aed1e6bf3bf26900a8e826f02a6357357b11ef0d Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Tue, 24 Sep 2024 17:09:17 +0100 Subject: [PATCH 15/49] Fixes a bug where submit_info would clear CI. --- src/submission/forms.py | 14 +++++++++++--- src/submission/views.py | 10 ++++++++-- src/templates/admin/submission/submit_review.html | 11 +++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/submission/forms.py b/src/submission/forms.py index 6778cc00c3..2373aff8cd 100755 --- a/src/submission/forms.py +++ b/src/submission/forms.py @@ -33,8 +33,12 @@ class ArticleStart(forms.ModelForm): class Meta: model = models.Article - fields = ('publication_fees', 'submission_requirements', 'copyright_notice', - 'competing_interests') + fields = ( + 'publication_fees', + 'submission_requirements', + 'copyright_notice', + 'competing_interests', + ) def __init__(self, *args, **kwargs): journal = kwargs.pop('journal', False) @@ -208,7 +212,6 @@ def __init__(self, *args, **kwargs): if editor_view: self.fields[element.name].required = False - def save(self, commit=True, request=None): article = super(ArticleInfo, self).save(commit=False) @@ -238,6 +241,10 @@ class ArticleInfoSubmit(ArticleInfo): # Filter licenses and sections to publicly available only FILTER_PUBLIC_FIELDS = True + def __init__(self, *args, **kwargs): + super(ArticleInfoSubmit, self).__init__(*args, **kwargs) + self.fields.pop('competing_interests') + class EditorArticleInfoSubmit(ArticleInfo): # Used when an editor is making a submission. @@ -245,6 +252,7 @@ class EditorArticleInfoSubmit(ArticleInfo): def __init__(self, *args, **kwargs): super(EditorArticleInfoSubmit, self).__init__(*args, **kwargs) + self.fields.pop('competing_interests') if self.fields.get('section'): self.fields['section'].label_from_instance = lambda obj: obj.display_name_public_submission self.fields['section'].help_text = "As an editor you will see all " \ diff --git a/src/submission/views.py b/src/submission/views.py index cd2fa76161..3779bc341f 100755 --- a/src/submission/views.py +++ b/src/submission/views.py @@ -75,7 +75,13 @@ def start(request, type=None): **{'request': request, 'article': new_article} ) - return redirect(reverse('submit_info', kwargs={'article_id': new_article.pk})) + return redirect( + reverse( + 'submit_info', + kwargs={ + 'article_id': new_article.pk}, + ), + ) template = 'admin/submission/start.html' context = { @@ -697,7 +703,7 @@ def submit_review(request, article_id): return redirect(reverse('core_dashboard')) - template = "admin/submission//submit_review.html" + template = "admin/submission/submit_review.html" context = { 'article': article, 'form': form, diff --git a/src/templates/admin/submission/submit_review.html b/src/templates/admin/submission/submit_review.html index 510fd8b325..e4ad6dbe4d 100644 --- a/src/templates/admin/submission/submit_review.html +++ b/src/templates/admin/submission/submit_review.html @@ -85,6 +85,17 @@

{% trans 'Agreements' %}

{{ article.license.name }}{% endif %} + {% if request.journal.submissionconfiguration.competing_interests %} + + Competing Interests + + + + {{ article.competing_interests|safe|default:"No competing interests declared" }} + + + {% endif %} + {% for field_answer in article.fieldanswer_set.all %} {{ field_answer.field.name }} From 2123e63bec85b609577b3c3bee2e52efd0fe1954 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Tue, 24 Sep 2024 20:24:01 +0100 Subject: [PATCH 16/49] Reverts previous change. Adds new subclass that adds competing_interests for the edit_metadata view. --- src/submission/forms.py | 12 ++++++------ src/submission/tests.py | 32 ++++++++++++++++++++++++++++++++ src/submission/views.py | 4 ++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/submission/forms.py b/src/submission/forms.py index 2373aff8cd..604012de7f 100755 --- a/src/submission/forms.py +++ b/src/submission/forms.py @@ -81,7 +81,7 @@ class Meta: 'language', 'section', 'license', 'primary_issue', 'article_number', 'is_remote', 'remote_url', 'peer_reviewed', 'first_page', 'last_page', 'page_numbers', 'total_pages', - 'competing_interests', 'custom_how_to_cite', 'rights', + 'custom_how_to_cite', 'rights', ) widgets = { 'title': forms.TextInput(attrs={'placeholder': _('Title')}), @@ -241,10 +241,6 @@ class ArticleInfoSubmit(ArticleInfo): # Filter licenses and sections to publicly available only FILTER_PUBLIC_FIELDS = True - def __init__(self, *args, **kwargs): - super(ArticleInfoSubmit, self).__init__(*args, **kwargs) - self.fields.pop('competing_interests') - class EditorArticleInfoSubmit(ArticleInfo): # Used when an editor is making a submission. @@ -252,7 +248,6 @@ class EditorArticleInfoSubmit(ArticleInfo): def __init__(self, *args, **kwargs): super(EditorArticleInfoSubmit, self).__init__(*args, **kwargs) - self.fields.pop('competing_interests') if self.fields.get('section'): self.fields['section'].label_from_instance = lambda obj: obj.display_name_public_submission self.fields['section'].help_text = "As an editor you will see all " \ @@ -260,6 +255,11 @@ def __init__(self, *args, **kwargs): "closed for public submission" +class EditArticleMetadata(ArticleInfo): + class Meta(ArticleInfo.Meta): + fields = ArticleInfo.Meta.fields + ('competing_interests',) + + class AuthorForm(forms.ModelForm): class Meta: diff --git a/src/submission/tests.py b/src/submission/tests.py index d3e490d575..37dbd1ef34 100644 --- a/src/submission/tests.py +++ b/src/submission/tests.py @@ -934,3 +934,35 @@ def setUpTestData(cls): def test_full_name(self): self.assertEqual('Dr. S. Bella Rogers Esq.', self.frozen_author.full_name()) + + +from django.test import TestCase +from .forms import EditArticleMetadata, ArticleInfoSubmit, \ + EditorArticleInfoSubmit + + +class ArticleFormTests(TestCase): + + def test_competing_interests_in_edit_article_metadata(self): + form = EditArticleMetadata() + self.assertIn( + 'competing_interests', + form.fields, + "'competing_interests' should be present in EditArticleMetadata", + ) + + def test_competing_interests_not_in_article_info_submit(self): + form = ArticleInfoSubmit() + self.assertNotIn( + 'competing_interests', + form.fields, + "'competing_interests' should NOT be present in ArticleInfoSubmit", + ) + + def test_competing_interests_not_in_editor_article_info_submit(self): + form = EditorArticleInfoSubmit() + self.assertNotIn( + 'competing_interests', + form.fields, + "'competing_interests' should NOT be present in EditorArticleInfoSubmit" + ) diff --git a/src/submission/views.py b/src/submission/views.py index 3779bc341f..dc0afb4f74 100755 --- a/src/submission/views.py +++ b/src/submission/views.py @@ -739,7 +739,7 @@ def edit_metadata(request, article_id): article=article, ) - info_form = forms.ArticleInfo( + info_form = forms.EditArticleMetadata( instance=article, additional_fields=additional_fields, submission_summary=submission_summary, @@ -773,7 +773,7 @@ def edit_metadata(request, article_id): return redirect(reverse_url) if 'metadata' in request.POST: - info_form = forms.ArticleInfo( + info_form = forms.EditArticleMetadata( request.POST, instance=article, additional_fields=additional_fields, From 83a1314d8a34e006e747ed9cb7a4b0374feea3be Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Tue, 24 Sep 2024 21:53:08 +0100 Subject: [PATCH 17/49] Remove import. --- src/submission/tests.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/submission/tests.py b/src/submission/tests.py index 37dbd1ef34..de565fe4ba 100644 --- a/src/submission/tests.py +++ b/src/submission/tests.py @@ -936,15 +936,10 @@ def test_full_name(self): self.assertEqual('Dr. S. Bella Rogers Esq.', self.frozen_author.full_name()) -from django.test import TestCase -from .forms import EditArticleMetadata, ArticleInfoSubmit, \ - EditorArticleInfoSubmit - - class ArticleFormTests(TestCase): def test_competing_interests_in_edit_article_metadata(self): - form = EditArticleMetadata() + form = forms.EditArticleMetadata() self.assertIn( 'competing_interests', form.fields, @@ -952,7 +947,7 @@ def test_competing_interests_in_edit_article_metadata(self): ) def test_competing_interests_not_in_article_info_submit(self): - form = ArticleInfoSubmit() + form = forms.ArticleInfoSubmit() self.assertNotIn( 'competing_interests', form.fields, @@ -960,7 +955,7 @@ def test_competing_interests_not_in_article_info_submit(self): ) def test_competing_interests_not_in_editor_article_info_submit(self): - form = EditorArticleInfoSubmit() + form = forms.EditorArticleInfoSubmit() self.assertNotIn( 'competing_interests', form.fields, From fcd6ce7813f145a69d093358c06aff10c5a41396 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 20 Sep 2024 11:12:22 +0100 Subject: [PATCH 18/49] Initial WIP. Adds setting and filter. --- src/core/templatetags/roles.py | 30 +++++++++++++++++++ .../admin/review/unassigned_article.html | 4 +-- src/utils/install/journal_defaults.json | 15 ++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/core/templatetags/roles.py b/src/core/templatetags/roles.py index 3b43d5e1a0..a9f091c8d9 100755 --- a/src/core/templatetags/roles.py +++ b/src/core/templatetags/roles.py @@ -1,6 +1,9 @@ from django import template from core import models +from core.middleware import GlobalRequestMiddleware +from submission import models +from utils import setting_handler register = template.Library() @@ -47,3 +50,30 @@ def role_id(request, role_slug): return role.pk except models.Role.DoesNotExist: return 0 + + +@register.filter +def se_can_see_pii(value, article): + # Before doing anything, check the setting is enabled: + se_pii_filter_enabled = setting_handler.get_setting( + setting_group_name='permission', + setting_name='se_pii_filter', + journal=article.journal, + ).processed_value + + if not se_pii_filter_enabled: + return value + + # Check if the user is an SE and return an anonymised value. + # If the user is not a section editor we assume they have permission + # to view the actual value. + request = GlobalRequestMiddleware.get_current_request() + stages = [ + models.STAGE_UNASSIGNED, + models.STAGE_UNDER_REVIEW, + models.STAGE_UNDER_REVISION, + ] + if request.user in article.section_editors() and article.stage in stages: + return 'Value Anonymised' + else: + return value diff --git a/src/templates/admin/review/unassigned_article.html b/src/templates/admin/review/unassigned_article.html index c8d9590f42..a14a410d55 100644 --- a/src/templates/admin/review/unassigned_article.html +++ b/src/templates/admin/review/unassigned_article.html @@ -104,8 +104,8 @@

Authors

{% for order in article.articleauthororder_set.all %} - {{ order.author.full_name }} - {{ order.author.email }} + {{ order.author.full_name|se_can_see_pii:article }} + {{ order.author.email|se_can_see_pii:article }} {% if order.author == article.correspondence_author %} {% else %} {% endif %} diff --git a/src/utils/install/journal_defaults.json b/src/utils/install/journal_defaults.json index e6f84d89f5..6cf53a0542 100644 --- a/src/utils/install/journal_defaults.json +++ b/src/utils/install/journal_defaults.json @@ -5117,5 +5117,20 @@ "value": { "default": "" } + }, + { + "group": { + "name": "permission" + }, + "setting": { + "description": "When this setting is enabled assigned section editors will not be able to see PII data about authors until a decision for an article has been made.", + "is_translatable": false, + "name": "se_pii_filter", + "pretty_name": "Section Editor Personally Identifiable Information Filter", + "type": "boolean" + }, + "value": { + "default": "" + } } ] From 279c6aa99b5ec852ba2132c5bf47e57dd66c0da5 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 20 Sep 2024 13:49:21 +0100 Subject: [PATCH 19/49] Moves to securitytags, adds checks. --- src/core/templatetags/roles.py | 31 ++----------------- src/security/templatetags/securitytags.py | 31 +++++++++++++++++++ src/templates/admin/core/dashboard.html | 4 +-- .../core/submission_list_element.html | 8 ++--- src/templates/admin/review/home.html | 4 +-- src/templates/admin/review/in_review.html | 6 ++-- .../admin/review/unassigned_article.html | 8 ++--- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/core/templatetags/roles.py b/src/core/templatetags/roles.py index a9f091c8d9..f972c7e388 100755 --- a/src/core/templatetags/roles.py +++ b/src/core/templatetags/roles.py @@ -1,9 +1,9 @@ from django import template from core import models -from core.middleware import GlobalRequestMiddleware + from submission import models -from utils import setting_handler + register = template.Library() @@ -50,30 +50,3 @@ def role_id(request, role_slug): return role.pk except models.Role.DoesNotExist: return 0 - - -@register.filter -def se_can_see_pii(value, article): - # Before doing anything, check the setting is enabled: - se_pii_filter_enabled = setting_handler.get_setting( - setting_group_name='permission', - setting_name='se_pii_filter', - journal=article.journal, - ).processed_value - - if not se_pii_filter_enabled: - return value - - # Check if the user is an SE and return an anonymised value. - # If the user is not a section editor we assume they have permission - # to view the actual value. - request = GlobalRequestMiddleware.get_current_request() - stages = [ - models.STAGE_UNASSIGNED, - models.STAGE_UNDER_REVIEW, - models.STAGE_UNDER_REVISION, - ] - if request.user in article.section_editors() and article.stage in stages: - return 'Value Anonymised' - else: - return value diff --git a/src/security/templatetags/securitytags.py b/src/security/templatetags/securitytags.py index 5f5d6fcb7a..2182f72a1f 100755 --- a/src/security/templatetags/securitytags.py +++ b/src/security/templatetags/securitytags.py @@ -1,5 +1,9 @@ from django import template + +from core.middleware import GlobalRequestMiddleware from security import logic +from submission import models +from utils import setting_handler register = template.Library() @@ -87,3 +91,30 @@ def is_repository_manager(context): def is_preprint_editor(context): request = context['request'] return request.user.is_preprint_editor(request) + + +@register.filter +def se_can_see_pii(value, article): + # Before doing anything, check the setting is enabled: + se_pii_filter_enabled = setting_handler.get_setting( + setting_group_name='permission', + setting_name='se_pii_filter', + journal=article.journal, + ).processed_value + + if not se_pii_filter_enabled: + return value + + # Check if the user is an SE and return an anonymised value. + # If the user is not a section editor we assume they have permission + # to view the actual value. + request = GlobalRequestMiddleware.get_current_request() + stages = [ + models.STAGE_UNASSIGNED, + models.STAGE_UNDER_REVIEW, + models.STAGE_UNDER_REVISION, + ] + if request.user in article.section_editors() and article.stage in stages: + return 'Value Anonymised' + else: + return value diff --git a/src/templates/admin/core/dashboard.html b/src/templates/admin/core/dashboard.html index 55ecca7554..d37ad01c73 100644 --- a/src/templates/admin/core/dashboard.html +++ b/src/templates/admin/core/dashboard.html @@ -1,5 +1,5 @@ {% extends "admin/core/base.html" %} -{% load roles %} +{% load roles securitytags %} {% block title %}Dashboard{% endblock title %} {% block title-section %}Dashboard{% endblock %} @@ -192,7 +192,7 @@

Section Editor

{{ assignment.article.safe_title }} - {{ assignment.article.author_list }} + {{ assignment.article.author_list|se_can_see_pii:assignment.article }} {{ assignment.article.date_submitted }} {{ assignment.article.get_stage_display }} diff --git a/src/templates/admin/elements/core/submission_list_element.html b/src/templates/admin/elements/core/submission_list_element.html index a61f62293a..d0ce776d0d 100644 --- a/src/templates/admin/elements/core/submission_list_element.html +++ b/src/templates/admin/elements/core/submission_list_element.html @@ -1,12 +1,12 @@ +{% load securitytags %}
  • {{ article.pk }} - {{ article.title|truncatechars_html:200|safe }} ({{ article.correspondence_author.last_name }})
    - Authors: {{ article.author_list }}
    - {% for editor in article.editors %}{% if forloop.first %}Editors: {% endif %}{{ editor.editor.full_name }} ( - {% if editor.editor_type == 'section-editor' %}SE{% else %}E - {% endif %}){% if not forloop.last %}, {% endif %}{% endfor %} + Authors: {{ article.author_list|se_can_see_pii:article }}
    + {% for editor in article.editors %}{% if forloop.first %}Editors: {% endif %}{{ editor.editor.full_name }} + ({% if editor.editor_type == 'section-editor' %}SE{% else %}E{% endif %}){% if not forloop.last %}, {% endif %}{% endfor %} View Article diff --git a/src/templates/admin/review/home.html b/src/templates/admin/review/home.html index c34a0b64c1..7706670e04 100644 --- a/src/templates/admin/review/home.html +++ b/src/templates/admin/review/home.html @@ -1,6 +1,6 @@ {% extends "admin/core/base.html" %} -{% load securitytags %} +{% load securitytags roles %} {% block title %}Articles in Review{% endblock %} @@ -47,7 +47,7 @@

    Articles in Review

    {{ article.pk }} {{ article.safe_title }} {{ article.date_submitted }} - {{ article.correspondence_author.full_name }} + {{ article.correspondence_author.full_name|se_can_see_pii:article }} {% for editor in article.editors %}{{ editor.editor.full_name }}{% if not forloop.last %}, {% endif %}{% endfor %} {{ article.section.name }} {% if article.projected_issue %}{{ article.projected_issue.display_title }}{% else %} diff --git a/src/templates/admin/review/in_review.html b/src/templates/admin/review/in_review.html index 2a0a7c0db1..8765beb393 100644 --- a/src/templates/admin/review/in_review.html +++ b/src/templates/admin/review/in_review.html @@ -1,11 +1,9 @@ {% extends "admin/core/base.html" %} -{% load static %} -{% load itertools %} -{% load roles %} +{% load static itertools roles securitytags %} {% block title %}Review {{ article.title }}{% endblock %} {% block title-section %}Peer Review{% endblock %} -{% block title-sub %}#{{ article.pk }} / {{ article.correspondence_author.last_name }} / {{ article.safe_title }}{% endblock %} +{% block title-sub %}#{{ article.pk }} / {{ article.correspondence_author.last_name|se_can_see_pii:article }} / {{ article.safe_title }}{% endblock %} {% block breadcrumbs %} {{ block.super }} diff --git a/src/templates/admin/review/unassigned_article.html b/src/templates/admin/review/unassigned_article.html index a14a410d55..30a8b0812f 100644 --- a/src/templates/admin/review/unassigned_article.html +++ b/src/templates/admin/review/unassigned_article.html @@ -1,11 +1,9 @@ {% extends "admin/core/base.html" %} -{% load static %} -{% load roles %} -{% load i18n %} +{% load static roles i18n securitytags %} {% block title %}Unassigned {{ article.title }}{% endblock %} {% block title-section %}Unassigned{% endblock %} -{% block title-sub %}#{{ article.pk }} / {{ article.correspondence_author.last_name }} / {{ article.safe_title }}{% endblock %} +{% block title-sub %}#{{ article.pk }} / {{ article.correspondence_author.last_name|se_can_see_pii:article }} / {{ article.safe_title }}{% endblock %} {% block breadcrumbs %} {{ block.super }} @@ -33,7 +31,7 @@

    Summary of Article

    {{ article.section.name }} {{ article.owner.full_name }} {{ article.license.short_name }} - {{ article.language }} + {{ article.get_language_display }} Started From ec1ed8e21ab8ebdb41b43d745f85b5420aefa123 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 20 Sep 2024 14:09:58 +0100 Subject: [PATCH 20/49] #4417 adds a tag for PII can_see_pii_tag Logic is moved to a helper function. --- src/security/templatetags/securitytags.py | 28 +++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/security/templatetags/securitytags.py b/src/security/templatetags/securitytags.py index 2182f72a1f..1515b72d3a 100755 --- a/src/security/templatetags/securitytags.py +++ b/src/security/templatetags/securitytags.py @@ -93,8 +93,7 @@ def is_preprint_editor(context): return request.user.is_preprint_editor(request) -@register.filter -def se_can_see_pii(value, article): +def can_see_pii(request, article): # Before doing anything, check the setting is enabled: se_pii_filter_enabled = setting_handler.get_setting( setting_group_name='permission', @@ -103,18 +102,39 @@ def se_can_see_pii(value, article): ).processed_value if not se_pii_filter_enabled: - return value + return False # Check if the user is an SE and return an anonymised value. # If the user is not a section editor we assume they have permission # to view the actual value. - request = GlobalRequestMiddleware.get_current_request() stages = [ models.STAGE_UNASSIGNED, models.STAGE_UNDER_REVIEW, models.STAGE_UNDER_REVISION, ] if request.user in article.section_editors() and article.stage in stages: + return True + return False + + +@register.filter +def se_can_see_pii(value, article): + request = GlobalRequestMiddleware.get_current_request() + + if can_see_pii(request, article): return 'Value Anonymised' else: return value + + +@register.simple_tag(takes_context=True) +def can_see_pii_tag(context, article): + request = context.get('request') + + if can_see_pii(request, article): + return False + else: + return True + + + From 88b0e361f1819899d709f0be04877d43b720fd47 Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 20 Sep 2024 14:10:29 +0100 Subject: [PATCH 21/49] #4417 applies filter/tags to metadata screen. --- src/templates/admin/elements/metadata.html | 38 ++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/templates/admin/elements/metadata.html b/src/templates/admin/elements/metadata.html index f5fa18dec1..eb39a58f18 100644 --- a/src/templates/admin/elements/metadata.html +++ b/src/templates/admin/elements/metadata.html @@ -1,11 +1,9 @@ -{% load settings %} -{% load roles %} -{% load static %} -{% load bool_fa %} +{% load settings roles static bool_fa securitytags %} {% user_has_role request 'editor' as user_is_editor %} {% user_has_role request 'section-editor' as user_is_section_editor %} {% user_has_role request 'production' as user_is_production %} +{% can_see_pii_tag article as can_see_pii %} {% if user_is_editor or user_is_production or user_is_section_editor %} {{ article.safe_title }}
  • {{ article.section.name }} {% if article.correspondence_author %} - {{ article.correspondence_author.full_name }}   + + {{ article.correspondence_author.full_name|se_can_see_pii:article }} + {% if can_see_pii %} +   +   + + {% endif %} + {% else %}   This article has no correspondence_author {% endif %} @@ -171,12 +176,25 @@

    Frozen Authors

    {% for f_author in article.frozenauthor_set.all %} - {{ f_author.full_name }} + {{ f_author.full_name|se_can_see_pii:article }} - {% if f_author.author.email %}{{ f_author.author.email }} -   -   - {% elif f_author.email %}{{ f_author.email}}{% else %}No email address recorded.{% endif %} + {% if f_author.author.email %}{{ f_author.author.email|se_can_see_pii:article }} + {% if can_see_pii %} +   +   + {% endif %} + {% elif f_author.email %} + {{ f_author.email|se_can_see_pii:article }} + {% else %} + No email address recorded. + {% endif %} + {{ f_author.display_email|bool_fa }} {% if f_author.author == article.correspondence_author %} From f3f4acde78f3c4e336b02be907af405bf48b4d7f Mon Sep 17 00:00:00 2001 From: Andy Byers Date: Fri, 20 Sep 2024 14:38:17 +0100 Subject: [PATCH 22/49] #4417 adds checks to edit/metadata.html --- src/templates/admin/submission/edit/metadata.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/templates/admin/submission/edit/metadata.html b/src/templates/admin/submission/edit/metadata.html index c3bf885c0f..32a4e58ab3 100644 --- a/src/templates/admin/submission/edit/metadata.html +++ b/src/templates/admin/submission/edit/metadata.html @@ -1,7 +1,7 @@ {% extends "admin/core/base.html" %}} -{% load foundation %} -{% load static %} -{% load bool_fa %} +{% load foundation static bool_fa securitytags %} + +{% can_see_pii_tag article as can_see_pii %} {% block title %}Edit Metadata - {{ article.pk }}{% endblock title %} @@ -151,10 +151,10 @@

    Authors

    {% for f_author in article.frozen_authors %} - {{ f_author.full_name }} - {{ f_author.email }} + {{ f_author.full_name|se_can_see_pii:article }} + {{ f_author.email|se_can_see_pii:article }} {{ f_author.display_email|bool_fa }} - {{ f_author.institution }} + {{ f_author.institution|se_can_see_pii:article }} {% if article.correspondence_author and f_author.author == article.correspondence_author %} {% else %}{% endif %} Date: Fri, 20 Sep 2024 15:59:16 +0100 Subject: [PATCH 23/49] #4417 anonymises the edit metadata template --- src/templates/admin/submission/edit/metadata.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/templates/admin/submission/edit/metadata.html b/src/templates/admin/submission/edit/metadata.html index 32a4e58ab3..1305463bb8 100644 --- a/src/templates/admin/submission/edit/metadata.html +++ b/src/templates/admin/submission/edit/metadata.html @@ -143,8 +143,8 @@

    Authors

    Display Email Link? Institution Correspondence Author - - + Edit + Delete @@ -157,8 +157,13 @@

    Authors

    {{ f_author.institution|se_can_see_pii:article }} {% if article.correspondence_author and f_author.author == article.correspondence_author %} {% else %}{% endif %} -
     Edit + {% if can_see_pii_tag %} +  Edit + {% else %} + Author data cannot be edited + {% endif %} + {% csrf_token %} diff --git a/src/themes/OLH/templates/core/accounts/activate_account.html b/src/themes/OLH/templates/core/accounts/activate_account.html index 68f2bb578f..586f65392c 100644 --- a/src/themes/OLH/templates/core/accounts/activate_account.html +++ b/src/themes/OLH/templates/core/accounts/activate_account.html @@ -1,6 +1,7 @@ {% extends "core/base.html" %} {% load foundation %} {% load i18n %} +{% load next_url %} {% block title %}{% trans "Activate Account" %}{% endblock title %} @@ -28,12 +29,17 @@

    {% trans "Activate Account" %}

    {% else %}

    {% trans "Error" %}

    - {% trans "There was no inactive account with this activation code found. It is possible that your account is already active, you can check by attempting to" %} - {% trans "login" %}. + {% blocktrans %} + There was no inactive account with this activation code found. It + is possible that your account is already active. You can check by + attempting to log in. + {% endblocktrans %}

    + + {% trans "Log in" %} + {% endif %} - -{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/src/themes/OLH/templates/core/accounts/orcid_registration.html b/src/themes/OLH/templates/core/accounts/orcid_registration.html index 118ab6e41d..29ce8f935a 100644 --- a/src/themes/OLH/templates/core/accounts/orcid_registration.html +++ b/src/themes/OLH/templates/core/accounts/orcid_registration.html @@ -1,6 +1,7 @@ {% extends "core/base.html" %} {% load foundation %} {% load i18n %} +{% load next_url %} {% block title %}{% trans "Unregistered ORCiD" %}{% endblock title %} @@ -16,16 +17,30 @@ {% block body %}
    -
    {% trans "Unregistered ORCiD" %}
    -

    {% trans "The ORCiD you logged in with is not currently linked with an account in our system. You can either register a new account, or login with an existing account to link your ORCiD for future use." %}

    - -{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/src/themes/OLH/templates/core/base.html b/src/themes/OLH/templates/core/base.html index c82da378a9..f93dbca599 100644 --- a/src/themes/OLH/templates/core/base.html +++ b/src/themes/OLH/templates/core/base.html @@ -5,6 +5,7 @@ {% load i18n %} {% load roles %} {% load hooks %} +{% load next_url %} @@ -139,7 +140,16 @@

    {{ request.journal.name }}

    {% endif %} {% else %} -
  • {% trans "Login" %} | {% trans "Register" %}

  • +
  • +

    + + {% trans "Log in" %} + | + + {% trans "Register" %} + +

    +
  • {% endif %}
    diff --git a/src/themes/OLH/templates/core/login.html b/src/themes/OLH/templates/core/login.html index 669cef1b3b..a44503c62a 100644 --- a/src/themes/OLH/templates/core/login.html +++ b/src/themes/OLH/templates/core/login.html @@ -2,6 +2,7 @@ {% load i18n %} {% load orcid %} {% load fqdn %} +{% load next_url %} {% block title %}{% trans "Login" %}{% endblock title %} @@ -25,10 +26,18 @@ diff --git a/src/themes/OLH/templates/core/nav.html b/src/themes/OLH/templates/core/nav.html index 658a4605bf..3b33121e33 100644 --- a/src/themes/OLH/templates/core/nav.html +++ b/src/themes/OLH/templates/core/nav.html @@ -1,6 +1,7 @@ {% load roles %} {% load i18n %} {% load hooks %} +{% load next_url %} diff --git a/src/themes/clean/templates/core/accounts/orcid_registration.html b/src/themes/clean/templates/core/accounts/orcid_registration.html index 3e02269020..f69790fee1 100644 --- a/src/themes/clean/templates/core/accounts/orcid_registration.html +++ b/src/themes/clean/templates/core/accounts/orcid_registration.html @@ -1,22 +1,33 @@ {% extends "core/base.html" %} {% load foundation %} +{% load next_url %} {% block title %}{% trans 'Unregistered ORCiD' %}{% endblock title %} {% block body %} -
    -
    {% trans 'Unregistered ORCiD' %}
    -

    {% blocktrans %}The ORCiD you logged in with is not currently linked with an account in our system. You can either - register a new account, or login with an existing account to link your ORCiD for future use.{% endblocktrans %}

    - -
    - -
    +
    + {% csrf_token %}
    -
    {% trans 'Login' %}
    +
    +

    {% trans "Unregistered ORCiD ID" %}

    +

    + {% blocktranslate %} + The ORCiD ID you logged in with is not currently linked + with an account in our system. You can either + register a new account, or login with an existing + account to link your ORCiD ID for future use. + {% endblocktranslate %} +

    +

    + + {% trans 'Register' %} + +

    @@ -26,25 +37,21 @@
    {% trans 'Login' %}
    - -

    {% trans 'Forgot your password' %}?

    +

    + +

    +

    + + {% trans "Forgotten your password?" %} + +

    +
    - -
    - -
    - -
    -
    -
    {% trans 'Register' %}
    -
    - {% trans 'Register' %} -
    -
    -
    -{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/src/themes/clean/templates/core/login.html b/src/themes/clean/templates/core/login.html index 2f008cd256..3a98bca212 100644 --- a/src/themes/clean/templates/core/login.html +++ b/src/themes/clean/templates/core/login.html @@ -1,6 +1,7 @@ {% extends "core/base.html" %} {% load i18n %} {% load orcid %} +{% load next_url %} {% block title %}{% trans "Login" %}{% endblock title %} @@ -12,13 +13,20 @@
    {% if settings.ENABLE_ORCID %}

    {% trans "Log in with your account" %}

    - {% trans "Log in with ORCiD" %} + + {% trans "Log in with ORCiD" %} +
    {% endif %} {% if settings.ENABLE_OIDC %} - {% trans "Login with" %} {{ settings.OIDC_SERVICE_NAME }} -
    + + {% trans "Login with" %} {{ settings.OIDC_SERVICE_NAME }} + +
    {% endif %}
    {% include "common/elements/static-error-messages.html" with form=form %} @@ -40,12 +48,15 @@

    {% trans "Log in with your account" %}

    class="btn btn-primary btn-block">{% trans "Log in" %}

    -

    {% trans "Forgotten your password?" %} +

    + + {% trans "Forgotten your password?" %} +

    -

    - {% trans "Register a new account" %} + + {% trans "Register a new account" %} +

    diff --git a/src/themes/clean/templates/core/nav.html b/src/themes/clean/templates/core/nav.html index 82ee2712d5..498e10828f 100644 --- a/src/themes/clean/templates/core/nav.html +++ b/src/themes/clean/templates/core/nav.html @@ -100,8 +100,16 @@ {% else %} - - + + {% endif %} diff --git a/src/themes/clean/templates/elements/journal_footer.html b/src/themes/clean/templates/elements/journal_footer.html index eb6d815eb0..e6053919a8 100644 --- a/src/themes/clean/templates/elements/journal_footer.html +++ b/src/themes/clean/templates/elements/journal_footer.html @@ -39,8 +39,9 @@
  • {% trans "Contact" %}
  • {% if not request.user.is_authenticated %}
  • - - {% trans 'Login' %} + + {% trans 'Log in' %} +
  • {% endif %} {% if journal_settings.general.switch_language %} diff --git a/src/themes/clean/templates/elements/press_footer.html b/src/themes/clean/templates/elements/press_footer.html index 8f5a35e662..216925a94f 100644 --- a/src/themes/clean/templates/elements/press_footer.html +++ b/src/themes/clean/templates/elements/press_footer.html @@ -16,7 +16,12 @@
  • {% trans "Sitemap" %}
  • {% trans "Contact" %}
  • {% if not request.user.is_authenticated %} -
  • {% trans 'Login' %}>
  • {% endif %} +
  • + + {% trans 'Log in' %} + +
  • + {% endif %} {% if journal_settings.general.switch_language %}
  • {% csrf_token %} diff --git a/src/themes/clean/templates/journal/become_reviewer.html b/src/themes/clean/templates/journal/become_reviewer.html index 66257a5554..1d10b1dc90 100644 --- a/src/themes/clean/templates/journal/become_reviewer.html +++ b/src/themes/clean/templates/journal/become_reviewer.html @@ -1,5 +1,6 @@ {% extends "core/base.html" %} {% load i18n %} +{% load next_url %} {% block page_title %}{% trans "Become a Reviewer" %}{% endblock %} {% block title %}{% trans "Become a Reviewer" %}{% endblock %} @@ -9,8 +10,9 @@ {% if not code == 'already-reviewer' %} {% if code == 'not-logged-in' %} - - {% trans "Login" %} + + {% trans "Log in" %} + {% else %} {% csrf_token %} @@ -18,4 +20,4 @@
    {% endif %} {% endif %} -{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/src/themes/clean/templates/journal/submissions.html b/src/themes/clean/templates/journal/submissions.html index bceee068a2..61dd0962e9 100644 --- a/src/themes/clean/templates/journal/submissions.html +++ b/src/themes/clean/templates/journal/submissions.html @@ -15,7 +15,9 @@

    {% trans 'Submissions' %}

    {% if not request.user.is_authenticated %}{% trans 'Register' %} - {% trans 'Login' %}{% else %} + + {% trans 'Log in' %} + {% else %} {% trans 'Start Submission' %}{% endif %}

    diff --git a/src/themes/clean/templates/press/nav.html b/src/themes/clean/templates/press/nav.html index 36d8a9ef04..352ef3f4a0 100644 --- a/src/themes/clean/templates/press/nav.html +++ b/src/themes/clean/templates/press/nav.html @@ -73,10 +73,18 @@
  • {% else %} - - + + {% endif %} diff --git a/src/themes/material/templates/core/accounts/activate_account.html b/src/themes/material/templates/core/accounts/activate_account.html index 6547562aef..63b1487e87 100644 --- a/src/themes/material/templates/core/accounts/activate_account.html +++ b/src/themes/material/templates/core/accounts/activate_account.html @@ -1,6 +1,7 @@ {% extends "core/base.html" %} {% load foundation %} {% load i18n %} +{% load next_url %} {% block title %}{% trans 'Activate Account' %}{% endblock title %} @@ -22,9 +23,15 @@ {% else %} {% trans "Error" %}

    - {% trans "There was no inactive account with this activation code found. It is possible that your account is already active, you can check by attempting to " %} - {% trans "login" %}. + {% blocktrans %} + There was no inactive account with this activation code found. It + is possible that your account is already active. You can check by + attempting to log in. + {% endblocktrans %}

    + + {% trans "Log in" %} + {% endif %} diff --git a/src/themes/material/templates/core/accounts/orcid_registration.html b/src/themes/material/templates/core/accounts/orcid_registration.html new file mode 100644 index 0000000000..932b6a3d88 --- /dev/null +++ b/src/themes/material/templates/core/accounts/orcid_registration.html @@ -0,0 +1,59 @@ +{% extends "core/base.html" %} +{% load materializecss %} +{% load i18n %} +{% load static %} +{% load next_url %} + +{% block title %}{% trans "Unregistered ORCiD" %}{% endblock title %} + +{% block body %} + +
    +
    +
    +
    + + {% trans "Unregistered ORCiD" %} + +

    {% blocktrans %} + The ORCiD you logged in with is not + currently linked with an account in our system. You can + either register a new account, or login with an existing + account to link your ORCiD for future use. + {% endblocktrans %}

    +

    + + {% trans "Register" %} + +

    +
    + {% include "elements/forms/errors.html" %} + {% csrf_token %} +
    + + +
    +
    + + +
    + +

    + +

    +

    + + {% trans "Reset Your Password" %} + +

    +
    +
    +
    +
    +
    + +{% endblock body %} diff --git a/src/themes/material/templates/core/login.html b/src/themes/material/templates/core/login.html index 3020fa0523..2d7d0656f3 100644 --- a/src/themes/material/templates/core/login.html +++ b/src/themes/material/templates/core/login.html @@ -1,6 +1,7 @@ {% extends "core/base.html" %} {% load i18n %} {% load orcid %} +{% load next_url %} {% block title %}{% trans "Login" %}{% endblock title %} @@ -13,14 +14,20 @@ {% csrf_token %}
    {% trans "Login with your account" %} - {% if settings.ENABLE_ORCID %} - {% trans "Log in with ORCiD" %} + {% if settings.ENABLE_ORCID %} + + {% trans "Log in with ORCiD" %} + {% endif %} {% if settings.ENABLE_OIDC %} - {% trans "Login with" %} {{ settings.OIDC_SERVICE_NAME }} -
    + + {% trans "Login with" %} {{ settings.OIDC_SERVICE_NAME }} + +
    {% endif %} {% if request.repository and request.repository.login_text %} {{ request.repository.login_text|safe }} @@ -42,12 +49,15 @@

    - {% trans "Register an Account" %} + + {% trans "Register an Account" %} +

    - {% trans "Reset Your Password" %} + + {% trans "Reset Your Password" %} +

    -
    diff --git a/src/themes/material/templates/core/nav.html b/src/themes/material/templates/core/nav.html index 5d949cb5a2..9aa53a7760 100644 --- a/src/themes/material/templates/core/nav.html +++ b/src/themes/material/templates/core/nav.html @@ -4,6 +4,7 @@ {% load roles %} {% load i18n %} {% load hooks %} +{% load next_url %}