Relative (prefixed root) path
Based on previous effort from cekk, giulia and all. I'm trying to go at this from a clean state, adding things step by step so that I understand them. I'll provide proper attributions at the end.
Deploy Preview for volto ready!
| Name | Link |
|---|---|
| Latest commit | 2f9f9e63b4c6c33e5fbf55a332bb982678fb4543 |
| Latest deploy log | https://app.netlify.com/sites/volto/deploys/638f0e078deef100096754ce |
| Deploy Preview | https://deploy-preview-3190--volto.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site settings.
@stevepiercy Any idea how to avoid that documentation validation check? I'm using example.com as an example, I've even tried not making it a link.
@stevepiercy Any idea how to avoid that documentation validation check? I'm using example.com as an example, I've even tried not making it a link.
There are two ways to do this.
-
Add this to
docs/conf.pyunderlinkcheck_ignore.r"http://example.com",That regex will ignore any URL that contains that pattern. I will add it to the main documentation as well.
-
Any URL in an inline literal or code-block are ignored. In this case, the URL is surrounded by
**which merely formats it as bold text. I made a suggestion during my review that converts it to an inline literal.
In this specific case, I think we should do option 2 because that will allow us to catch improper formatting of the URL example.com.
@tiberiuichim also, what about Sitemap?
cool! i'm testing it and will get back with some feedbacks soon
@sneridagh sitemap, indeed. I took care of it. No need for traefik or some other thing, I was thinking to add a CI workflow dedicated to the prefixed path feature.
@stevepiercy
Actually, we should move this entry into the toctree, following that syntax, then remove this list, delete the toctree option :hidden: true, and change to :maxdepth: 1.
~~I'm not sure what is needed here. I don't know where the toctree is. I'd be grateful if you could commit the required change. Thanks!~~ Edit: no longer needed, I've figured it out.
This list might have been an artifact from Mkdocs.
No, I've created the indexes on the initial "plone 6 Volto docs" PR, as I've noticed they were needed by sphinx.
Actually, we should move this entry into the toctree, following that syntax, then remove this list, delete the toctree option :hidden: true, and change to :maxdepth: 1.
~I'm not sure what is needed here. I don't know where the toctree is. I'd be grateful if you could commit the required change. Thanks!~ Edit: no longer needed, I've figured it out.
This is what I mean. Also the toctree can be simplified further.
---
html_meta:
"description": "Deploying Volto frontend"
"property=og:description": "Deploying Volto frontend"
"property=og:title": "Deploying"
"keywords": "Volto, Plone, frontend, React, deploying"
---
# Deploying
```{toctree}
:maxdepth: 1
simple
pm2
seamless-mode
apache
sentry
performance
prefixed-root
```
Another problem: in control-panels, save button works fine but clicking the X, does not brings me to the list of control-panels as expected. It seems that we enter in a strange loop.
Panels with "back" button (for example dexterity types one) works fine.
Test summary
Run details
| Project | Volto |
| Status | Passed |
| Commit | 2f9f9e63b4 |
| Started | Dec 6, 2022 9:44 AM |
| Ended | Dec 6, 2022 10:00 AM |
| Duration | 15:48 💡 |
| OS | Linux Ubuntu - |
| Browser | Multiple |
View run in Cypress Dashboard ➡️
This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard
I tried to redo all the necessary changes that made the prefix run with 16.3.0 to run on 16.20.4. I am trying to create a how-to replicate the work done on 16.3.0 to enable this feature on newer version of Volto, without any tests.
To do that I identified the work (diff - 16.3.0, relative_path_redo) => 16.20.4
// 1
// ./omelette/razzle.config.js
// Right before => return config
const prefixPath = process.env.RAZZLE_PREFIX_PATH || '';
if (prefixPath) {
if (target === 'web' && dev) {
config.devServer.publicPath = prefixPath;
}
const pp = config.output.publicPath;
config.output.publicPath = `${pp}${prefixPath.slice(1)}/`;
}
// 2
// ./omelette/src/components/theme/Navigation/NavItem.jsx
// Add new prop to NavLink
import { matchPath } from 'react-router';
..
isActive={(match, location) => {
const active = match
? match.isExact
? true
: settings.prefixPath
? settings.prefixPath === match.url &&
match.url === location.pathname
: matchPath(location.pathname, {
path: match.path,
exact: false,
strict: false,
})
: false;
return active;
}}
..
// 3
// Add new attr to config object at frontend/omelette/src/config/index.js
..
prefixPath: process.env.RAZZLE_PREFIX_PATH || '',
..
// 4
// ./frontend//omelette/src/helpers/Api/APIResourceWithAuth.js
// Add new const Path like so right before const request
const path = `${apiPath}${APISUFIX}${req.path.replace(
settings.prefixPath,
'',
)}`;
const request = superagent
.get(path)
// 5
// Edit ./frontend/omelette/src/helpers/Url/Url.js @ flattenToAppURL function
// flattenToAppURL is small function, so compare the changes then make the change.
export function flattenToAppURL(url) {
const { settings } = config;
const isPrefixPath = url?.indexOf(settings.prefixPath) === 0;
return (
url &&
`${isPrefixPath ? '' : settings.prefixPath}${url
.replace(settings.internalApiPath, '')
.replace(settings.apiPath, '')
.replace(settings.publicURL, '')}`
);
}
// same thing with flattenHTMLToAppURL
export function flattenHTMLToAppURL(html) {
const { settings } = config;
const replacer = config.settings.prefixPath ?? '';
return settings.internalApiPath
? html
.replace(new RegExp(settings.internalApiPath, 'g'), replacer)
.replace(new RegExp(settings.apiPath, 'g'), replacer)
: html.replace(new RegExp(settings.apiPath, 'g'), replacer);
}
// 6 Changes @ frontend/omelette/src/middleware
// ./index.js, export new file
export prefixPathRoot from './prefixPathRoot';
// Create new js file named ./prefixPathRoot.js
/**
* A middleware that prefixes all paths with the relative root path.
* To use the relative paths, set the RAZZLE_PREFIX_PATH env var.
*
* This middleware is meant to catch route destinations that are hardcoded,
* for example the Logo hardcodes its destination as "/".
*/
import config from '@plone/volto/registry';
const prefixPathRoot = (history) => ({ dispatch, getState }) => (next) => (
action,
) => {
if (typeof action === 'function') {
return next(action);
}
switch (action.type) {
case '@@router/LOCATION_CHANGE':
const { pathname } = action.payload.location;
const { prefixPath } = config.settings;
if (!prefixPath) {
break;
}
if (!pathname.startsWith(prefixPath)) {
const newPathname = `${prefixPath}${pathname === '/' ? '' : pathname}`;
action.payload.location.pathname = newPathname;
history.replace(newPathname);
}
return next(action);
default:
return next(action);
}
return next(action);
};
export default prefixPathRoot;
// 7 ./frontend/omelette/src/routes.js
// Find const routes, and update with so
// Make sure to check against new versions of this const routes implemenation
const routes = [
{
path: config.settings.prefixPath || '/',
component: App,
routes: [
// redirect to external links if path is in blacklist
...(config.settings?.externalRoutes || []).map((route) => ({
...route.match,
component: NotFound,
})),
...[
// addon routes have a higher priority then default routes
...(config.addonRoutes || []),
...((config.settings?.isMultilingual && multilingualRoutes) || []),
...defaultRoutes,
].map((route) =>
config.settings.prefixPath
? {
...route,
path: Array.isArray(route.path)
? route.path.map(
(path) => `${config.settings.prefixPath}${path}`,
)
: `${config.settings.prefixPath}${route.path}`,
}
: route,
),
],
},
];
// 8 ./frontend/omelette/src/server.jsx
// Find const server, and after it add
if (process.env.RAZZLE_PREFIX_PATH)
server.use(
process.env.RAZZLE_PREFIX_PATH,
express.static(
process.env.BUILD_DIR
? path.join(process.env.BUILD_DIR, 'public')
: process.env.RAZZLE_PUBLIC_DIR,
),
);
// 9 ./frontend/omelette/src/store.js
import prefixPathRoot from '@plone/volto/middleware';
// add the following to const stack,
..
prefixPathRoot(history),
..
// 10 ./frontend/omelette/src/express-middleware/devproxy.js
// change pathRewrite to the following:
pathRewrite: (path, req) => {
const { apiPathURL, instancePath } = getEnv();
const target =
config.settings.proxyRewriteTarget ||
`/VirtualHostBase/http/${apiPathURL.hostname}:${apiPathURL.port}${instancePath}/++api++/VirtualHostRoot`;
return `${target}${path
.replace(config.settings.prefixPath, '')
.replace('/++api++', '')}`;
},
// 11 ./omelette/src/helpers/Api/Api.js
// Edit adjustedPath from const to let then add the if statement below
let adjustedPath = path[0] !== '/' ? `/${path}` : path;
if (adjustedPath.indexOf(settings.prefixPath) === 0) {
adjustedPath = adjustedPath.slice(settings.prefixPath.length);
}
What happend:
- All the js/css resources were called with the prefix as expected.
- I was able to see the prefixed path on the homepage.
- page edits + /contents page work as well.
-
But when navigating to a new path/page like controlpanel, I can see the URL/history changes but new page is not displayed. Its like the URL changes, history objects gets the push but somehow react does not get the state change. Am I missing something?
Solved: I was missing the fact that I need to change the routes.js in the Volto site package as well. Things are working smoothly.
Related to #4290 , to determine which one is the canonical and if we can close this one.