diff --git a/src/submission/forms.py b/src/submission/forms.py index b499a145e0..8bf36fcc55 100755 --- a/src/submission/forms.py +++ b/src/submission/forms.py @@ -569,3 +569,15 @@ def save(self, commit=True): if commit: affiliation.save() return affiliation + + +class FrozenAuthorAccountForm(forms.ModelForm): + """ + A form to power the view that lets users link an existing + author record to an existing account record. + Not intended for use entering a new author record. + """ + + class Meta: + model = models.FrozenAuthor + fields = ("author",) diff --git a/src/submission/logic.py b/src/submission/logic.py index 2e7c3fd837..645a99b2c7 100755 --- a/src/submission/logic.py +++ b/src/submission/logic.py @@ -621,3 +621,26 @@ def remove_credit_role(request, article): }, ) return author + + +def get_current_authors(article, request): + authors = [] + for author, credits in article.authors_and_credits().items(): + # Prepare CREDiT form + credit_form = get_credit_form(request, author) + + # Detected unlinked accounts + unlinked_account = None + if author.email and not author.author: + try: + unlinked_account = core_models.Account.objects.get( + email__iexact=author.email, + accountrole__role__slug="author", + ) + except ( + core_models.Account.DoesNotExist, + core_models.Account.MultipleObjectsReturned, + ): + pass + authors.append((author, credits, credit_form, unlinked_account)) + return authors diff --git a/src/submission/tests/test_logic.py b/src/submission/tests/test_logic.py index ef651b7eac..0864f28e60 100644 --- a/src/submission/tests/test_logic.py +++ b/src/submission/tests/test_logic.py @@ -8,6 +8,7 @@ from mock import patch from utils.testing import helpers +from submission import models as submission_models class TestSubmitAuthorsLogic(TestCase): @@ -216,3 +217,25 @@ def test_add_author_from_search_orcid_public_affiliations(self, get_orcid_detail last_author.primary_affiliation().__str__(), "Birkbeck", ) + + def test_get_current_authors_detects_unlinked_authors(self): + self.client.force_login(self.kathleen) + + # Create unlinked author record with same email as existing user + frozen_author, _created = submission_models.FrozenAuthor.objects.get_or_create( + article=self.article, + first_name="T.", + middle_name="S.", + last_name="Eliot", + frozen_email=self.eliot.email, + ) + response = self.client.get( + reverse("submit_authors", kwargs={"article_id": self.article.pk}), + SERVER_NAME=self.journal_one.domain, + ) + _kathleen_author_tup, eliot_author_tup = response.context["authors"] + _eliot_author, _credits, _credit_form, unlinked_account = eliot_author_tup + self.assertEqual( + unlinked_account, + self.eliot, + ) diff --git a/src/submission/tests/test_views.py b/src/submission/tests/test_views.py index 0f1079a4b2..aa486292f4 100644 --- a/src/submission/tests/test_views.py +++ b/src/submission/tests/test_views.py @@ -349,3 +349,31 @@ def test_edit_author_save_author(self): self.kathleen_author.first_name, "K.", ) + + @override_settings(URL_CONFIG="domain") + def test_link_author_to_account(self): + self.client.force_login(self.kathleen) + + email = "cwexmgdydil9hbgiw99l@example.org" + # Create an unlinked author record + frozen_author, _created = models.FrozenAuthor.objects.get_or_create( + article=self.article, + frozen_email=email, + ) + # Create an account with the same email + account = helpers.create_user(email, ["author"], self.journal_one) + self.client.post( + reverse( + "submission_link_author_to_account", + kwargs={ + "article_id": self.article.pk, + "author_id": frozen_author.pk, + }, + ), + SERVER_NAME=self.journal_one.domain, + ) + frozen_author.refresh_from_db() + self.assertEqual( + frozen_author.author, + account, + ) diff --git a/src/submission/urls.py b/src/submission/urls.py index f3cc9fa770..2dbf576b82 100755 --- a/src/submission/urls.py +++ b/src/submission/urls.py @@ -28,6 +28,11 @@ views.delete_frozen_author, name="submission_delete_frozen_author", ), + re_path( + r"^(?P\d+)/authors/(?P\d+)/link_to_account/$", + views.link_author_to_account, + name="submission_link_author_to_account", + ), # Affiliations re_path( r"^(?P\d+)/author/(?P\d+)/organization/search/$", diff --git a/src/submission/views.py b/src/submission/views.py index 193336309e..8c547b7f94 100755 --- a/src/submission/views.py +++ b/src/submission/views.py @@ -316,10 +316,7 @@ def submit_authors(request, article_id): article.save() return redirect(reverse("submit_files", kwargs={"article_id": article_id})) - authors = [] - for author, credits in article.authors_and_credits().items(): - credit_form = logic.get_credit_form(request, author) - authors.append((author, credits, credit_form)) + authors = logic.get_current_authors(article, request) template = "admin/submission/submit_authors.html" context = { @@ -374,10 +371,7 @@ def edit_current_authors(request, article_id): elif "remove_credit" in request.POST: last_changed_author = logic.remove_credit_role(request, article) - authors = [] - for author, credits in article.authors_and_credits().items(): - credit_form = logic.get_credit_form(request, author) - authors.append((author, credits, credit_form)) + authors = logic.get_current_authors(article, request) template = "admin/elements/current_authors_inner.html" context = { @@ -927,10 +921,7 @@ def edit_author_metadata(request, article_id): author = logic.add_author_from_search(search_term, request, article) last_changed_author = author - authors = [] - for author, credits in article.authors_and_credits().items(): - credit_form = logic.get_credit_form(request, author) - authors.append((author, credits, credit_form)) + authors = logic.get_current_authors(article, request) template = "admin/submission/edit/author_metadata.html" context = { @@ -1033,6 +1024,45 @@ def order_authors(request, article_id): return HttpResponse("Thanks") +@require_POST +@user_can_edit_article +def link_author_to_account(request, article_id, author_id): + next_url = request.GET.get("next", "") + + article = get_object_or_404(models.Article, pk=article_id, journal=request.journal) + author = get_object_or_404(models.FrozenAuthor, pk=author_id, article=article) + account = get_object_or_404( + core_models.Account, + email__iexact=author.email, + accountrole__role__slug="author", + ) + + author_account_form = forms.FrozenAuthorAccountForm( + {"author": account.pk}, + instance=author, + ) + if author_account_form.is_valid(): + author_account_form.save() + messages.add_message( + request, + messages.SUCCESS, + "%(author_name)s (%(email)s) is now linked to a user account." + % {"author_name": author.full_name(), "email": author.email}, + ) + + if next_url: + return redirect(next_url) + else: + return redirect( + reverse( + "submission_edit_author_metadata", + kwargs={ + "article_id": article_id, + }, + ) + ) + + @editor_user_required def fields(request, field_id=None): """ diff --git a/src/templates/admin/elements/current_authors_inner.html b/src/templates/admin/elements/current_authors_inner.html index ad6288ecf4..ab97512b53 100644 --- a/src/templates/admin/elements/current_authors_inner.html +++ b/src/templates/admin/elements/current_authors_inner.html @@ -24,7 +24,7 @@

{% trans "Current authors" %}

- {% for author, credits, credit_form in authors %} + {% for author, credits, credit_form, unlinked_account in authors %} {% with "author-"|concat:author.pk as section_id %}
{% trans "Current authors" %} {% if article.correspondence_author and article.correspondence_author == author.author %} {% include "admin/submission/submit_correspondence_author.html" with article=article %} {% endif %} + {% if unlinked_account %} +
+

+ A user account was found that has the same email address as + this author ({{ unlinked_account.email }}), but they are not linked yet. + Would you like to link the author to the account? +

+
+ {% csrf_token %} +
+ +
+
+
+ {% endif %}
{% if authors|length > 1 %}
{{ forloop.counter }}
diff --git a/src/templates/admin/submission/edit/author.html b/src/templates/admin/submission/edit/author.html index a2aec40198..f74515a0cb 100644 --- a/src/templates/admin/submission/edit/author.html +++ b/src/templates/admin/submission/edit/author.html @@ -71,15 +71,29 @@

{% trans "Name, bio, and identifiers" %}

{% include "admin/elements/forms/field.html" with field=form.frozen_biography %}
- {% if author.author and author.author == request.user %} + {% if author.author %}
Email
{{ author.email|default:"No email" }}
-

- If the email is incorrect, edit the - email on your user profile. -

+ {% if author.author == request.user %} +

+ If the email is incorrect, edit the + email on your user profile. +

+ {% else %} +

+ If the email is incorrect, edit the email on the author's + {% spaceless %} + + user account + (opens in new tab) + . + {% endspaceless %} +

+ {% endif %}
{% else %} diff --git a/src/templates/admin/submission/submit_correspondence_author.html b/src/templates/admin/submission/submit_correspondence_author.html index 6045b69325..7507820a33 100644 --- a/src/templates/admin/submission/submit_correspondence_author.html +++ b/src/templates/admin/submission/submit_correspondence_author.html @@ -84,8 +84,8 @@ {% include "admin/elements/button_copy.html" with content=content label_copy=label_copy label_copied=label_copied %} {% if authors|length > 1 %}

{% blocktrans %} - After they have made their account, remove and re-add - them to the author list via the search. + After they have made their account, a button will appear + above their author record letting you link up their account. {% endblocktrans %}

{% else %}

{% blocktrans %}