From 953443381648b516cea9f24329cdeb9c53a9faff Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Wed, 27 Nov 2024 11:43:58 -0500 Subject: [PATCH 01/11] STCOR-906 consume react-query QueryClient in (#1567) Correctly pass all three required arguments from `` to `loginServices::logout()`: gateway URL, redux store, react-query QueryClient. The third was missing, resulting in an NPE during `logout()` that caused flow to skip to the `finally` clause, skipping the code that cleared localforage. Sometimes localforage appears to get cleared anyway, but sometimes not, in which case the session may fail to terminate even when the fixed-length session timer reaches 00:00. This is one of those "How did this ever work?!? And how did we not notice?!?" situations. Refs [STCOR-906](https://folio-org.atlassian.net/browse/STCOR-906) --- CHANGELOG.md | 1 + src/components/Logout/Logout.js | 4 +++- src/components/Logout/Logout.test.js | 1 + src/loginServices.js | 4 +++- src/loginServices.test.js | 25 +++++++++++++++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f461b8a..c8de23b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Provide `` and `stripes.hasAnyPermission()`. Refs STCOR-910. * Use the `users-keycloak/_self` endpoint conditionally when the `users-keycloak` interface is present; otherwise, use `bl-users/_self` within `useUserTenantPermissions`. Refs STCOR-905. * Don't override initial discovery and okapi data in test mocks. Refs STCOR-913. +* `` must consume `QueryClient` in order to supply it to `loginServices::logout()`. Refs STCOR-907. ## [10.2.0](https://github.com/folio-org/stripes-core/tree/v10.2.0) (2024-10-11) [Full Changelog](https://github.com/folio-org/stripes-core/compare/v10.1.1...v10.2.0) diff --git a/src/components/Logout/Logout.js b/src/components/Logout/Logout.js index 1f3650ca8..3ba33c401 100644 --- a/src/components/Logout/Logout.js +++ b/src/components/Logout/Logout.js @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { useLocation } from 'react-router'; import { FormattedMessage } from 'react-intl'; +import { useQueryClient } from 'react-query'; import { branding } from 'stripes-config'; import { @@ -35,6 +36,7 @@ const Logout = () => { const stripes = useStripes(); const [didLogout, setDidLogout] = useState(false); const location = useLocation(); + const queryClient = useQueryClient(); const messageId = location.pathName === '/logout-timeout' ? 'stripes-core.rtr.idleSession.sessionExpiredSoSad' : 'stripes-core.logoutComplete'; @@ -42,7 +44,7 @@ const Logout = () => { () => { if (stripes.okapi.isAuthenticated) { // returns a promise, which we ignore - logout(stripes.okapi.url, stripes.store) + logout(stripes.okapi.url, stripes.store, queryClient) .then(setDidLogout(true)); } else { setDidLogout(true); diff --git a/src/components/Logout/Logout.test.js b/src/components/Logout/Logout.test.js index f887ee111..ae9ede3b5 100644 --- a/src/components/Logout/Logout.test.js +++ b/src/components/Logout/Logout.test.js @@ -9,6 +9,7 @@ import { getUnauthorizedPathFromSession, logout, setUnauthorizedPathToSession } jest.mock('../OrganizationLogo'); jest.mock('../../StripesContext'); jest.mock('react-router'); +jest.mock('react-query'); jest.mock('../../loginServices', () => ({ ...jest.requireActual('../../loginServices'), diff --git a/src/loginServices.js b/src/loginServices.js index 9216bedb2..132c3f8a0 100644 --- a/src/loginServices.js +++ b/src/loginServices.js @@ -532,7 +532,9 @@ export async function logout(okapiUrl, store, queryClient) { store.dispatch(resetStore()); // clear react-query cache - queryClient.removeQueries(); + if (queryClient) { + queryClient.removeQueries(); + } }) // clear shared storage .then(localforage.removeItem(SESSION_NAME)) diff --git a/src/loginServices.test.js b/src/loginServices.test.js index 16163936c..e05342b5c 100644 --- a/src/loginServices.test.js +++ b/src/loginServices.test.js @@ -564,6 +564,31 @@ describe('logout', () => { expect(global.fetch).not.toHaveBeenCalled(); }); }); + + describe('react-query client', () => { + afterEach(() => { + mockFetchCleanUp(); + }); + + it('calls removeQueries given valid client', async () => { + global.fetch = jest.fn().mockImplementation(() => Promise.resolve()); + const store = { + dispatch: jest.fn(), + }; + const rqc = { + removeQueries: jest.fn(), + }; + + let res; + await logout('', store, rqc) + .then(() => { + res = true; + }); + + expect(res).toBe(true); + expect(rqc.removeQueries).toHaveBeenCalled(); + }); + }); }); describe('getLocale', () => { From 1189bd5d722b3471b2484ce59fbd7eb9af090c53 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Sat, 30 Nov 2024 13:17:54 -0500 Subject: [PATCH 02/11] Lokalise: Translations update (#1569) This pull request was initiated by Lokalise (user Peter Murray) at 2024-11-30 18:54:38 --- translations/stripes-core/ar.json | 4 +++- translations/stripes-core/ber.json | 4 +++- translations/stripes-core/ca.json | 4 +++- translations/stripes-core/cs_CZ.json | 4 +++- translations/stripes-core/da.json | 4 +++- translations/stripes-core/de.json | 6 ++++-- translations/stripes-core/en_GB.json | 4 +++- translations/stripes-core/en_SE.json | 20 +++++++++++--------- translations/stripes-core/en_US.json | 3 +-- translations/stripes-core/es.json | 4 +++- translations/stripes-core/es_419.json | 4 +++- translations/stripes-core/es_ES.json | 4 +++- translations/stripes-core/fr.json | 4 +++- translations/stripes-core/fr_FR.json | 4 +++- translations/stripes-core/he.json | 4 +++- translations/stripes-core/hi_IN.json | 4 +++- translations/stripes-core/hu.json | 4 +++- translations/stripes-core/it_IT.json | 4 +++- translations/stripes-core/ja.json | 4 +++- translations/stripes-core/ko.json | 4 +++- translations/stripes-core/nb.json | 4 +++- translations/stripes-core/nl.json | 4 +++- translations/stripes-core/nn.json | 4 +++- translations/stripes-core/pl.json | 4 +++- translations/stripes-core/pt_BR.json | 4 +++- translations/stripes-core/pt_PT.json | 4 +++- translations/stripes-core/ru.json | 4 +++- translations/stripes-core/sk.json | 4 +++- translations/stripes-core/sv.json | 6 ++++-- translations/stripes-core/ur.json | 4 +++- translations/stripes-core/zh_CN.json | 4 +++- translations/stripes-core/zh_TW.json | 4 +++- 32 files changed, 104 insertions(+), 43 deletions(-) diff --git a/translations/stripes-core/ar.json b/translations/stripes-core/ar.json index 436e7cbb1..a2d510c07 100644 --- a/translations/stripes-core/ar.json +++ b/translations/stripes-core/ar.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ber.json b/translations/stripes-core/ber.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/ber.json +++ b/translations/stripes-core/ber.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ca.json b/translations/stripes-core/ca.json index a9e11baed..680f90a92 100644 --- a/translations/stripes-core/ca.json +++ b/translations/stripes-core/ca.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/cs_CZ.json b/translations/stripes-core/cs_CZ.json index db71a933c..e3f75cdcd 100644 --- a/translations/stripes-core/cs_CZ.json +++ b/translations/stripes-core/cs_CZ.json @@ -158,5 +158,7 @@ "tenantChoose": "Vybrat nájemce/knihovnu", "tenantLibrary": "Nájemce/Knihovna", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Vaše relace brzy skončí! Zbývající čas:" + "rtr.fixedLengthSession.timeRemaining": "Vaše relace brzy skončí! Zbývající čas:", + "logoutComplete": "Odhlásili jste se.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/da.json b/translations/stripes-core/da.json index b15ff3a45..21ebe2f55 100644 --- a/translations/stripes-core/da.json +++ b/translations/stripes-core/da.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/de.json b/translations/stripes-core/de.json index 4fa2a2bda..e48fc04e8 100644 --- a/translations/stripes-core/de.json +++ b/translations/stripes-core/de.json @@ -88,7 +88,7 @@ "mainnav.topLevelLabel": "Primär", "mainnav.applicationListLabel": "App-Liste", "errors.default.error": "Leider stimmen die Angaben nicht mit unserem Datenbestand überein.", - "errors.username.incorrect": "Dieses FOLIO-Konto kann nicht gefunden werden. Bitte wenden Sie sich an Ihre Folio-Systemadministration.", + "errors.username.incorrect": "Dieses FOLIO-Konto kann nicht gefunden werden. Bitte wenden Sie sich an Ihre FOLIO-Systemadministration.", "errors.password.incorrect": "Anmeldeinformationen stimmen nicht", "errors.user.blocked": "Aus Sicherheitsgründen wurde Ihr Konto gesperrt. Bitte versuchen Sie es erneut oder wenden Sie sich an Ihre FOLIO-Systemadministration.", "errors.password.match.error": "Passwort stimmt nicht überein. Passwort erneut eingeben.", @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/en_GB.json b/translations/stripes-core/en_GB.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/en_GB.json +++ b/translations/stripes-core/en_GB.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/en_SE.json b/translations/stripes-core/en_SE.json index 9f01fdbf4..29f79a8b6 100644 --- a/translations/stripes-core/en_SE.json +++ b/translations/stripes-core/en_SE.json @@ -1,13 +1,13 @@ { - "front.welcome": "Välkommen till FOLIO!", - "front.home": "Start", + "front.welcome": "Welcome, the Future Of Libraries Is OPEN!", + "front.home": "Home", "front.about": "Software versions", - "button.new": "+ Ny", - "button.new_tooltip": "Lägg till {entry}", - "button.edit": "Redigera", - "button.delete": "Radera", - "button.deleteEntry": "Radera {entry}", - "button.saveAndClose": "Spara och stäng", + "button.new": "+ New", + "button.new_tooltip": "Add {entry}", + "button.edit": "Edit", + "button.delete": "Delete", + "button.deleteEntry": "Delete {entry}", + "button.saveAndClose": "Save and close", "button.cancel": "Cancel", "label.confirmDeleteEntry": "{name} will be removed", "label.editEntry": "Edit {entry}", @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/en_US.json b/translations/stripes-core/en_US.json index f49fa0f0f..29f79a8b6 100644 --- a/translations/stripes-core/en_US.json +++ b/translations/stripes-core/en_US.json @@ -161,5 +161,4 @@ "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", "logoutComplete": "You have logged out.", "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." - -} +} \ No newline at end of file diff --git a/translations/stripes-core/es.json b/translations/stripes-core/es.json index 70ada74ba..3fe8201b7 100644 --- a/translations/stripes-core/es.json +++ b/translations/stripes-core/es.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/es_419.json b/translations/stripes-core/es_419.json index 70ada74ba..3fe8201b7 100644 --- a/translations/stripes-core/es_419.json +++ b/translations/stripes-core/es_419.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/es_ES.json b/translations/stripes-core/es_ES.json index 70ada74ba..3fe8201b7 100644 --- a/translations/stripes-core/es_ES.json +++ b/translations/stripes-core/es_ES.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/fr.json b/translations/stripes-core/fr.json index b8c7fffe2..9ba2098e0 100644 --- a/translations/stripes-core/fr.json +++ b/translations/stripes-core/fr.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/fr_FR.json b/translations/stripes-core/fr_FR.json index cc7e91f0c..44d76e700 100644 --- a/translations/stripes-core/fr_FR.json +++ b/translations/stripes-core/fr_FR.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "Vous vous êtes déconnecté.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/he.json b/translations/stripes-core/he.json index f919a66cc..da72e1cc1 100644 --- a/translations/stripes-core/he.json +++ b/translations/stripes-core/he.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/hi_IN.json b/translations/stripes-core/hi_IN.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/hi_IN.json +++ b/translations/stripes-core/hi_IN.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/hu.json b/translations/stripes-core/hu.json index bd489afe1..c5f86493e 100644 --- a/translations/stripes-core/hu.json +++ b/translations/stripes-core/hu.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/it_IT.json b/translations/stripes-core/it_IT.json index 978bfb128..1296a2547 100644 --- a/translations/stripes-core/it_IT.json +++ b/translations/stripes-core/it_IT.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ja.json b/translations/stripes-core/ja.json index fd719b594..62674c9f5 100644 --- a/translations/stripes-core/ja.json +++ b/translations/stripes-core/ja.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "セッションはまもなく終了します。残時間:" + "rtr.fixedLengthSession.timeRemaining": "セッションはまもなく終了します。残時間:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ko.json b/translations/stripes-core/ko.json index 4e8854b2a..5de86ce04 100644 --- a/translations/stripes-core/ko.json +++ b/translations/stripes-core/ko.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/nb.json b/translations/stripes-core/nb.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/nb.json +++ b/translations/stripes-core/nb.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/nl.json b/translations/stripes-core/nl.json index f86713ef1..b3bae7e86 100644 --- a/translations/stripes-core/nl.json +++ b/translations/stripes-core/nl.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/nn.json b/translations/stripes-core/nn.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/nn.json +++ b/translations/stripes-core/nn.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/pl.json b/translations/stripes-core/pl.json index 8584cf659..e5a3c8576 100644 --- a/translations/stripes-core/pl.json +++ b/translations/stripes-core/pl.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/pt_BR.json b/translations/stripes-core/pt_BR.json index a19720ce3..5eae408a5 100644 --- a/translations/stripes-core/pt_BR.json +++ b/translations/stripes-core/pt_BR.json @@ -158,5 +158,7 @@ "tenantChoose": "Selecione seu locatário/biblioteca", "tenantLibrary": "Locatário/Biblioteca", "errors.saml.missingToken": "Nenhum parâmetro de consulta code .", - "rtr.fixedLengthSession.timeRemaining": "Sua sessão terminará em breve! Tempo restante:" + "rtr.fixedLengthSession.timeRemaining": "Sua sessão terminará em breve! Tempo restante:", + "logoutComplete": "Você efetuou logout.", + "errors.oidc": "Erro: o servidor está proibido, inacessível ou indisponível." } \ No newline at end of file diff --git a/translations/stripes-core/pt_PT.json b/translations/stripes-core/pt_PT.json index bcd753d21..2550529ce 100644 --- a/translations/stripes-core/pt_PT.json +++ b/translations/stripes-core/pt_PT.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ru.json b/translations/stripes-core/ru.json index b14c1bf7a..8b98ba32a 100644 --- a/translations/stripes-core/ru.json +++ b/translations/stripes-core/ru.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/sk.json b/translations/stripes-core/sk.json index 8ab8d4d50..d7c56f250 100644 --- a/translations/stripes-core/sk.json +++ b/translations/stripes-core/sk.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/sv.json b/translations/stripes-core/sv.json index 996b8361a..95e8540c9 100644 --- a/translations/stripes-core/sv.json +++ b/translations/stripes-core/sv.json @@ -11,7 +11,7 @@ "button.cancel": "Ångra", "label.confirmDeleteEntry": "{name} will be removed", "label.editEntry": "Redigera {entry}", - "label.createEntry": "Create {entry}", + "label.createEntry": "Skapa {entry}", "label.missingRequiredField": "Please fill this in to continue", "label.okay": "Okej", "successfullyDeleted": "The {entry} {name} was successfully deleted.", @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/ur.json b/translations/stripes-core/ur.json index 1bfbcd716..29f79a8b6 100644 --- a/translations/stripes-core/ur.json +++ b/translations/stripes-core/ur.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/zh_CN.json b/translations/stripes-core/zh_CN.json index 576f2bb99..ec6d3354b 100644 --- a/translations/stripes-core/zh_CN.json +++ b/translations/stripes-core/zh_CN.json @@ -158,5 +158,7 @@ "tenantChoose": "选择租户/图书馆", "tenantLibrary": "租户/图书馆", "errors.saml.missingToken": "无 代码查询参数。", - "rtr.fixedLengthSession.timeRemaining": "您的会话即将结束!剩余时间:" + "rtr.fixedLengthSession.timeRemaining": "您的会话即将结束!剩余时间:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file diff --git a/translations/stripes-core/zh_TW.json b/translations/stripes-core/zh_TW.json index 4b2ed6887..c53dddb71 100644 --- a/translations/stripes-core/zh_TW.json +++ b/translations/stripes-core/zh_TW.json @@ -158,5 +158,7 @@ "tenantChoose": "Select your tenant/library", "tenantLibrary": "Tenant/Library", "errors.saml.missingToken": "No code query parameter.", - "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:" + "rtr.fixedLengthSession.timeRemaining": "Your session will end soon! Time remaining:", + "logoutComplete": "You have logged out.", + "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." } \ No newline at end of file From 48684560f6f05a92e2f29ee56086a1da390307ab Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Mon, 2 Dec 2024 13:42:12 -0500 Subject: [PATCH 03/11] STCOR-912 spread session, _self data when resuming session (#1568) Spread together existing session data with the response data from the `_self` endpoint. The latter may contain updated information such as new permissions, but it may also be sparsely populated, e.g. if the user has not selected a default service point but the active service point has been saved to the session. We take the union of the data, with the `_self` data overwriting matching values. Think of it like a bitwise-or. Refs [STCOR-912](https://folio-org.atlassian.net/browse/STCOR-912) --- CHANGELOG.md | 1 + src/loginServices.js | 5 ++++- src/loginServices.test.js | 8 ++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8de23b99..6032c8b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Use the `users-keycloak/_self` endpoint conditionally when the `users-keycloak` interface is present; otherwise, use `bl-users/_self` within `useUserTenantPermissions`. Refs STCOR-905. * Don't override initial discovery and okapi data in test mocks. Refs STCOR-913. * `` must consume `QueryClient` in order to supply it to `loginServices::logout()`. Refs STCOR-907. +* On resuming session, spread session and `_self` together to preserve session values. Refs STCOR-912. ## [10.2.0](https://github.com/folio-org/stripes-core/tree/v10.2.0) (2024-10-11) [Full Changelog](https://github.com/folio-org/stripes-core/compare/v10.1.1...v10.2.0) diff --git a/src/loginServices.js b/src/loginServices.js index 132c3f8a0..5a38dc465 100644 --- a/src/loginServices.js +++ b/src/loginServices.js @@ -809,7 +809,10 @@ export function validateUser(okapiUrl, store, tenant, session) { // data isn't provided by _self. store.dispatch(setSessionData({ isAuthenticated: true, - user, + // spread data from the previous session (which may include session-specific + // values such as the current service point), and the restructured user object + // (which includes permissions in a lookup-friendly way) + user: { ...session.user, ...user }, perms, tenant: sessionTenant, token, diff --git a/src/loginServices.test.js b/src/loginServices.test.js index e05342b5c..da048d851 100644 --- a/src/loginServices.test.js +++ b/src/loginServices.test.js @@ -350,7 +350,11 @@ describe('validateUser', () => { }; const session = { - user: { id: 'id', username: 'username' }, + user: { + id: 'id', + username: 'username', + storageOnlyValue: 'is still persisted', + }, perms: { foo: true }, tenant: sessionTenant, token: 'token', @@ -361,7 +365,7 @@ describe('validateUser', () => { await validateUser('url', store, tenant, session); const updatedSession = { - user: data.user, + user: { ...session.user, ...data.user }, isAuthenticated: true, perms: { ask: true, tell: true }, tenant: session.tenant, From d01bb489311b5e9b350faea310b2afb9c8fb3f44 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Thu, 5 Dec 2024 10:35:43 -0500 Subject: [PATCH 04/11] test ResetPasswordNotAvailableScreen (#1573) --- .../ResetPasswordNotAvailableScreen.test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/components/ResetPasswordNotAvailableScreen/ResetPasswordNotAvailableScreen.test.js diff --git a/src/components/ResetPasswordNotAvailableScreen/ResetPasswordNotAvailableScreen.test.js b/src/components/ResetPasswordNotAvailableScreen/ResetPasswordNotAvailableScreen.test.js new file mode 100644 index 000000000..fd2211747 --- /dev/null +++ b/src/components/ResetPasswordNotAvailableScreen/ResetPasswordNotAvailableScreen.test.js @@ -0,0 +1,14 @@ +import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; + +import BadRequestScreen from './ResetPasswordNotAvailableScreen'; + +jest.mock('../../Pluggable', () => (props) => props.children); + +describe('ResetPasswordNotAvailableScreen', () => { + it('renders expected message', () => { + render(); + + screen.getByText('stripes-core.front.error.header'); + screen.getByText('stripes-core.front.error.setPassword.message'); + }); +}); From a99341cdc7fa573825f8b0848ace31173e9eaeb2 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Thu, 5 Dec 2024 11:00:02 -0500 Subject: [PATCH 05/11] test BadRequestScreen (#1572) --- .../BadRequestScreen/BadRequestScreen.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/components/BadRequestScreen/BadRequestScreen.test.js diff --git a/src/components/BadRequestScreen/BadRequestScreen.test.js b/src/components/BadRequestScreen/BadRequestScreen.test.js new file mode 100644 index 000000000..9fa418b16 --- /dev/null +++ b/src/components/BadRequestScreen/BadRequestScreen.test.js @@ -0,0 +1,16 @@ +import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; + +import BadRequestScreen from './BadRequestScreen'; + +jest.mock('../../Pluggable', () => (props) => props.children); + +describe('BadRequestScreen', () => { + it('renders expected message', () => { + render(); + + screen.getByText('stripes-core.front.error.header'); + screen.getByText(/stripes-core.front.error.general.message/); + }); +}); + + From bb49c800f1e0645847f41fe49739807c7608bc5c Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Thu, 5 Dec 2024 16:25:01 -0500 Subject: [PATCH 06/11] test IfInterface (#1574) --- src/components/IfInterface/IfInterface.js | 20 ++++++----- .../IfInterface/IfInterface.test.js | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/components/IfInterface/IfInterface.test.js diff --git a/src/components/IfInterface/IfInterface.js b/src/components/IfInterface/IfInterface.js index 78edb9435..cd7f9acdc 100644 --- a/src/components/IfInterface/IfInterface.js +++ b/src/components/IfInterface/IfInterface.js @@ -1,14 +1,16 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import { StripesContext } from '../../StripesContext'; +import { useStripes } from '../../StripesContext'; -const IfInterface = ({ children, name, version }) => ( - - {stripes => ( - stripes.hasInterface(name, version) ? children : null - )} - -); +const IfInterface = ({ children, name, version }) => { + const stripes = useStripes(); + const hasInterface = stripes.hasInterface(name, version); + + if (typeof children === 'function') { + return children({ hasInterface }); + } + + return hasInterface ? children : null; +}; IfInterface.propTypes = { children: PropTypes.node, diff --git a/src/components/IfInterface/IfInterface.test.js b/src/components/IfInterface/IfInterface.test.js new file mode 100644 index 000000000..430df9222 --- /dev/null +++ b/src/components/IfInterface/IfInterface.test.js @@ -0,0 +1,33 @@ +import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; + +import { useStripes } from '../../StripesContext'; +import Stripes from '../../Stripes'; +import IfInterface from './IfInterface'; + +jest.mock('../../StripesContext'); +const stripes = new Stripes({ + discovery: { + interfaces: { + foo: '1.0' + } + }, + logger: { + log: jest.fn(), + } +}); + +// IfInterface is just a component version of Stripes::hasInterface +// See more extensive tests there. +describe('IfInterface', () => { + it('returns true if interface is present', () => { + useStripes.mockReturnValue(stripes); + render(monkey); + expect(screen.queryByText(/monkey/)).toBeTruthy(); + }); + + it('returns false if interface is absent', () => { + useStripes.mockReturnValue(stripes); + render(monkey); + expect(screen.queryByText(/monkey/)).toBeFalsy(); + }); +}); From 85a6b578ad47c65320e76f85578eced38b8885c9 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Fri, 6 Dec 2024 08:07:13 -0500 Subject: [PATCH 07/11] STCOR-921 typo: add missing whitespace (#1575) Refs STCOR-921 --- .../SessionEventContainer/FixedLengthSessionWarning.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SessionEventContainer/FixedLengthSessionWarning.js b/src/components/SessionEventContainer/FixedLengthSessionWarning.js index 8871979e4..df6097add 100644 --- a/src/components/SessionEventContainer/FixedLengthSessionWarning.js +++ b/src/components/SessionEventContainer/FixedLengthSessionWarning.js @@ -49,7 +49,7 @@ const FixedLengthSessionWarning = () => { return '00:00'; }; - return {timestampFormatter()}; + return {timestampFormatter()}; }; export default FixedLengthSessionWarning; From c08b60de8812bc453ca9f6a0471c02cbae74e750 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Fri, 6 Dec 2024 10:12:29 -0500 Subject: [PATCH 08/11] test SSOLogin (#1571) --- src/components/SSOLogin/SSOLogin.test.js | 21 ++++++++ .../SSOLogin/tests/SSOLogin-test.js | 51 ------------------- 2 files changed, 21 insertions(+), 51 deletions(-) create mode 100644 src/components/SSOLogin/SSOLogin.test.js delete mode 100644 src/components/SSOLogin/tests/SSOLogin-test.js diff --git a/src/components/SSOLogin/SSOLogin.test.js b/src/components/SSOLogin/SSOLogin.test.js new file mode 100644 index 000000000..6e620991f --- /dev/null +++ b/src/components/SSOLogin/SSOLogin.test.js @@ -0,0 +1,21 @@ +import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; +import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; + +import SSOLogin from './SSOLogin'; + +describe('Login via SSO', () => { + it('renders a button', () => { + const fx = jest.fn(); + render(); + screen.getByText('stripes-core.loginViaSSO'); + }); + + it('calls the callback', async () => { + const fx = jest.fn(); + render(); + await userEvent.click(screen.getByText('stripes-core.loginViaSSO')); + + expect(fx).toHaveBeenCalled(); + }); +}); + diff --git a/src/components/SSOLogin/tests/SSOLogin-test.js b/src/components/SSOLogin/tests/SSOLogin-test.js deleted file mode 100644 index ef705fb28..000000000 --- a/src/components/SSOLogin/tests/SSOLogin-test.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import { beforeEach, it, describe } from 'mocha'; -import ButtonInteractor from '@folio/stripes-components/lib/Button/tests/interactor'; - -import Harness from '../../../../test/bigtest/helpers/Harness'; -import { mount } from '../../../../test/bigtest/helpers/render-helpers'; -import SSOLogin from '..'; -import translations from '../../../../translations/stripes-core/en'; - -// These are SSO component-based tests. There are also route-based tests -// at /test/bigtest/tests/sso-login-test.js. -// -// BigTest OG first runs tests in /src (which are all component-based tests -// and therefore only call mount) and then the tests in /tests (which are all -// route-based tests and therefore call setupApplication or one of its -// derivatives). The two types don't play nice together because of they way -// the do (or don't) clean up their mount points after running. -describe('Login via SSO', () => { - describe('SSO form presentation', () => { - const ssoLoginButton = new ButtonInteractor('[data-test-sso-login-button]'); - let clicked = false; - - beforeEach(async () => { - await mount( - - { clicked = true; }} /> - - ); - }); - - it('button should be rendered', () => { - expect(ssoLoginButton.isPresent).to.be.true; - expect(ssoLoginButton.isButton).to.be.true; - }); - - it('text should be rendered as label of button', () => { - expect(ssoLoginButton.text).to.equal(translations.loginViaSSO); - }); - - describe('clicking the SSO Login button', () => { - beforeEach(async () => { - await ssoLoginButton.click(); - }); - - it('should trigger handleSSOLogin callback', () => { - expect(clicked).to.be.true; - }); - }); - }); -}); From 8839a9941c3298b6bba5176e0ae103a2c8f65e19 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:20:48 -0500 Subject: [PATCH 09/11] Update translation strings This pull request was initiated by Lokalise (user Peter Murray) at 2024-12-13 23:51:31 --- translations/stripes-core/zh_CN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translations/stripes-core/zh_CN.json b/translations/stripes-core/zh_CN.json index ec6d3354b..90e679edc 100644 --- a/translations/stripes-core/zh_CN.json +++ b/translations/stripes-core/zh_CN.json @@ -159,6 +159,6 @@ "tenantLibrary": "租户/图书馆", "errors.saml.missingToken": "无 代码查询参数。", "rtr.fixedLengthSession.timeRemaining": "您的会话即将结束!剩余时间:", - "logoutComplete": "You have logged out.", - "errors.oidc": "Error: server is forbidden, unreachable, or unavailable." + "logoutComplete": "您已登出", + "errors.oidc": "错误:服务器被禁止、无法连接或不可用。" } \ No newline at end of file From 93297f688c6340623cc8019ee3cd0e0c28a756bc Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Mon, 16 Dec 2024 08:40:28 -0500 Subject: [PATCH 10/11] STCOR-924 implement CODEOWNERS (#1576) Refs STCOR-924 --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..2c33a6190 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @folio-org/stripes-force + From f7d2b36428bc80ae728d11e5315e2cf83d93b452 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:36:39 -0500 Subject: [PATCH 11/11] Update translation strings This pull request was initiated by Lokalise (user Peter Murray) at 2024-12-28 00:07:33 --- translations/stripes-core/zh_TW.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/translations/stripes-core/zh_TW.json b/translations/stripes-core/zh_TW.json index c53dddb71..45dddb494 100644 --- a/translations/stripes-core/zh_TW.json +++ b/translations/stripes-core/zh_TW.json @@ -84,7 +84,7 @@ "settingPassword": "正在設定密碼...", "mainnav.showAllApplicationsButtonLabel": "應用程式", "mainnav.showAllApplicationsButtonAriaLabel": "顯示所有應用程式", - "mainnav.currentAppAriaLabel": "目前開啟的應用程式: {appName} (點擊返回)", + "mainnav.currentAppAriaLabel": "目前開啟的應用程式 : {appName} ( 點擊返回 )", "mainnav.topLevelLabel": "主要", "mainnav.applicationListLabel": "應用程式清單", "errors.default.error": "抱歉,輸入的資訊與我們的紀錄不符。", @@ -122,7 +122,7 @@ "errors.password.whiteSpace.invalid": "密碼不能含有空格。", "mainnav.myProfileAriaLabel": "{tenantName} {servicePointName}設定檔", "mainnav.skipMainNavigation": "跳過主導覽", - "errors.user.timeout": "連線已過期。請再次登入以恢復連線。", + "errors.user.timeout": "工作階段已過期。請再次登入以恢復工作階段。", "errors.password.consecutiveWhitespaces.invalid": "密碼不得包含連續的空格字元。", "about.missingModuleCount": "{count, number}缺少{count, plural, one {介面} other {介面}}} }}", "about.incompatibleModuleCount": "{count, number}不相容於介面{count, plural, one { version } other { versions }}", @@ -130,16 +130,16 @@ "routeErrorBoundary.goToAppHomeLabel": "返回首頁", "routeErrorBoundary.goToModuleHomeLabel": "返回{name}登入頁", "routeErrorBoundary.goToModuleSettingsHomeLabel": "返回{name}設定", - "logoutKeepSso": "從 FOLIO 登出,保持 SSO 連線", + "logoutKeepSso": "從 FOLIO 登出,保持 SSO 連線工作階段", "mainnav.profileDropdown.locale": "語言環境", "mainnav.profileDropdown.permissions": "權限", "front.error.header": "錯誤 404", "errors.password.compromised.invalid": "密碼不得為常用、被預測或被外洩的密碼", "createResetPassword.ruleTemplate": "必須{description}", "front.error.general.message": "在此伺服器上找不到請求的 URL {br}{url}{br} 。", - "front.error.setPassword.message": "請登出目前的 FOLIO 連線以設定密碼。登出後,請嘗試使用此連結再次設定您的密碼。", + "front.error.setPassword.message": "請登出目前的 FOLIO 工作階段以設定密碼。登出後,請嘗試使用此連結再次設定您的密碼。", "title.noPermission": "沒有權限", - "front.error.noPermission": "您無權查看此應用程式/記錄", + "front.error.noPermission": "您無權查看此應用程式 / 紀錄", "button.duplicate": "複製", "stale.warning": "伺服器上的應用程式已更改,需要重新整理。", "stale.reload": "點擊此處重新載入。", @@ -151,7 +151,7 @@ "rtr.idleSession.timeRemaining": "Time remaining", "rtr.idleSession.keepWorking": "Keep working", "rtr.idleSession.sessionExpiredSoSad": "Your session expired due to inactivity.", - "rtr.idleSession.logInAgain": "Log in again", + "rtr.idleSession.logInAgain": "再次登入", "title.logout": "Log out", "about.applicationCount": "{count} applications", "about.applicationsVersionsTitle": "Applications/modules/interfaces",