Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relative (prefixed root) path #3190

Open
wants to merge 74 commits into
base: main
Choose a base branch
from
Open

Relative (prefixed root) path #3190

wants to merge 74 commits into from

Conversation

tiberiuichim
Copy link
Contributor

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.

@netlify
Copy link

netlify bot commented Mar 24, 2022

Deploy Preview for volto ready!

Name Link
🔨 Latest commit 2f9f9e6
🔍 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.

@stevepiercy
Copy link
Collaborator

stevepiercy commented Mar 28, 2022

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

return (
url &&
url
`${settings.prefixPath}${url
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a problem with listing blocks (and maybe in other parts).

in universalLink the url will be "flattened" several times and with your changes, i have links inside listing blocks like "/my-domain/my-domain/example-page".

Probably in universalLink code we should avoid multple flattenToAppURL calls, but maybe also in that function we should avoid to append the same string several times.

Copy link
Contributor

@pnicolli pnicolli Mar 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am more inclined towards the latest solution. This function should be idempotent imho, so it should not prepend the prefix every time it's called.
I wouldn't check if the url already starts with the prefixPath. Maybe we should only add the prefixPath if the url starts with http?

@@ -279,6 +280,18 @@ const defaultModify = ({
}),
]
: [];

const prefixPath = process.env.RAZZLE_PREFIX_PATH || '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Razzle docs, there should be an env var that already does this: PUBLIC_PATH
https://razzlejs.org/docs/environment-variables

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.env.PUBLIC_PATH: Only used in razzle build. You can alter the webpack.config.output.publicPath of the client assets (bundle, css, and images). This is useful if you plan to serve your assets from a CDN. Make sure to include a trailing slash (e.g. PUBLIC_PATH=https://cdn.example.com/). If you are using React and altering the public path, make sure to also include the crossorigin attribute on your <script> tag in src/server.js.
process.env.CLIENT_PUBLIC_PATH: The NODE_ENV=development build's BUILD_TARGET=client has a different PUBLIC_PATH than BUILD_TARGET=server. Default is http://${process.env.HOST}:${process.env.PORT + 1}/

Nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how usable those will be, as we'll need the same var inside Volto, for path adjustments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the aforementioned docs, all those vars are available in process.env at build time for the client, as well as all the RAZZLE_ vars that we already know. I have actually never tested this fact, on the other hand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give it a try, though I'm wary of this, as the PUBLIC_PATH in razzle docs show it as a full URL, while we're dealing with paths fragments in the Volto implementation. Maybe if we take public_path as an override even for apiPath...

@cekk
Copy link
Member

cekk commented Apr 8, 2022

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 cekk self-assigned this May 3, 2022
@cypress
Copy link

cypress bot commented May 5, 2022



Test summary

451 0 20 0


Run details

Project Volto
Status Passed
Commit 2f9f9e6
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

@nileshgulia1 nileshgulia1 mentioned this pull request Jan 21, 2023
9 tasks
@FarooqAlaulddin
Copy link

FarooqAlaulddin commented May 27, 2023

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.

@@ -0,0 +1,68 @@
---
html_meta:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
html_meta:
myst:
html_meta:

Also indent lines 3-6 with two more spaces. The format of this changed since this PR was created.

@sneridagh
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants