volto icon indicating copy to clipboard operation
volto copied to clipboard

Relative (prefixed root) path

Open tiberiuichim opened this issue 3 years ago • 12 comments

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.

tiberiuichim avatar Mar 24 '22 05:03 tiberiuichim

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...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

netlify[bot] avatar Mar 24 '22 05:03 netlify[bot]

@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.

tiberiuichim avatar Mar 25 '22 19:03 tiberiuichim

@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.

  1. Add this to docs/conf.py under linkcheck_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.

  2. 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.

stevepiercy avatar Mar 25 '22 23:03 stevepiercy

@tiberiuichim also, what about Sitemap?

sneridagh avatar Mar 26 '22 19:03 sneridagh

cool! i'm testing it and will get back with some feedbacks soon

cekk avatar Mar 26 '22 19:03 cekk

@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.

tiberiuichim avatar Mar 27 '22 06:03 tiberiuichim

@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.

tiberiuichim avatar Mar 27 '22 12:03 tiberiuichim

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
```

stevepiercy avatar Mar 28 '22 10:03 stevepiercy

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.

cekk avatar Apr 08 '22 13:04 cekk



Test summary

451 0 20 0


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

cypress[bot] avatar May 05 '22 10:05 cypress[bot]

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.

FarooqAlaulddin avatar May 27 '23 02:05 FarooqAlaulddin

Related to #4290 , to determine which one is the canonical and if we can close this one.

sneridagh avatar Mar 31 '24 15:03 sneridagh