diff --git a/cms/sass/base/_palette.scss b/cms/sass/base/_palette.scss index 52eb14e35d..22de58983f 100644 --- a/cms/sass/base/_palette.scss +++ b/cms/sass/base/_palette.scss @@ -6,6 +6,7 @@ $dark-grey: #5C5956; $mid-grey: #A9A7A5; $light-grey: #F6F4F4; $white: #FFF; +$highlight: #E3D2D2; $disabled: #5c595626; diff --git a/cms/sass/components/_drawer.scss b/cms/sass/components/_drawer.scss new file mode 100644 index 0000000000..caa04a4640 --- /dev/null +++ b/cms/sass/components/_drawer.scss @@ -0,0 +1,66 @@ +.drawer.is-open { + transform: translateX(0); +} + +.drawer { + position: fixed; + top: 0; + right: 0; + max-width: 25rem; + width: 100%; + height: 100vh; + background-color: #F6F4F4; + padding: calc(90px + 1.5rem) 1.5rem 1.5rem; + + transform: translateX(100%); + transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15); + will-change: transform; + z-index: 1000; + + h1 { + font-family: 'Source Sans Pro', sans-serif; + line-height: 1.4; + font-weight: 400; + font-size: 1.2rem; + margin: 1rem 0 0 0; + padding-bottom: 1rem; + border-bottom: 1px solid #282624; + } + + .drawer__close { + border: none; + position: absolute; + top: 5rem; + right: 1rem; + } + + .drawer__content { + p.intro-text { + margin-top: 1rem; + font-size: .9rem; + } + + p.closing-text { + margin-top: 1rem; + margin-bottom: 2rem; + font-size: 1rem; + } + + form { + margin-bottom: 0; + .form__question { + margin: 0; + label { + font-size: 1rem; + margin: 0 0 .25rem 0; + } + + input { + width: 100%; + margin: 0 0 1rem 0; + } + } + } + } +} \ No newline at end of file diff --git a/cms/sass/main.scss b/cms/sass/main.scss index 5b1efd2925..b39e92a570 100644 --- a/cms/sass/main.scss +++ b/cms/sass/main.scss @@ -34,6 +34,7 @@ "components/buttons", "components/card", "components/cookie-consent", + "components/drawer", "components/dropdown", "components/file_history", "components/filters", diff --git a/cms/sass/pages/_journal-details.scss b/cms/sass/pages/_journal-details.scss index 001b677a69..5ddd169daa 100644 --- a/cms/sass/pages/_journal-details.scss +++ b/cms/sass/pages/_journal-details.scss @@ -2,36 +2,53 @@ .journal-details { - .journal-details__alt-title { - display: block; - color: $dark-grey; - font-weight: 400; - } - - .three-col { - display: flex; - justify-content: space-between; - flex-direction: column; - - @media (min-width: 769px) { - flex-direction: row; + .journal-details__alt-title { + display: block; + color: $dark-grey; + font-weight: 400; } - > section { - width: 100%; + .three-col { + display: flex; + justify-content: space-between; + flex-direction: column; - @media (min-width: 769px) { - padding-bottom: 0; - width: calc(100% / 3 - 20px); - } + @media (min-width: 769px) { + flex-direction: row; + } - @media (min-width: 1280px) { - width: calc(100% / 3 - 40px); - } + > section { + width: 100%; + + @media (min-width: 769px) { + padding-bottom: 0; + width: calc(100% / 3 - 20px); + } + + @media (min-width: 1280px) { + width: calc(100% / 3 - 40px); + } + } + } + + .card { + dl { + width: 100%; + .ur_nudge { + margin-top: .75rem; + padding: .5rem .5rem .5rem 2.5rem; + background-color: $light-grey; + margin-left: -2.5rem; + width: calc(100% + 4rem); + .ur_nudge--question { + font-style: italic; + } + } + } + } + + .tabs__menu { + margin-bottom: $spacing-04; } - } - .tabs__menu { - margin-bottom: $spacing-04; - } } diff --git a/doajtest/testbook/public_site/ToC.yml b/doajtest/testbook/public_site/ToC.yml index 44ae08c027..30d13aeaed 100644 --- a/doajtest/testbook/public_site/ToC.yml +++ b/doajtest/testbook/public_site/ToC.yml @@ -1,14 +1,15 @@ suite: Public Site testset: ToC tests: -- title: Test Correctly Displayed Discontinued Date +- title: Test Correctly Displayed Discontinued Date context: role: anonymous + setup: + - To prepare to do this test make sure there are 3 journals publically available in DOAJ + - one with discontinued date in the past + - one with discontinued date in the future + - one with discontinued date today steps: - - step: To prepare to do this test make sure there are 3 journals publically available in DOAJ - one with discontinued date in the past - one with discontinued date in the future - one with discontinued date today - step: Search for every journal from the list above results: - On the ToC of the journal with discontinued date in the past or today - the discontinued date is displayed @@ -41,4 +42,47 @@ tests: path: /search/journals - step: click the journal title to go to the ToC page results: - - At the bottom of the ToC page is a "Last Full Review" date, showing the date set in the journal record. \ No newline at end of file + - At the bottom of the ToC page is a "Last Full Review" date, showing the date set in the journal record. + +- title: Update Request link behaviour for publisher and non-owner + context: + role: anonymous + path: /testdrive/ur_nudge + steps: + - step: Ensure you are not logged in. You may use a private browsing window to do this if you are already logged in. + - step: Go to the public page of "Journal of Exoplanetary Biosignatures XXXXX". Where XXXX is the unique + identifier provided on the testdrive page. You may follow the link from the testdrive page to get to the journal's + public page. + results: + - In the "Journal metadata" column, in the "Publisher" tile, a message is displayed "Are you associated with this + journal? Help us keep your record accurate! Click here to submit an update." + - step: Click the "Click here to submit an update" link. + results: + - A right-hand drawer opens displaying a login form. + - step: Log in using the admin credentials for Celestial Frontiers Press provided on the testdrive page. + results: + - The update request form for the journal "Journal of Exoplanetary Biosignatures" is displayed. + - step: Without logging out, return to the public page of "Journal of Exoplanetary Biosignatures". + results: + - In the "Journal metadata" column, in the "Publisher" tile, a message is displayed "This is your journal! Help us + keep your record accurate. Click here to submit an update." + - step: Click the "Click here to submit an update" link. + results: + - The update request form for the journal "Journal of Exoplanetary Biosignatures" is displayed. + - step: Without logging out, go to the public page of "Annals of Stellar Habitat Studies XXXXX". Where XXXXX is the + unique identifier provided on the testdrive page. You may follow the link from the testdrive page to get to the + journal's public page. + results: + - In the "Journal metadata" column, in the "Publisher" tile, no update request link is displayed. + - step: Log out. + - step: Go to the public page of "Annals of Stellar Habitat Studies". + results: + - In the "Journal metadata" column, in the "Publisher" tile, the update request link is displayed. + - step: Click the "Click here to submit an update" link. + results: + - A right-hand drawer opens displaying a login form. + - step: Log in as "Celestial Frontier Press" using the credentials provided on the testdrive page. + results: + - You are redirected to the "My journals" section of the Publisher Dashboard. + - A message is displayed at the top of the page "You are not the owner of a journal you're trying to access. + Here are the journals associated with your account." diff --git a/doajtest/testdrive/ur_nudge.py b/doajtest/testdrive/ur_nudge.py new file mode 100644 index 0000000000..f409716713 --- /dev/null +++ b/doajtest/testdrive/ur_nudge.py @@ -0,0 +1,71 @@ +from portality import constants +from doajtest.testdrive.factory import TestDrive +from doajtest.fixtures.v2.journals import JournalFixtureFactory +from doajtest.fixtures.v2.applications import ApplicationFixtureFactory +from portality import models +from portality.core import app + +class UrNudge(TestDrive): + def setup(self) -> dict: + random_id = self.create_random_str() + + + un = "CelestialFrontierPress_" + random_id + pw = self.create_random_str() + acc = models.Account.make_account("cfp@example.com", un, un, [constants.ROLE_PUBLISHER, constants.ROLE_API]) + acc.set_password(pw) + acc.generate_api_key() + acc.save() + + un1 = "DeepOrbitAcademicPress_" + random_id + pw1 = self.create_random_str() + acc1 = models.Account.make_account("doap@example.com", un1, un1, [constants.ROLE_PUBLISHER, constants.ROLE_API]) + acc1.set_password(pw1) + acc1.save() + + source = JournalFixtureFactory.make_journal_source(in_doaj=True) + j = models.Journal(**source) + j.remove_current_application() + j.set_id(j.makeid()) + j.set_owner(acc.id) + j.bibjson().title = "Journal of Exoplanetary Biosignatures " + random_id + del j.bibjson().discontinued_date + del j.bibjson().is_replaced_by + del j.bibjson().replaces + j.bibjson().eissn = self.generate_unique_issn() + j.bibjson().pissn = self.generate_unique_issn() + j.save() + + source = JournalFixtureFactory.make_journal_source(in_doaj=True) + j1 = models.Journal(**source) + j1.remove_current_application() + j1.bibjson().title = "Annals of Stellar Habitat Studies " + random_id + del j1.bibjson().discontinued_date + del j1.bibjson().is_replaced_by + del j1.bibjson().replaces + j1.set_id(j.makeid()) + j1.set_owner(acc1.id) + j1.bibjson().eissn = self.generate_unique_issn() + j1.bibjson().pissn = self.generate_unique_issn() + j1.save() + + return { + "accounts": { + "username": acc.id, + "password": pw + }, + "journals": {j.bibjson().title: app.config.get("BASE_URL") + "/toc/" + j.bibjson().eissn, + j1.bibjson().title: app.config.get("BASE_URL") + "/toc/" + j1.bibjson().eissn}, + "non_renderable": { + "accounts": [acc.id, acc1.id], + "journals": [j.id, j1.id], + } + } + + def teardown(self, params) -> dict: + ids = params["non_renderable"] + for aid in ids["accounts"]: + models.Account.remove_by_id(aid) + for jid in ids["journals"]: + models.Journal.remove_by_id(jid) + return {"status": "success"} \ No newline at end of file diff --git a/portality/static/js/dashboard.js b/portality/static/js/dashboard.js index 2ee2e7ce77..ba939ebbf0 100644 --- a/portality/static/js/dashboard.js +++ b/portality/static/js/dashboard.js @@ -213,11 +213,11 @@ ${status}`; if (historicalNumbers) { statisticsFrag += `
`; - if (current_user.role.includes("admin") || historicalNumbers.associate_editors.length > 0) { + if (doaj.current_user.role.includes("admin") || historicalNumbers.associate_editors.length > 0) { statisticsFrag += `

Statistics for the current year (${historicalNumbers.year})

`; } - if (current_user.role.includes("admin")) { + if (doaj.current_user.role.includes("admin")) { // Ready applications by editor statisticsFrag += `

Editor's Ready Applications: `; statisticsFrag += `${historicalNumbers.editor.id} ${historicalNumbers.editor.count}

`; diff --git a/portality/static/js/doaj.fieldrender.edges.js b/portality/static/js/doaj.fieldrender.edges.js index a1799f220a..da1c85022e 100644 --- a/portality/static/js/doaj.fieldrender.edges.js +++ b/portality/static/js/doaj.fieldrender.edges.js @@ -37,7 +37,7 @@ $.extend(true, doaj, { must: [ es.newTermFilter({ field: "index.flag_assignees.exact", - value: doaj.session.currentUserId + value: doaj.current_user.id }) ] } @@ -707,7 +707,7 @@ $.extend(true, doaj, { facetOptions += ``; } - let exportNotes = current_user && current_user.role.includes("ultra_admin_reports_with_notes") + let exportNotes = doaj.current_user && doaj.current_user.role.includes("ultra_admin_reports_with_notes") let notesFrag = ""; if (exportNotes) { notesFrag = `
@@ -765,7 +765,7 @@ $.extend(true, doaj, { let name = this.context.find(nameSelector).val(); let notes = false; - let exportNotes = current_user && current_user.role.includes("ultra_admin_reports_with_notes") + let exportNotes = doaj.current_user && doaj.current_user.role.includes("ultra_admin_reports_with_notes") if (exportNotes) { let includeNotesSelector = edges.css_class_selector(this.namespace, "notes", this); notes = this.context.find(includeNotesSelector).is(":checked"); @@ -5390,7 +5390,7 @@ $.extend(true, doaj, { if (resultobj.index.is_flagged || s2o) { field += '
' if (resultobj.index.is_flagged) { - if (resultobj.index.flag_assignees.includes(doaj.session.currentUserId)) { + if (resultobj.index.flag_assignees.includes(doaj.current_user.id)) { field += doaj.fieldRender.fragment.fullFlagHTML; } else { field += doaj.fieldRender.fragment.emptyFlagHTML; diff --git a/portality/static/js/formulaic.js b/portality/static/js/formulaic.js index 78d1283064..17fb010f74 100644 --- a/portality/static/js/formulaic.js +++ b/portality/static/js/formulaic.js @@ -1005,7 +1005,7 @@ var formulaic = { let flagDetailsText_deadline = ""; if ($assigneeInput.is(":disabled")) { - if ($assigneeInput.val() === doaj.session.currentUserId) { + if ($assigneeInput.val() === doaj.current_user.id) { flagDetailsText += this.fullFlagHTML(); } else { @@ -1041,7 +1041,7 @@ var formulaic = { if ($(".flag").length > 0) { $(".flag").remove(); } - if ($assigneeInput.val() === doaj.session.currentUserId) { + if ($assigneeInput.val() === doaj.current_user.id) { if ($(".flag").length > 0) { $(".flag").remove(); } diff --git a/portality/static/js/toc_drawer.js b/portality/static/js/toc_drawer.js new file mode 100644 index 0000000000..205aea0e51 --- /dev/null +++ b/portality/static/js/toc_drawer.js @@ -0,0 +1,61 @@ +doaj.toc_drawer = {}; + +doaj.toc_drawer.init = function () { + if (doaj.current_user) { + return + } + + // the open link + $("#ur_nudge--link").on("click", doaj.toc_drawer.onOpen); + + // the explicit close link + $('[data-dismiss="drawer-login"]').on('click', doaj.toc_drawer.onClose); + + // click anywhere else on the page + $(document).on('click', doaj.toc_drawer.onClickAway); + + // hit the escape key + $(document).on('keydown', doaj.toc_drawer.onEscape); +} + +doaj.toc_drawer.open = function () { + $("#drawer-login").addClass('is-open') + $("#user").focus() +} + +doaj.toc_drawer.close = function() { + const $drawer = $('#drawer-login'); + if ($drawer.css('display') !== 'block') { + return; + } + $drawer.removeClass('is-open'); +} + +// event handlers + +doaj.toc_drawer.onEscape = function (event) { + if (event.key === 'Escape') { + doaj.toc_drawer.close(); + } +} + +doaj.toc_drawer.onOpen = function (event) { + event.preventDefault(); + doaj.toc_drawer.open(); +} + +doaj.toc_drawer.onClose = function (event) { + event.preventDefault(); + doaj.toc_drawer.close(); +} + +doaj.toc_drawer.onClickAway = function (event) { + if ( + !$(event.target).closest('#ur_nudge--link').length && + !$(event.target).closest('.drawer').length && + !$(event.target).closest('.drawer__content').length && + !$(event.target).closest('[data-toggle="drawer-login"]').length + ) { + doaj.toc_drawer.close(); + } +} \ No newline at end of file diff --git a/portality/templates-v2/_account/includes/_login_form.html b/portality/templates-v2/_account/includes/_login_form.html index 8582f5ed3f..ff63ea6500 100644 --- a/portality/templates-v2/_account/includes/_login_form.html +++ b/portality/templates-v2/_account/includes/_login_form.html @@ -1,6 +1,6 @@ {% from "includes/_formhelpers.html" import render_field %} -
+
{{ render_field(form.user, placeholder="email@example.com") }}
diff --git a/portality/templates-v2/includes/_js_includes.html b/portality/templates-v2/includes/_js_includes.html index 99a347045a..ec0bf11dd1 100644 --- a/portality/templates-v2/includes/_js_includes.html +++ b/portality/templates-v2/includes/_js_includes.html @@ -40,6 +40,15 @@ +{% if current_user and not current_user.is_anonymous %} + +{% endif %} + - + {% include "_tourist/includes/_tourist.html" %} diff --git a/portality/templates-v2/public/includes/_login_modal.html b/portality/templates-v2/public/includes/_login_modal.html new file mode 100644 index 0000000000..adf4b7315a --- /dev/null +++ b/portality/templates-v2/public/includes/_login_modal.html @@ -0,0 +1,20 @@ + diff --git a/portality/templates-v2/public/layouts/toc.html b/portality/templates-v2/public/layouts/toc.html index 7935a3c204..089fc0a6f1 100644 --- a/portality/templates-v2/public/layouts/toc.html +++ b/portality/templates-v2/public/layouts/toc.html @@ -154,3 +154,12 @@

{% include "includes/_hotjar.html" %} {% endblock %} + +{%- block public_js -%} + + +{%- endblock -%} diff --git a/portality/templates-v2/public/toc_main.html b/portality/templates-v2/public/toc_main.html index 7cffdd330a..275ecd7d28 100644 --- a/portality/templates-v2/public/toc_main.html +++ b/portality/templates-v2/public/toc_main.html @@ -11,6 +11,7 @@ "Public Domain" : ["https://creativecommons.org/publicdomain/zero/1.0/", ["zero"]], } %} + {% include "public/includes/_login_modal.html" with context %}

About

@@ -301,6 +302,20 @@

Journal metadata

{{ bibjson.publisher_name }} {% if bibjson.publisher_country %}, {{ bibjson.publisher_country_name() }}{% endif %} + {% if not current_user.is_authenticated or current_user.id == journal.owner %} +
+ {% if not current_user.is_authenticated %} + Are you associated with this journal?
+ Help us keep your record accurate!
+ {% elif current_user.id == journal.owner %} + This is your journal!
+ Help us keep your record accurate.
+ {% endif %} + Click here to submit an update. +
+ {% endif %} + + {% endif %} {% if bibjson.institution_name %} @@ -352,4 +367,3 @@

Journal metadata

{% endblock %} - diff --git a/portality/ui/messages.py b/portality/ui/messages.py index c435552354..fd5db3727c 100644 --- a/portality/ui/messages.py +++ b/portality/ui/messages.py @@ -163,6 +163,8 @@ class Messages(object): DEFAULT_500_DESCRIPTION = "An error has occurred. It looks like something has gone wrong." + JOURNAL__YOU_ARE_NOT_OWNER = "You are not the owner of a journal you're trying to access. Here are the journals associated with your account." + @classmethod def flash(cls, tup): if isinstance(tup, tuple): diff --git a/portality/view/account.py b/portality/view/account.py index ae77c8acc3..1504993e8a 100644 --- a/portality/view/account.py +++ b/portality/view/account.py @@ -226,7 +226,6 @@ def login(): return render_template(templates.LOGIN_TO_APPLY, form=form) return render_template(templates.GLOBAL_LOGIN, form=form) - @blueprint.route('/forgot', methods=['GET', 'POST']) @ssl_required @write_required() diff --git a/portality/view/doaj.py b/portality/view/doaj.py index 3f757cc3fb..2d2737837d 100644 --- a/portality/view/doaj.py +++ b/portality/view/doaj.py @@ -10,19 +10,17 @@ from flask_login import current_user, login_required from portality.ui import exceptions as ui_exceptions -from portality.bll import exceptions, exceptions as bll_exceptions +from portality.bll import exceptions as bll_exceptions from portality import constants from portality import dao from portality import models -from portality import store from portality.bll import DOAJ from portality.core import app -from portality.decorators import ssl_required, api_key_required from portality.lcc import lcc_jstree from portality.lib import plausible from portality.ui.messages import Messages from portality.ui import templates -from portality.bll import DOAJ +from portality.view.account import LoginForm # ~~DOAJ:Blueprint~~ blueprint = Blueprint('doaj', __name__) @@ -325,14 +323,15 @@ def toc(identifier=None): if journal.is_in_doaj() is False: raise ui_exceptions.JournalWithdrawn() - # journal = find_toc_journal_by_identifier(identifier) bibjson = journal.bibjson() real_identifier = find_correct_redirect_identifier(identifier, bibjson) + current_info = {'next': url_for('publisher.update_request', journal_id=journal.id)} + form = LoginForm(request.form, csrf_enabled=False, **current_info) if real_identifier: - return redirect(url_for('doaj.toc', identifier=real_identifier), 301) + return redirect(url_for('doaj.toc', identifier=real_identifier, form=form), 301) else: # now render all that information - return render_template(templates.PUBLIC_TOC_MAIN, journal=journal, bibjson=bibjson, tab="main") + return render_template(templates.PUBLIC_TOC_MAIN, journal=journal, bibjson=bibjson, tab="main", form=form) @blueprint.route("/toc/articles/") diff --git a/portality/view/publisher.py b/portality/view/publisher.py index f5d23d062d..b19fbade80 100644 --- a/portality/view/publisher.py +++ b/portality/view/publisher.py @@ -100,6 +100,9 @@ def update_request(journal_id): if e.reason == AuthoriseException.WRONG_STATUS: journal, _ = journalService.journal(journal_id) return render_template(templates.PUBLISHER_APPLICATION_ALREADY_SUBMITTED, journal=journal) + elif e.reason == AuthoriseException.NOT_OWNER: + flash(Messages.JOURNAL__YOU_ARE_NOT_OWNER, "info") + return redirect(url_for("publisher.journals")) else: abort(404) except lock.Locked as e: