diff --git a/cms/sass/base/_general.scss b/cms/sass/base/_general.scss index 0cc4ed5ceb..92ae817cf3 100644 --- a/cms/sass/base/_general.scss +++ b/cms/sass/base/_general.scss @@ -333,3 +333,13 @@ fieldset { height: 1em; width: auto; } + +.focus-overlay { + position: fixed; + height: 100%; + width: 100%; + background-color: rgba($dark-grey, 0.7); + z-index: 999; + top: 0; + left: 0; +} \ No newline at end of file diff --git a/cms/sass/components/_alert.scss b/cms/sass/components/_alert.scss index 2132c103cc..9e84164989 100644 --- a/cms/sass/components/_alert.scss +++ b/cms/sass/components/_alert.scss @@ -13,6 +13,27 @@ cursor: pointer; } + button { + margin-bottom: 0; + } + p { + margin-bottom: 0; + } + + &.focus { + position: relative; + width: 100%; + display: flex; + flex-direction: column; + background-color: $white; + z-index: 1000; + button { + margin-top: 1.5rem; + width: max-content; + align-self: center; + } + } + &--message { background-color: $warm-black; color: $white; diff --git a/doajtest/testbook/new_application_form/maned_form.yml b/doajtest/testbook/new_application_form/maned_form.yml index 23483521db..7f31df03d6 100644 --- a/doajtest/testbook/new_application_form/maned_form.yml +++ b/doajtest/testbook/new_application_form/maned_form.yml @@ -50,7 +50,8 @@ tests: subject classifications - step: Remove one subject classification - step: Click "Unlock & Close" - results: Confirmation dialog is displayed warning you about unsaved changes + results: + - Confirmation dialog is displayed warning you about unsaved changes - step: Click "Stay on Page" and then 'Save' results: - The form saves @@ -59,6 +60,7 @@ tests: - step: Click "Unlock and Close" results: - The page closes, and you are able to return to the search interface + - title: Note features for admin context: role: admin @@ -99,3 +101,49 @@ tests: - step: Attempt to paste the value (use separate editor) results: - Correct value is pasted + +- title: Reject application that is a continuation + steps: + - step: go to /testdrive/reject_with_continuation + - step: login as ProfessorFrostfire (you may want to log in using an incognito window) + - step: open "Advances in Cold Lava Mechanics" application (available on your dashboard) + - step: change the status to "Rejected" in the "Change status" input + - step: save the application + results: + - application is saved + - a warning appears with information about an older journal and a link in "focus" mode + - the form is dimmed; interactions outside the warning are blocked + - the "Got it!" button is displayed at the bottom + - step: click the link in the warning + results: + - the "Annals of Cryovolcanic Ecology" form opens in a new tab + - step: Unlock & Close the journal form, close the tab, and return to the application form + - step: click the "Got it" button + results: + - the warning is now in standard mode + - the "Got it!" button is hidden + - the application is active and can be unlocked & closed + - step: Unlock & Close + - step: open the application again (navigate to `/admin/application/` or search for "Advances in Cold Lava Mechanics" in `/admin/applications`) + results: + - the warning about the older journal is displayed at the top + - the application is active and editable + - it can be unlocked & closed + - step: change the status back to "In progress" and save (do not close) + results: + - the warning disappears + - step: once saved, click the "Quick Reject" button + - step: choose any rejection reason and click "Quick Reject" + results: + - application is saved + - the warning appears again at the top + - the form is dimmed; interactions outside the warning are blocked + - step: click "Got it" + results: + - the warning returns to standard mode + - the application is active and editable + - it can be unlocked & closed + - step: change any information (except status) and save + results: + - the application is saved correctly + - the warning remains in standard mode diff --git a/doajtest/testdrive/reject_with_continuation.py b/doajtest/testdrive/reject_with_continuation.py new file mode 100644 index 0000000000..f5e678ed4d --- /dev/null +++ b/doajtest/testdrive/reject_with_continuation.py @@ -0,0 +1,77 @@ +from doajtest.fixtures import ApplicationFixtureFactory, JournalFixtureFactory +from portality import constants +from doajtest.testdrive.factory import TestDrive +from portality import models +from portality.models import EditorGroup + +class RejectWithContinuation(TestDrive): + def setup(self) -> dict: + + random_str = self.create_random_str() + + un = "ProfessorFrostfire_" + random_str + pw1 = self.create_random_str() + admin = models.Account.make_account(un + "@example.com", un, un, [constants.ROLE_ADMIN]) + admin.set_password(pw1) + admin.save(blocking=True) + + eg = EditorGroup(**{ + "name": "The Quill & Gavel Club " + random_str, + "maned": admin.id, + "editor": admin.id, + "associates": [admin.id] + }) + eg.save(blocking=True) + + jsource = JournalFixtureFactory.make_journal_source(True) + jtitle = "Annals of Cryovolcanic Ecology " + random_str + journal = models.Journal(**jsource) + journal.set_id(jtitle.replace(" ", "").lower()) + jeissn = self.generate_unique_issn() + peissn = self.generate_unique_issn() + journal.bibjson().eissn = jeissn + journal.bibjson().pissn = peissn + journal.bibjson().title = jtitle + journal.set_editor_group(eg.id) + journal.set_editor(admin.id) + journal.save() + + asource = ApplicationFixtureFactory.make_application_source() + atitle = "Advances in Cold Lava Mechanics " + random_str + application = models.Application(**asource) + application.set_id(atitle.replace(" ", "").lower()) + application.bibjson().eissn = self.generate_unique_issn() + application.bibjson().pissn = self.generate_unique_issn() + application.bibjson().title = atitle + application.bibjson().replaces = [jeissn] + application.set_application_status(constants.APPLICATION_STATUS_IN_PROGRESS) + application.set_editor_group(eg.id) + application.set_editor(admin.id) + application.remove_current_journal() + application.remove_related_journal() + del application.bibjson().discontinued_date + application.save(blocking=True) + + return { + "admin": { + "username": admin.id, + "password": pw1 + }, + "records": { + "application": {"title": application.bibjson().title, "id": application.id}, + "journal": journal.bibjson().title, + }, + "non_renderable": { + "journal": journal.id, + "application": application.id, + "edgroup": eg.id + } + } + + def teardown(self, params): + models.Account.remove_by_id(params["admin"]["username"]) + eg = EditorGroup.remove_by_id(params["non_renderable"]["edgroup"]) + models.Journal.remove_by_id(params["non_renderable"]["journal"]) + models.Application.remove_by_id(params["non_renderable"]["application"]) + + return {"status": "success"} diff --git a/portality/constants.py b/portality/constants.py index 4ed8d7c3a5..f65feef89d 100644 --- a/portality/constants.py +++ b/portality/constants.py @@ -28,6 +28,8 @@ APPLICATION_TYPE_UPDATE_REQUEST = "update_request" APPLICATION_TYPE_NEW_APPLICATION = "new_application" +APP_PROCESSOR_INFO_IS_BEING_REJECTED = "is_being_rejected" + INDEX_RECORD_TYPE_UPDATE_REQUEST_UNFINISHED = "Update Request (in progress)" INDEX_RECORD_TYPE_UPDATE_REQUEST_FINISHED = "Update Request (finished)" INDEX_RECORD_TYPE_NEW_APPLICATION_UNFINISHED = "Application (in progress)" diff --git a/portality/forms/application_processors.py b/portality/forms/application_processors.py index 8e033d80bc..3ca4935ff4 100644 --- a/portality/forms/application_processors.py +++ b/portality/forms/application_processors.py @@ -460,6 +460,10 @@ def finalise(self, account, save_target=True, email_alert=True): # if the application was instead rejected, carry out the rejection actions elif self.source.application_status != constants.APPLICATION_STATUS_REJECTED and self.target.application_status == constants.APPLICATION_STATUS_REJECTED: + # FIXME: I'm not really sure what `info` is for - it has been defined but unused in the + # base class for ages. This doesn't feel right, though, but since it's unused doesn't + # cause any actual problems right now + self.info = constants.APP_PROCESSOR_INFO_IS_BEING_REJECTED # reject the application applicationService.reject_application(self.target, current_user._get_current_object()) diff --git a/portality/static/js/application_form.js b/portality/static/js/application_form.js index 04981952d8..b62581e268 100644 --- a/portality/static/js/application_form.js +++ b/portality/static/js/application_form.js @@ -457,6 +457,12 @@ doaj.af.EditorialApplicationForm = class extends doaj.af.BaseApplicationForm { $(sec).show(); }); + $("#cont_confirmation").click(function() { + $(this).parent().removeClass("focus"); + $(this).hide(); + $("#focus-overlay").hide(); + }) + var that = this; $("#unlock").click(function(event) { event.preventDefault(); diff --git a/portality/templates-v2/management/admin/includes/_warning_continuation.html b/portality/templates-v2/management/admin/includes/_warning_continuation.html new file mode 100644 index 0000000000..ba468e4504 --- /dev/null +++ b/portality/templates-v2/management/admin/includes/_warning_continuation.html @@ -0,0 +1,11 @@ +{%- if continuation_info.initial_warning -%} +
+{%- endif -%} +
+

This application has been rejected. We found a related older journal record. You may want to withdraw it.
+ {{ continuation_info.journal.bibjson().title }} +

+ {%- if continuation_info.initial_warning -%} + + {%- endif -%} +
\ No newline at end of file diff --git a/portality/templates-v2/management/admin/maned_application.html b/portality/templates-v2/management/admin/maned_application.html index 0c7bbe8086..70067b1e5f 100644 --- a/portality/templates-v2/management/admin/maned_application.html +++ b/portality/templates-v2/management/admin/maned_application.html @@ -13,6 +13,12 @@ {% block page_title %}{% if obj.application_type == constants.APPLICATION_TYPE_UPDATE_REQUEST %}Update Request{% else %}Application{% endif %}: {{ obj.bibjson().title }}{% endblock %} {% block body_id %}apply{% endblock %} +{% block permanent_warnings %} + {% if obj.application_status == constants.APPLICATION_STATUS_REJECTED and continuation_info.journal %} + {% include "management/admin/includes/_warning_continuation.html" with context %} + {% endif %} +{% endblock %} + {% block admin_content scoped %} {% include "management/_application-form/includes/_editorial_form_body.html" %} diff --git a/portality/templates-v2/management/base.html b/portality/templates-v2/management/base.html index a47c4cea80..ad745ce6e7 100644 --- a/portality/templates-v2/management/base.html +++ b/portality/templates-v2/management/base.html @@ -171,6 +171,7 @@

+ {% block permanent_warnings %}{% endblock %} {% include "includes/_flash_notification.html" %} {% block management_content %}{% endblock %} diff --git a/portality/view/admin.py b/portality/view/admin.py index d0ed408f1f..4d2947de53 100644 --- a/portality/view/admin.py +++ b/portality/view/admin.py @@ -431,7 +431,7 @@ def update_requests(): @write_required() @login_required @ssl_required -def application(application_id): +def application(application_id, **kwargs): auth_svc = DOAJ.authorisationService() application_svc = DOAJ.applicationService() @@ -457,8 +457,15 @@ def application(application_id): if request.method == "GET": fc.processor(source=ap) + + continuation_info = {"initial_warning": request.args.get("info") == constants.APP_PROCESSOR_INFO_IS_BEING_REJECTED} + replaces = Journal.find_by_issn(ap.bibjson().replaces) + if replaces: + continuation_info["journal"] = replaces[0] + return fc.render_template(obj=ap, lock=lockinfo, form_diff=form_diff, - current_journal=current_journal, lcc_tree=lcc_jstree, autochecks=autochecks) + current_journal=current_journal, lcc_tree=lcc_jstree, autochecks=autochecks, + continuation_info=continuation_info) elif request.method == "POST": processor = fc.processor(formdata=request.form, source=ap) @@ -473,7 +480,7 @@ def application(application_id): flash('Application updated.', 'success') for a in processor.alert: flash_with_url(a, "success") - return redirect(url_for("admin.application", application_id=ap.id, _anchor='done')) + return redirect(url_for("admin.application", application_id=ap.id, _anchor='done', info=processor.info)) except Exception as e: flash("unexpected field " + str(e)) return redirect(url_for("admin.application", application_id=ap.id, _anchor='cannot_edit')) @@ -542,7 +549,7 @@ def application_quick_reject(application_id): flash(msg, "success") # redirect the user back to the edit page - return redirect(url_for('.application', application_id=application_id)) + return redirect(url_for('admin.application', application_id=application_id, info=constants.APP_PROCESSOR_INFO_IS_BEING_REJECTED)) @blueprint.route("/admin_site_search", methods=["GET"])