diff --git a/docs/source/configuration/how-to.md b/docs/source/configuration/how-to.md index 5d4eafd897..a1023c1837 100644 --- a/docs/source/configuration/how-to.md +++ b/docs/source/configuration/how-to.md @@ -20,7 +20,7 @@ then access any of its internal configuration to retrieve the configuration you like: ```js -const absoluteUrl = `${config.settings.apiPath}/${content.url}` +const absoluteUrl = `${config.settings.apiPath}/${content.url}`; ``` Both the main project and individual add-ons can extend Volto's configuration registry. @@ -150,3 +150,31 @@ export default function applyConfig(config) { return config; } +``` + +## `nonContentRoutes` and `nonContentRoutesPublic` + +`nonContentRoutes` is a list of routes reserved in Volto for its functionality as a content management system. +These functions include user authentication and registration, changing settings through control panels, generating a site map, and other functions. +Examples of these routes include `/login`, `/register`, and `/\/controlpanel\/.*$/`. +Editors can't use them for content. +The HTML attribute class value `cms-ui` is applied to members of `nonContentRoutes`. +You can configure `nonContentRoutes` with either a regular expression or a string representing the end of the URI. + +`nonContentRoutesPublic` is a subset of `nonContentRoutes`. +These routes are used for public sections of a Volto site that do not require authentication. +This subset includes `/login`, `/search`, and `/sitemap`. + +The following example shows how to configure settings for `nonContentRoutes` and `nonContentRoutesPublic`. + +```js +export default function applyConfig(config) { + config.settings = { + ...config.settings, + nonContentRoutes:[....], + nonContentRoutesPublic: [....] + }; + + return config; +} +``` diff --git a/packages/volto/news/6173.feature b/packages/volto/news/6173.feature new file mode 100644 index 0000000000..1d887eae5d --- /dev/null +++ b/packages/volto/news/6173.feature @@ -0,0 +1 @@ +Added `config.settings.nonContentRoutesPublic` to avoid `isCmsUi` issues in these public routes. @giuliaghisini diff --git a/packages/volto/src/config/NonContentRoutesPublic.jsx b/packages/volto/src/config/NonContentRoutesPublic.jsx new file mode 100644 index 0000000000..30583c15f3 --- /dev/null +++ b/packages/volto/src/config/NonContentRoutesPublic.jsx @@ -0,0 +1,15 @@ +// PublicUi routes that are nonContentRoutes, and should not be members of isCmsUi +// You can include either RegEx or a string representing the ending of the +// nonContentRoute. For example, '/add' will match '/foo/bar/add'. +export const nonContentRoutesPublic = [ + '/login', + '/logout', + '/sitemap', + '/register', + '/search', + '/change-password', + '/contact-form', + '/register', + /\/passwordreset\/.*$/, + '/passwordreset', +]; diff --git a/packages/volto/src/config/index.js b/packages/volto/src/config/index.js index 9933a3c32b..aff8393606 100644 --- a/packages/volto/src/config/index.js +++ b/packages/volto/src/config/index.js @@ -8,6 +8,7 @@ import { layoutViewsNamesMapping, } from './Views'; import { nonContentRoutes } from './NonContentRoutes'; +import { nonContentRoutesPublic } from './NonContentRoutesPublic'; import { groupBlocksOrder, requiredBlocks, @@ -111,6 +112,7 @@ let config = { legacyTraverse: process.env.RAZZLE_LEGACY_TRAVERSE || false, cookieExpires: 15552000, //in seconds. Default is 6 month (15552000) nonContentRoutes, + nonContentRoutesPublic, imageObjects: ['Image'], reservedIds: ['login', 'layout', 'plone', 'zip', 'properties'], downloadableObjects: ['File'], //list of content-types for which the direct download of the file will be carried out if the user is not authenticated diff --git a/packages/volto/src/helpers/Url/Url.js b/packages/volto/src/helpers/Url/Url.js index fe58098824..889d2c04a2 100644 --- a/packages/volto/src/helpers/Url/Url.js +++ b/packages/volto/src/helpers/Url/Url.js @@ -139,7 +139,10 @@ export const isCmsUi = memoize((currentPathname) => { // because the regexp test does not take that into account // https://github.com/plone/volto/issues/870 return settings.nonContentRoutes.reduce( - (acc, route) => acc || new RegExp(route).test(`/${fullPath}`), + (acc, route) => + acc || + (!settings.nonContentRoutesPublic?.includes(route) && + new RegExp(route).test(fullPath)), false, ); }); diff --git a/packages/volto/src/helpers/Url/Url.test.js b/packages/volto/src/helpers/Url/Url.test.js index b85ef9fc3a..d5ca89fb3f 100644 --- a/packages/volto/src/helpers/Url/Url.test.js +++ b/packages/volto/src/helpers/Url/Url.test.js @@ -151,15 +151,29 @@ describe('Url', () => { describe('isCmsUi', () => { [...settings.nonContentRoutes, '/controlpanel/mypanel'].forEach((route) => { if (typeof route === 'string') { - it(`matches non-content-route ${route}`, () => { - expect(isCmsUi(`/mycontent/${route}`)).toBe(true); - }); + if (settings.nonContentRoutesPublic.includes(route)) { + it(`matches non-content-route-public ${route}`, () => { + expect(isCmsUi(route)).toBe(false); + }); + } else { + it(`matches non-content-route ${route}`, () => { + expect(isCmsUi(`/mycontent/${route}`)).toBe(true); + }); + } } }); it('returns false on non-cms-ui views', () => { expect(isCmsUi('/mycontent')).toBe(false); }); + + it('returns true on non content routes', () => { + expect(isCmsUi('/mycontent/historyview')).toBe(true); + }); + + it('returns false on public non content routes', () => { + expect(isCmsUi('/mycontent/login')).toBe(false); + }); }); describe('flattenHTMLToAppURL', () => { diff --git a/packages/volto/test-setup-config.jsx b/packages/volto/test-setup-config.jsx index b42bd47dc4..7d12bf40a6 100644 --- a/packages/volto/test-setup-config.jsx +++ b/packages/volto/test-setup-config.jsx @@ -10,6 +10,7 @@ import React from 'react'; import config from '@plone/volto/registry'; import { loadables } from '@plone/volto/config/Loadables'; import { nonContentRoutes } from '@plone/volto/config/NonContentRoutes'; +import { nonContentRoutesPublic } from '@plone/volto/config/NonContentRoutesPublic'; import { contentIcons } from '@plone/volto/config/ContentIcons'; import { styleClassNameConverters, @@ -33,6 +34,7 @@ config.set('settings', { defaultPageSize: 25, isMultilingual: false, nonContentRoutes, + nonContentRoutesPublic, contentIcons: contentIcons, loadables, lazyBundles: {