plone / volto

React-based frontend for the Plone Content Management System
https://demo.plone.org/
MIT License
429 stars 571 forks source link

PLIP for new URL helpers API #3774

Open tiberiuichim opened 1 year ago

tiberiuichim commented 1 year ago

Seamless mode was a step forward for the evolution of Volto, but we're still limited in what we can achieve because of the following problem:

That singleton is "non-threadsafe". The only place it matters (right now) is if we want to use the same Volto application to serve multiple websites.

For the client code it's really simple to get the proper "public url": just use window.location. For the server we can use a header passed down from the front proxy, to identify the "current" public url. But if we stash that value, as we do it now, we're running into problems with corrupt URLs being generated or used.

So we have the following "pieces":

We have a proposal to restructure the way we use the helpers that keeps the changes minimal.

Let's take the following component:

import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers/Url/Url';
import { ConditionalLink, UniversalLink } from '@plone/volto/components';

const LinkMore = ({ data, isEditMode }) => {
  let href = data.linkHref?.[0]?.['@id'] || '';
  let link = null;
  if (isInternalURL(href)) {
    link = (
      <ConditionalLink to={flattenToAppURL(href)} condition={!isEditMode}>
        {data.linkTitle || href}
      </ConditionalLink>
    );
  } else if (href) {
    link = <UniversalLink href={href}>{data.linkTitle || href}</UniversalLink>;
  }

  return link ? <div className="link-more">{link}</div> : null;
}

There would be two changes:

import { useHelpers} from '@plone/volto/helpers';
import { ConditionalLink, UniversalLink } from '@plone/volto/components';

const LinkMore = ({ data, isEditMode }) => {
  let href = data.linkHref?.[0]?.['@id'] || '';
  let link = null;
  const { flattenToAppURL, isInternalURL } = useHelpers();
  if (isInternalURL(href)) {
    link = (
      <ConditionalLink to={flattenToAppURL(href)} condition={!isEditMode}>
        {data.linkTitle || href}
      </ConditionalLink>
    );
  } else if (href) {
    link = <UniversalLink href={href}>{data.linkTitle || href}</UniversalLink>;
  }

  return link ? <div className="link-more">{link}</div> : null;
}

For class-based components we would provide a HOC, injectHelpers:

import { injectHelpers } from '@plone/volto/helpers';

const LinkMore = injectHelpers(LinkMoreComponent);

The specifics on the useHelpers are (hopefully) implementation details. In principle we'll create initialize the store, from the server, with the proper data so that it can be used from the hook (and injectHelpers will use the hook, similar to how we do with injectLazyLibs/useLazyLibs).

We will need to adjust the tests and provide proper migration. We could even provide a transition period, when the helpers are still provided as they are now, but marked as deprecated, so that people have a chance of transitioning their projects and addons.

tiberiuichim commented 1 year ago

So, the proposal is to migrate all Volto to use the hook/HOC, but keep the existing methods, to be non-breaking and give a chance to projects to migrate.

tiberiuichim commented 1 year ago

@sneridagh useUrl, useHelpers or useUrlHelpers ?

djay commented 8 months ago

@tiberiuichim we've been working on this. But I propose renaming this multi-tenanted mode. The implication of this PLIP is that once you turn it on you can use the same frontend for as many sites as you want (as long as they use the same theme). What we propose is that once turned on, the existing methods would raise an exception. This way you can know for sure any addons are compatible. So it's like a feature flag, backwards compatible as long as you don't turn it on. The next implication is that lots of use of env variables will have to change.