diff --git a/.eslintignore b/.eslintignore
index 7df5c928c1..77fb2df91b 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -4,3 +4,5 @@ packages/volto
packages/volto-guillotina
!.*
dist
+packages/registry/lib
+packages/registry/docs
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..24ab7677d5
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+- [ ] I signed and returned the [Plone Contributor Agreement](https://plone.org/foundation/contributors-agreement), and received and accepted an invitation to join a team in the Plone GitHub organization.
+- [ ] I verified there aren't other open [pull requests](https://github.com/plone/volto/pulls) for the same change.
+- [ ] I followed the guidelines in [Contributing to Volto](https://6.docs.plone.org/volto/contributing/index.html).
+- [ ] I succesfully ran [code linting checks](https://6.docs.plone.org/volto/contributing/linting.html) on my changes locally.
+- [ ] I succesfully ran [unit tests](https://6.docs.plone.org/volto/contributing/testing.html) on my changes locally.
+- [ ] I succesfully ran [acceptance tests](https://6.docs.plone.org/volto/contributing/acceptance-tests.html) on my changes locally.
+- [ ] If needed, I added new tests for my changes.
+- [ ] If needed, I added [documentation](https://6.docs.plone.org/volto/contributing/documentation.html#narrative-documentation) for my changes, either in the Storybook or narrative documentation.
+- [ ] I included a [change log entry](https://6.docs.plone.org/contributing/index.html#contributing-change-log-label) in my commits.
+
+-----
+
+If your pull request closes an open issue, include the exact text below, immediately followed by the issue number. When your pull request gets merged, then that issue will close automatically.
+
+Closes #
diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml
index 6d93708898..82480849c7 100644
--- a/.github/workflows/acceptance.yml
+++ b/.github/workflows/acceptance.yml
@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -57,7 +57,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -105,7 +105,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -153,7 +153,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -201,7 +201,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -249,7 +249,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -296,7 +296,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -344,7 +344,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -392,7 +392,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -440,7 +440,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
# python-version: [3.7]
steps:
- uses: actions/checkout@v4
@@ -489,7 +489,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
env:
generator-directory: ./packages/generator-volto
project-directory: ./my-volto-app
@@ -521,6 +521,9 @@ jobs:
- name: Install yalc
run: npm -g install yalc
+ - name: Build dependencies
+ run: make build-deps
+
- name: Install a yalc'ed version of the current Volto in the project - publish
run: |
yalc publish packages/types
@@ -597,7 +600,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
@@ -647,7 +650,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
+ node-version: [22.x]
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
index 47fa637159..418c2c4f18 100644
--- a/.github/workflows/changelog.yml
+++ b/.github/workflows/changelog.yml
@@ -6,7 +6,7 @@ on:
- main
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
towncrier:
diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml
index b0519c45da..3878529afb 100644
--- a/.github/workflows/code-analysis.yml
+++ b/.github/workflows/code-analysis.yml
@@ -2,7 +2,7 @@ name: Code Analysis Check
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
prettier:
diff --git a/.github/workflows/deployment_tests.yml b/.github/workflows/deployment_tests.yml
index 5f22b40b67..9f6edc6e22 100644
--- a/.github/workflows/deployment_tests.yml
+++ b/.github/workflows/deployment_tests.yml
@@ -2,7 +2,7 @@ name: Deployment Tests
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
vitessr:
diff --git a/.github/workflows/docs-rtd-pr-preview.yml b/.github/workflows/docs-rtd-pr-preview.yml
index 83d7e72494..a123d8ef75 100644
--- a/.github/workflows/docs-rtd-pr-preview.yml
+++ b/.github/workflows/docs-rtd-pr-preview.yml
@@ -10,6 +10,7 @@ on:
- "docs/source/**"
- .readthedocs.yaml
- requirements-docs.txt
+ - "packages/registry/docs/**"
permissions:
pull-requests: write
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 75da9c3140..0c868b5779 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -10,6 +10,9 @@ on:
paths:
- 'docs/**'
- 'styles/**'
+ - '.github/workflows/docs.yml'
+ - 'requirements-docs.txt'
+ - '.vale.ini'
jobs:
docs:
@@ -32,9 +35,7 @@ jobs:
run: pip install virtualenv
- name: pip install requirements
- run: |
- pip install -r requirements-docs.txt
- sudo snap install --edge vale
+ run: pip install -r requirements-docs.txt
- name: Check for broken links
run: make docs-linkcheckbroken
@@ -43,7 +44,4 @@ jobs:
run: make docs-html
- name: Run vale
- run: |
- git clone https://github.com/errata-ai/Microsoft.git
- cp -r ./Microsoft/Microsoft ./styles
- vale --no-exit ./docs
+ run: make docs-vale VALEOPTS=--no-exit
diff --git a/.github/workflows/readme-link-check.yml b/.github/workflows/readme-link-check.yml
index 7f53b65824..42c96e1455 100644
--- a/.github/workflows/readme-link-check.yml
+++ b/.github/workflows/readme-link-check.yml
@@ -20,4 +20,4 @@ jobs:
- name: Check links in README.md with awesome_bot
run: |
gem install awesome_bot
- awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md
+ awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:8080,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index b479cc7fb1..138349a40b 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -2,7 +2,7 @@ name: Unit Tests
on: [push, pull_request]
env:
- node-version: 20.x
+ node-version: 22.x
jobs:
volto:
@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -40,7 +40,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [18.x, 20.x]
+ node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
@@ -77,16 +77,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
- matrix:
- node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
# node setup
- - name: Use Node.js ${{ matrix.node-version }}
+ - name: Use Node.js ${{ env.node-version }}
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version: ${{ env.node-version }}
- name: Enable corepack
run: corepack enable
@@ -152,16 +150,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
- matrix:
- node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
# node setup
- - name: Use Node.js ${{ matrix.node-version }}
+ - name: Use Node.js ${{ env.node-version }}
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version: ${{ env.node-version }}
- name: Enable corepack
run: corepack enable
diff --git a/.prettierignore b/.prettierignore
index cf39cfcd94..1749ca9b36 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -5,6 +5,7 @@
styles/rules/*
node_modules
packages/volto/types/*
+packages/registry/docs/*
storybook-static
apps/vite-ssr/src/routeTree.gen.ts
apps/vite/src/routeTree.gen.ts
diff --git a/Makefile b/Makefile
index 4ff9cb9510..72656906b6 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ include variables.mk
# Sphinx variables
# You can set these variables from the command line.
SPHINXOPTS ?=
+VALEOPTS ?=
# Internal variables.
SPHINXBUILD = "$(realpath bin/sphinx-build)"
SPHINXAUTOBUILD = "$(realpath bin/sphinx-autobuild)"
@@ -66,7 +67,7 @@ test: ## Run unit tests
.PHONY: clean
clean: ## Clean development environment
rm -rf node_modules
- find ./packages -name node_modules -exec rm -rf {} \;
+ find ./packages -name node_modules -not -path "./packages/volto/__tests__/*" -exec rm -rf {} \;
.PHONY: install
install: ## Set up development environment
@@ -120,7 +121,7 @@ docs-linkcheckbroken: bin/python docs-news ## Run linkcheck and show only broke
.PHONY: docs-vale
docs-vale: bin/python docs-news ## Install (once) and run Vale style, grammar, and spell checks
bin/vale sync
- bin/vale --no-wrap $(VALEFILES)
+ bin/vale --no-wrap $(VALEOPTS) $(VALEFILES)
@echo
@echo "Vale is finished; look for any errors in the above output."
diff --git a/apps/nextjs/.eslintrc.js b/apps/nextjs/.eslintrc.js
deleted file mode 100644
index d0e37670c7..0000000000
--- a/apps/nextjs/.eslintrc.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @type {import('eslint').Linter.Config} */
-module.exports = {
- extends: 'next/core-web-vitals',
- ignorePatterns: ['.next/**', 'dist/**', 'node_modules/**'],
- settings: {
- next: {
- rootDir: 'apps/nextjs/',
- },
- },
-};
diff --git a/apps/nextjs/.eslintrc.json b/apps/nextjs/.eslintrc.json
new file mode 100644
index 0000000000..035e32e888
--- /dev/null
+++ b/apps/nextjs/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+ "extends": ["next/core-web-vitals", "next/typescript"],
+ "ignorePatterns": [".next/**", "dist/**", "node_modules/**"],
+ "settings": {
+ "next": {
+ "rootDir": "apps/nextjs/"
+ }
+ }
+}
diff --git a/apps/nextjs/.gitignore b/apps/nextjs/.gitignore
index e5671ba458..29b159ef05 100644
--- a/apps/nextjs/.gitignore
+++ b/apps/nextjs/.gitignore
@@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
+.yarn/install-state.gz
# testing
/coverage
diff --git a/apps/nextjs/LICENSE b/apps/nextjs/LICENSE
index c0af2b1b65..f6a5160bd5 100644
--- a/apps/nextjs/LICENSE
+++ b/apps/nextjs/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Plone Foundation
+Copyright (c) 2024 Plone Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/apps/nextjs/README.md b/apps/nextjs/README.md
index 88a23215b6..4660a9939a 100644
--- a/apps/nextjs/README.md
+++ b/apps/nextjs/README.md
@@ -1,10 +1,16 @@
# Plone on Next.js
-This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library. This is intended to serve as both a playground for the development of both packages and as demo of Plone using Next.js.
+This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library.
+This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Next.js.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
## Development
-To start, from the root of the monorepo:
+To start, from the root of the monorepo, issue the following commands in a shell session.
```shell
pnpm install
@@ -20,65 +26,93 @@ make backend-docker-start
## Deployment at Vercel
-
-We introduce an environment variable `API_SERVER_URL`.
-You need to create this environment variable in the Vercel deployment's control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown.
+For deploying your app at Vercel, you need to create the environment variable `API_SERVER_URL` in Vercel's deployment control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown.
```shell
API_SERVER_URL=https://my-server-DNS-name.tld/api
```
+For production deployments, you will need to force the deployment URL, otherwise you will have issues with CORS.
+To do so, set another environment variable for the production URL, `NEXT_PRODUCTION_URL`.
+This URL needs to be scheme-less, without `http` or `https`, and consist only of the domain name:
+
+```shell
+NEXT_PRODUCTION_URL=my-nextjs-production-DNS-name.tld
+```
+
### Application rewrite configuragtion
-To avoid issues with CORS and maintain the server counterpart private, our Next.js app should have a rewrite, configured as follows:
+To avoid issues with CORS and maintain the server counterpart private, your Next.js app should have a rewrite, configured as follows:
```jsx
const nextConfig = {
// Rewrite to the backend to avoid CORS
async rewrites() {
- const apiServerURL =
- process.env.API_SERVER_URL ||
- 'http://localhost:8080/Plone/%2B%2Bapi%2B%2B';
+ let apiServerURL, vhmRewriteRule;
+ if (
+ process.env.API_SERVER_URL &&
+ (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL)
+ ) {
+ // We are in Vercel
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule = `/VirtualHostBase/https/${
+ process.env.NEXT_PRODUCTION_URL
+ ? // We are in the production deployment
+ process.env.NEXT_PRODUCTION_URL
+ : // We are in the preview deployment
+ process.env.NEXT_PUBLIC_VERCEL_URL
+ }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
+ } else if (process.env.API_SERVER_URL) {
+ // We are in development
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
+ } else {
+ // We are in development and the API_SERVER_URL is not set, so we use a local backend
+ apiServerURL = 'http://localhost:8080';
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
+ }
return [
{
source: '/\\+\\+api\\+\\+/:slug*',
destination:
- `${apiServerURL}/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot/:slug*`,
+ `${apiServerURL}${vhmRewriteRule}/:slug*`,
},
];
},
};
```
-Plone Client uses the `++api++` prefix as default, so we should create a redirect in our app pointing to the API server, but using Plone's traditional virtual host management configuration.
+Plone Client uses the `++api++` prefix as default, so you should create a redirect in your app pointing to the API server, but using Plone's traditional virtual host management configuration.
-Next.js rewrites are picky on the `destination` field, because its rewrite library does not support URLs with regular expression operators.
-Therefore, we can't use the usual `++api++` route for the rewrite.
-This will allow us to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules.
-We will fallback to configure a `api` route in our reverse proxy of choice.
+Next.js rewrites are picky with the `destination` field, because its rewrite library does not support URLs with regular expression operators.
+Therefore, you can't use the usual `++api++` route for the rewrite.
+This will allow you to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules.
+You will fallback to configure a `api` route in your reverse proxy of choice.
### Plone backend
You have to deploy the Plone backend elsewhere, since Vercel is serverless oriented.
-We need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section.
+You need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section.
-We will fallback to configure an `api` route in our reverse proxy of choice.
+You will fallback to configure an `api` route in your reverse proxy of choice.
-For example, if we use `traefik`:
+For example, if you use `traefik`:
```yaml
- ## VHM rewrite /api/ (Plone Next.js)
- - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)"
- ## We remove the incoming /api and just use the path
- - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1"
-
- ## /api router
- - traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`)
- - traefik.http.routers.rt-backend-api.entrypoints=https
- - traefik.http.routers.rt-backend-api.tls=true
- - traefik.http.routers.rt-backend-api.service=svc-backend
- - traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api
+## VHM rewrite /api/ (Plone Next.js)
+- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)"
+## We remove the incoming /api and just use the path
+- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1"
+
+## /api router
+- traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`)
+- traefik.http.routers.rt-backend-api.entrypoints=https
+- traefik.http.routers.rt-backend-api.tls=true
+- traefik.http.routers.rt-backend-api.service=svc-backend
+- traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api
```
## About this app
@@ -95,16 +129,18 @@ pnpm dev
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+You can start editing the page by modifying `app/page.tsx`.
+The page auto-updates as you edit the file.
## Learn More
To learn more about Next.js, take a look at the following resources:
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and its API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/).
+Your feedback and contributions are welcome!
## Deploy on Vercel
diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.mjs
similarity index 53%
rename from apps/nextjs/next.config.js
rename to apps/nextjs/next.config.mjs
index ae8d5d1813..2bc72a90f0 100644
--- a/apps/nextjs/next.config.js
+++ b/apps/nextjs/next.config.mjs
@@ -1,7 +1,10 @@
-const path = require('path');
+// import path from 'path';
/** @type {import('next').NextConfig} */
const nextConfig = {
+ typescript: {
+ ignoreBuildErrors: true,
+ },
// sassOptions: {
// includePaths: [path.join(__dirname, 'src/lib/components/src/styles')],
// },
@@ -18,17 +21,26 @@ const nextConfig = {
// Rewrite to the backend to avoid CORS
async rewrites() {
let apiServerURL, vhmRewriteRule;
- if (process.env.API_SERVER_URL) {
- apiServerURL = process.env.API_SERVER_URL;
- vhmRewriteRule = `/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
- } else if (
+ if (
process.env.API_SERVER_URL &&
- !process.env.NEXT_PUBLIC_VERCEL_URL
+ (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL)
) {
- throw new Error(
- 'API_SERVER_URL set and NEXT_PUBLIC_VERCEL_URL not present.',
- );
+ // We are in Vercel
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule = `/VirtualHostBase/https/${
+ process.env.NEXT_PRODUCTION_URL
+ ? // We are in the production deployment
+ process.env.NEXT_PRODUCTION_URL
+ : // We are in the preview deployment
+ process.env.NEXT_PUBLIC_VERCEL_URL
+ }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`;
+ } else if (process.env.API_SERVER_URL) {
+ // We are in development
+ apiServerURL = process.env.API_SERVER_URL;
+ vhmRewriteRule =
+ '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
} else {
+ // We are in development and the API_SERVER_URL is not set, so we use a local backend
apiServerURL = 'http://localhost:8080';
vhmRewriteRule =
'/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot';
@@ -46,4 +58,4 @@ const nextConfig = {
},
};
-module.exports = nextConfig;
+export default nextConfig;
diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 6d4a71dc0f..8b935ff8bb 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -13,21 +13,22 @@
"@plone/blocks": "workspace: *",
"@plone/client": "workspace: *",
"@plone/components": "workspace: *",
- "@plone/registry": "workspace: *",
"@plone/providers": "workspace: *",
- "@tanstack/react-query": "^5.37.1",
- "next": "14.2.2",
+ "@plone/registry": "workspace: *",
+ "@tanstack/react-query": "^5.59.0",
+ "next": "14.2.14",
"react": "^18",
- "react-aria-components": "^1.1.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18"
},
"devDependencies": {
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@plone/types": "workspace: *",
+ "@tanstack/react-query-devtools": "^5.59.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
- "eslint-config-next": "14.2.2",
- "typescript": "^5.4.5"
+ "eslint-config-next": "14.2.14",
+ "typescript": "^5.6.3"
}
}
diff --git a/apps/nextjs/src/app/Providers.tsx b/apps/nextjs/src/app/Providers.tsx
index 27b47036f6..e1c0fa9295 100644
--- a/apps/nextjs/src/app/Providers.tsx
+++ b/apps/nextjs/src/app/Providers.tsx
@@ -1,15 +1,32 @@
'use client';
import React from 'react';
-import { useRouter } from 'next/navigation';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { PloneClientProvider } from '@plone/providers';
+import {
+ useRouter,
+ usePathname,
+ useSearchParams,
+ useParams,
+} from 'next/navigation';
+import { QueryClient } from '@tanstack/react-query';
+import { PloneProvider } from '@plone/providers';
import PloneClient from '@plone/client';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-import { RouterProvider } from 'react-aria-components';
-import { FlattenToAppURLProvider } from '@plone/components';
import { flattenToAppURL } from './utils';
import config from './config';
+// Custom hook to unify the location object between NextJS and Plone
+function useLocation() {
+ const pathname = usePathname();
+ const search = useSearchParams();
+
+ return {
+ pathname,
+ search,
+ searchStr: '',
+ hash: (typeof window !== 'undefined' && window.location.hash) || '',
+ href: (typeof window !== 'undefined' && window.location.href) || '',
+ };
+}
+
const Providers: React.FC<{
children?: React.ReactNode;
}> = ({ children }) => {
@@ -36,19 +53,25 @@ const Providers: React.FC<{
}),
);
- let router = useRouter();
+ const router = useRouter();
return (
-
-
-
-
- {children}
-
-
-
-
-
+ {
+ router.push(to);
+ }}
+ useParams={useParams}
+ useHref={(to) => flattenToAppURL(to)}
+ flattenToAppURL={flattenToAppURL}
+ >
+ {children}
+
+
);
};
diff --git a/apps/nextjs/src/app/config.ts b/apps/nextjs/src/app/config.ts
index 47bad754da..8d0b440630 100644
--- a/apps/nextjs/src/app/config.ts
+++ b/apps/nextjs/src/app/config.ts
@@ -1,12 +1,30 @@
-const settings = {
- apiPath: process.env.NEXT_PUBLIC_VERCEL_URL
- ? // Vercel does not prepend the schema to the NEXT_PUBLIC_VERCEL_URL automatic env var
- `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`
- : 'http://localhost:3000',
-};
+import config from '@plone/registry';
+import type { ConfigType } from '@plone/registry';
+import { slate } from '@plone/blocks';
+import { blocksConfig } from '@plone/blocks';
-const config = {
- settings,
+const settings: Partial = {
+ slate,
};
+if (process.env.NEXT_PUBLIC_VERCEL_URL) {
+ // This app is at Vercel
+ if (process.env.NEXT_PRODUCTION_URL) {
+ // This app is in a production deployment, so set the apiPath to the production URL
+ settings.apiPath = process.env.NEXT_PRODUCTION_URL;
+ } else {
+ // This app is in a preview deployment, so set the apiPath to the Vercel URL
+ settings.apiPath = `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`;
+ }
+} else {
+ // This app is in development, so set the apiPath to localhost
+ settings.apiPath = 'http://localhost:3000/';
+}
+
+// @ts-expect-error Improve typings
+config.set('settings', settings);
+
+// @ts-expect-error Improve typings
+config.set('blocks', { blocksConfig });
+
export default config;
diff --git a/apps/nextjs/src/app/content.tsx b/apps/nextjs/src/app/content.tsx
index 92e757e7da..84e2b244b7 100644
--- a/apps/nextjs/src/app/content.tsx
+++ b/apps/nextjs/src/app/content.tsx
@@ -1,46 +1,30 @@
'use client';
-
import { useQuery } from '@tanstack/react-query';
import { usePathname } from 'next/navigation';
-import Link from 'next/link';
-import { flattenToAppURL } from './utils';
import { usePloneClient } from '@plone/providers';
-import { Breadcrumbs } from '@plone/components';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+
import '@plone/components/dist/basic.css';
export default function Content() {
const { getContentQuery } = usePloneClient();
const pathname = usePathname();
- const { data, isLoading } = useQuery(getContentQuery({ path: pathname }));
+ const { data } = useQuery(getContentQuery({ path: pathname }));
if (data) {
return (
);
}
diff --git a/apps/nextjs/src/app/layout.tsx b/apps/nextjs/src/app/layout.tsx
index 3c118fb9fa..8d254759a2 100644
--- a/apps/nextjs/src/app/layout.tsx
+++ b/apps/nextjs/src/app/layout.tsx
@@ -5,7 +5,7 @@ import Providers from './Providers';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
- title: 'NextJS-powered Plone',
+ title: 'Next.js app powered by Plone',
description: '',
};
diff --git a/apps/nextjs/src/middleware.ts b/apps/nextjs/src/middleware.ts
index 59c2d7b5d4..736d67530c 100644
--- a/apps/nextjs/src/middleware.ts
+++ b/apps/nextjs/src/middleware.ts
@@ -2,10 +2,7 @@ import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
- // Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers);
- // console.log(request.nextUrl.pathname);
- // requestHeaders.set('x-hello-from-middleware1', 'hello');
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
diff --git a/apps/nextjs/tsconfig.json b/apps/nextjs/tsconfig.json
index 33bcb0c4f4..4b7888fa43 100644
--- a/apps/nextjs/tsconfig.json
+++ b/apps/nextjs/tsconfig.json
@@ -1,38 +1,34 @@
{
- "compilerOptions": {
- "target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ]
- },
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- "**/*.js",
- "**/*.jx",
- ".next/types/**/*.ts"
+ "compilerOptions": {
+ "forceConsistentCasingInFileNames": true,
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
],
- "exclude": [
- "node_modules",
- ]
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ "**/*.js",
+ "**/*.jsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": ["node_modules"]
}
diff --git a/apps/plone/.editorconfig b/apps/plone/.editorconfig
deleted file mode 100644
index 6c43b88c57..0000000000
--- a/apps/plone/.editorconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-# EditorConfig Configurtaion file, for more details see:
-# http://EditorConfig.org
-# EditorConfig is a convention description, that could be interpreted
-# by multiple editors to enforce common coding conventions for specific
-# file types
-
-# top-most EditorConfig file:
-# Will ignore other EditorConfig files in Home directory or upper tree level.
-root = true
-
-
-[*] # For All Files
-# Unix-style newlines with a newline ending every file
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-# Set default charset
-charset = utf-8
-# Indent style default
-indent_style = space
-
-[*.{py,cfg,ini}]
-# 4 space indentation
-indent_size = 4
-
-[*.{html,dtml,pt,zpt,xml,zcml,js,jsx,json,less,css,yaml,yml}]
-# 2 space indentation
-indent_size = 2
-
-[{Makefile,.gitmodules}]
-# Tab indentation (no size specified, but view as 4 spaces)
-indent_style = tab
-indent_size = unset
-tab_width = unset
diff --git a/apps/plone/.eslintrc.js b/apps/plone/.eslintrc.js
deleted file mode 100644
index 6148e2e62f..0000000000
--- a/apps/plone/.eslintrc.js
+++ /dev/null
@@ -1,63 +0,0 @@
-const fs = require('fs');
-const projectRootPath = __dirname;
-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
-
-let voltoPath = './node_modules/@plone/volto';
-
-let configFile;
-if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
- configFile = `${projectRootPath}/tsconfig.json`;
-else if (fs.existsSync(`${projectRootPath}/jsconfig.json`))
- configFile = `${projectRootPath}/jsconfig.json`;
-
-if (configFile) {
- const jsConfig = require(configFile).compilerOptions;
- const pathsConfig = jsConfig.paths;
- if (pathsConfig['@plone/volto'])
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
-}
-
-const reg = new AddonConfigurationRegistry(__dirname);
-
-// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
-const addonAliases = Object.keys(reg.packages).map((o) => [
- o,
- reg.packages[o].modulePath,
-]);
-
-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
-
-const defaultConfig = {
- extends: `${voltoPath}/.eslintrc`,
- ignorePatterns: [
- // '.storybook/**/*',
- 'src/addons/**/node_modules',
- 'src/addons/**/cypress',
- 'src/addons/**/build',
- '!src/addons/volto-volto-project',
- ],
- settings: {
- 'import/resolver': {
- alias: {
- map: [
- ['@plone/volto', '@plone/volto/src'],
- ['@plone/volto-slate', '@plone/volto-slate/src'],
- ...addonAliases,
- ['@root', `${__dirname}/src`],
- ['~', `${__dirname}/src`],
- ],
- extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
- },
- 'babel-plugin-root-import': {
- rootPathSuffix: 'src',
- },
- },
- },
-};
-
-const config = addonExtenders.reduce(
- (acc, extender) => extender.modify(acc),
- defaultConfig,
-);
-
-module.exports = config;
diff --git a/apps/plone/.gitignore b/apps/plone/.gitignore
deleted file mode 100644
index a8b3ab728b..0000000000
--- a/apps/plone/.gitignore
+++ /dev/null
@@ -1,82 +0,0 @@
-.vscode/
-logs
-*.log
-npm-debug.log*
-.DS_Store
-
-coverage
-
-# Node
-node_modules
-coverage
-jsdoc
-webpack-assets.json
-webpack-stats.json
-npm-debug.log
-dist
-junit.xml
-eslint.xml
-yarn-error.log
-build
-
-# Other
-.DS_Store
-.idea
-lighthouse-report.html
-.vscode/
-.#*
-*~
-
-# Python
-/bin/
-/lib/
-.Python
-include
-pip-selfcheck.json
-pyvenv.cfg
-share
-
-# locales
-locales/*.json
-
-# Tests
-/tests/bin
-/tests/develop-eggs
-/tests/parts
-/tests/.installed.cfg
-*.pyc
-geckodriver.log
-log.html
-output.xml
-report.html
-selenium-screenshot-*.png
-/selenium/
-cypress/videos/
-cypress/screenshots
-
-# Local environment setup
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-# generic
-data
-omelette
-
-# build
-public/critical.css
-src/addons/*
-/cache
-
-# yarn 3
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/sdks
-!.yarn/versions
-
-!src/addons/volto-volto-project
diff --git a/apps/plone/.prettierignore b/apps/plone/.prettierignore
deleted file mode 100644
index 84cca9725b..0000000000
--- a/apps/plone/.prettierignore
+++ /dev/null
@@ -1,3 +0,0 @@
-src/**/CHANGELOG.md
-src/**/README.md
-!src/addons/volto-volto-project
\ No newline at end of file
diff --git a/apps/plone/.storybook/main.js b/apps/plone/.storybook/main.js
deleted file mode 100644
index 0455b194c9..0000000000
--- a/apps/plone/.storybook/main.js
+++ /dev/null
@@ -1,177 +0,0 @@
-const webpack = require('webpack');
-const fs = require('fs');
-const path = require('path');
-
-const projectRootPath = path.resolve('.');
-const lessPlugin = require('@plone/volto/webpack-plugins/webpack-less-plugin');
-const scssPlugin = require('razzle-plugin-scss');
-
-const createConfig = require('../node_modules/razzle/config/createConfigAsync.js');
-const razzleConfig = require(path.join(projectRootPath, 'razzle.config.js'));
-
-const SVGLOADER = {
- test: /icons\/.*\.svg$/,
- use: [
- {
- loader: 'svg-loader',
- },
- {
- loader: 'svgo-loader',
- options: {
- plugins: [
- {
- name: 'preset-default',
- params: {
- overrides: {
- convertPathData: false,
- removeViewBox: false,
- },
- },
- },
- 'removeTitle',
- 'removeUselessStrokeAndFill',
- ],
- },
- },
- ],
-};
-
-const defaultRazzleOptions = {
- verbose: false,
- debug: {},
- buildType: 'iso',
- cssPrefix: 'static/css',
- jsPrefix: 'static/js',
- enableSourceMaps: true,
- enableReactRefresh: true,
- enableTargetBabelrc: false,
- enableBabelCache: true,
- forceRuntimeEnvVars: [],
- mediaPrefix: 'static/media',
- staticCssInDev: false,
- emitOnErrors: false,
- disableWebpackbar: false,
- browserslist: [
- '>1%',
- 'last 4 versions',
- 'Firefox ESR',
- 'not ie 11',
- 'not dead',
- ],
-};
-
-module.exports = {
- core: {
- builder: 'webpack5',
- },
- stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- // '@storybook/preset-scss',
- ],
- webpackFinal: async (config, { configType }) => {
- // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
- // You can change the configuration based on that.
- // 'PRODUCTION' is used when building the static version of storybook.
-
- // Make whatever fine-grained changes you need
- let baseConfig;
- baseConfig = await createConfig(
- 'web',
- 'dev',
- {
- // clearConsole: false,
- modifyWebpackConfig: razzleConfig.modifyWebpackConfig,
- plugins: razzleConfig.plugins,
- },
- webpack,
- false,
- undefined,
- [],
- defaultRazzleOptions,
- );
- const AddonConfigurationRegistry = require('@plone/volto/addon-registry');
-
- const registry = new AddonConfigurationRegistry(projectRootPath);
-
- config = lessPlugin({ registry }).modifyWebpackConfig({
- env: { target: 'web', dev: 'dev' },
- webpackConfig: config,
- webpackObject: webpack,
- options: {},
- });
-
- config = scssPlugin.modifyWebpackConfig({
- env: { target: 'web', dev: 'dev' },
- webpackConfig: config,
- webpackObject: webpack,
- options: { razzleOptions: {} },
- });
-
- // Put the SVG loader on top and prevent the asset/resource rule
- // from processing the app's SVGs
- config.module.rules.unshift(SVGLOADER);
- const fileLoaderRule = config.module.rules.find((rule) =>
- rule.test.test('.svg'),
- );
- fileLoaderRule.exclude = /icons\/.*\.svg$/;
-
- config.plugins.unshift(
- new webpack.DefinePlugin({
- __DEVELOPMENT__: true,
- __CLIENT__: true,
- __SERVER__: false,
- }),
- );
-
- const resultConfig = {
- ...config,
- resolve: {
- ...config.resolve,
- alias: { ...config.resolve.alias, ...baseConfig.resolve.alias },
- fallback: { ...config.resolve.fallback, zlib: false },
- },
- };
-
- // Addons have to be loaded with babel
- const addonPaths = registry.addonNames.map((addon) =>
- fs.realpathSync(registry.packages[addon].modulePath),
- );
- resultConfig.module.rules[1].exclude = (input) =>
- // exclude every input from node_modules except from @plone/volto
- /node_modules\/(?!(@plone\/volto)\/)/.test(input) &&
- // If input is in an addon, DON'T exclude it
- !addonPaths.some((p) => input.includes(p));
-
- const addonExtenders = registry.getAddonExtenders().map((m) => require(m));
-
- const extendedConfig = addonExtenders.reduce(
- (acc, extender) =>
- extender.modify(acc, { target: 'web', dev: 'dev' }, config),
- resultConfig,
- );
-
- // Note: we don't actually support razzle plugins, which are also a feature
- // of the razzle.extend.js addons file. Those features are probably
- // provided in a different manner by Storybook plugins (for example scss
- // loaders).
-
- return extendedConfig;
- },
- babel: async (options) => {
- return {
- ...options,
- plugins: [
- ...options.plugins,
- [
- './node_modules/babel-plugin-root-import/build/index.js',
- {
- rootPathSuffix: './src',
- },
- ],
- ],
- // any extra options you want to set
- };
- },
-};
diff --git a/apps/plone/.storybook/manager.js b/apps/plone/.storybook/manager.js
deleted file mode 100644
index cda31aa1fe..0000000000
--- a/apps/plone/.storybook/manager.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { addons } from '@storybook/addons';
-
-addons.setConfig({
- isFullscreen: false,
- showNav: true,
- showPanel: true,
- panelPosition: 'bottom',
- sidebarAnimations: true,
- enableShortcuts: true,
- isToolshown: true,
- theme: undefined,
- selectedPanel: undefined,
- initialActive: 'sidebar',
- showRoots: true,
-});
diff --git a/apps/plone/.storybook/preview.js b/apps/plone/.storybook/preview.js
deleted file mode 100644
index bcbfccb212..0000000000
--- a/apps/plone/.storybook/preview.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import '@plone/volto/config'; // This is the bootstrap for the global config - client side
-import React from 'react';
-import { StaticRouter } from 'react-router-dom';
-import { IntlProvider } from 'react-intl';
-// eslint-disable-next-line import/no-unresolved
-import enMessages from '@root/../locales/en.json';
-
-import '@root/theme';
-
-export const parameters = {
- actions: { argTypesRegex: '^on[A-Z].*' },
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
-};
-
-export const decorators = [
- (Story) => (
-
-
-
-
-
- ),
-];
diff --git a/apps/plone/Makefile b/apps/plone/Makefile
deleted file mode 100644
index 4a5a606c1f..0000000000
--- a/apps/plone/Makefile
+++ /dev/null
@@ -1,106 +0,0 @@
-### Defensive settings for make:
-# https://tech.davis-hansson.com/p/make/
-SHELL:=bash
-.ONESHELL:
-.SHELLFLAGS:=-eu -o pipefail -c
-.SILENT:
-.DELETE_ON_ERROR:
-MAKEFLAGS+=--warn-undefined-variables
-MAKEFLAGS+=--no-builtin-rules
-
-# Update the versions depending on your project requirements | Last Updated 2023-03-02
-DOCKER_IMAGE=plone/server-dev:6.0.6
-DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:6.0.6
-KGS=
-NODEBIN = ./node_modules/.bin
-
-# Plone 5 legacy
-DOCKER_IMAGE5=plone/plone-backend:5.2.12
-KGS5=plone.restapi==8.43.3 plone.volto==4.1.0 plone.rest==3.0.1
-TESTING_ADDONS=plone.app.robotframework==2.0.0 plone.app.testing==7.0.0
-
-# Project settings
-
-DIR=$(shell basename $$(pwd))
-
-# Recipe snippets for reuse
-
-# We like colors
-# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
-RED=`tput setaf 1`
-GREEN=`tput setaf 2`
-RESET=`tput sgr0`
-YELLOW=`tput setaf 3`
-
-
-# Top-level targets
-.PHONY: all
-all: project
-
-.PHONY: help
-help: ## Show this help.
- @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
-
-.PHONY: start-backend-docker
-start-backend-docker: ## Starts a Docker-based backend
- @echo "$(GREEN)==> Start Docker-based Plone Backend$(RESET)"
- docker run -it --rm --name=backend -p 8080:8080 -e SITE=Plone -e ADDONS='$(KGS)' $(DOCKER_IMAGE)
-
-.PHONY: install
-install: ## Install the frontend
- @echo "Install frontend"
- $(MAKE) omelette
- $(MAKE) preinstall
- yarn install
-
-.PHONY: preinstall
-preinstall: ## Preinstall task, checks if missdev (mrs-developer) is present and runs it
- if [ -f $$(pwd)/mrs.developer.json ]; then make develop; fi
-
-.PHONY: develop
-develop: ## Runs missdev in the local project (mrs.developer.json should be present)
- if [ -f $$(pwd)/jsconfig.json ]; then npx -p mrs-developer missdev --config=jsconfig.json --output=addons --fetch-https; fi
- if [ ! -f $$(pwd)/jsconfig.json ]; then npx -p mrs-developer missdev --output=addons --fetch-https; fi
-
-.PHONY: omelette
-omelette: ## Creates the omelette folder that contains a link to the installed version of Volto (a softlink pointing to node_modules/@plone/volto)
- if [ ! -d omelette ]; then ln -sf node_modules/@plone/volto omelette; fi
-
-.PHONY: patches
-patches:
- /bin/bash patches/patchit.sh > /dev/null 2>&1 ||true
-
-.PHONY: start-test-acceptance-server start-test-backend
-start-test-acceptance-server start-test-backend : ## Start Test Plone Backend
- @echo "$(GREEN)==> Start Test Plone Backend$(RESET)"
- docker run -i --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE)
- ## KGS in case you need a Plone 5.2 series (comment/remove above line):
- # docker run -i --rm -e ZSERVER_HOST=0.0.0.0 -e ZSERVER_PORT=55001 -p 55001:55001 -e ADDONS='$(KGS5) $(TESTING_ADDONS)' -e APPLY_PROFILES=plone.app.contenttypes:plone-content,plone.restapi:default,plone.volto:default-homepage -e CONFIGURE_PACKAGES=plone.app.contenttypes,plone.restapi,plone.volto,plone.volto.cors $(DOCKER_IMAGE5) ./bin/robot-server plone.app.robotframework.testing.VOLTO_ROBOT_TESTING
-
-.PHONY: start-test-acceptance-frontend
-start-test-acceptance-frontend: ## Start the Acceptance Frontend Fixture
- RAZZLE_API_PATH=http://127.0.0.1:55001/plone yarn build && yarn start:prod
-
-.PHONY: test-acceptance
-test-acceptance: ## Start Core Cypress Acceptance Tests
- $(NODEBIN)/cypress open
-
-.PHONY: test-acceptance-headless
-test-acceptance-headless: ## Start Core Cypress Acceptance Tests in headless mode
- $(NODEBIN)/cypress run
-
-.PHONY: full-test-acceptance
-full-test-acceptance: ## Runs Core Full Acceptance Testing in headless mode
- $(NODEBIN)/start-test "make start-test-acceptance-server" http-get://127.0.0.1:55001/plone "make start-test-acceptance-frontend" http://127.0.0.1:3000 "make test-acceptance-headless"
-
-.PHONY: test-acceptance
-test-acceptance-addon: ## Start Core Cypress Acceptance Tests for an addon
- $(NODEBIN)/cypress open -P $(ADDONPATH)
-
-.PHONY: test-acceptance-headless
-test-acceptance-addon-headless: ## Start Core Cypress Acceptance Tests for an addon in headless mode
- $(NODEBIN)/cypress run -P $(ADDONPATH)
-
-.PHONY: full-test-acceptance-addon
-full-test-acceptance-addon: ## Runs Core Full Acceptance Testing for an addon in headless mode
- $(NODEBIN)/start-test "make start-test-acceptance-server" http-get://127.0.0.1:55001/plone "make start-test-acceptance-frontend" http://127.0.0.1:3000 "make test-acceptance-addon-headless"
diff --git a/apps/plone/README.md b/apps/plone/README.md
deleted file mode 100644
index da034e8ad3..0000000000
--- a/apps/plone/README.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# README.md
-
-This README guides you for development with Volto and Plone.
-
-
-## Documentation
-
-[Volto Hands-On](https://training.plone.org/voltohandson/index.html) is a training on how to create your own website.
-
-
-## Quick Start
-
-Below is a list of useful commands.
-
-### `make install`
-
-Installs and checks out the `mrs-developer` directives (`make develop`), creates a shortcut to the Volto source code (`omelette` folder), then triggers the install of the frontend environment.
-
-
-### `yarn start`
-
-Runs the project in development mode.
-You can view your application at `http://localhost:3000`.
-
-The page will reload if you make edits.
-
-
-### `yarn build`
-
-Builds the app for production to the `build` folder.
-
-The build is minified and the filenames include the hashes.
-Your app is ready to be deployed!
-
-
-### `yarn start:prod`
-
-Runs the compiled app in production.
-
-You can view your application at `http://localhost:3000`.
-
-
-### `yarn test`
-
-Runs the test watcher (Jest) in an interactive mode.
-By default, runs tests related to files changed since the last commit.
-
-
-### `yarn i18n`
-
-Runs the test i18n runner, which extracts all the translation strings and generates the needed files.
-
-### mrs-developer
-
-[mrs-developer](https://github.com/collective/mrs-developer) is a great tool for developing multiple packages at the same time.
-
-mrs-developer should work with this project by running the configured shortcut script:
-
-```bash
-make develop
-```
-
-Volto's latest Razzle configuration will honor your `tsconfig.json` or `jsconfig.json` file for any customizations.
-
-In case you don't want (or can't) install mrs-developer globally, you can install it in this project by running:
-
-```bash
-yarn add -W mrs-developer
-```
-
-
-## Acceptance tests
-
-To run the project acceptance tests while developing using Cypress as the test runner, there are some `Makefile` commands in place in the repository root.
-
-Run them in order:
-
-- `start-test-acceptance-server`: Start the server fixture in Docker (previous build required).
-- `start-test-acceptance-frontend`: Start the Core Acceptance Frontend Fixture in development mode.
-- `test-acceptance`: Start the Core Cypress Acceptance Tests in development mode.
-- `full-test-acceptance`: Start the entire suite (backend + frontend + headless tests) using Cypress Acceptance Tests in headless (CI) mode.
diff --git a/apps/plone/babel.config.js b/apps/plone/babel.config.js
deleted file mode 100644
index a900a7555e..0000000000
--- a/apps/plone/babel.config.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('@plone/volto/babel');
diff --git a/apps/plone/cypress.config.js b/apps/plone/cypress.config.js
deleted file mode 100644
index 08d55e62ba..0000000000
--- a/apps/plone/cypress.config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- e2e: {
- baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/tests/**/*.cy.{js,jsx}',
- },
-});
diff --git a/apps/plone/cypress/fixtures/broccoli.jpg b/apps/plone/cypress/fixtures/broccoli.jpg
deleted file mode 100644
index 456706d662..0000000000
Binary files a/apps/plone/cypress/fixtures/broccoli.jpg and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/example.json b/apps/plone/cypress/fixtures/example.json
deleted file mode 100644
index 02e4254378..0000000000
--- a/apps/plone/cypress/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io",
- "body": "Fixtures are a great way to mock data for responses to routes"
-}
diff --git a/apps/plone/cypress/fixtures/file.pdf b/apps/plone/cypress/fixtures/file.pdf
deleted file mode 100644
index ef9c798194..0000000000
Binary files a/apps/plone/cypress/fixtures/file.pdf and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/halfdome2022.jpg b/apps/plone/cypress/fixtures/halfdome2022.jpg
deleted file mode 100644
index 58c9411ca2..0000000000
Binary files a/apps/plone/cypress/fixtures/halfdome2022.jpg and /dev/null differ
diff --git a/apps/plone/cypress/fixtures/image.png b/apps/plone/cypress/fixtures/image.png
deleted file mode 100644
index 4c109bab8d..0000000000
Binary files a/apps/plone/cypress/fixtures/image.png and /dev/null differ
diff --git a/apps/plone/cypress/plugins/index.js b/apps/plone/cypress/plugins/index.js
deleted file mode 100644
index dffed2532f..0000000000
--- a/apps/plone/cypress/plugins/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-};
diff --git a/apps/plone/cypress/support/commands.js b/apps/plone/cypress/support/commands.js
deleted file mode 100644
index 03fe48c752..0000000000
--- a/apps/plone/cypress/support/commands.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import '@plone/volto/cypress/add-commands';
-
-// Set PLONE_SITE_ID and PLONE_API_URL as cypress environment variables
-// if testing without using localhost or a site id of `plone`.
-
-// --- CUSTOM COMMANDS -------------------------------------------------------------
-Cypress.Commands.add('custom_command', () => {
- // Custom code here...
-});
diff --git a/apps/plone/cypress/support/e2e.js b/apps/plone/cypress/support/e2e.js
deleted file mode 100644
index 48912f7f19..0000000000
--- a/apps/plone/cypress/support/e2e.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'cypress-axe';
-import 'cypress-file-upload';
-import './commands';
-import { setup, teardown } from './reset-fixture';
-
-beforeEach(function () {
- cy.log('Setting up API fixture');
- setup();
-});
-
-afterEach(function () {
- cy.log('Tearing down API fixture');
- teardown();
-});
diff --git a/apps/plone/cypress/support/reset-fixture.js b/apps/plone/cypress/support/reset-fixture.js
deleted file mode 100644
index 4e2b4372d5..0000000000
--- a/apps/plone/cypress/support/reset-fixture.js
+++ /dev/null
@@ -1,43 +0,0 @@
-function setup() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_setupplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Setting up API fixture'));
-}
-
-function teardown() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_teardownplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Tearing down API fixture'));
-}
-
-function main() {
- const command = process.argv[2];
- switch (command) {
- case 'setup':
- setup();
- break;
- case 'teardown':
- teardown();
- break;
- default:
- setup();
- }
-}
-
-// This is the equivalent of `if __name__ == '__main__'` in Python :)
-if (require.main === module) {
- main();
-}
-
-module.exports = {
- setup,
- teardown,
-};
diff --git a/apps/plone/locales/de/LC_MESSAGES/volto.po b/apps/plone/locales/de/LC_MESSAGES/volto.po
deleted file mode 100644
index 49d2e20662..0000000000
--- a/apps/plone/locales/de/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Translation of volto.pot to German
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: German \n"
-"Language: de\n"
-"Language-Team: German \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: de\n"
-"Language-Name: Deutsch\n"
-"Preferred-Encodings: utf-8 latin1\n"
-"X-Is-Fallback-For: de-at de-li de-lu de-ch de-de\n"
-
-
diff --git a/apps/plone/locales/en/LC_MESSAGES/volto.po b/apps/plone/locales/en/LC_MESSAGES/volto.po
deleted file mode 100644
index 12b9a47628..0000000000
--- a/apps/plone/locales/en/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Last-Translator: \n"
-"Language: \n"
-"Language-Team: \n"
-"Content-Type: \n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-
diff --git a/apps/plone/locales/es/LC_MESSAGES/volto.po b/apps/plone/locales/es/LC_MESSAGES/volto.po
deleted file mode 100644
index 784b64fb17..0000000000
--- a/apps/plone/locales/es/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,21 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-12-03 03:20-0400\n"
-"PO-Revision-Date: 2019-12-12 06:07-0400\n"
-"Last-Translator: Leonardo J. Caballero G. \n"
-"Language: es\n"
-"Language-Team: ES \n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"X-Generator: Virtaal 0.7.1\n"
-"Language-Code: es\n"
-"Language-Name: Español\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
-"X-Is-Fallback-For: es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve\n"
-
-
diff --git a/apps/plone/locales/eu/LC_MESSAGES/volto.po b/apps/plone/locales/eu/LC_MESSAGES/volto.po
deleted file mode 100644
index 984adca72e..0000000000
--- a/apps/plone/locales/eu/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-# Translation of volto.pot to EU
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Plone i18n \n"
-"Language: eu\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: eu\n"
-"Language-Name: eu\n"
-"Preferred-Encodings: utf-8 latin1\n"
-
-
diff --git a/apps/plone/locales/fr/LC_MESSAGES/volto.po b/apps/plone/locales/fr/LC_MESSAGES/volto.po
deleted file mode 100644
index f6050e4530..0000000000
--- a/apps/plone/locales/fr/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Translation of volto.pot to French
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Benoît Suttor \n"
-"Language: fr\n"
-"Language-Team: French \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: fr\n"
-"Language-Name: French\n"
-"Preferred-Encodings: utf-8 latin1\n"
-"X-Is-Fallback-For: fr-be fr-ca fr-lu fr-mc fr-ch fr-fr\n"
-
-
diff --git a/apps/plone/locales/it/LC_MESSAGES/volto.po b/apps/plone/locales/it/LC_MESSAGES/volto.po
deleted file mode 100644
index 12b9a47628..0000000000
--- a/apps/plone/locales/it/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Last-Translator: \n"
-"Language: \n"
-"Language-Team: \n"
-"Content-Type: \n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-
diff --git a/apps/plone/locales/ja/LC_MESSAGES/volto.po b/apps/plone/locales/ja/LC_MESSAGES/volto.po
deleted file mode 100644
index a543785fbf..0000000000
--- a/apps/plone/locales/ja/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2019-11-15 12:06+0900\n"
-"Last-Translator: Manabu TERADA \n"
-"Language: ja\n"
-"Language-Team: Plone Japanese Team \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: ja\n"
-"Language-Name: Japanese\n"
-"Preferred-Encodings: utf-8\n"
-"X-Is-Fallback-For: ja-jp\n"
-
-
diff --git a/apps/plone/locales/nl/LC_MESSAGES/volto.po b/apps/plone/locales/nl/LC_MESSAGES/volto.po
deleted file mode 100644
index a9816b4cea..0000000000
--- a/apps/plone/locales/nl/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: PlonenPOT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: 2017-02-28 08:52+0100\n"
-"Last-Translator: Dutch \n"
-"Language: nl\n"
-"Language-Team: Dutch \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Language-Code: nl\n"
-"Language-Name: Nederlands\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/pt/LC_MESSAGES/volto.po b/apps/plone/locales/pt/LC_MESSAGES/volto.po
deleted file mode 100644
index 3713b40e7d..0000000000
--- a/apps/plone/locales/pt/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,19 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-12-17T20:38:19.520Z\n"
-"PO-Revision-Date: \n"
-"Last-Translator: Emanuel de Jesus , 2019\n"
-"Language: pt\n"
-"Language-Team: Portuguese (https://www.transifex.com/plone/teams/14552/pt/)\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Domain: volto\n"
-"Language-Code: pt\n"
-"Language-Name: Portuguese\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po b/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po
deleted file mode 100644
index d788679fdc..0000000000
--- a/apps/plone/locales/pt_BR/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2017-02-28 08:52+0100\n"
-"Last-Translator: Léu Almeida \n"
-"Language: pt-BR\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"MIME-Version: 1.0\n"
-"Language-Code: pt-br\n"
-"Language-Name: Português do Brasil\n"
-"Preferred-Encodings: utf-8\n"
-
-
diff --git a/apps/plone/locales/ro/LC_MESSAGES/volto.po b/apps/plone/locales/ro/LC_MESSAGES/volto.po
deleted file mode 100644
index 42c60f4d17..0000000000
--- a/apps/plone/locales/ro/LC_MESSAGES/volto.po
+++ /dev/null
@@ -1,18 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2016-10-22 16:41-0500\n"
-"Last-Translator: Plone i18n \n"
-"Language: ro\n"
-"Language-Team: Plone i18n \n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"MIME-Version: 1.0\n"
-"Language-Code: ro\n"
-"Language-Name: Romanian\n"
-"Preferred-Encodings: utf-8 latin1\n"
-
-
diff --git a/apps/plone/locales/volto.pot b/apps/plone/locales/volto.pot
deleted file mode 100644
index 430c431165..0000000000
--- a/apps/plone/locales/volto.pot
+++ /dev/null
@@ -1,16 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2018-10-03T09:01:24.737Z\n"
-"Last-Translator: Plone i18n \n"
-"Language-Team: Plone i18n \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"Language-Code: en\n"
-"Language-Name: English\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
-
-
diff --git a/apps/plone/mrs.developer.json b/apps/plone/mrs.developer.json
deleted file mode 100644
index 502bb075b9..0000000000
--- a/apps/plone/mrs.developer.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "volto-volto-project": {
- "local": "addons/volto-volto-project/src"
- }
-}
\ No newline at end of file
diff --git a/apps/plone/package.json b/apps/plone/package.json
deleted file mode 100644
index e87b8c95e3..0000000000
--- a/apps/plone/package.json
+++ /dev/null
@@ -1,361 +0,0 @@
-{
- "name": "plone",
- "description": "Plone CMS Volto frontend project",
- "license": "MIT",
- "version": "1.0.0",
- "scripts": {
- "start": "razzle start",
- "build": "razzle build --noninteractive",
- "lint": "./node_modules/eslint/bin/eslint.js --max-warnings=0 'src/**/*.{js,jsx,ts,tsx}'",
- "lint:fix": "./node_modules/eslint/bin/eslint.js --fix 'src/**/*.{js,jsx,ts,tsx}'",
- "lint:ci": "./node_modules/eslint/bin/eslint.js --max-warnings=0 -f checkstyle 'src/**/*.{js,jsx,ts,tsx}' > eslint.xml",
- "prettier": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "prettier:fix": "./node_modules/.bin/prettier --single-quote --write 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "prettier:ci": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,css,scss}'",
- "stylelint": "stylelint 'theme/**/*.{css,scss,less}' 'src/**/*.{css,scss,less}'",
- "stylelint:overrides": "stylelint 'theme/**/*.overrides' 'src/**/*.overrides'",
- "stylelint:fix": "yarn stylelint --fix && yarn stylelint:overrides --fix",
- "test": "razzle test --passWithNoTests",
- "typecheck": "tsc --project tsconfig.json --noEmit",
- "cypress:open": "make test-acceptance",
- "cypress:run": "test-acceptance-headless",
- "start:prod": "NODE_ENV=production node build/server.js",
- "i18n": "rm -rf build/messages && NODE_ENV=production i18n",
- "storybook": "start-storybook -p 6006",
- "build-storybook": "build-storybook"
- },
- "workspaces": [
- "src/addons/volto-volto-project"
- ],
- "addons": [
- "volto-volto-project"
- ],
- "jest": {
- "modulePathIgnorePatterns": [
- "api"
- ],
- "transform": {
- "^.+\\.js(x)?$": "babel-jest",
- "^.+\\.(png)$": "jest-file",
- "^.+\\.(jpg)$": "jest-file",
- "^.+\\.(svg)$": "./node_modules/@plone/volto/jest-svgsystem-transform.js"
- },
- "transformIgnorePatterns": [
- "/node_modules/(?!@plone/volto).+\\.js$"
- ],
- "moduleNameMapper": {
- "@plone/volto/cypress/(.*)$": "/node_modules/@plone/volto/cypress/$1",
- "@plone/volto/addon-registry": "/node_modules/@plone/volto/addon-registry",
- "@plone/volto/webpack-plugins/webpack-less-plugin": "/node_modules/@plone/volto/webpack-plugins/webpack-less-plugin",
- "@plone/volto/babel": "/node_modules/@plone/volto/babel",
- "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
- "@plone/volto-slate/(.*)$": "/node_modules/@plone/volto/packages/volto-slate/src/$1",
- "load-volto-addons": "/node_modules/@plone/volto/jest-addons-loader.js",
- "@package/(.*)$": "/src/$1",
- "@root/(.*)$": "/src/$1",
- "~/(.*)$": "/src/$1",
- "\\.(css|less|scss|sass)$": "identity-obj-proxy"
- },
- "coverageThreshold": {
- "global": {
- "branches": 10,
- "functions": 10,
- "lines": 10,
- "statements": 10
- }
- },
- "setupFiles": [
- "@plone/volto/test-setup-globals.js",
- "@plone/volto/test-setup-config.jsx"
- ],
- "globals": {
- "__DEV__": true
- }
- },
- "prettier": {
- "trailingComma": "all",
- "singleQuote": true,
- "overrides": [
- {
- "files": "*.overrides",
- "options": {
- "parser": "less"
- }
- }
- ]
- },
- "stylelint": {
- "extends": [
- "stylelint-config-idiomatic-order"
- ],
- "plugins": [
- "stylelint-prettier"
- ],
- "overrides": [
- {
- "files": [
- "**/*.less"
- ],
- "customSyntax": "postcss-less"
- },
- {
- "files": [
- "**/*.overrides"
- ],
- "customSyntax": "postcss-less"
- },
- {
- "files": [
- "**/*.scss"
- ],
- "customSyntax": "postcss-scss"
- }
- ],
- "rules": {
- "prettier/prettier": true,
- "rule-empty-line-before": [
- "always-multi-line",
- {
- "except": [
- "first-nested"
- ],
- "ignore": [
- "after-comment"
- ]
- }
- ]
- },
- "ignoreFiles": "theme/themes/default/**/*.overrides"
- },
- "browserslist": [
- ">1%",
- "last 4 versions",
- "Firefox ESR",
- "not ie 11",
- "not dead"
- ],
- "engines": {
- "node": "^16 || ^18 || ^20"
- },
- "dependencies": {
- "@loadable/component": "5.14.1",
- "@loadable/server": "5.14.0",
- "@plone/registry": "workspace:*",
- "@plone/scripts": "workspace:*",
- "@plone/volto": "workspace:*",
- "@plone/volto-slate": "workspace:*",
- "@redux-devtools/extension": "^3.3.0",
- "classnames": "2.2.6",
- "connected-react-router": "6.8.0",
- "debug": "4.3.2",
- "decorate-component-with-props": "1.2.1",
- "dependency-graph": "0.10.0",
- "detect-browser": "5.1.0",
- "diff": "3.5.0",
- "express": "4.19.2",
- "filesize": "6",
- "github-slugger": "1.4.0",
- "history": "4.10.1",
- "hoist-non-react-statics": "3.3.2",
- "http-proxy-middleware": "2.0.1",
- "image-extensions": "1.1.0",
- "immutable": "3",
- "is-hotkey": "0.2.0",
- "is-url": "1.2.4",
- "jotai": "2.0.3",
- "jwt-decode": "2.2.0",
- "linkify-it": "3.0.2",
- "locale": "0.1.0",
- "lodash": "4.17.21",
- "lodash-move": "1.1.1",
- "moment": "2.29.4",
- "object-assign": "4.1.1",
- "prepend-http": "2",
- "pretty-bytes": "5.3.0",
- "prismjs": "1.27.0",
- "process": "^0.11.10",
- "promise-file-reader": "1.0.2",
- "prop-types": "15.7.2",
- "query-string": "7.1.0",
- "rc-time-picker": "3.7.3",
- "react": "18.2.0",
- "react-anchor-link-smooth-scroll": "1.0.12",
- "react-animate-height": "2.0.17",
- "react-beautiful-dnd": "13.0.0",
- "react-cookie": "4.1.1",
- "react-dates": "21.5.1",
- "react-detect-click-outside": "1.1.1",
- "react-dnd": "5.0.0",
- "react-dnd-html5-backend": "5.0.1",
- "react-dom": "18.2.0",
- "react-dropzone": "11.1.0",
- "react-fast-compare": "2.0.4",
- "react-image-gallery": "1.2.7",
- "react-intersection-observer": "9.1.0",
- "react-intl": "3.12.1",
- "react-intl-redux": "2.3.0",
- "react-medium-image-zoom": "3.0.15",
- "react-popper": "^2.3.0",
- "react-redux": "8.1.2",
- "react-router": "5.2.0",
- "react-router-config": "5.1.1",
- "react-router-dom": "5.2.0",
- "react-router-hash-link": "2.4.3",
- "react-select": "4.3.1",
- "react-select-async-paginate": "0.5.3",
- "react-share": "2.3.1",
- "react-side-effect": "2.1.2",
- "react-simple-code-editor": "0.7.1",
- "react-sortable-hoc": "2.0.0",
- "react-test-renderer": "18.2.0",
- "react-toastify": "5.5.0",
- "react-transition-group": "4.4.5",
- "react-virtualized": "9.22.3",
- "redux": "4.2.1",
- "redux-actions": "3.0.0",
- "redux-connect": "10.0.0",
- "redux-localstorage-simple": "2.5.1",
- "redux-mock-store": "1.5.4",
- "redux-thunk": "2.4.2",
- "rrule": "2.7.1",
- "semantic-ui-less": "2.4.1",
- "semantic-ui-react": "2.1.5",
- "serialize-javascript": "3.1.0",
- "slate": "0.100.0",
- "slate-hyperscript": "0.100.0",
- "slate-react": "0.98.4",
- "superagent": "3.8.2",
- "tlds": "1.203.1",
- "undoo": "0.5.0",
- "universal-cookie": "4.0.4",
- "universal-cookie-express": "4.0.3",
- "url": "^0.11.3",
- "use-deep-compare-effect": "1.8.1",
- "uuid": "^8.3.2",
- "volto-volto-project": "workspace:*"
- },
- "devDependencies": {
- "@babel/core": "^7.0.0",
- "@babel/eslint-parser": "7.22.15",
- "@babel/plugin-proposal-export-default-from": "7.18.10",
- "@babel/plugin-proposal-export-namespace-from": "7.18.9",
- "@babel/plugin-proposal-json-strings": "7.18.6",
- "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
- "@babel/plugin-proposal-throw-expressions": "7.18.6",
- "@babel/plugin-syntax-export-namespace-from": "7.8.3",
- "@babel/runtime": "7.20.6",
- "@babel/types": "7.20.5",
- "@jest/globals": "^29.7.0",
- "@loadable/babel-plugin": "5.13.2",
- "@loadable/webpack-plugin": "5.15.2",
- "@plone/scripts": "workspace:*",
- "@plone/types": "1.0.0-alpha.11",
- "@plone/volto-coresandbox": "workspace:*",
- "@sinonjs/fake-timers": "^6.0.1",
- "@storybook/addon-actions": "^8.0.4",
- "@storybook/addon-controls": "^8.0.4",
- "@storybook/addon-docs": "^8.0.4",
- "@storybook/addon-essentials": "^8.0.4",
- "@storybook/addon-links": "^8.0.4",
- "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
- "@storybook/builder-webpack5": "^6.5.15",
- "@storybook/manager-webpack5": "^6.5.15",
- "@storybook/react": "^8.0.4",
- "@storybook/react-webpack5": "^8.0.4",
- "@storybook/theming": "^8.0.4",
- "@testing-library/cypress": "10.0.1",
- "@testing-library/jest-dom": "6.4.2",
- "@testing-library/react": "14.2.0",
- "@testing-library/react-hooks": "8.0.1",
- "@types/jest": "^29.5.8",
- "@types/loadable__component": "^5.13.9",
- "@types/lodash": "^4.14.201",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "@types/react-router-dom": "^5.3.3",
- "@types/react-test-renderer": "18.0.7",
- "@types/uuid": "^9.0.2",
- "@typescript-eslint/eslint-plugin": "^7.7.0",
- "@typescript-eslint/parser": "^7.7.0",
- "autoprefixer": "10.4.8",
- "axe-core": "4.4.2",
- "babel-loader": "9.1.0",
- "babel-plugin-add-module-exports": "0.2.1",
- "babel-plugin-lodash": "3.3.4",
- "babel-plugin-react-intl": "5.1.17",
- "babel-plugin-root-import": "6.1.0",
- "babel-preset-razzle": "4.2.18",
- "bundlewatch": "0.3.3",
- "circular-dependency-plugin": "5.2.2",
- "css-loader": "5.2.7",
- "cypress": "13.13.2",
- "cypress-axe": "1.5.0",
- "cypress-file-upload": "5.0.8",
- "deep-freeze": "0.0.1",
- "eslint": "^8.57.0",
- "eslint-config-prettier": "^9.1.0",
- "eslint-config-react-app": "^7.0.1",
- "eslint-import-resolver-alias": "^1.1.2",
- "eslint-import-resolver-babel-plugin-root-import": "^1.1.1",
- "eslint-import-resolver-typescript": "^3.6.1",
- "eslint-plugin-import": "^2.29.1",
- "eslint-plugin-jsx-a11y": "^6.7.1",
- "eslint-plugin-prettier": "^5.1.3",
- "eslint-plugin-react": "^7.34.1",
- "eslint-plugin-react-hooks": "^4.6.0",
- "full-icu": "1.4.0",
- "glob": "7.1.6",
- "html-webpack-plugin": "5.5.0",
- "identity-obj-proxy": "3.0.0",
- "jest": "26.6.3",
- "jest-environment-jsdom": "^26",
- "jest-file": "1.0.0",
- "jest-junit": "8.0.0",
- "jsdom": "^16.7.0",
- "jsonwebtoken": "9.0.0",
- "less": "3.11.1",
- "less-loader": "11.1.0",
- "lodash-webpack-plugin": "0.11.6",
- "mini-css-extract-plugin": "2.7.2",
- "moment-locales-webpack-plugin": "1.2.0",
- "mrs-developer": "^2.1.1",
- "postcss": "8.4.31",
- "postcss-flexbugs-fixes": "5.0.2",
- "postcss-less": "6.0.0",
- "postcss-load-config": "3.1.4",
- "postcss-loader": "7.0.2",
- "postcss-overrides": "3.1.4",
- "postcss-scss": "4.0.6",
- "prettier": "3.2.5",
- "razzle": "4.2.18",
- "razzle-dev-utils": "4.2.18",
- "razzle-plugin-scss": "4.2.18",
- "react-docgen-typescript-plugin": "^1.0.5",
- "react-error-overlay": "6.0.9",
- "react-is": "^18.2.0",
- "release-it": "^17.1.1",
- "semver": "^7.5.4",
- "start-server-and-test": "1.14.0",
- "storybook": "^8.0.4",
- "style-loader": "3.3.1",
- "stylelint": "^16.3.1",
- "stylelint-config-idiomatic-order": "10.0.0",
- "stylelint-prettier": "5.0.0",
- "svg-loader": "0.0.2",
- "svgo-loader": "3.0.3",
- "terser-webpack-plugin": "5.3.6",
- "tmp": "0.2.1",
- "ts-jest": "^26.4.2",
- "ts-loader": "9.4.4",
- "tsconfig": "*",
- "typescript": "^5.4.2",
- "use-trace-update": "1.3.2",
- "wait-on": "6.0.0",
- "webpack": "5.90.1",
- "webpack-bundle-analyzer": "4.10.1",
- "webpack-dev-server": "4.11.1",
- "webpack-node-externals": "3.0.0",
- "why": "0.6.2"
- },
- "theme": "volto-volto-project"
-}
diff --git a/apps/plone/patches/patchit.sh b/apps/plone/patches/patchit.sh
deleted file mode 100644
index 7cd11b5edc..0000000000
--- a/apps/plone/patches/patchit.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-patch --quiet -p0 -N node_modules/razzle/config/createJestConfig.js < patches/razzle-jest.patch
diff --git a/apps/plone/patches/razzle-jest.patch b/apps/plone/patches/razzle-jest.patch
deleted file mode 100644
index 5b822cf57f..0000000000
--- a/apps/plone/patches/razzle-jest.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- node_modules/razzle/config/createJestConfig.js 2020-05-09 19:48:07.276871617 +0200
-+++ patched.js 2020-05-09 19:50:07.049471664 +0200
-@@ -60,6 +60,7 @@
- 'moduleDirectories',
- 'moduleFileExtensions',
- 'moduleNameMapper',
-+ 'modulePathIgnorePatterns',
- 'modulePaths',
- 'snapshotSerializers',
- 'setupFiles',
diff --git a/apps/plone/public/android-chrome-192x192.png b/apps/plone/public/android-chrome-192x192.png
deleted file mode 100644
index 5a31bad458..0000000000
Binary files a/apps/plone/public/android-chrome-192x192.png and /dev/null differ
diff --git a/apps/plone/public/android-chrome-512x512.png b/apps/plone/public/android-chrome-512x512.png
deleted file mode 100644
index b522d84589..0000000000
Binary files a/apps/plone/public/android-chrome-512x512.png and /dev/null differ
diff --git a/apps/plone/public/apple-touch-icon.png b/apps/plone/public/apple-touch-icon.png
deleted file mode 100644
index f0a5f8bc85..0000000000
Binary files a/apps/plone/public/apple-touch-icon.png and /dev/null differ
diff --git a/apps/plone/public/favicon-16x16.png b/apps/plone/public/favicon-16x16.png
deleted file mode 100644
index bf6cf158a0..0000000000
Binary files a/apps/plone/public/favicon-16x16.png and /dev/null differ
diff --git a/apps/plone/public/favicon-32x32.png b/apps/plone/public/favicon-32x32.png
deleted file mode 100644
index 0411131449..0000000000
Binary files a/apps/plone/public/favicon-32x32.png and /dev/null differ
diff --git a/apps/plone/public/favicon.ico b/apps/plone/public/favicon.ico
deleted file mode 100644
index b9c0d0c0a6..0000000000
Binary files a/apps/plone/public/favicon.ico and /dev/null differ
diff --git a/apps/plone/public/icon.svg b/apps/plone/public/icon.svg
deleted file mode 100644
index 72640f02bd..0000000000
--- a/apps/plone/public/icon.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/apps/plone/public/index.html.spa b/apps/plone/public/index.html.spa
deleted file mode 100644
index 27e655dcab..0000000000
--- a/apps/plone/public/index.html.spa
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
- React App
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/plone/public/robots.txt b/apps/plone/public/robots.txt
deleted file mode 100644
index 54fb98911d..0000000000
--- a/apps/plone/public/robots.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-User-agent: *
-
diff --git a/apps/plone/public/site.webmanifest b/apps/plone/public/site.webmanifest
deleted file mode 100644
index b20abb7cbb..0000000000
--- a/apps/plone/public/site.webmanifest
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "",
- "short_name": "",
- "icons": [
- {
- "src": "/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/apps/plone/razzle.config.js b/apps/plone/razzle.config.js
deleted file mode 100644
index 6bab4ae583..0000000000
--- a/apps/plone/razzle.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Replace with custom razzle config when needed.
- * @module razzle.config
- */
-const fs = require('fs');
-
-let voltoPath = './node_modules/@plone/volto';
-
-let configFile;
-if (fs.existsSync(`${this.projectRootPath}/tsconfig.json`))
- configFile = `${this.projectRootPath}/tsconfig.json`;
-else if (fs.existsSync(`${this.projectRootPath}/jsconfig.json`))
- configFile = `${this.projectRootPath}/jsconfig.json`;
-
-if (configFile) {
- const jsConfig = require(configFile).compilerOptions;
- const pathsConfig = jsConfig.paths;
- if (pathsConfig['@plone/volto'])
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
-}
-
-const defaultVoltoRazzleConfig = require(`${voltoPath}/razzle.config`);
-const { modifyWebpackConfig } = defaultVoltoRazzleConfig;
-
-const customModifyWebpackConfig = ({
- env: { target, dev },
- webpackConfig,
- webpackObject,
- options,
-}) => {
- const config = modifyWebpackConfig({
- env: { target, dev },
- webpackConfig,
- webpackObject,
- options,
- });
- // add custom code here..
- return config;
-};
-
-module.exports = {
- ...defaultVoltoRazzleConfig,
- modifyWebpackConfig: customModifyWebpackConfig,
-};
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml
deleted file mode 100644
index 51d072e448..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/acceptance.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-name: Acceptance tests
-on: [push]
-
-env:
- ADDON_NAME: "volto-volto-project"
- ADDON_PATH: "volto-volto-project"
- VOLTO_VERSION: "17.0.0"
-
-jobs:
-
- acceptance:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
-
- - name: Install Cypress
- run: |
- cd acceptance
- yarn
-
- - name: "Cypress: Acceptance tests"
- uses: cypress-io/github-action@v6
- env:
- BABEL_ENV: production
- CYPRESS_RETRIES: 2
- with:
- parallel: false
- browser: chrome
- working-directory: acceptance
- spec: cypress/tests/*.js
- install: false
- start: |
- docker compose -f ci.yml --profile prod up
- wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000'
-
- # Upload Cypress screenshots
- - uses: actions/upload-artifact@v3
- if: failure()
- with:
- name: cypress-screenshots-acceptance
- path: acceptance/cypress/screenshots
-
- # Upload Cypress videos
- - uses: actions/upload-artifact@v3
- if: failure()
- with:
- name: cypress-videos-acceptance
- path: acceptance/cypress/videos
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml
deleted file mode 100644
index 113171e7e5..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/changelog.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Changelog check
-on:
- pull_request:
- types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
- branches:
- - main
-
-env:
- node-version: 18.x
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- with:
- # Fetch all history
- fetch-depth: '0'
-
- - name: Install pipx
- run: pip install towncrier
-
- # node setup
- - name: Use Node.js ${{ env.node-version }}
- uses: actions/setup-node@v3
- with:
- node-version: ${{ env.node-version }}
- cache: 'yarn'
-
- # node install
- - name: Install dependencies
- run: yarn
-
- - name: Check for presence of a Change Log fragment (only pull requests)
- run: |
- # Fetch the pull request' base branch so towncrier will be able to
- # compare the current branch with the base branch.
- # Source: https://github.com/actions/checkout/#fetch-all-branches.
- git fetch --no-tags origin main
- towncrier check --compare-with origin/main
- env:
- BASE_BRANCH: ${{ github.base_ref }}
- if: github.event_name == 'pull_request'
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml
deleted file mode 100644
index a7eeb58513..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/code.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Code analysis checks
-on: [push]
-jobs:
- codeanalysis:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
-
- steps:
- - name: Main checkout
- uses: actions/checkout@v3
-
- - name: Linting
- run: make lint
diff --git a/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml b/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml
deleted file mode 100644
index 8dbb3de936..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.github/workflows/unit.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Unit Tests
-on: [push]
-jobs:
- unit:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
-
- steps:
- - name: Main checkout
- uses: actions/checkout@v3
-
- - name: Unit tests
- run: make test-ci
diff --git a/apps/plone/src/addons/volto-volto-project/.gitignore b/apps/plone/src/addons/volto-volto-project/.gitignore
deleted file mode 100644
index f11144ebfb..0000000000
--- a/apps/plone/src/addons/volto-volto-project/.gitignore
+++ /dev/null
@@ -1,27 +0,0 @@
-.vscode/
-.history
-logs
-*.log
-npm-debug.log*
-.DS_Store
-*.swp
-yarn-error.log
-
-node_modules
-build
-dist
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-.changelog.draft
-
-# yarn 3
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/sdks
-!.yarn/versions
diff --git a/apps/plone/src/addons/volto-volto-project/CHANGELOG.md b/apps/plone/src/addons/volto-volto-project/CHANGELOG.md
deleted file mode 100644
index 413fb920e1..0000000000
--- a/apps/plone/src/addons/volto-volto-project/CHANGELOG.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Release Notes
-
-
-
-
diff --git a/apps/plone/src/addons/volto-volto-project/Makefile b/apps/plone/src/addons/volto-volto-project/Makefile
deleted file mode 100644
index c13a5ffc57..0000000000
--- a/apps/plone/src/addons/volto-volto-project/Makefile
+++ /dev/null
@@ -1,151 +0,0 @@
-# Yeoman Volto App development
-
-### Defensive settings for make:
-# https://tech.davis-hansson.com/p/make/
-SHELL:=bash
-.ONESHELL:
-.SHELLFLAGS:=-eu -o pipefail -c
-.SILENT:
-.DELETE_ON_ERROR:
-MAKEFLAGS+=--warn-undefined-variables
-MAKEFLAGS+=--no-builtin-rules
-
-CURRENT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-
-# Recipe snippets for reuse
-
-# We like colors
-# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
-RED=`tput setaf 1`
-GREEN=`tput setaf 2`
-RESET=`tput sgr0`
-YELLOW=`tput setaf 3`
-
-PLONE_VERSION=6
-VOLTO_VERSION=17.0.0
-
-ADDON_NAME='volto-volto-project'
-ADDON_PATH='volto-volto-project'
-COMPOSE_FILE=dockerfiles/docker-compose.yml
-ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml
-CMD=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} docker compose
-DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${COMPOSE_FILE}
-DEV_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
-LIVE_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
-ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE}
-
-.PHONY: build-backend
-build-backend: ## Build
- @echo "$(GREEN)==> Build Backend Container $(RESET)"
- ${DEV_COMPOSE} build backend
-
-.PHONY: start-backend
-start-backend: ## Starts Docker backend
- @echo "$(GREEN)==> Start Docker-based Plone Backend $(RESET)"
- ${DEV_COMPOSE} up backend -d
-
-.PHONY: stop-backend
-stop-backend: ## Stop Docker backend
- @echo "$(GREEN)==> Stop Docker-based Plone Backend $(RESET)"
- ${DEV_COMPOSE} stop backend
-
-.PHONY: build-live
-build-live: ## Build Addon live
- @echo "$(GREEN)==> Build Addon development container $(RESET)"
- ${LIVE_COMPOSE} build addon-live
-
-.PHONY: build-addon
-build-addon: ## Build Addon dev
- @echo "$(GREEN)==> Build Addon development container $(RESET)"
- ${DEV_COMPOSE} build addon-dev
-
-.PHONY: start-dev
-start-dev: ## Starts Dev container
- @echo "$(GREEN)==> Start Addon Development container $(RESET)"
- ${DEV_COMPOSE} up addon-dev
-
-.PHONY: dev
-dev: ## Develop the addon
- @echo "$(GREEN)==> Start Development Environment $(RESET)"
- make build-backend
- make start-backend
- make build-addon
- make start-dev
-
-.PHONY: help
-help: ## Show this help.
- @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
-
-## Setup the local environment
-.PHONY: install
-install: ## Install the local environment, Cypress, build acceptance containers
- yarn
- make install-acceptance
-
-# Dev Helpers
-.PHONY: i18n
-i18n: ## Sync i18n
- @echo "$(YELLOW)==> Do not forget to setup the local environment (make install) $(RESET)"
- yarn i18n
-
-.PHONY: format
-format: ## Format codebase
- ${DEV_COMPOSE} run --rm addon-dev lint:fix
- ${DEV_COMPOSE} run --rm addon-dev prettier:fix
- ${DEV_COMPOSE} run --rm addon-dev stylelint:fix
-
-.PHONY: lint
-lint: ## Lint Codebase
- ${DEV_COMPOSE} run --rm addon-dev lint
- ${DEV_COMPOSE} run --rm addon-dev prettier
- ${DEV_COMPOSE} run --rm addon-dev stylelint --allow-empty-input
-
-.PHONY: test
-test: ## Run unit tests
- ${DEV_COMPOSE} run --rm addon-dev test --watchAll
-
-.PHONY: test-ci
-test-ci: ## Run unit tests in CI
- ${DEV_COMPOSE} run -e CI=1 addon-dev test
-
-## Acceptance
-.PHONY: install-acceptance
-install-acceptance: ## Install Cypress, build acceptance containers
- (cd acceptance && yarn)
- ${ACCEPTANCE} --profile dev --profile prod build
-
-.PHONY: start-test-acceptance-server
-start-test-acceptance-server: ## Start acceptance server (for use it in while developing)
- ${ACCEPTANCE} --profile dev up
-
-.PHONY: start-test-acceptance-server-prod
-start-test-acceptance-server-prod: ## Start acceptance server in prod (used by CI)
- ${ACCEPTANCE} --profile prod up -d
-
-.PHONY: test-acceptance
-test-acceptance: ## Start Cypress (for use it while developing)
- (cd acceptance && ./node_modules/.bin/cypress open)
-
-.PHONY: test-acceptance-headless
-test-acceptance-headless: ## Run cypress tests in CI
- (cd acceptance && ./node_modules/.bin/cypress run)
-
-.PHONY: stop-test-acceptance-server
-stop-test-acceptance-server: ## Stop acceptance server (for use it while finished developing)
- ${ACCEPTANCE} --profile dev down
-
-.PHONY: status-test-acceptance-server
-status-test-acceptance-server: ## Status of Acceptance Server (for use it while developing)
- ${ACCEPTANCE} ps
-
-.PHONY: debug-frontend
-debug-frontend: ## Run bash in the Frontend container (for debug infrastructure purposes)
- ${DEV_COMPOSE} run --entrypoint bash addon-dev
-
-.PHONY: pull-backend-image
-pull-backend-image: ## Pulls and updates the backend image (for use it while developing)
- docker pull ghcr.io/voltovolto-project:latest
-
-.PHONY: release
-release: ## Release a version of the add-on
- yarn release
diff --git a/apps/plone/src/addons/volto-volto-project/README.md b/apps/plone/src/addons/volto-volto-project/README.md
deleted file mode 100644
index df94efe809..0000000000
--- a/apps/plone/src/addons/volto-volto-project/README.md
+++ /dev/null
@@ -1,135 +0,0 @@
-# volto-volto-project
-
-You can develop an add-on in isolation using the boilerplate provided by the add-on generator.
-The project is configured with the current add-on installed and ready to work with.
-This is useful to bootstrap an isolated environment that can be used to quickly develop the add-on or for demo purposes.
-It's also useful when testing an add-on in a CI environment.
-
-This process is similar to when you develop a Plone backend add-on in Python, then embed a ready to use Plone build (using buildout or pip) to develop and test the package.
-
-The Dockerized approach performs all these actions in a custom built Docker environment:
-
-1. generates a vanilla project using the official Volto Yo Generator (`@plone/generator-volto`)
-2. configures it to use the add-on with the name stated in the `package.json`
-3. links the root of the add-on inside the created project
-
-After that you can use the inner Dockerized project, and run any standard Volto command for linting, acceptance test, or unit tests using Makefile commands provided for your convenience.
-
-
-## Set up the environment
-
-Run once:
-
-```shell
-make dev
-```
-
-This will build and launch the backend and frontend containers.
-There's no need to build them again after the first time, unless something has changed from the container setup.
-
-To make your IDE play well with this setup, you must run `yarn` once to install the required packages:
-
-```shell
-yarn
-```
-
-
-## Development
-
-This section describes various Makefile commands to ease development.
-
-
-### Build the containers manually
-
-```shell
-make build-backend
-make build-addon
-```
-
-
-### Run the containers
-
-```shell
-make start-dev
-```
-
-This will start both the frontend and backend containers.
-
-
-### Stop backend (Docker)
-
-After developing, you should stop the running backend.
-
-```shell
-make stop-backend
-```
-
-
-### Linting
-
-```shell
-make lint
-```
-
-
-### Formatting
-
-```shell
-make format
-```
-
-
-### i18n
-
-```shell
-make i18n
-```
-
-
-### Unit tests
-
-```shell
-make test
-```
-
-
-### Acceptance tests
-
-First install requirements to run acceptance tests with the following command.
-Run it only once.
-
-```shell
-make install-acceptance
-```
-
-The frontend runs in development mode to allow development while writing.
-To start the servers:
-
-```shell
-make start-test-acceptance-server
-```
-
-To run Cypress tests after you finish development:
-
-```shell
-make test-acceptance
-```
-
-Shut down the backend server after you complete development with tests.
-
-```shell
-make stop-test-acceptance-server
-```
-
-
-## Release
-
-```shell
-make release
-```
-
-To release a release candidate (rc) version:
-
-```shell
-make release-rc
-```
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml b/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml
deleted file mode 100644
index b0c643462a..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/ci.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-version: "3"
-
-services:
-
- addon-acceptance:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- ports:
- - 3000:3000
- depends_on:
- - backend-acceptance
- profiles:
- - prod
-
- backend-acceptance:
- image: plone/server-acceptance:${PLONE_VERSION:-6}
- ports:
- - 55001:55001
- profiles:
- - dev
- - prod
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js
deleted file mode 100644
index d841d7ab7c..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- viewportWidth: 1280,
- e2e: {
- baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/tests/*.cy.{js,jsx}',
- },
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg
deleted file mode 100644
index 456706d662..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/broccoli.jpg and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json
deleted file mode 100644
index 02e4254378..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io",
- "body": "Fixtures are a great way to mock data for responses to routes"
-}
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf
deleted file mode 100644
index ef9c798194..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/file.pdf and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg
deleted file mode 100644
index 58c9411ca2..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/halfdome2022.jpg and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png
deleted file mode 100644
index 4c109bab8d..0000000000
Binary files a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/fixtures/image.png and /dev/null differ
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js
deleted file mode 100644
index dffed2532f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/plugins/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-module.exports = (on, config) => {
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js
deleted file mode 100644
index 44a5000326..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/commands.js
+++ /dev/null
@@ -1 +0,0 @@
-import '@plone/volto-testing/cypress/support/commands';
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js
deleted file mode 100644
index 48912f7f19..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/e2e.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'cypress-axe';
-import 'cypress-file-upload';
-import './commands';
-import { setup, teardown } from './reset-fixture';
-
-beforeEach(function () {
- cy.log('Setting up API fixture');
- setup();
-});
-
-afterEach(function () {
- cy.log('Tearing down API fixture');
- teardown();
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js
deleted file mode 100644
index 4e2b4372d5..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/reset-fixture.js
+++ /dev/null
@@ -1,43 +0,0 @@
-function setup() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_setupplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Setting up API fixture'));
-}
-
-function teardown() {
- const api_url = Cypress.env('API_PATH') || 'http://localhost:55001/plone';
- cy.request({
- method: 'POST',
- url: `${api_url}/RobotRemote`,
- headers: { Accept: 'text/xml', 'content-type': 'text/xml' },
- body: 'run_keywordremote_zodb_teardownplone.app.robotframework.testing.PLONE_ROBOT_TESTING',
- }).then(() => cy.log('Tearing down API fixture'));
-}
-
-function main() {
- const command = process.argv[2];
- switch (command) {
- case 'setup':
- setup();
- break;
- case 'teardown':
- teardown();
- break;
- default:
- setup();
- }
-}
-
-// This is the equivalent of `if __name__ == '__main__'` in Python :)
-if (require.main === module) {
- main();
-}
-
-module.exports = {
- setup,
- teardown,
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js
deleted file mode 100644
index 8ed5b53ab2..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/support/slate.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export const createSlateBlock = () => {
- cy.get('.ui.basic.icon.button.block-add-button').first().click();
- cy.get('.blocks-chooser .title').contains('Text').click();
- cy.get('.ui.basic.icon.button.slate').contains('Text').click();
- return getSelectedSlateEditor();
-};
-
-export const getSelectedSlateEditor = () => {
- return cy.get('.slate-editor.selected [contenteditable=true]').click();
-};
-
-export const getSlateEditorAndType = (selector, type) => {
- return cy.get(selector).focus().click().wait(1000).type(type).wait(1000);
-};
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js b/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js
deleted file mode 100644
index 2f55da049a..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/cypress/tests/basic.cy.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import {
- getSlateEditorAndType,
- getSelectedSlateEditor,
-} from '../support/slate';
-
-context('Basic Acceptance Tests', () => {
- describe('Text Block Tests', () => {
- beforeEach(() => {
- cy.intercept('GET', `/**/*?expand*`).as('content');
- cy.intercept('GET', '/**/Document').as('schema');
-
- // given a logged in editor and a page in edit mode
- cy.autologin();
- cy.createContent({
- contentType: 'Document',
- contentId: 'document',
- contentTitle: 'Document',
- });
- cy.visit('/');
- cy.wait('@content');
- });
-
- it('As editor I can add a page with a text block', function () {
- // when I add a page with a text block
- cy.get('#toolbar-add').click();
- cy.get('#toolbar-add-document').click();
- cy.get('.documentFirstHeading')
- .type('My Page')
- .get('.documentFirstHeading')
- .contains('My Page');
-
- getSlateEditorAndType(
- '.block .slate-editor [contenteditable=true]',
- 'This is the text',
- );
-
- getSelectedSlateEditor().contains('This is the text');
- cy.get('#toolbar-save').click();
- cy.wait('@content');
- cy.url().should('eq', Cypress.config().baseUrl + '/my-page');
- });
-
- it('As editor I can add a link to a text block', function () {
- cy.navigate('/document/edit');
- cy.wait('@schema');
-
- cy.get('.block.inner.title .documentFirstHeading');
-
- cy.log('when I create a link');
-
- cy.getSlateEditorAndType(
- 'Colorless green ideas sleep furiously.',
- ).setSlateSelection('furiously');
- cy.clickSlateButton('Add link');
- cy.get('.slate-toolbar .link-form-container input').type(
- 'https://google.com{enter}',
- );
-
- cy.get('#toolbar-save', { timeout: 10000 }).click();
- cy.wait('@content');
-
- cy.log('then the page view should contain a link');
-
- cy.get('.container p').contains('Colorless green ideas sleep furiously.');
- cy.get('.container p a')
- .should('have.text', 'furiously')
- .and('have.attr', 'href')
- .and('include', 'https://google.com');
- });
- });
-});
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml b/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml
deleted file mode 100644
index 0af8d4a15f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/docker-compose.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-version: "3"
-
-services:
-
- addon-dev:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile.acceptance
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- volumes:
- - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- HOST: 0.0.0.0
- ports:
- - 3000:3000
- - 3001:3001
- tty: true
- depends_on:
- - backend-acceptance
- profiles:
- - dev
-
- addon-acceptance:
- build:
- context: ../
- dockerfile: ./dockerfiles/Dockerfile
- args:
- ADDON_NAME: "${ADDON_NAME}"
- ADDON_PATH: "${ADDON_PATH}"
- VOLTO_VERSION: ${VOLTO_VERSION:-17}
- environment:
- RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone
- RAZZLE_API_PATH: http://localhost:55001/plone
- HOST: 0.0.0.0
- ports:
- - 3000:3000
- depends_on:
- - backend-acceptance
- profiles:
- - prod
-
- backend-acceptance:
- image: plone/server-acceptance:${PLONE_VERSION:-6}
- ports:
- - 55001:55001
- profiles:
- - dev
- - prod
diff --git a/apps/plone/src/addons/volto-volto-project/acceptance/package.json b/apps/plone/src/addons/volto-volto-project/acceptance/package.json
deleted file mode 100644
index c11c8e33cb..0000000000
--- a/apps/plone/src/addons/volto-volto-project/acceptance/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "dependencies": {
- "@plone/volto-testing": "^4.0.0"
- }
-}
diff --git a/apps/plone/src/addons/volto-volto-project/babel.config.js b/apps/plone/src/addons/volto-volto-project/babel.config.js
deleted file mode 100644
index 51bd52b5df..0000000000
--- a/apps/plone/src/addons/volto-volto-project/babel.config.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = function (api) {
- api.cache(true);
- const presets = ['razzle'];
- const plugins = [
- [
- 'react-intl', // React Intl extractor, required for the whole i18n infrastructure to work
- {
- messagesDir: './build/messages/',
- },
- ],
- ];
-
- return {
- plugins,
- presets,
- };
-};
diff --git a/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile b/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile
deleted file mode 100644
index c413663a7f..0000000000
--- a/apps/plone/src/addons/volto-volto-project/dockerfiles/Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-# syntax=docker/dockerfile:1
-ARG VOLTO_VERSION
-FROM plone/frontend-builder:${VOLTO_VERSION} as builder
-
-ARG ADDON_NAME
-ARG ADDON_PATH
-
-# Copy addon code
-COPY --chown=node:node ./ /app/src/addons/${ADDON_PATH}/
-
-# Install
-RUN </node_modules/@plone/volto/cypress',
- '@plone/volto/babel': '/node_modules/@plone/volto/babel',
- '@plone/volto/(.*)$': '/node_modules/@plone/volto/src/$1',
- '@package/(.*)$': '/src/$1',
- '@root/(.*)$': '/src/$1',
- '~/(.*)$': '/src/$1',
- 'load-volto-addons':
- '/node_modules/@plone/volto/jest-addons-loader.js',
- '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
- },
- transform: {
- '^.+\\.js(x)?$': 'babel-jest',
- '^.+\\.(png)$': 'jest-file',
- '^.+\\.(jpg)$': 'jest-file',
- '^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
- },
- coverageThreshold: {
- global: {
- branches: 5,
- functions: 5,
- lines: 5,
- statements: 5,
- },
- },
-};
diff --git a/apps/plone/src/addons/volto-volto-project/locales/volto.pot b/apps/plone/src/addons/volto-volto-project/locales/volto.pot
deleted file mode 100644
index 1b22bc00e9..0000000000
--- a/apps/plone/src/addons/volto-volto-project/locales/volto.pot
+++ /dev/null
@@ -1,14 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2021-01-29T12:34:47.097Z\n"
-"Last-Translator: Plone i18n \n"
-"Language-Team: Plone i18n \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"Language-Code: en\n"
-"Language-Name: English\n"
-"Preferred-Encodings: utf-8\n"
-"Domain: volto\n"
diff --git a/apps/plone/src/addons/volto-volto-project/package.json b/apps/plone/src/addons/volto-volto-project/package.json
deleted file mode 100644
index 3a01e12350..0000000000
--- a/apps/plone/src/addons/volto-volto-project/package.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "name": "volto-volto-project",
- "version": "0.1.0",
- "description": "volto-volto-project: Volto add-on",
- "main": "src/index.ts",
- "license": "MIT",
- "keywords": [
- "volto-addon",
- "volto",
- "plone",
- "react"
- ],
- "publishConfig": {
- "access": "public"
- },
- "scripts": {
- "i18n": "rm -rf build/messages && NODE_ENV=production i18n --addon",
- "dry-release": "release-it --dry-run",
- "release": "release-it",
- "release-major-alpha": "release-it major --preRelease=alpha",
- "release-alpha": "release-it --preRelease=alpha",
- "release-rc": "release-it --preRelease=rc"
- },
- "devDependencies": {
- "@babel/eslint-parser": "7.22.15",
- "@plone/scripts": "workspace:*",
- "eslint": "8.49.0",
- "eslint-config-prettier": "9.0.0",
- "eslint-config-react-app": "7.0.1",
- "eslint-plugin-flowtype": "8.0.3",
- "eslint-plugin-import": "2.28.1",
- "eslint-plugin-jsx-a11y": "^6.7.1",
- "eslint-plugin-prettier": "5.0.0",
- "eslint-plugin-react": "7.33.2",
- "eslint-plugin-react-hooks": "4.6.0",
- "postcss-scss": "4.0.8",
- "prettier": "3.0.3",
- "release-it": "^16.1.5",
- "stylelint": "15.10.3",
- "stylelint-config-idiomatic-order": "9.0.0",
- "stylelint-config-sass-guidelines": "10.0.0",
- "stylelint-prettier": "4.0.2"
- }
-}
diff --git a/apps/plone/src/addons/volto-volto-project/src/index.ts b/apps/plone/src/addons/volto-volto-project/src/index.ts
deleted file mode 100644
index 2681f2ee1d..0000000000
--- a/apps/plone/src/addons/volto-volto-project/src/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { ConfigData } from '@plone/registry';
-
-const applyConfig = (config: ConfigData) => {
- return config;
-};
-
-export default applyConfig;
diff --git a/apps/plone/src/addons/volto-volto-project/src/theme/theme.config b/apps/plone/src/addons/volto-volto-project/src/theme/theme.config
deleted file mode 100644
index 5d3e8f2dd3..0000000000
--- a/apps/plone/src/addons/volto-volto-project/src/theme/theme.config
+++ /dev/null
@@ -1,88 +0,0 @@
-/*******************************
- Theme Selection
-*******************************/
-
-/* To override a theme for an individual element specify theme name below */
-
-/* Global */
-@site : 'pastanaga';
-@reset : 'pastanaga';
-
-/* Elements */
-@button : 'pastanaga';
-@container : 'pastanaga';
-@divider : 'pastanaga';
-@flag : 'pastanaga';
-@header : 'pastanaga';
-@icon : 'pastanaga';
-@image : 'pastanaga';
-@input : 'pastanaga';
-@label : 'pastanaga';
-@list : 'pastanaga';
-@loader : 'pastanaga';
-@placeholder : 'pastanaga';
-@rail : 'pastanaga';
-@reveal : 'pastanaga';
-@segment : 'pastanaga';
-@step : 'pastanaga';
-
-/* Collections */
-@breadcrumb : 'pastanaga';
-@form : 'pastanaga';
-@grid : 'pastanaga';
-@menu : 'pastanaga';
-@message : 'pastanaga';
-@table : 'pastanaga';
-
-/* Modules */
-@accordion : 'pastanaga';
-@checkbox : 'pastanaga';
-@dimmer : 'pastanaga';
-@dropdown : 'pastanaga';
-@embed : 'pastanaga';
-@modal : 'pastanaga';
-@nag : 'pastanaga';
-@popup : 'pastanaga';
-@progress : 'pastanaga';
-@rating : 'pastanaga';
-@search : 'pastanaga';
-@shape : 'pastanaga';
-@sidebar : 'pastanaga';
-@sticky : 'pastanaga';
-@tab : 'pastanaga';
-@transition : 'pastanaga';
-
-/* Views */
-@ad : 'pastanaga';
-@card : 'pastanaga';
-@comment : 'pastanaga';
-@feed : 'pastanaga';
-@item : 'pastanaga';
-@statistic : 'pastanaga';
-
-/* Extras */
-@main : 'pastanaga';
-@custom : 'pastanaga';
-
-/*******************************
- Folders
-*******************************/
-
-/* Path to theme packages */
-@themesFolder : '~volto-themes';
-
-/* Path to site override folder */
-@siteFolder : "volto-volto-project/theme";
-
-/*******************************
- Import Theme
-*******************************/
-
-@import (multiple) "~semantic-ui-less/theme.less";
-@fontPath : "~volto-themes/@{theme}/assets/fonts";
-
-.loadAddonOverrides() {
- @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
-}
-
-/* End Config */
diff --git a/apps/plone/src/addons/volto-volto-project/towncrier.toml b/apps/plone/src/addons/volto-volto-project/towncrier.toml
deleted file mode 100644
index 489f792262..0000000000
--- a/apps/plone/src/addons/volto-volto-project/towncrier.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[tool.towncrier]
-filename = "CHANGELOG.md"
-directory = "news/"
-title_format = "## {version} ({project_date})"
-underlines = ["", "", ""]
-template = "./node_modules/@plone/scripts/templates/towncrier_template.jinja"
-start_string = "\n"
-issue_format = "[#{issue}](https://github.com/voltovolto-project/pull/{issue})"
-
-[[tool.towncrier.type]]
-directory = "breaking"
-name = "Breaking"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "feature"
-name = "Feature"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "bugfix"
-name = "Bugfix"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "internal"
-name = "Internal"
-showcontent = true
-
-[[tool.towncrier.type]]
-directory = "documentation"
-name = "Documentation"
-showcontent = true
diff --git a/apps/plone/src/client.js b/apps/plone/src/client.js
deleted file mode 100644
index 9be1247a3a..0000000000
--- a/apps/plone/src/client.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import client from '@plone/volto/start-client';
-
-client();
-
-if (module.hot) {
- module.hot.accept();
-}
diff --git a/apps/plone/src/config.js b/apps/plone/src/config.js
deleted file mode 100644
index 9e0bea7532..0000000000
--- a/apps/plone/src/config.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/** Volto Project Configuration
- * The recommended way for configuring a Volto project is using an add-on.
- * The project should only be considered as boilerplate, and all the code and
- * configuration should happen and be placed in add-ons.
- *
- * Both configuring directly and placing code in a project is discouraged, and might be
- * removed at some point from Volto.
- *
- * The local project is left for backwards compatibility for existing projects.
- */
-
-// [Internal] All the imports of modules required for the configuration *must* happen
-// here BEFORE the following line
-import '@plone/volto/config';
-
-export default function applyConfig(config) {
- return config;
-}
diff --git a/apps/plone/src/customizations/README.md b/apps/plone/src/customizations/README.md
deleted file mode 100644
index 29ccb1bcee..0000000000
--- a/apps/plone/src/customizations/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-# Local customizations
-
-To override a file in the Volto package, add the directory structure and file to this folder.
-For example, to override the `Logo.svg` file, add the directory structure `components/theme/Logo`, and add the file `Logo.svg` to it.
-To apply your customizations, restart the server.
diff --git a/apps/plone/src/index.js b/apps/plone/src/index.js
deleted file mode 100644
index 9ae184cc6b..0000000000
--- a/apps/plone/src/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import start from '@plone/volto/start-server';
-
-const reloadServer = start();
-
-if (module.hot) {
- reloadServer();
-}
diff --git a/apps/plone/src/reducers/index.js b/apps/plone/src/reducers/index.js
deleted file mode 100644
index 520fa47c6d..0000000000
--- a/apps/plone/src/reducers/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Root reducer.
- * @module reducers/root
- */
-
-import defaultReducers from '@plone/volto/reducers';
-
-/**
- * Root reducer.
- * @function
- * @param {Object} state Current state.
- * @param {Object} action Action to be handled.
- * @returns {Object} New state.
- */
-const reducers = {
- ...defaultReducers,
- // Add your reducers here
-};
-
-export default reducers;
diff --git a/apps/plone/src/routes.js b/apps/plone/src/routes.js
deleted file mode 100644
index d23ac9305d..0000000000
--- a/apps/plone/src/routes.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Routes.
- * @module routes
- */
-
-import { defaultRoutes } from '@plone/volto/routes';
-import config from '@plone/volto/registry';
-
-/**
- * Routes array.
- * @array
- * @returns {array} Routes.
- */
-const routes = [
- {
- path: '/',
- component: config.getComponent('App').component, // Change this if you want a different component
- routes: [
- // Add your routes here
- ...(config.addonRoutes || []),
- ...defaultRoutes,
- ],
- },
-];
-
-export default routes;
diff --git a/apps/plone/src/theme.js b/apps/plone/src/theme.js
deleted file mode 100644
index 41018009fb..0000000000
--- a/apps/plone/src/theme.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import 'semantic-ui-less/semantic.less';
-import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
diff --git a/apps/plone/tsconfig.json b/apps/plone/tsconfig.json
deleted file mode 100644
index a6ea060da5..0000000000
--- a/apps/plone/tsconfig.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "compilerOptions": {
- "target": "ESNext",
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "module": "commonjs",
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "moduleResolution": "Node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
- "paths": {
- "@plone/volto/*": ["../node_modules/@plone/volto/src/*", "../node_modules/@plone/volto/types/*"]
- },
- "baseUrl": "src"
- },
- "include": ["src"],
- "exclude": [
- "node_modules",
- "build",
- "public",
- "coverage",
- "src/**/*.test.{js,jsx,ts,tsx}",
- "src/**/*.spec.{js,jsx,ts,tsx}",
- "src/**/*.stories.{js,jsx,ts,tsx}"
- ]
-}
diff --git a/apps/remix/README.md b/apps/remix/README.md
index a339650494..f9d62166c0 100644
--- a/apps/remix/README.md
+++ b/apps/remix/README.md
@@ -3,6 +3,10 @@
This is a proof of concept of a [Remix](https://remix.run) app, using the `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Remix.
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
## Development
diff --git a/apps/remix/app/client.ts b/apps/remix/app/client.ts
new file mode 100644
index 0000000000..0eec9cd62e
--- /dev/null
+++ b/apps/remix/app/client.ts
@@ -0,0 +1,8 @@
+import ploneClient from '@plone/client';
+import config from '@plone/registry';
+
+const cli = ploneClient.initialize({
+ apiPath: config.settings.apiPath,
+});
+
+export { cli as ploneClient };
diff --git a/apps/remix/app/config.ts b/apps/remix/app/config.ts
index c2f3c47921..ae8881a358 100644
--- a/apps/remix/app/config.ts
+++ b/apps/remix/app/config.ts
@@ -1,9 +1,15 @@
+import config from '@plone/registry';
+import { blocksConfig, slate } from '@plone/blocks';
+
const settings = {
apiPath: 'http://localhost:8080/Plone',
+ slate,
};
-const config = {
- settings,
-};
+// @ts-expect-error We need to fix typing
+config.set('settings', settings);
+
+// @ts-expect-error We need to fix typing
+config.set('blocks', { blocksConfig });
export default config;
diff --git a/apps/remix/app/entry.client.tsx b/apps/remix/app/entry.client.tsx
index 3f01ce18b1..4680b84a57 100644
--- a/apps/remix/app/entry.client.tsx
+++ b/apps/remix/app/entry.client.tsx
@@ -7,6 +7,7 @@
import { RemixBrowser } from '@remix-run/react';
import { startTransition, StrictMode } from 'react';
import { hydrateRoot } from 'react-dom/client';
+import './config';
startTransition(() => {
hydrateRoot(
diff --git a/apps/remix/app/entry.server.tsx b/apps/remix/app/entry.server.tsx
index dda7030aea..9af132970a 100644
--- a/apps/remix/app/entry.server.tsx
+++ b/apps/remix/app/entry.server.tsx
@@ -11,6 +11,7 @@ import { createReadableStreamFromReadable } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { isbot } from 'isbot';
import { renderToPipeableStream } from 'react-dom/server';
+import './config';
const ABORT_DELAY = 5_000;
diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx
index f76fa2596c..ecfb28c8be 100644
--- a/apps/remix/app/root.tsx
+++ b/apps/remix/app/root.tsx
@@ -2,22 +2,33 @@ import { cssBundleHref } from '@remix-run/css-bundle';
import type { LinksFunction } from '@remix-run/node';
import {
Links,
- LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
+ useHref,
+ useLocation,
+ useNavigate,
+ useParams,
} from '@remix-run/react';
import { useState } from 'react';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { PloneClientProvider } from '@plone/providers';
+import { QueryClient } from '@tanstack/react-query';
import PloneClient from '@plone/client';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import '@plone/components/dist/basic.css';
+import { flattenToAppURL } from './utils';
+import { PloneProvider } from '@plone/providers';
+import config from '@plone/registry';
+
export const links: LinksFunction = () => [
...(cssBundleHref ? [{ rel: 'stylesheet', href: cssBundleHref }] : []),
];
+function useHrefLocal(to: string) {
+ return useHref(flattenToAppURL(to));
+}
+
export default function App() {
const [queryClient] = useState(
() =>
@@ -34,10 +45,16 @@ export default function App() {
const [ploneClient] = useState(() =>
PloneClient.initialize({
- apiPath: 'http://localhost:8080/Plone',
+ apiPath: config.settings.apiPath,
}),
);
+ const RRNavigate = useNavigate();
+
+ const navigate = (to: string) => {
+ return RRNavigate(flattenToAppURL(to));
+ };
+
return (
@@ -47,15 +64,19 @@ export default function App() {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
);
diff --git a/apps/remix/app/routes/$.tsx b/apps/remix/app/routes/$.tsx
new file mode 100644
index 0000000000..abd1fd4bbd
--- /dev/null
+++ b/apps/remix/app/routes/$.tsx
@@ -0,0 +1,79 @@
+import {
+ json,
+ type LoaderFunctionArgs,
+ type MetaFunction,
+} from '@remix-run/node';
+import {
+ dehydrate,
+ QueryClient,
+ HydrationBoundary,
+ useQuery,
+} from '@tanstack/react-query';
+import { flattenToAppURL } from '../utils';
+import { useLoaderData, useLocation } from '@remix-run/react';
+import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
+
+export const meta: MetaFunction = () => {
+ return [
+ { title: 'Plone on Remix' },
+ { name: 'description', content: 'Welcome to Plone on Remix!' },
+ ];
+};
+
+const expand = ['breadcrumbs', 'navigation'];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ });
+
+ const { getContentQuery } = ploneClient;
+
+ await queryClient.prefetchQuery(
+ getContentQuery({ path: flattenToAppURL(request.url), expand }),
+ );
+
+ return json({ dehydratedState: dehydrate(queryClient) });
+};
+
+function Page() {
+ const { getContentQuery } = usePloneClient();
+ const pathname = useLocation().pathname;
+ const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+
+ if (!data) return null;
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Index() {
+ const { dehydratedState } = useLoaderData();
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/remix/app/routes/_index.tsx b/apps/remix/app/routes/_index.tsx
index 1736d4a86b..2ad6a19bbe 100644
--- a/apps/remix/app/routes/_index.tsx
+++ b/apps/remix/app/routes/_index.tsx
@@ -9,10 +9,12 @@ import {
HydrationBoundary,
useQuery,
} from '@tanstack/react-query';
-import ploneClient from '@plone/client';
import { flattenToAppURL } from '../utils';
import { useLoaderData, useLocation } from '@remix-run/react';
import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
export const meta: MetaFunction = () => {
return [
@@ -35,10 +37,7 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
},
});
- const cli = ploneClient.initialize({
- apiPath: 'http://localhost:8080/Plone',
- });
- const { getContentQuery } = cli;
+ const { getContentQuery } = ploneClient;
await queryClient.prefetchQuery(
getContentQuery({ path: flattenToAppURL(request.url), expand }),
@@ -47,17 +46,34 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
return json({ dehydratedState: dehydrate(queryClient) });
};
-export default function Index() {
- const { dehydratedState } = useLoaderData();
+function Page() {
const { getContentQuery } = usePloneClient();
const pathname = useLocation().pathname;
const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+ if (!data) return null;
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Index() {
+ const { dehydratedState } = useLoaderData();
+
return (
-
-
{JSON.stringify(data, null, 2)}
-
+
);
}
diff --git a/apps/remix/app/utils.ts b/apps/remix/app/utils.ts
index f6513198f8..c297613f90 100644
--- a/apps/remix/app/utils.ts
+++ b/apps/remix/app/utils.ts
@@ -9,9 +9,9 @@ import config from './config';
*/
export function flattenToAppURL(url: string) {
const { settings } = config;
+
return (
- (url &&
- url.replace(settings.apiPath, '').replace('http://localhost:3000', '')) ||
- '/'
+ url &&
+ url.replace(settings.apiPath, '').replace('http://localhost:3000', '')
);
}
diff --git a/apps/remix/package.json b/apps/remix/package.json
index b422edc87d..31d7ec65e8 100644
--- a/apps/remix/package.json
+++ b/apps/remix/package.json
@@ -11,24 +11,27 @@
"typecheck": "tsc"
},
"dependencies": {
+ "@plone/blocks": "workspace: *",
"@plone/client": "workspace: *",
+ "@plone/components": "workspace: *",
"@plone/providers": "workspace: *",
- "@remix-run/css-bundle": "^2.4.0",
- "@remix-run/node": "^2.8.1",
- "@remix-run/react": "^2.8.1",
- "@remix-run/serve": "^2.8.1",
- "@tanstack/react-query": "^5.37.1",
+ "@plone/registry": "workspace: *",
+ "@remix-run/css-bundle": "^2.13.1",
+ "@remix-run/node": "^2.13.1",
+ "@remix-run/react": "^2.13.1",
+ "@remix-run/serve": "^2.13.1",
+ "@tanstack/react-query": "^5.59.0",
"isbot": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@remix-run/dev": "^2.8.1",
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@remix-run/dev": "^2.13.1",
+ "@tanstack/react-query-devtools": "^5.59.0",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
- "typescript": "^5.4.2",
- "vite": "^5.1.0",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.9",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
diff --git a/apps/remix/tsconfig.json b/apps/remix/tsconfig.json
index 9d87dd378f..42f049fa4a 100644
--- a/apps/remix/tsconfig.json
+++ b/apps/remix/tsconfig.json
@@ -25,8 +25,6 @@
"paths": {
"~/*": ["./app/*"]
},
-
- // Vite takes care of building everything, not tsc.
"noEmit": true
}
}
diff --git a/apps/remix/vite.config.ts b/apps/remix/vite.config.ts
index eeb1fd83d0..03bfa30a99 100644
--- a/apps/remix/vite.config.ts
+++ b/apps/remix/vite.config.ts
@@ -1,12 +1,20 @@
import { vitePlugin as remix } from '@remix-run/dev';
-import { installGlobals } from '@remix-run/node';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
-
-installGlobals();
+import { PloneRegistryVitePlugin } from '@plone/registry/vite-plugin';
export default defineConfig({
- plugins: [remix(), tsconfigPaths()],
+ plugins: [
+ remix({
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ },
+ }),
+ tsconfigPaths(),
+ PloneRegistryVitePlugin(),
+ ],
server: {
port: 3000,
},
diff --git a/apps/vite-ssr/README.md b/apps/vite-ssr/README.md
index 2894982c36..f672cf5770 100644
--- a/apps/vite-ssr/README.md
+++ b/apps/vite-ssr/README.md
@@ -1,6 +1,11 @@
# Plone on Vite with SSR mode
-This is a proof of concept of a [Vite](https://vitejs.dev) build, using `@plone/client` and `@plone/components` libraries.
+This is a proof of concept of a [Vite](https://vite.dev/) build, using `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Vite built with server side rendering (SSR).
It also uses [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview) for its routing library.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/apps/vite-ssr/package.json b/apps/vite-ssr/package.json
index 99ff75eb99..6a25419d6e 100644
--- a/apps/vite-ssr/package.json
+++ b/apps/vite-ssr/package.json
@@ -17,15 +17,14 @@
"@plone/providers": "workspace:*",
"@plone/registry": "workspace:*",
"@plone/blocks": "workspace:*",
- "@tanstack/react-query": "^5.37.1",
- "@tanstack/react-router": "^1.28.2",
- "@tanstack/react-router-server": "^1.28.2",
- "@tanstack/router-devtools": "^1.28.2",
- "@tanstack/router-vite-plugin": "^1.28.2",
+ "@tanstack/react-query": "^5.59.0",
+ "@tanstack/react-router": "^1.34.9",
+ "@tanstack/start": "^1.34.9",
+ "@tanstack/router-vite-plugin": "^1.34.8",
"axios": "^1.6.5",
"get-port": "^7.0.0",
"react": "^18.2.0",
- "react-aria-components": "^1.1.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18.2.0",
"sirv": "^2.0.4"
},
@@ -34,7 +33,8 @@
"@babel/generator": "^7.23.6",
"@plone/types": "workspace:*",
"@rollup/plugin-babel": "^6.0.4",
- "@tanstack/react-query-devtools": "^5.37.1",
+ "@tanstack/react-query-devtools": "^5.59.0",
+ "@tanstack/router-devtools": "^1.34.9",
"@types/express": "^4.17.21",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
@@ -44,7 +44,7 @@
"isbot": "^4.3.0",
"node-fetch": "^3.3.2",
"serve-static": "^1.15.0",
- "typescript": "^5.4.2",
+ "typescript": "^5.6.3",
"vite": "^5.2.9",
"vite-plugin-babel": "^1.2.0"
}
diff --git a/apps/vite-ssr/src/entry-client.tsx b/apps/vite-ssr/src/entry-client.tsx
index e178ab84f1..1f2e057b2c 100644
--- a/apps/vite-ssr/src/entry-client.tsx
+++ b/apps/vite-ssr/src/entry-client.tsx
@@ -1,6 +1,6 @@
import ReactDOM from 'react-dom/client';
-import { StartClient } from '@tanstack/react-router-server/client';
+import { StartClient } from '@tanstack/start/client';
import { createRouter } from './router';
import './config';
diff --git a/apps/vite-ssr/src/entry-server.tsx b/apps/vite-ssr/src/entry-server.tsx
index f701a08446..af56e3e91f 100644
--- a/apps/vite-ssr/src/entry-server.tsx
+++ b/apps/vite-ssr/src/entry-server.tsx
@@ -3,7 +3,7 @@ import ReactDOMServer from 'react-dom/server';
import { createMemoryHistory } from '@tanstack/react-router';
import { ServerResponse } from 'http';
import express from 'express';
-import { StartServer } from '@tanstack/react-router-server/server';
+import { StartServer } from '@tanstack/start/server';
import { createRouter } from './router';
import './config';
diff --git a/apps/vite-ssr/src/routeTree.gen.ts b/apps/vite-ssr/src/routeTree.gen.ts
index 81ad7b6840..64cb826aac 100644
--- a/apps/vite-ssr/src/routeTree.gen.ts
+++ b/apps/vite-ssr/src/routeTree.gen.ts
@@ -37,14 +37,23 @@ const IndexRoute = IndexImport.update({
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/$': {
+ id: '/$'
+ path: '/$'
+ fullPath: '/$'
preLoaderRoute: typeof SplatImport
parentRoute: typeof rootRoute
}
'/error': {
+ id: '/error'
+ path: '/error'
+ fullPath: '/error'
preLoaderRoute: typeof ErrorImport
parentRoute: typeof rootRoute
}
@@ -53,10 +62,34 @@ declare module '@tanstack/react-router' {
// Create and export the route tree
-export const routeTree = rootRoute.addChildren([
+export const routeTree = rootRoute.addChildren({
IndexRoute,
SplatRoute,
ErrorRoute,
-])
+})
/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/$",
+ "/error"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/$": {
+ "filePath": "$.tsx"
+ },
+ "/error": {
+ "filePath": "error.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/vite-ssr/src/router.tsx b/apps/vite-ssr/src/router.tsx
index 7a3a80f339..512782c598 100644
--- a/apps/vite-ssr/src/router.tsx
+++ b/apps/vite-ssr/src/router.tsx
@@ -1,16 +1,9 @@
-import {
- QueryClient,
- QueryClientProvider,
- dehydrate,
- hydrate,
-} from '@tanstack/react-query';
+import { QueryClient, dehydrate, hydrate } from '@tanstack/react-query';
import PloneClient from '@plone/client';
import { createRouter as createReactRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
import { PloneClientProvider } from '@plone/providers';
-import { FlattenToAppURLProvider } from '@plone/components';
-import { flattenToAppURL } from './utils';
const queryClient = new QueryClient({
defaultOptions: {
@@ -45,12 +38,8 @@ export function createRouter() {
},
Wrap: ({ children }: { children: any }) => {
return (
-
-
-
- {children}
-
-
+
+ {children}
);
},
diff --git a/apps/vite-ssr/src/routes/__root.tsx b/apps/vite-ssr/src/routes/__root.tsx
index ef78a13d81..74bc65767a 100644
--- a/apps/vite-ssr/src/routes/__root.tsx
+++ b/apps/vite-ssr/src/routes/__root.tsx
@@ -4,14 +4,16 @@ import {
Outlet,
createRootRouteWithContext,
useRouter,
- useRouterState,
+ useLocation,
} from '@tanstack/react-router';
-import { DehydrateRouter } from '@tanstack/react-router-server/client';
+import { DehydrateRouter } from '@tanstack/start/client';
import { RouterContext } from '../routerContext';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { RouterProvider } from 'react-aria-components';
import '@plone/components/dist/basic.css';
+import { AppRouterProvider } from '@plone/providers';
+import { flattenToAppURL } from '../utils';
export const Route = createRootRouteWithContext()({
component: RootComponent,
@@ -54,11 +56,19 @@ function RootComponent() {
)}
- router.navigate({ to: path })}
+
+ router.navigate({ to: flattenToAppURL(path) })
+ }
+ // TODO: Investigate why this fails in @tanstack/router :/
+ useHref={(to) => {
+ return flattenToAppURL(to);
+ }}
+ flattenToAppURL={flattenToAppURL}
>
{/* Start rendering router matches */}
-
+
diff --git a/apps/vite-ssr/src/utils.ts b/apps/vite-ssr/src/utils.ts
index 4c1b6f5968..044a5b66dd 100644
--- a/apps/vite-ssr/src/utils.ts
+++ b/apps/vite-ssr/src/utils.ts
@@ -12,7 +12,9 @@ export function flattenToAppURL(url: string | undefined) {
const { settings } = config;
return (
(url &&
- url.replace(settings.apiPath, '').replace('http://localhost:3000', '')) ||
+ url
+ .replace(settings.apiPath, '')
+ .replace('http://localhost:8080/Plone', '')) ||
'/'
);
}
diff --git a/apps/vite/README.md b/apps/vite/README.md
index 417e695ef3..3e508c1a97 100644
--- a/apps/vite/README.md
+++ b/apps/vite/README.md
@@ -1,6 +1,11 @@
# Plone on Vite
-This is a proof of concept of a [Vite](https://vitejs.dev) build, using `@plone/client` and `@plone/components` libraries.
+This is a proof of concept of a [Vite](https://vite.dev/) build, using `@plone/client` and `@plone/components` libraries.
This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Vite.
It also uses [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview) for its routing library.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/apps/vite/package.json b/apps/vite/package.json
index 4bef020587..ec91f232ee 100644
--- a/apps/vite/package.json
+++ b/apps/vite/package.json
@@ -15,18 +15,19 @@
"@plone/registry": "workspace:*",
"@plone/blocks": "workspace:*",
"@tanstack/react-query": "^5.37.1",
- "@tanstack/react-router": "^1.29.2",
+ "@tanstack/react-router": "^1.67.0",
+ "@tanstack/router-devtools": "^1.67.0",
"axios": "^1.6.5",
"react": "^18.2.0",
- "react-aria-components": "^1.2.1",
+ "react-aria-components": "^1.4.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^5.37.1",
- "@tanstack/router-devtools": "^1.29.2",
+ "@tanstack/router-plugin": "^1.66.1",
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
- "@vitejs/plugin-react": "^4.2.1",
- "vite": "^5.0.13"
+ "@vitejs/plugin-react": "^4.3.2",
+ "vite": "^5.4.9"
}
}
diff --git a/apps/vite/src/main.tsx b/apps/vite/src/main.tsx
index 7bdb58def6..204f5a8a31 100644
--- a/apps/vite/src/main.tsx
+++ b/apps/vite/src/main.tsx
@@ -1,12 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
-import { RouterProvider, createRouter } from '@tanstack/react-router';
+import {
+ RouterProvider,
+ createRouter,
+ useLocation,
+ useParams,
+} from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClient } from '@tanstack/react-query';
import PloneClient from '@plone/client';
-import { PloneClientProvider } from '@plone/providers';
-import { FlattenToAppURLProvider } from '@plone/providers';
+import { PloneProvider } from '@plone/providers';
import { flattenToAppURL } from './utils';
import './config';
@@ -50,12 +54,21 @@ const rootElement = document.getElementById('app')!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
-
-
-
-
-
-
- ,
+
+ router.navigate({ to: flattenToAppURL(path) })
+ }
+ // TODO: Investigate why this fails in @tanstack/router :/
+ useHref={(to) => {
+ return flattenToAppURL(to);
+ }}
+ useParams={useParams}
+ flattenToAppURL={flattenToAppURL}
+ >
+
+ ,
);
}
diff --git a/apps/vite/src/routeTree.gen.ts b/apps/vite/src/routeTree.gen.ts
index 20ade03340..2c6342ccd1 100644
--- a/apps/vite/src/routeTree.gen.ts
+++ b/apps/vite/src/routeTree.gen.ts
@@ -31,10 +31,16 @@ const IndexRoute = IndexImport.update({
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/$': {
+ id: '/$'
+ path: '/$'
+ fullPath: '/$'
preLoaderRoute: typeof SplatImport
parentRoute: typeof rootRoute
}
@@ -43,6 +49,63 @@ declare module '@tanstack/react-router' {
// Create and export the route tree
-export const routeTree = rootRoute.addChildren([IndexRoute, SplatRoute])
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/$': typeof SplatRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/$'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/$'
+ id: '__root__' | '/' | '/$'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ SplatRoute: typeof SplatRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ SplatRoute: SplatRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/$"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/$": {
+ "filePath": "$.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/vite/src/routes/__root.tsx b/apps/vite/src/routes/__root.tsx
index da25473043..a60ef833f4 100644
--- a/apps/vite/src/routes/__root.tsx
+++ b/apps/vite/src/routes/__root.tsx
@@ -1,15 +1,9 @@
import * as React from 'react';
-import {
- Link,
- Outlet,
- createRootRouteWithContext,
- useRouter,
-} from '@tanstack/react-router';
+import { Outlet, createRootRouteWithContext } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import { QueryClient } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
-import { RouterProvider } from 'react-aria-components';
import PloneClient from '@plone/client';
import '@plone/components/dist/basic.css';
@@ -22,13 +16,11 @@ export const Route = createRootRouteWithContext<{
});
function RootComponent() {
- const router = useRouter();
-
return (
- router.navigate({ to: path })}>
+ <>
-
+ >
);
}
diff --git a/apps/vite/src/routes/index.tsx b/apps/vite/src/routes/index.tsx
index 81b7ae161c..b7619d303c 100644
--- a/apps/vite/src/routes/index.tsx
+++ b/apps/vite/src/routes/index.tsx
@@ -1,5 +1,5 @@
-import { createFileRoute } from '@tanstack/react-router';
import * as React from 'react';
+import { createFileRoute } from '@tanstack/react-router';
import { flattenToAppURL } from '../utils';
import { useSuspenseQuery } from '@tanstack/react-query';
import { usePloneClient } from '@plone/providers';
@@ -23,6 +23,7 @@ function IndexComponent() {
const { data } = useSuspenseQuery(
getContentQuery({ path: flattenToAppURL('/'), expand }),
);
+
return (
<>
` with your add-on name:
diff --git a/docs/source/backend/index.md b/docs/source/backend/index.md
index 8dbab251c4..614be62855 100644
--- a/docs/source/backend/index.md
+++ b/docs/source/backend/index.md
@@ -34,6 +34,23 @@ Block transformers
Search and indexing integration
: By providing the right adapters, you can extract searchable text from blocks.
+Client reducer content transforms
+: These transforms run in the client when the response from the backend is received.
+ These are useful when you need to modify the response from the backend on-the-fly for amending the backend data, such as a data migration of any kind.
+ You can register a utility that mutates the response at your convenience.
+
+ ```ts
+ import { upgradeV20241023 } from './upgrades/upgradeV20241023';
+
+ config.registerUtility({
+ name: 'upgradeV20241023',
+ type: 'transform',
+ dependencies: { reducer: 'content' },
+ method: upgradeV20241023,
+ });
+ ```
+
+ The `type` of the utility needs to be `transform`, and the `dependencies` set to `{reducer: 'content'}`.
## Proxied backend routes
diff --git a/docs/source/client/quick-start.md b/docs/source/client/quick-start.md
index dd3174a9b7..ad62cd181f 100644
--- a/docs/source/client/quick-start.md
+++ b/docs/source/client/quick-start.md
@@ -21,9 +21,11 @@ These functions can be used in other use cases like command line helpers, script
To install the Javascript Plone client run the following command:
```shell
-yarn add @plone/client
+pnpm add @plone/client
```
+or use your package manager of choice.
+
## `ploneClient` entry point
The main artifact that the client provides is the `ploneClient` entry point.
diff --git a/docs/source/configuration/environmentvariables.md b/docs/source/configuration/environmentvariables.md
index a63c6144f1..701ce5c7c2 100644
--- a/docs/source/configuration/environmentvariables.md
+++ b/docs/source/configuration/environmentvariables.md
@@ -21,7 +21,7 @@ All configurable environment variables work at runtime, not only at build time.
You could, for example, build your Volto application, then start it in production with the `RAZZLE_API_PATH` environment variable.
```shell
-yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod
+pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod
```
This brings you a lot of power since you don't have to rebuild on every configuration change.
@@ -40,7 +40,7 @@ You can also generate builds on your continuous integration, then deploy them an
However, if you are not able to upgrade the packages `plone.restapi` (8.12.1 or greater) and `plone.rest` (2.0.0a1 or greater) in the backend, you can adjust your web server configuration and use the `RAZZLE_LEGACY_TRAVERSE` flag.
```shell
- RAZZLE_LEGACY_TRAVERSE=true yarn start:prod
+ RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod
```
`VOLTO_ROBOTSTXT`
@@ -50,7 +50,7 @@ You can also generate builds on your continuous integration, then deploy them an
```shell
VOLTO_ROBOTSTXT="User-agent: *
- Disallow: /" yarn start
+ Disallow: /" pnpm start
```
```{note}
@@ -67,19 +67,19 @@ You can also generate builds on your continuous integration, then deploy them an
It helps you identify problems with a customization that does not work as you expect.
```shell
- DEBUG=volto:shadowing yarn start
+ DEBUG=volto:shadowing pnpm start
```
`i18n` enables the log of missing internationalization messages in the console.
```shell
- DEBUG=volto:i18n yarn start
+ DEBUG=volto:i18n pnpm start
```
`*` enables logging everywhere it exists in Volto.
```shell
- DEBUG=volto:* yarn start
+ DEBUG=volto:* pnpm start
```
`DEBUG_ADDONS_LOADER`
@@ -107,34 +107,34 @@ You can also generate builds on your continuous integration, then deploy them an
`ADDONS` can be used to temporarily add an add-on to your build for testing purposes.
```shell
- yarn add volto-slate
- ADDONS=volto-slate:asDefault yarn start
+ pnpm add @kitconcept/volto-light-theme
+ ADDONS=@kitconcept/volto-light-theme pnpm start
```
`ADDONS` can also be used to temporarily enable a feature or a set of customizations.
```shell
# given a folder './packages/coresandbox', like in vanilla Volto
- ADDONS=coresandbox:multilingualFixture yarn start
+ ADDONS=coresandbox:multilingualFixture pnpm start
```
If you need to specify several add-ons, separate them with a semicolon (`;`):
```shell
- ADDONS="test-addon;test-addon2" yarn start
+ ADDONS="test-addon;test-addon2" pnpm start
```
-
+
You can specify profiles for installation:
-
+
```shell
- ADDONS="test-addon:profile1;test-addon2:profile2" yarn start
+ ADDONS="test-addon:profile1;test-addon2:profile2" pnpm start
```
The following code snippets demonstrate how to configure add-ons.
First in `package.json`:
-
+
```json
"addons": [
"@kitconcept/volto-blocks-grid"
@@ -152,8 +152,8 @@ You can also generate builds on your continuous integration, then deploy them an
And finally using `ADDONS`:
```shell
- yarn add volto-slate
- ADDONS=volto-slate:asDefault yarn start
+ pnpm add volto-slate
+ ADDONS=volto-slate:asDefault pnpm start
```
As a result, your app will load the add-ons in the following order:
@@ -165,14 +165,14 @@ You can also generate builds on your continuous integration, then deploy them an
```{important}
The `ADDONS` key is a Volto specific configuration.
Simply setting `ADDONS` doesn't download the JavaScript package.
- This has to be covered another way, by either installing the add-on package (with `yarn add`), or loading it as a development package with `mrs-developer`.
+ This has to be covered another way, by either installing the add-on package (with `pnpm add`), or loading it as a development package with `mrs-developer`.
```
`BUILD_DIR`
This is a runtime-only environment variable that directs the build to run Volto from a specific location, other than the default folder `build`.
```shell
- yarn
+ pnpm install
BUILD_DIR=dist node dist/server.js
```
@@ -182,7 +182,7 @@ You can also generate builds on your continuous integration, then deploy them an
It can be relative to the current project or absolute.
```shell
- VOLTOCONFIG=../../volto.config.js yarn start
+ VOLTOCONFIG=../../volto.config.js pnpm start
```
````
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/docs/source/configuration/internalproxy.md b/docs/source/configuration/internalproxy.md
index ec8612fe65..dde6c9b369 100644
--- a/docs/source/configuration/internalproxy.md
+++ b/docs/source/configuration/internalproxy.md
@@ -61,7 +61,7 @@ export const settings = {
or use the environment variable:
```bash
-RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite yarn start
+RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite pnpm start
```
This redefines the request path from the internal proxy of the server side Node.js process to the Plone content backend API, but leaves the frontend Volto process making all content requests to `http://localhost:3000/++api++/`.
diff --git a/docs/source/configuration/volto-config-js.md b/docs/source/configuration/volto-config-js.md
index a8e4c4faa7..8bb16ce951 100644
--- a/docs/source/configuration/volto-config-js.md
+++ b/docs/source/configuration/volto-config-js.md
@@ -70,11 +70,11 @@ This environment variable allows you to specify a custom location for {file}`vol
It can be relative to the current project or absolute.
```shell
-VOLTOCONFIG=../../volto.config.js yarn start
+VOLTOCONFIG=../../volto.config.js pnpm start
```
```shell
-VOLTOCONFIG=$(pwd)/volto.config.js yarn start
+VOLTOCONFIG=$(pwd)/volto.config.js pnpm start
```
You can also set it from the root of the monorepo:
diff --git a/docs/source/configuration/zero-config-builds.md b/docs/source/configuration/zero-config-builds.md
index 7b4c396075..5ac09d7d87 100644
--- a/docs/source/configuration/zero-config-builds.md
+++ b/docs/source/configuration/zero-config-builds.md
@@ -17,7 +17,7 @@ In the past (before Volto 13), Volto was configured in build time using several
environment variables, commonly supplied via the command line, such as the following:
```shell
-PORT=11001 RAZZLE_API_PATH=https://plone.org/api yarn build`
+PORT=11001 RAZZLE_API_PATH=https://plone.org/api pnpm build`
```
and since Razzle is an isomorphic application, some of these values passed on build time, were
diff --git a/docs/source/contributing/acceptance-tests.md b/docs/source/contributing/acceptance-tests.md
index ea9b66ec1f..c74bd9d4cb 100644
--- a/docs/source/contributing/acceptance-tests.md
+++ b/docs/source/contributing/acceptance-tests.md
@@ -72,7 +72,7 @@ There is a directory per spec.
This directory is hot reloaded with your changes as you write the tests.
```{seealso}
-[Cypress documentation](https://docs.cypress.io/guides/overview/why-cypress)
+[Cypress documentation](https://docs.cypress.io/app/get-started/why-cypress)
```
diff --git a/docs/source/contributing/developing-core.md b/docs/source/contributing/developing-core.md
index eb79d97c8a..0e961feb81 100644
--- a/docs/source/contributing/developing-core.md
+++ b/docs/source/contributing/developing-core.md
@@ -71,15 +71,15 @@ Volto has the following folder structure.
```
-## Development pre-requisites
+## Development prerequisites
-To set up a Volto core development environment, your system must satisfy the following pre-requisites.
+To set up a Volto core development environment, your system must satisfy the following prerequisites.
```{include} ./install-operating-system.md
```
- {term}`nvm`
-- {term}`Node.js` LTS 20.x
+- {term}`Node.js` LTS 22.x
- {term}`pnpm`
- {term}`GNU make`
- {term}`Docker`
@@ -330,13 +330,6 @@ By default, the use of TypeScript is required in Plone frontend libraries, Volto
The monorepository consists of several core libraries.
-### Volto project generator
-
-`@plone/generator-volto` is a Yeoman generator that helps you set up Volto via command line.
-It generates all the boilerplate needed to start developing a Plone Volto project.
-It is used by [CookieCutter Plone Starter](https://github.com/collective/cookiecutter-plone-starter), the recommended way to set up Plone projects.
-The generator features an `addon` template for scaffolding Volto add-ons in your projects.
-
### Registry
`@plone/registry` provides support for building an add-on registry and infrastructure for JavaScript and TypeScript-based apps.
@@ -354,6 +347,17 @@ Used by Volto, you can also use it in other JavaScript frameworks and environmen
`@plone/volto-slate` is the glue package that provides support for the Slate library in Volto.
+### Volto project generator
+
+`@plone/generator-volto` is a Yeoman generator that helps you set up Volto via command line.
+It generates all the boilerplate needed to start developing a Plone Volto project.
+It is used by [CookieCutter Plone Starter](https://github.com/collective/cookiecutter-plone-starter), the recommended way to set up Plone projects.
+The generator features an `addon` template for scaffolding Volto add-ons in your projects.
+
+```{deprecated} 18.0.0-alpha.43
+For Volto 18, `@plone/generator-volto` is replaced by [Cookieplone](https://github.com/plone/cookieplone).
+```
+
## Supported frontends
@@ -362,7 +366,7 @@ Volto is the default frontend, and is React-based.
Classic UI is the Python-based, server-side rendered frontend.
In Volto's `apps` folder, you'll find a Volto project scaffolding that uses Volto as a library.
-This is the same as that which you'll have when you run the Volto generator or `cookiecutter-plone-starter`.
+This is the same as that which you'll have when you follow the instructions in {doc}`plone:install/create-project`).
## Experimental frontends
diff --git a/docs/source/contributing/linting.md b/docs/source/contributing/linting.md
index cd291fd556..feea6f0307 100644
--- a/docs/source/contributing/linting.md
+++ b/docs/source/contributing/linting.md
@@ -76,7 +76,7 @@ From here we will have access to the commands to check for errors and to fix the
You can run the pnpm `eslint`, `prettier`, and `stylelint` commands from the Volto package folder:
```shell
-pnpm lint
+pnpm lint
pnpm prettier
pnpm stylelint
```
@@ -89,8 +89,13 @@ pnpm prettier:fix
pnpm stylelint:fix
```
-````{note}
-The same commands can be found in your Volto add-on projects, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file.
+```{versionadded} Volto 18.0.0-alpha.43
+[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator.
+Cookieplone uses the frontend code installed using `pnpm` instead of `yarn`.
+```
+
+````{deprecated} Volto 18.0.0
+The same commands can be found in your Volto legacy add-ons and projects created with `@plone/generator-volto`, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file.
You will use similar commands to run the linting commands, but with `yarn` instead of `pnpm`:
diff --git a/docs/source/contributing/testing.md b/docs/source/contributing/testing.md
index a1244c9496..37f241151d 100644
--- a/docs/source/contributing/testing.md
+++ b/docs/source/contributing/testing.md
@@ -66,10 +66,9 @@ This makes it faster and easier to test code changes.
In GitHub workflows or for testing add-ons, it's useful to use an alternate Jest configuration.
Volto provides a way to do so using a file {file}`jest.config.js`, or pointing the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable.
-Because the Volto add-ons and Volto add-ons projects still use `yarn`, you must run the test command using `yarn` instead of `pnpm`.
```shell
-RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test
+RAZZLE_JEST_CONFIG=my-custom-jest-config.js pnpm test
```
```{note}
@@ -85,7 +84,7 @@ Sometimes you need to enable different configurations and enable optional compon
You can use the `ADDONS` environment variable to define them.
```bash
-ADDONS=test-addon,test-addon2 yarn start
+ADDONS=test-addon,test-addon2 pnpm start
```
See {doc}`../configuration/environmentvariables` for more information.
diff --git a/docs/source/contributing/version-policy.md b/docs/source/contributing/version-policy.md
index a262808480..19bac961b8 100644
--- a/docs/source/contributing/version-policy.md
+++ b/docs/source/contributing/version-policy.md
@@ -69,11 +69,12 @@ See also [Plone REST API Python and Plone compatibility](https://github.com/plon
## Node.js
Volto runs using [Node.js](https://nodejs.org/en).
-Volto always supports only [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
+Volto supports only the latest two [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
We recommend using the current LTS version.
+- Node.js 22 LTS: Supported since Volto 18.
- Node.js 20 LTS: Supported since Volto 17.
-- Node.js 18 LTS: Supported since Volto 17.
+- Node.js 18: No longer supported. It was supported in Volto 16 - 17.
- Node.js 16: No longer supported. It was supported in Volto 14 - 16.
- Node.js 14: No longer supported. It was supported in Volto 8.8.0 - 16.
- Node.js 12: No longer supported. It was supported in Volto 4 - 15.
diff --git a/docs/source/deploying/seamless-mode.md b/docs/source/deploying/seamless-mode.md
index a678728a73..0173bdbaf3 100644
--- a/docs/source/deploying/seamless-mode.md
+++ b/docs/source/deploying/seamless-mode.md
@@ -48,13 +48,13 @@ All the environment variables that are configurable now work at runtime, not at
Before Volto 13, you'd do:
```bash
-RAZZLE_API_PATH=https://plone.org yarn build && yarn start:prod
+RAZZLE_API_PATH=https://plone.org pnpm build && pnpm start:prod
```
From Volto 13 onwards, you can now do:
```bash
-yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod
+pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod
```
````
@@ -130,7 +130,7 @@ server {
error_log /dev/stdout;
# [seamless mode] Recommended as default configuration, using seamless mode new plone.rest traversal
- # yarn build && yarn start:prod
+ # pnpm build && pnpm start:prod
location ~ /\+\+api\+\+($|/.*) {
rewrite ^/\+\+api\+\+($|/.*) /VirtualHostBase/http/myservername.org/Plone/++api++/VirtualHostRoot/$1 break;
proxy_pass http://backend;
@@ -138,7 +138,7 @@ server {
# Legacy deployment example, using RAZZLE_LEGACY_TRAVERSE Volto won't append ++api++ automatically
# Recommended only if you can't upgrade to latest `plone.restapi` and `plone.rest`
- # yarn build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true yarn start:prod
+ # pnpm build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod
# location ~ /api($|/.*) {
# rewrite ^/api($|/.*) /VirtualHostBase/http/myservername.org/Plone/VirtualHostRoot/_vh_api$1 break;
# proxy_pass http://backend;
diff --git a/docs/source/deploying/sentry.md b/docs/source/deploying/sentry.md
index f92ba34ecc..4266ffd4b8 100644
--- a/docs/source/deploying/sentry.md
+++ b/docs/source/deploying/sentry.md
@@ -82,7 +82,7 @@ SENTRY_AUTH_TOKEN=foo \
SENTRY_ORG=my_organization \
SENTRY_PROJECT=new_project \
SENTRY_RELEASE=2.0.0 \
-SENTRY_DSN=https://boo@sentry.com/1 yarn build
+SENTRY_DSN=https://boo@sentry.com/1 pnpm build
node build/server.js
```
@@ -265,7 +265,7 @@ SENTRY_PROJECT=new_project
SENTRY_RELEASE=2.0.0
SENTRY_DSN=https://boo@sentry.com/1
SENTRY_FRONTEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"},"extras":{"logger":"javascript-frontend", "release":"1.4.1"}}'
-SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} yarn build
+SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} pnpm build
node build/server.js
```
diff --git a/docs/source/deploying/simple.md b/docs/source/deploying/simple.md
index 1215cb3cd5..31b3894a3e 100644
--- a/docs/source/deploying/simple.md
+++ b/docs/source/deploying/simple.md
@@ -12,7 +12,7 @@ myst:
Volto is a Node.js application that runs on your machine/server and listens to a port. Once you are ready to deploy it, you should build it running:
```bash
-$ yarn build
+$ pnpm build
```
The Volto configuration determines the external URL Volto will be served, so if you just issue this command, the build will get that values and build an static bundle with that values (PORT=3000, API_PATH=http://localhost:8080/Plone).
@@ -20,13 +20,13 @@ The Volto configuration determines the external URL Volto will be served, so if
In order to make Volto work on a server under an specific DNS name, you must parametrize the build like:
```bash
-$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api yarn build
+$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api pnpm build
```
After the build, the bundle is created in `/build` folder, then in order to launch your application you can run:
```bash
-$ yarn start:prod
+$ pnpm start:prod
```
or
```bash
diff --git a/docs/source/development/creating-project.md b/docs/source/development/creating-project.md
index 5052533f81..f5ef93fefc 100644
--- a/docs/source/development/creating-project.md
+++ b/docs/source/development/creating-project.md
@@ -9,60 +9,15 @@ myst:
# Create a Volto project without a backend
-This document shows how to create a Volto project with the frontend only when you have your own existing backend, such as Plone {term}`Classic UI`, {term}`Nick`, or {term}`Guillotina`.
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator.
+Even if you don't need the backend, you can create a Plone project, then use only the {file}`frontend` folder for your purposes.
+As a bonus, it will contain the means for deploying your project.
```{seealso}
To create a full Plone project with both frontend and backend, see {doc}`plone:install/create-project` instead.
To contribute to Volto, see {doc}`../contributing/developing-core`.
```
-
-For using Volto for a project—in other words, use Volto as a library—you should use Volto's project generator `@plone/generator-volto`.
-It's a boilerplate project generator based on Yeoman that will provide you with the basic files and folder structure to bootstrap a Volto site.
-In addition to bootstrapping stand-alone Volto projects, it can also bootstrap Volto add-ons.
-
-1. Open a terminal and execute:
-
- ```shell
- npm install -g yo @plone/generator-volto
- # Install the latest and stable release of Volto with the following command
- yo @plone/volto
- # or you can install the "canary" release, including any alpha release
- yo @plone/volto --canary
- # or you can install any specific released version
- yo @plone/volto --volto=15.0.0
- # you can even pass a GitHub repo and specific branch
- yo @plone/volto --volto=plone/volto#16.0.0
- # you can bootstrap with add-ons
- yo @plone/volto --addon=volto-form-block
- ```
-
-2. Answer the questions when prompted, and provide the name of the new app (folder) to be created.
- For the sake of this documentation, use `myvoltoproject` as the project name.
-
- ````{note}
- You can run the generator with parameters to tailor your requirements.
-
- ```shell
- yo @plone/volto --help
- ```
-
- ```{seealso}
- [`@plone/generator-volto` `README.md`](https://github.com/plone/volto/blob/main/packages/generator-volto/README.md).
- ```
- ````
-
-3. Change your working directory to the newly created folder `myvoltoproject` (or whatever name you entered).
-
- ```shell
- cd myvoltoproject
- ```
-
-4. `@plone/generator-volto` installed the dependencies for you.
- Start the project.
-
- ```shell
- yarn start
- ```
-
- This starts the development server, which compiles the project code, and when done, it serves the app at http://localhost:3000.
diff --git a/docs/source/development/environment-variables.md b/docs/source/development/environment-variables.md
index 6b8b79efc3..78dc734177 100644
--- a/docs/source/development/environment-variables.md
+++ b/docs/source/development/environment-variables.md
@@ -33,7 +33,7 @@ docker run -it --rm -p 55001:8080 -e SITE=Plone -e ADDONS='plone.restapi==8.20.0
```
```shell
-RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' yarn start
+RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' pnpm start
```
Let's say that you do have it in a customized site id: http://localhost:55001/myplonesite
@@ -43,7 +43,7 @@ docker run -it --rm -p 55001:8080 -e SITE=myplonesite -e ADDONS='plone.restapi==
```
```shell
-RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' yarn start
+RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' pnpm start
```
### Debug an external site (provided you have access to it)
@@ -55,7 +55,7 @@ This is an advanced feature, and needs understanding of what you are doing and w
Let's say you want to debug a deployed site in production, but the build does not allow you to look deeper into the tracebacks. You could bootstrap a frontend in your machine, and point it to the production server, combining environment variables like:
```
-RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ yarn start
+RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ pnpm start
```
This has the drawback that could be that the proxy does not work well with the proxied SSL connection.
@@ -63,7 +63,7 @@ This has the drawback that could be that the proxy does not work well with the p
If you have access (via tunnel) to the port of the deployed backend is even more easier:
```
-RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone yarn start
+RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone pnpm start
```
This will use the internal proxy to access the backend, bypassing CORS.
diff --git a/docs/source/development/how-to-restrict-blocks.md b/docs/source/development/how-to-restrict-blocks.md
index ed274ee37b..62931acdf8 100644
--- a/docs/source/development/how-to-restrict-blocks.md
+++ b/docs/source/development/how-to-restrict-blocks.md
@@ -27,11 +27,20 @@ The function has this signature:
}
```
-`properties` is the current object data.
-`block` is the block being evaluated in `BlockChooser`.
-`navRoot` is the nearest navigation root object.
-`contentType` is the current content type.
-`user` is an object that represents the currently authenticated user.
+`properties`
+: The current object data.
+
+`block`
+: The block being evaluated in `BlockChooser`.
+
+`navRoot`
+: The nearest navigation root object.
+
+`contentType`
+: The current content type.
+
+`user`
+: An object that represents the currently authenticated user.
In the following configuration example, you can restrict a block so that it cannot be added unless the content type is `News Item` or the content item is in a specific path in the content tree (`/folder`):
diff --git a/docs/source/development/i18n.md b/docs/source/development/i18n.md
index 15e9c32e8c..f9f2f4dd03 100644
--- a/docs/source/development/i18n.md
+++ b/docs/source/development/i18n.md
@@ -17,7 +17,7 @@ This chapter describes the most common use cases for internationalization when d
## Process and file structure overview
Volto uses the library [react-intl](https://www.npmjs.com/package/react-intl) to provide translations for any potential language.
-Anything in the [official documentation of react-intl](https://formatjs.io/docs/react-intl/) also applies to Volto.
+Anything in the [official documentation of react-intl](https://formatjs.github.io/docs/react-intl/) also applies to Volto.
The workflow for creating *new* translatable text strings is as follows:
@@ -79,13 +79,13 @@ function HelloWorld(props) {
The identifier `hello_world` is then commonly used across all the translations.
There are more features available, such as using placeholders.
-See the documentation for all features in the [`FormattedMessage` component](https://formatjs.io/docs/react-intl/components#formattedmessage).
+See the documentation for all features in the [`FormattedMessage` component](https://formatjs.github.io/docs/react-intl/components#formattedmessage).
### Translate attributes
As `FormatMessage` is only suitable for creating text within HTML elements, it cannot be used for translating individual attributes.
-But with the method [`formatMessage`](https://formatjs.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings.
+But with the method [`formatMessage`](https://formatjs.github.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings.
This approach can be best explained with an example.
Assume you have a component called `TeaserImage` which contains an image that has, for accessibility reasons, the `alt` attribute.
@@ -97,7 +97,7 @@ To translate the `alt` attribute, you have to do the following steps:
import { defineMessages, injectIntl, intlShape } from 'react-intl';
```
-2. Define a message (or more) via [`defineMessages`](https://formatjs.io/docs/react-intl/api/#definemessagesdefinemessage):
+2. Define a message (or more) via [`defineMessages`](https://formatjs.github.io/docs/react-intl/api/#definemessagesdefinemessage):
```js
const messages = defineMessages({
@@ -145,7 +145,7 @@ Volto provides an i18n extraction script to get all translatable strings from yo
You can invoke this script with the following command.
```sh
-yarn i18n
+pnpm i18n
```
This will generate the following output:
@@ -181,7 +181,7 @@ defineMessages({
});
```
-Then run `yarn i18n`.
+Then run `pnpm i18n`.
You will find the translation ready to override in your `locales` directory, such as `locales/de/LC_MESSAGES/volto.po`.
```text
@@ -190,7 +190,7 @@ msgid "Back"
msgstr "My overridden translation"
```
-After you set the override, then run `yarn i18n` again to create the `de.json` translation files.
+After you set the override, then run `pnpm i18n` again to create the `de.json` translation files.
Restart Volto to see the changes applied.
@@ -204,10 +204,10 @@ If this was not the case, then all the translations in the customized components
You can add or override translated messages in your customizations by following the steps described in {ref}`override-i18n-messages`.
-## Contribute translations for an unsupported language
+(contribute-translations-for-an-unsupported-language-label)=
-The Volto project welcomes all speakers from the world to include any language, which is not supported yet.
+## Contribute translations for an unsupported language
-If your language's `.po` file is not available in Volto, [open an issue in GitHub](https://github.com/plone/volto/issues).
-Either you can create the `.po` file or we can do it for you.
-After that, you can start contributing your translations.
+```{seealso}
+{ref}`contributing-plone-core-translations-translate-volto-label`
+```
diff --git a/docs/source/development/ie11compat.md b/docs/source/development/ie11compat.md
deleted file mode 100644
index 16735ed7ae..0000000000
--- a/docs/source/development/ie11compat.md
+++ /dev/null
@@ -1,64 +0,0 @@
----
-myst:
- html_meta:
- "description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling"
- "property=og:description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling"
- "property=og:title": "Legacy Browser Support (IE11 compatibility)"
- "keywords": "Volto, Plone, frontend, React, IE11 compatibility, polyfills, legacy browser support"
----
-
-# Legacy Browser Support (IE11 compatibility)
-
-There are some caveats if we still want to target IE11 as supported browser.
-
-```{important}
-This documentation is orientative. Volto does NOT support legacy or vendor deprecated browsers (as in IE11).
-```
-
-## Version pinning
-
-These package versions should be pinned to this especific versions, unless
-their code or dependencies have some es6 only compatible, because their
-maintainers mainly target the Node.js world.
-
-* "query-string": "4.1.0"
-* "superagent": "3.8.2"
-
-## Polyfills
-
-Then in the project that should target it, these changes are required:
-
-add as a dependency `@babel/polyfill`.
-
- yarn add @babel/polyfill
-
-and in `src/client.jsx`:
-
-```js
-import '@babel/polyfill';
-```
-
-```{seealso}
-See https://babeljs.io/docs/babel-polyfill for more updated information
-```
-
-## babel-env
-
-Razzle supports `@babel/preset-env`, that supports including `browserlist` in
-`package.json`. So you can add this to `package.json`:
-
-```json
- "browserslist": [
- "last 2 version",
- "IE 11"
- ],
-```
-
-This supports the query specific DSL for `browserlist` targeting the browsers
-that you need to add.
-
-## Pre-transpiling
-
-Some packages in `node_modules` are ES6 only, for some older browsers, you might want to add a pre (or post) transpiling. There's a script `pre-build-transpiling.js` (Volto root folder) that might help you with it. Also this command line might help:
-
- ./node_modules/.bin/babel --presets="@babel/env" XXX --out-dir XXX
diff --git a/docs/source/development/index.md b/docs/source/development/index.md
index 90c28191f8..a1e5952999 100644
--- a/docs/source/development/index.md
+++ b/docs/source/development/index.md
@@ -36,5 +36,4 @@ pluggables
widget
how-to-restrict-blocks
color-picker-widget
-ie11compat
```
diff --git a/docs/source/development/lazyload.md b/docs/source/development/lazyload.md
index 668a7190b8..7a04d373cf 100644
--- a/docs/source/development/lazyload.md
+++ b/docs/source/development/lazyload.md
@@ -36,7 +36,7 @@ You can find the complete `@loadable/component` documentation here: https://load
You can check the code splitting state by using the included bundle analyzer:
```shell
-yarn analyze
+pnpm --filter=volto analyze
```
A browser will open with the bundle inspector.
diff --git a/docs/source/index.md b/docs/source/index.md
index ef65b2294f..2b47ebce16 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -1,18 +1,18 @@
---
myst:
html_meta:
- "description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release."
- "property=og:description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release."
- "property=og:title": "Frontend"
- "keywords": "Volto, Plone, frontend, React"
+ "description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6."
+ "property=og:description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6."
+ "property=og:title": "Volto UI"
+ "keywords": "Volto, Plone, frontend, user interface, React"
---
(volto-index-label)=
-# Frontend
+# Volto UI
-Volto is a React-based frontend for the [Plone CMS](https://plone.org).
-It is the default frontend starting with the Plone 6 release.
+The Volto user interface (UI) is a React-based frontend for the [Plone content management system](https://plone.org).
+It is the default user interface starting with the release of Plone 6.
Volto provides an attractive proposition: integration with the modern frontend development world, access to the huge ecosystem of React libraries and add-ons, combined with the ability to use the mature Plone CMS backend as a development platform.
@@ -38,7 +38,7 @@ An integrator is someone who uses Volto to build a project.
### Users
-A user of Volto is someone who edits content in a Plone content management system with Volto as the frontend.
+A user of Volto is someone who edits content in a Plone content management system with Volto as the user interface.
- {doc}`user-manual/index` provides information about how to manage content in a Plone site.
diff --git a/docs/source/release-notes/index.md b/docs/source/release-notes/index.md
index 91db671d96..228069b61f 100644
--- a/docs/source/release-notes/index.md
+++ b/docs/source/release-notes/index.md
@@ -17,6 +17,190 @@ myst:
+## 18.0.0-alpha.47 (2024-10-27)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+
+### Feature
+
+- URL Management control panel: add a filter for redirects created after a given date.
+ **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later.
+ @davisagli [#6414](https://github.com/plone/volto/issues/6414)
+- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: Add feature to bulk upload redirects in a CSV file.
+ **Note:** This requires `plone.restapi` 9.8.0 or later.
+ @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425)
+
+### Bugfix
+
+- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355)
+- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419)
+- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436)
+
+### Internal
+
+- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308)
+- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408)
+- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418)
+- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424)
+
+### Documentation
+
+- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426)
+- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433)
+- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438)
+
+## 18.0.0-alpha.46 (2024-10-10)
+
+### Breaking
+
+- Added `react/jsx-key` rule for ESlint to detect missing key property in iterators. @sneridagh [#6387](https://github.com/plone/volto/issues/6387)
+
+### Bugfix
+
+- Revert "Fix block chooser search is not focusable when clicked on add button" #5867 @sneridagh
+ It was causing a regression described in #6389 [#6390](https://github.com/plone/volto/issues/6390)
+
+### Internal
+
+- Added missing improvement to the `build-deps` Makefile command. @sneridagh [#6383](https://github.com/plone/volto/issues/6383)
+
+### Documentation
+
+- Add reference to Translate Volto in the main documentation. @stevepiercy [#6386](https://github.com/plone/volto/issues/6386)
+
+## 18.0.0-alpha.45 (2024-10-08)
+
+### Bugfix
+
+- Added missing arg for `buildStyleClassNamesExtenders` @sneridagh [#6381](https://github.com/plone/volto/issues/6381)
+
+### Documentation
+
+- Removed pin on Vale for documentation spelling, grammar, and style checks, and upgrade to v3.x configuration.
+ Updated CI and `Makefile` to allow `VALEOPTS=--no-exit`. @stevepiercy [#6376](https://github.com/plone/volto/issues/6376)
+
+## 18.0.0-alpha.44 (2024-10-03)
+
+### Feature
+
+- Added `config.settings.nonContentRoutesPublic` to avoid `isCmsUi` issues in these public routes. @giuliaghisini [#6173](https://github.com/plone/volto/issues/6173)
+- Add language independent field icon. @iRohitSingh [#6297](https://github.com/plone/volto/issues/6297)
+- Improve DiffField.jsx to render only the blocks on the page with better support for displaying HTML elements such as images. @dobri1408 [#6309](https://github.com/plone/volto/issues/6309)
+- Updated Italian Italian translations. @gianniftp [#6342](https://github.com/plone/volto/issues/6342)
+- Update Brazilian Portuguese translations. @ericof
+
+### Bugfix
+
+- Increase specificity of table header style selector to properly override colors for better contrast @jackahl [#2487](https://github.com/plone/volto/issues/2487)
+- Change Form input:focus text color to the `textColor` value for a11y.
+ Add Cypress test for contact form inputs. @ThomasKindermann @tedw87 [#2570](https://github.com/plone/volto/issues/2570)
+- Add missing `for` attribute to checkbox label to improve accessibility. @gomez [#6249](https://github.com/plone/volto/issues/6249)
+- - Fixed build style classnames in edit mode. Also use buildStyleClassNamesExtenders. @giuliaghisini [#6259](https://github.com/plone/volto/issues/6259)
+- Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
+- - Join validation errors in one single toast and update errors from response. @cekk
+ - Toast content now has a
wrapper instead of a
. @cekk [#6295](https://github.com/plone/volto/issues/6295)
+- Fixed findBlocks when no blocks are passed. @giuliaghisini [#6299](https://github.com/plone/volto/issues/6299)
+- Fixed toolbar buttons not having a focus outline. @JeffersonBledsoe [#6313](https://github.com/plone/volto/issues/6313)
+- fix: SidebarPopup close on ESC keypress @nileshgulia1 [#6315](https://github.com/plone/volto/issues/6315)
+- Changed sidebar accordion text colour from @teal to @textColor. @JeffersonBledsoe [#6330](https://github.com/plone/volto/issues/6330)
+- Labels accessibility for ArrayWidget, SelectWidget, TokenWidget. @folix-01 [#6332](https://github.com/plone/volto/issues/6332)
+- Use lighter blue as link color in inverted tables to improve contrast for a11y @jackahl [#6334](https://github.com/plone/volto/issues/6334)
+
+### Internal
+
+- Added a pull request template as a checklist before submitting a PR. @stevepiercy [#6365](https://github.com/plone/volto/issues/6365)
+
+### Documentation
+
+- Update references to cookiecutter-plone-starter in docs. @davisagli [#6289](https://github.com/plone/volto/issues/6289)
+- - Revised Cookieplone section in the Upgrade Guide. @stevepiercy [#6290](https://github.com/plone/volto/issues/6290)
+- - Fix the MyST syntax for the label `upgrade-18-cookieplone-label`. @stevepiercy [#6360](https://github.com/plone/volto/issues/6360)
+- Fixed spelling of prerequisites. @stevepiercy [#6362](https://github.com/plone/volto/issues/6362)
+- Fix links to Vite website. @stevepiercy [#6366](https://github.com/plone/volto/issues/6366)
+
+## 18.0.0-alpha.43 (2024-09-13)
+
+### Breaking
+
+- Move `Tags` component to the slot `belowContent`. @wesleybl [#6269](https://github.com/plone/volto/issues/6269)
+
+### Feature
+
+- Refactor AlbumView from class to functional component. @Tishasoumya-02 [#4077](https://github.com/plone/volto/issues/4077)
+- Add setting `unwantedControlPanelsFields` and use it in the function `filterControlPanelsSchema`. @wesleybl [#4819](https://github.com/plone/volto/issues/4819)
+- Refactor the `Register` component from class-based to functional. @Tishasoumya-02 [#4861](https://github.com/plone/volto/issues/4861)
+- Refactor Toolbar/More component from class to functional component. @Tishasoumya-02 [#4955](https://github.com/plone/volto/issues/4955)
+- Refactor Blocks/LeadImage/Edit component from class to functional component. @Tishasoumya-02 [#4959](https://github.com/plone/volto/issues/4959)
+- Refactor Blocks/Video/Edit component from class to functional component. @Tishasoumya-02 [#4960](https://github.com/plone/volto/issues/4960)
+- Refactor ControlPanel/Users/Aliases component , replacing class component lifecycle methods with functional hooks. @Tishasoumya-02 [#4985](https://github.com/plone/volto/issues/4985)
+- Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02 [#4986](https://github.com/plone/volto/issues/4986)
+- Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02 [#4993](https://github.com/plone/volto/issues/4993)
+- Refactor AddonsControlpanel from class to functional components. @Tishasoumya [#4995](https://github.com/plone/volto/issues/4995)
+- Refactor Preference/Change Password from class to functional component. @Tishasoumya-02 [#5044](https://github.com/plone/volto/issues/5044)
+- Refactor Preference/PersonalPreferences from class to functional component. @Tishasoumya-02 [#5045](https://github.com/plone/volto/issues/5045)
+- Refactor Preference/PersonalInformation from Class to Functional component. @Tishasoumya-02 [#5046](https://github.com/plone/volto/issues/5046)
+- Refactor Contents/ContentsUploadModal & Storybook-@Tishasoumya-02 [#5047](https://github.com/plone/volto/issues/5047)
+- Storybook test for ContentBreadcrumbs component. @Tishasoumya [#5048](https://github.com/plone/volto/issues/5048)
+- Storybook Actions component test. @Tishasoumya-02 [#5049](https://github.com/plone/volto/issues/5049)
+- Refactor Display from class to functional component. @Tishasoumya-02 [#5066](https://github.com/plone/volto/issues/5066)
+- Refactor `ReferenceWidget` from class-based to functional component. @Tishasoumya [#5093](https://github.com/plone/volto/issues/5093)
+- Refactor the table of contents block component from a class component to a functional component. @Prince0906 [#6167](https://github.com/plone/volto/issues/6167)
+- Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico [#6186](https://github.com/plone/volto/issues/6186)
+- Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach [#6213](https://github.com/plone/volto/issues/6213)
+- The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
+ Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli [#6248](https://github.com/plone/volto/issues/6248)
+- Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
+
+### Bugfix
+
+- Fix search block showing no option select in sort on property @iRohitSingh [#5055](https://github.com/plone/volto/issues/5055)
+- Displays validation error messages on control panel forms. @wesleybl [#5274](https://github.com/plone/volto/issues/5274)
+- Fix `initialValue` block setting. @wesleybl [#5971](https://github.com/plone/volto/issues/5971)
+- When user changes location, set the `userSession.token` value based on cookie. This fixes the login status not being properly determined by the application. @tiberiu-ichim [#6071](https://github.com/plone/volto/issues/6071)
+- Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02 [#6147](https://github.com/plone/volto/issues/6147)
+- Add Cypress test for search block sort on property. @iRohitSingh [#6226](https://github.com/plone/volto/issues/6226)
+- Remove unused i18n message for SortOn component. @davisagli [#6230](https://github.com/plone/volto/issues/6230)
+- Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli [#6235](https://github.com/plone/volto/issues/6235)
+- Fix loading of .cjs in webpack. @davisagli [#6237](https://github.com/plone/volto/issues/6237)
+- fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one,
+ onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini [#6240](https://github.com/plone/volto/issues/6240)
+- Changed imports from relative to absolute to avoid type errors in add-on tests. @wesleybl [#6244](https://github.com/plone/volto/issues/6244)
+- In the URL Management control panel, allow external URLs as targets. @davisagli [#6247](https://github.com/plone/volto/issues/6247)
+- Disable save button when loading POST query @sabrina-bongiovanni [#6252](https://github.com/plone/volto/issues/6252)
+- Fix `TypeError: values[0] is undefined` in Contents properties modal. @davisagli [#6258](https://github.com/plone/volto/issues/6258)
+- Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
+- Place actions storybook in correct path. @Tishasoumya-02 [#6275](https://github.com/plone/volto/issues/6275)
+- Remove `Generate tabs for items other than folders` field from navigation control panel. @wesleybl [#6278](https://github.com/plone/volto/issues/6278)
+- Fix error rendering empty grid blocks. @MAX-786 [#6279](https://github.com/plone/volto/issues/6279)
+- Fixed instant error on click on an item of the "Order" tab @sneridagh [#6284](https://github.com/plone/volto/issues/6284)
+
+### Internal
+
+- Refactored the `SelectWidget` component to a functional component. @lorstenoplo [#5570](https://github.com/plone/volto/issues/5570)
+- Allow setting the language for `make backend-docker-start`. Default is `en`. Added usage documentation in Contributing > Develop Volto core, and reorganized it. Cleaned up Configuration > Multilingual. @stevepiercy [#6231](https://github.com/plone/volto/issues/6231)
+- Revert rename of `.gitkeep`. @stevepiercy [#6232](https://github.com/plone/volto/issues/6232)
+- Upgrade Cypress to 13.13.2. @wesleybl [#6241](https://github.com/plone/volto/issues/6241)
+- Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy [#6242](https://github.com/plone/volto/issues/6242)
+- Add `make i18n` command. @Faakhir30 [#6274](https://github.com/plone/volto/issues/6274)
+- Update to Plone 6.0.13 @sneridagh [#6285](https://github.com/plone/volto/issues/6285)
+
+### Documentation
+
+- Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy [#6239](https://github.com/plone/volto/issues/6239)
+- Fix redirect to Docker documentation. @stevepiercy [#6262](https://github.com/plone/volto/issues/6262)
+- Added upgrade guide notice about CookiePlone as the recommended way for generating projects and add-on boilerplate @sneridagh [#6286](https://github.com/plone/volto/issues/6286)
+
## 18.0.0-alpha.42 (2024-07-30)
### Breaking
diff --git a/docs/source/theming/index.md b/docs/source/theming/index.md
index dd0743403d..fe401772f3 100644
--- a/docs/source/theming/index.md
+++ b/docs/source/theming/index.md
@@ -19,4 +19,5 @@ theming-engine
theming-strategy
custom-styling
using-third-party-themes
+theming-a-base-theme
```
diff --git a/docs/source/theming/theming-a-base-theme.md b/docs/source/theming/theming-a-base-theme.md
new file mode 100644
index 0000000000..f3730dead1
--- /dev/null
+++ b/docs/source/theming/theming-a-base-theme.md
@@ -0,0 +1,46 @@
+---
+myst:
+ html_meta:
+ "description": "Customize a base theme such as Volto Light Theme via SCSS."
+ "property=og:description": "Customize a base theme such as Volto Light Theme via SCSS."
+ "property=og:title": "Theming a base theme such as Volto Light Theme"
+ "keywords": "Volto, Plone, frontend, React, themes, Volto Light Theme"
+---
+
+# Customize a base theme
+
+You can customize a base theme for your add-on.
+The following examples use [Volto Light Theme](https://github.com/kitconcept/volto-light-theme) as a base theme.
+
+
+## File structure
+
+In your Volto add-on's {file}`src` folder, create a subfolder named {file}`theme`.
+Inside {file}`theme` create two empty files named {file}`_main.scss` and {file}`_variables.scss`.
+Refer to the following file system diagram.
+
+```text
+src/
+├── components
+├── index.js
+└── theme
+ ├── _main.scss
+ └── _variables.scss
+```
+
+
+## `_variables.scss`
+
+{file}`_variables.scss` is where you can override SCSS variables of the base theme.
+
+```scss
+$text-color: #000;
+$font-size: 18px;
+$line-height: 24px;
+```
+
+
+## `_main.scss`
+
+{file}`_main.scss` is where you should put all custom styles.
+You can also include other SCSS or CSS files here.
diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md
index b199673aea..b527a00374 100644
--- a/docs/source/upgrade-guide/index.md
+++ b/docs/source/upgrade-guide/index.md
@@ -28,6 +28,19 @@ Thus it is safe to run it on top of your project and answer the prompts.
## Upgrading to Volto 18.x.x
+### Node.js version support: adding 22, dropping 18
+
+Added support for Node.js 22.
+It is the long-term support (LTS) release effective 2024-10-29.
+
+Long-term support for {term}`Node.js` 18 by the Node.js community will end on 2025-04-30.
+Volto 18 no longer supports Node.js 18, since Volto only supports the latest two LTS versions.
+Volto might continue to work on Node.js 18, but it is not tested in CI.
+
+Please update your projects to a supported Node.js version, either 20 or 22.
+Version 22 is recommended, as the latest LTS version of Node.js.
+
+
### Volto's internal `dependencies` and `devDependencies` are now properly sorted out
Volto internal `dependencies` and `devDependencies` have been correctly sorted out.
@@ -40,7 +53,7 @@ For this purpose, we have developed a {ref}`new utility {
+ const config = modifyWebpackConfig({
+ env: { target, dev },
+ webpackConfig,
+ webpackObject,
+ options,
++ paths,
+ });
+ // add custom code here..
+ return config;
+```
+
+The change involves adding a new `paths` argument to the `customModifyWebpackConfig` function.
+
+### Added rule for ESlint to detect missing key property in iterators.
+
+The `react/jsx-key` rule has been enabled in ESlint for catching missing `key` in JSX iterators.
+You might catch some violations in your project or add-on code after running ESlint.
+Adding the missing `key` property whenever the violation is reported will fix it.
+
+### `@plone/registry` moved to ESM
+
+The `@plone/registry` package has been moved to ESM.
+The add-on registry scripts have also been refactored to TypeScript.
+For maximum compatibility with CommonJS builds, the default exports have been moved to named exports.
+The modules affected are now built, and the import paths have changed, too.
+These changes force some import path changes that you should patch in your Plone project or add-on boilerplates.
+
+```{note}
+As always, when something changes in the boilerplate, you may regenerate one from Cookieplone and move your code into it, instead of fiddling with it.
+```
+
+For example, in your project's {file}`.eslintrc.js`:
+
+```diff
+ const fs = require('fs');
+ const projectRootPath = __dirname;
+-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
++const { AddonRegistry } = require('@plone/registry/addon-registry');
+
+ let voltoPath = './node_modules/@plone/volto';
+
+@@ -17,15 +17,15 @@ if (configFile) {
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
+ }
+
+-const reg = new AddonConfigurationRegistry(__dirname);
++const { registry } = AddonRegistry.init(__dirname);
+
+ // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
+-const addonAliases = Object.keys(reg.packages).map((o) => [
++const addonAliases = Object.keys(registry.packages).map((o) => [
+ o,
+- reg.packages[o].modulePath,
++ registry.packages[o].modulePath,
+ ]);
+
+-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
++const addonExtenders = registry.getEslintExtenders().map((m) => require(m));
+```
+
+Also in the Storybook configuration {file}`.storybook/main.js`.
+
+```diff
+- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
++ const { AddonRegistry } = require('@plone/registry/addon-registry');
+
+- const registry = new AddonConfigurationRegistry(projectRootPath);
++ const { registry } = AddonRegistry.init(projectRootPath);
+```
+
+```{versionadded} Volto 18.0.0-alpha.47
+```
+
+```{versionadded} @plone/registry 3.0.0-alpha.0
+```
+
+### Add missing overrides to projects in `package.json`
+
+This will fix some issues with Hot Module Reload in projects.
+It's required in Volto `18.0.0-alpha.47` and later, otherwise the site breaks in development mode.
+Add this object to the `pnpm` key in your project {file}`package.json`.
+
+```json
+ "pnpm": {
+ "overrides": {
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
+ "react-refresh": "^0.14.2"
+ }
+ },
+```
+
+```{versionadded} Volto 18.0.0-alpha.47
+```
+
+```{versionadded} @plone/registry 3.0.0-alpha.0
+```
+
+### Deprecation notices for Volto 18
+
+#### `@plone/generator-volto`
+
+```{deprecated} Volto 18.0.0
+```
+
+The Node.js-based Volto project boilerplate generator is deprecated from Volto 18 onwards.
+After the release of Volto 18, it will be marked as deprecated, archived, and it won't receive any further updates.
+Although you can still migrate your project to Volto 18 using this boilerplate, you should migrate to using [Cookieplone](https://github.com/plone/cookieplone).
+
+##### Alternative
+
+Migrate your project to use a [Cookieplone](https://github.com/plone/cookieplone) boilerplate.
+
+#### Volto project configurations
+
+```{deprecated} Volto 18.0.0
+```
+
+Configuring Volto using {file}`src/config.js` at the project level is deprecated in Volto 18, and will be removed in Volto 19.
+
+```{seealso}
+See https://github.com/plone/volto/issues/6396 for details.
+```
+
+##### Alternative
+
+You should configure your projects in a policy add-on.
+You can move your project to use [Cookieplone](https://github.com/plone/cookieplone) which provides the necessary boilerplate for it.
+
+#### Semantic UI
+
+```{deprecated} Volto 18.0.0
+```
+
+The Semantic UI library is not maintained anymore, and will be removed in Plone 7.
+You should no longer use Semantic UI in add-ons and projects.
+
+```{seealso}
+Related PLIPs:
+
+- https://github.com/plone/volto/issues/6321
+- https://github.com/plone/volto/issues/6323
+```
+
+##### Alternatives
+
+You can use any supported component framework of your choice for implementing new components, especially in the public theme side.
+If you create new widgets or components for the CMSUI—in other words, the non-public side—you should use the [`@plone/components`](https://github.com/plone/volto/tree/main/packages/components) library as an alternative.
+Even though it's still in the development phase, it will be completed in the next few months, and will be supported in the future.
+
+#### `lodash` library
+
+```{deprecated} Volto 18.0.0
+```
+
+`lodash` is deprecated in Volto 18, and will be removed in Plone 7.
+
+`lodash` has not received any updates since 2021.
+It has performance issues from bloated bundles and it's not prepared for ESM.
+Lots of `lodash` utility helpers can be replaced with vanilla ES.
+These issues cause concern about its future maintainability.
+
+In place of `lodash`, Plone 7 will use both the `lodash-es` library, which is ESM ready, and modern vanilla ES alternatives whenever possible.
+
+##### Alternatives
+
+```{seealso}
+The following links suggest alternatives to `lodash`.
+
+- https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore
+- https://javascript.plainenglish.io/you-dont-need-lodash-how-i-gave-up-lodash-693c8b96a07c
+
+If you still need some of the utilities in `lodash` and cannot use vanilla ES, you can use `lodash-es` instead.
+```
+
+#### `@loadable/component` and Volto `Loadables` framework
+
+```{deprecated} Volto 18.0.0
+```
+
+`@loadable/component` and the Volto `Loadables` framework is deprecated in Volto 18, and will be removed from Plone 7.
+It's a Webpack-only library, and it does not have Vite plugin support.
+Since React 18, this library is no longer necessary, as it has the initial implementation of the "concurrent mode".
+React 19 will further improve it and add more features around it.
+
+##### Alternatives
+
+Use plain React 18 lazy load features and its idioms for lazy load components.
+
+```jsx
+const myLazyComponent = lazy(()=> import('@plone/volto/components/theme/MyLazyComponent/MyLazyComponent'))
+
+const RandomComponent = (props) => (
+
+
+
+)
+```
+
+There's no support for pre-loading or lazy loading entire libraries as in `@loadable/component`.
+With the removal of barrel imports files, as described in the next deprecation notice, it is now unnecessary.
+
+#### Removal of barrel import files
+
+```{deprecated} Volto 18.0.0
+```
+
+Volto previously used barrel imports, which are centralized files where other imports are re-exported, to improve the developer user experience.
+With barrel imports, a developer only needs to remember to import from the re-exported place, not the full path.
+
+Since the barrel imports directly import all the code, a lot of imports ended up in the same main chunk of code.
+It became a bad practice.
+Modern bundlers, such as Vite, rely upon the import path to determine whether to bundle code together or not, reducing the bundle size.
+
+The barrel imports must be removed to increase the natural number of chunks that Volto divides on—especially on routes—resulting in code splitting done the right and natural way.
+This forces us to rewrite all the imports everywhere—including core, projects, and add-ons—once we implement it.
+The barrel imports files include the following.
+
+- {file}`src/components/index.js`
+- {file}`src/helpers/index.js`
+- {file}`src/actions/index.js`
+
+##### Alternative
+
+Implement only direct imports in code, preparing now for the upcoming change.
+
+```diff
+-import { BodyClass } from '@plone/volto/helpers';
++import BodyClass from '@plone/volto/helpers/BodyClass/BodyClass';
+```
+
+Once this is implemented, a code modification will be provided for a smooth migration.
+
+
(volto-upgrade-guide-17.x.x)=
## Upgrading to Volto 17.x.x
@@ -501,7 +792,7 @@ Upgrade your local dependencies in projects and add-ons by editing your {file}`p
### TypeScript support in Volto
-```{versionadded} 17.0.0-alpha.27
+```{versionadded} Volto 17.0.0-alpha.27
```
We added full support of TypeScript in Volto core.
@@ -604,7 +895,7 @@ make develop
### Upgrade ESlint and use `@babel/eslint-parser`
-```{versionchanged} 17.0.0-alpha.27
+```{versionchanged} Volto 17.0.0-alpha.27
```
ESlint uses a library to parse the language under analysis.
@@ -617,7 +908,7 @@ Once upgraded, run the linters again to make sure that your code is free of viol
Technically not a breaking, the API nor the component contract has changed, but it's worth noting this change in here.
-```{versionadded} 17.0.0-alpha.1
+```{versionadded} Volto 17.0.0-alpha.1
The `BlockChooser` component now uses `popperjs` library to position itself in the screen.
It spawns at the end of the body instead of inner the block that called it.
This is better from the UI point of view, since any other element can take precedence in the CSS element flow, preventing the block chooser to get overlapped by anything else.
@@ -671,11 +962,11 @@ This is because the overrides that `@testing-library/cypress` introduce can be r
Since there are some commands that can call exports in {file}`cypress/support/commands.js`, this import may be run more than once, and then it errors.
So you have to make sure that import is run only once while the tests are run.
-Check the official [Cypress Migration Guide](https://docs.cypress.io/guides/references/migration-guide) for more information.
+Check the official [Cypress Migration Guide](https://docs.cypress.io/app/references/migration-guide) for more information.
### New Image component
-```{versionadded} 17.0.0-alpha.21
+```{versionadded} Volto 17.0.0-alpha.21
A new image component has been added to core to render optimized images.
It requires the latest version of `plone.restapi` (>=8.42.0) installed in the backend to work properly.
```
@@ -727,11 +1018,11 @@ From Volto 16.0.0-alpha.15 onwards, `volto-slate` is integrated into Volto core
The previous text block `text` based on `draftJS` is now deprecated and is restricted (hidden) in Volto bootstrap.
This is a major change and should be planned in advance before you install 16.0.0-alpha.15 or later.
-```{versionadded} 16.0.0-alpha.15
+```{versionadded} Volto 16.0.0-alpha.15
`volto-slate` added to Volto core as the default text block.
```
-```{deprecated} 16.0.0-alpha.15
+```{deprecated} Volto 16.0.0-alpha.15
`text` text block based on `draftJS` is now deprecated and will be removed from core in Volto 18.
```
@@ -825,7 +1116,7 @@ Version 16 is recommended.
### Upgraded to Razzle 4
-```{versionadded} 16.0.0-alpha.38
+```{versionadded} Volto 16.0.0-alpha.38
Volto has upgraded from Razzle 3 to Razzle 4.
You should perform these steps in case you are upgrading to this version or above.
```
@@ -1063,7 +1354,7 @@ If you have already updated your configuration to use Cypress 10 or later in a p
It is possible that forcing your project to use older versions might still work with old configurations.
```{seealso}
-See https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0 for more information.
+See https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-100 for more information.
```
### The complete configuration registry is passed to the add-ons and the project configuration pipeline
@@ -1128,7 +1419,7 @@ It maintains signature compatibility with `registry.resolve`, but introduces new
See documentation for more information.
-````{versionchanged} 16.0.0-alpha.23
+````{versionchanged} Volto 16.0.0-alpha.23
The `component` argument changed in 16.0.0-alpha.23.
The `component` key has been flattened for simplification and now it's mapped directly to the `component` argument of `registerComponent`:
@@ -1207,7 +1498,7 @@ During the alpha stage, we received feedback and determined that it's too diffic
We decided it is best to deal with it as any other schema field and enhance it via schema enhancers.
This improves the developer experience, especially when dealing with variations that can provide their own styles and other schema fields.
-```{deprecated} 16.0.0-alpha.46
+```{deprecated} Volto 16.0.0-alpha.46
The options `enableStyling` and `stylesSchema` no longer work.
You need to provide them using your own block schema.
If you are extending an existing one, you should add it as a normal `schemaEnhancer` modification.
@@ -1224,7 +1515,7 @@ Since then, the add-on story has improved.
It now makes sense to extract this feature into its own add-on.
Integrate Sentry in your app with [`@plone-collective/volto-sentry`](https://www.npmjs.com/package/@plone-collective/volto-sentry).
-```{versionchanged} 16.0.0.alpha.45
+```{versionchanged} Volto 16.0.0.alpha.45
```
### Upgrade `husky` to latest version
@@ -1245,7 +1536,7 @@ You can copy over the Volto ones (take a look in Volto's `.husky` folder).
### Better defaults handling
-````{versionadded} 16.0.0-alpha.51
+````{versionadded} Volto 16.0.0-alpha.51
Prior to this version, we handled default values in schemas for blocks settings in a faulty and buggy manner.
The state inferred was not deterministic and depended on the fields with defaults present.
@@ -1361,7 +1652,7 @@ A new `@root` alias has been set up to replace the `~` alias.
Support for the `~` alias is still in place, but we now mark it as deprecated.
The use of `~` will be removed in Volto 16.
-```{deprecated} 15.0
+```{deprecated} Volto 15.0
```
## Upgrading to Volto 14.x.x
diff --git a/package.json b/package.json
index c5be31bcc6..603b737d9a 100644
--- a/package.json
+++ b/package.json
@@ -36,14 +36,17 @@
"husky": "9.0.11",
"lint-staged": "15.2.2",
"tsconfig": "workspace:*",
- "typescript": "^5.4.5",
- "vitest": "^1.5.0"
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
},
"packageManager": "pnpm@9.1.1",
"pnpm": {
"overrides": {
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
"react-refresh": "0.14.0"
+ },
+ "patchedDependencies": {
+ "jest-resolve@26.6.2": "patches/jest-resolve@26.6.2.patch"
}
}
}
diff --git a/packages/blocks/README.md b/packages/blocks/README.md
index 019efc997b..f5fe18bc4f 100644
--- a/packages/blocks/README.md
+++ b/packages/blocks/README.md
@@ -5,3 +5,8 @@ This package is in alpha stages, experimental, under heavy development, and is s
It's one of the Plone frontend strategic packages and part of the headless CMS story.
`@plone/blocks` is not part of Volto, and thus, is not used by it.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index 3b945309d6..d8b191191e 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -61,7 +61,7 @@
"parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2",
- "vitest": "^1.3.1"
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md
index b609e68f8e..0761493643 100644
--- a/packages/client/CHANGELOG.md
+++ b/packages/client/CHANGELOG.md
@@ -8,6 +8,31 @@
+## 1.0.0-alpha.19 (2024-10-18)
+
+### Feature
+
+- Add support for the `depth` parameter on `getNavigationQuery`. @ebrehault [#6400](https://github.com/plone/volto/pull/6400)
+
+### Internal
+
+- Upgrade `@tanstack/react-query`. @sneridagh [#6069](https://github.com/plone/volto/pull/6069)
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/pull/6373)
+- Added watch script to package.json @pnicolli @deodorhunter [#6382](https://github.com/plone/volto/pull/6382)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/pull/6407)
+
+## 1.0.0-alpha.18 (2024-10-03)
+
+### Bugfix
+
+- Fixed client copy mutation, cleanup up move mutation for consistency @pnicolli [#6349](https://github.com/plone/volto/pull/6349)
+
+## 1.0.0-alpha.17 (2024-09-13)
+
+### Documentation
+
+- Add documentation about optional `token` parameter for `ploneClient` initialization. @MAX-786 [#6076](https://github.com/plone/volto/pull/6076)
+
## 1.0.0-alpha.16 (2024-06-06)
### Bugfix
diff --git a/packages/client/news/6076.documentation b/packages/client/news/6076.documentation
deleted file mode 100644
index 138045af4e..0000000000
--- a/packages/client/news/6076.documentation
+++ /dev/null
@@ -1 +0,0 @@
-Add documentation about optional `token` parameter for `ploneClient` initialization. @MAX-786
\ No newline at end of file
diff --git a/packages/client/package.json b/packages/client/package.json
index e3563d194d..d33b80c4f4 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "1.0.0-alpha.16",
+ "version": "1.0.0-alpha.19",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
@@ -46,6 +46,7 @@
"react-query"
],
"scripts": {
+ "watch": "parcel watch",
"build": "parcel build",
"build:force": "parcel build --no-cache",
"test": "vitest",
@@ -83,15 +84,15 @@
"react-dom": "^18.2.0",
"release-it": "17.1.1",
"tsup": "^8.0.2",
- "typescript": "5.4.2",
+ "typescript": "^5.4.5",
"uuid": "^9.0.1",
- "vite": "^5.1.7",
+ "vite": "^5.4.8",
"vite-plugin-dts": "^3.7.3",
- "vitest": "^1.3.1",
+ "vitest": "^2.1.3",
"wait-on": "^7.2.0"
},
"dependencies": {
- "@tanstack/react-query": "^5.37.1",
+ "@tanstack/react-query": "^5.59.0",
"axios": "^1.6.7",
"debug": "4.3.4",
"query-string": "^9.0.0",
diff --git a/packages/client/src/restapi/content/add.test.tsx b/packages/client/src/restapi/content/add.test.tsx
index f62c221efe..4d65805d6c 100644
--- a/packages/client/src/restapi/content/add.test.tsx
+++ b/packages/client/src/restapi/content/add.test.tsx
@@ -6,6 +6,7 @@ import { beforeEach } from 'vitest';
import { expect, test } from 'vitest';
import PloneClient from '../../client';
import { CreateContentArgs } from './add';
+import { createContent } from '../content/add';
const cli = PloneClient.initialize({
apiPath: 'http://localhost:55001/plone',
@@ -46,6 +47,40 @@ describe('[POST] Content', () => {
expect(result.current.data?.title).toBe('My Page');
});
+ test('Hook - create content in path', async () => {
+ const myPageData = {
+ '@type': 'Document',
+ title: 'My Page',
+ };
+
+ await createContent({
+ path: '/',
+ data: myPageData,
+ config: cli.config,
+ });
+
+ const path = '/my-page';
+ const data: CreateContentArgs['data'] = {
+ '@type': 'Document',
+ title: 'My nested Page',
+ };
+
+ const { result } = renderHook(() => useMutation(createContentMutation()), {
+ wrapper: createWrapper(),
+ });
+
+ act(() => {
+ result.current.mutate({ path, data });
+ });
+
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+ expect(result.current.data?.['@id']).toBe(
+ 'http://localhost:55001/plone/my-page/my-nested-page',
+ );
+ expect(result.current.data?.title).toBe('My nested Page');
+ });
+
test('Hook - Successful - setup/tearingDown setup', async () => {
const path = '/';
const data: CreateContentArgs['data'] = {
diff --git a/packages/client/src/restapi/copymove/copy.test.tsx b/packages/client/src/restapi/copymove/copy.test.tsx
index e278ad3d88..0c79277546 100644
--- a/packages/client/src/restapi/copymove/copy.test.tsx
+++ b/packages/client/src/restapi/copymove/copy.test.tsx
@@ -43,7 +43,7 @@ describe('[POST] Copy', () => {
});
act(() => {
- result.current.mutate({ data: copyData });
+ result.current.mutate({ path: '/', data: copyData });
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
@@ -80,7 +80,7 @@ describe('[POST] Copy', () => {
});
act(() => {
- result.current.mutate({ data: copyMultipleData });
+ result.current.mutate({ path: '/', data: copyMultipleData });
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
diff --git a/packages/client/src/restapi/copymove/copy.ts b/packages/client/src/restapi/copymove/copy.ts
index 3935275dd5..bf300f958f 100644
--- a/packages/client/src/restapi/copymove/copy.ts
+++ b/packages/client/src/restapi/copymove/copy.ts
@@ -1,37 +1,35 @@
import { z } from 'zod';
import { ApiRequestParams, apiRequest } from '../../API';
-import {
- PloneClientConfig,
- PloneClientConfigSchema,
-} from '../../validation/config';
+import { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as copyDataSchema } from '../../validation/copymove';
import { CopyMoveResponse as CopyResponse } from '@plone/types';
-export const copyArgsSchema = z.object({
- data: copyDataSchema,
- config: PloneClientConfigSchema,
-});
-
-export type CopyArgs = z.infer;
+export type CopyArgs = z.infer & {
+ config: PloneClientConfig;
+};
export const copy = async ({
+ path,
data,
config,
}: CopyArgs): Promise => {
- const validatedArgs = copyArgsSchema.parse({
+ const validatedArgs = copyDataSchema.parse({
+ path,
data,
- config,
});
const options: ApiRequestParams = {
+ config,
data: validatedArgs.data,
- config: validatedArgs.config,
};
- return apiRequest('post', '/@copy', options);
+ const copyPath = `${validatedArgs.path}/@copy`;
+
+ return apiRequest('post', copyPath, options);
};
export const copyMutation = ({ config }: { config: PloneClientConfig }) => ({
mutationKey: ['post', 'copy'],
- mutationFn: ({ data }: Omit) => copy({ data, config }),
+ mutationFn: ({ path, data }: Omit) =>
+ copy({ path, data, config }),
});
diff --git a/packages/client/src/restapi/copymove/move.ts b/packages/client/src/restapi/copymove/move.ts
index 3f9e3ceea7..6f1015caad 100644
--- a/packages/client/src/restapi/copymove/move.ts
+++ b/packages/client/src/restapi/copymove/move.ts
@@ -1,37 +1,29 @@
import { z } from 'zod';
import { ApiRequestParams, apiRequest } from '../../API';
-import {
- PloneClientConfig,
- PloneClientConfigSchema,
-} from '../../validation/config';
+import { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as moveDataSchema } from '../../validation/copymove';
import { CopyMoveResponse as MoveResponse } from '@plone/types';
-export const MoveArgsSchema = z.object({
- path: z.string(),
- data: moveDataSchema,
- config: PloneClientConfigSchema,
-});
-
-export type MoveArgs = z.infer;
+export type MoveArgs = z.infer & {
+ config: PloneClientConfig;
+};
export const move = async ({
path,
data,
config,
}: MoveArgs): Promise => {
- const validatedArgs = MoveArgsSchema.parse({
+ const validatedArgs = moveDataSchema.parse({
path,
data,
- config,
});
const options: ApiRequestParams = {
+ config,
data: validatedArgs.data,
- config: validatedArgs.config,
};
- const movePath = `/${validatedArgs.path}/@move`;
+ const movePath = `${validatedArgs.path}/@move`;
return apiRequest('post', movePath, options);
};
diff --git a/packages/client/src/restapi/navigation/get.test.tsx b/packages/client/src/restapi/navigation/get.test.tsx
index f6a47d68e5..e456738842 100644
--- a/packages/client/src/restapi/navigation/get.test.tsx
+++ b/packages/client/src/restapi/navigation/get.test.tsx
@@ -2,12 +2,24 @@ import { renderHook, waitFor } from '@testing-library/react';
import { createWrapper } from '../../testUtils';
import { useQuery } from '@tanstack/react-query';
import ploneClient from '../../client';
+import { setup, teardown } from '../../resetFixture';
+import { createContent } from '../content/add';
const cli = ploneClient.initialize({
apiPath: 'http://localhost:55001/plone',
});
-const { getNavigationQuery } = cli;
+const { getNavigationQuery, login } = cli;
+
+await login({ username: 'admin', password: 'secret' });
+
+beforeEach(async () => {
+ await setup();
+});
+
+afterEach(async () => {
+ await teardown();
+});
describe('[GET] Navigation', () => {
test('Hook - Successful', async () => {
@@ -39,4 +51,57 @@ describe('[GET] Navigation', () => {
expect(result.current.error).toBeDefined();
});
+
+ test('Depth parameter', async () => {
+ await createContent({
+ path: '/',
+ data: {
+ '@type': 'Document',
+ title: 'Level 1',
+ },
+ config: cli.config,
+ });
+ await createContent({
+ path: '/level-1',
+ data: {
+ '@type': 'Document',
+ title: 'Level 2',
+ },
+ config: cli.config,
+ });
+ await createContent({
+ path: '/level-1/level-2',
+ data: {
+ '@type': 'Document',
+ title: 'Level 3',
+ },
+ config: cli.config,
+ });
+
+ const depth2 = renderHook(
+ () => useQuery(getNavigationQuery({ path: '/', depth: 2 })),
+ {
+ wrapper: createWrapper(),
+ },
+ );
+ await waitFor(() => expect(depth2.result.current.isSuccess).toBe(true));
+ const level1_1 = depth2.result.current.data?.items.find(
+ (item) => item.title === 'Level 1',
+ );
+ expect(level1_1.items[0].title).toBe('Level 2');
+ expect(level1_1.items[0].items.length).toBe(0);
+
+ const depth3 = renderHook(
+ () => useQuery(getNavigationQuery({ path: '/', depth: 3 })),
+ {
+ wrapper: createWrapper(),
+ },
+ );
+ await waitFor(() => expect(depth3.result.current.isSuccess).toBe(true));
+ const level1_2 = depth3.result.current.data?.items.find(
+ (item) => item.title === 'Level 1',
+ );
+ expect(level1_2.items[0].items.length).toBe(1);
+ expect(level1_2.items[0].items[0].title).toBe('Level 3');
+ });
});
diff --git a/packages/client/src/restapi/navigation/get.ts b/packages/client/src/restapi/navigation/get.ts
index 8f4c6b6211..d5b6577a17 100644
--- a/packages/client/src/restapi/navigation/get.ts
+++ b/packages/client/src/restapi/navigation/get.ts
@@ -5,6 +5,7 @@ import { z } from 'zod';
const getNavigationSchema = z.object({
path: z.string(),
+ depth: z.number().optional(),
});
export type NavigationArgs = z.infer & {
@@ -13,10 +14,12 @@ export type NavigationArgs = z.infer & {
export const getNavigation = async ({
path,
+ depth,
config,
}: NavigationArgs): Promise => {
const validatedArgs = getNavigationSchema.parse({
path,
+ depth,
});
const options: ApiRequestParams = {
@@ -25,11 +28,18 @@ export const getNavigation = async ({
};
const navigationPath = `${validatedArgs.path}/@navigation`;
+ if (validatedArgs.depth) {
+ options.params['expand.navigation.depth'] = validatedArgs.depth;
+ }
return apiRequest('get', navigationPath, options);
};
-export const getNavigationQuery = ({ path, config }: NavigationArgs) => ({
- queryKey: [path, 'get', 'navigation'],
- queryFn: () => getNavigation({ path, config }),
+export const getNavigationQuery = ({
+ path,
+ depth,
+ config,
+}: NavigationArgs) => ({
+ queryKey: [path, depth, 'get', 'navigation'],
+ queryFn: () => getNavigation({ path, depth, config }),
});
diff --git a/packages/client/src/validation/copymove.ts b/packages/client/src/validation/copymove.ts
index ee27382a81..f712bd708a 100644
--- a/packages/client/src/validation/copymove.ts
+++ b/packages/client/src/validation/copymove.ts
@@ -1,5 +1,8 @@
import { z } from 'zod';
export const copyMoveDataSchema = z.object({
- source: z.union([z.string(), z.array(z.string())]),
+ path: z.string(),
+ data: z.object({
+ source: z.union([z.string(), z.array(z.string())]),
+ }),
});
diff --git a/packages/components/.release-it.json b/packages/components/.release-it.json
index 7959b21d1c..0069a0a569 100644
--- a/packages/components/.release-it.json
+++ b/packages/components/.release-it.json
@@ -5,7 +5,7 @@
"hooks": {
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft && pipx run towncrier build --yes --version ${version}",
- "pnpm build"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 09194352a8..b034a50895 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -8,6 +8,50 @@
+## 2.0.0-alpha.16 (2024-10-18)
+
+### Breaking
+
+- Removed the `FlattenToAppURLProvider` since it's no longer needed. @sneridagh
+ The components in here need it.
+ Refactored the `Link` component to not use it, since `react-aria-components` uses the React Client Routing facilities that can be injected into the React tree.
+
+ Breaking:
+ - Use the new providers in `@plone/providers` instead to make the new `Link` work with them. [#6069](https://github.com/plone/volto/issues/6069)
+
+### Internal
+
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Adjust the path to perform a proper `git diff` between the cached and current commits to determine whether to build the Storybook for the components package on Netlify. @stevepiercy [#6410](https://github.com/plone/volto/issues/6410)
+
+## 2.0.0-alpha.15 (2024-10-14)
+
+### Bugfix
+
+- Fix Select component logic to support `items` use case @sneridagh [#6405](https://github.com/plone/volto/issues/6405)
+
+## 2.0.0-alpha.14 (2024-10-10)
+
+### Bugfix
+
+- Add missing export for new components @sneridagh [#6391](https://github.com/plone/volto/issues/6391)
+
+## 2.0.0-alpha.13 (2024-10-08)
+
+### Bugfix
+
+- Fixed flattenToAppURL types @pnicolli @deodorhunter [#6382](https://github.com/plone/volto/issues/6382)
+
+### Internal
+
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/issues/6373)
+
+## 2.0.0-alpha.12 (2024-10-03)
+
+### Feature
+
+- Update RAC to 1.4.0 - Added new `Disclosure` component and new Color widgets @sneridagh [#6364](https://github.com/plone/volto/issues/6364)
+
## 2.0.0-alpha.11 (2024-06-06)
### Bugfix
diff --git a/packages/components/netlify.toml b/packages/components/netlify.toml
index dd96154375..dbedbc629a 100644
--- a/packages/components/netlify.toml
+++ b/packages/components/netlify.toml
@@ -1,2 +1,2 @@
[build]
- ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ./src/"
+ ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- packages/components/src/"
diff --git a/packages/components/package.json b/packages/components/package.json
index 34e00c6126..8c49018249 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "2.0.0-alpha.11",
+ "version": "2.0.0-alpha.16",
"repository": {
"type": "git",
"url": "http://github.com/plone/components.git"
@@ -110,17 +110,17 @@
"parcel": "^2.12.0",
"release-it": "17.1.1",
"storybook": "^8.0.4",
- "typescript": "^5.4.5",
- "vite": "^5.1.7",
- "vitest": "^1.3.1",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.3",
"vitest-axe": "^0.1.0"
},
"dependencies": {
- "@react-aria/utils": "^3.22.0",
- "@react-spectrum/utils": "^3.11.1",
+ "@react-aria/utils": "^3.25.3",
+ "@react-spectrum/utils": "^3.11.11",
"@storybook/test": "^8.0.4",
- "clsx": "^2.0.0",
- "react-aria-components": "^1.2.0"
+ "clsx": "^2.1.1",
+ "react-aria-components": "^1.4.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
diff --git a/packages/components/src/components/ColorArea/ColorArea.stories.tsx b/packages/components/src/components/ColorArea/ColorArea.stories.tsx
new file mode 100644
index 0000000000..b4ac8b95ea
--- /dev/null
+++ b/packages/components/src/components/ColorArea/ColorArea.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorArea } from './ColorArea';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorArea.css';
+
+const meta = {
+ title: 'Widgets/ColorArea',
+ component: ColorArea,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ defaultValue: 'hsl(30, 100%, 50%)',
+};
diff --git a/packages/components/src/components/ColorArea/ColorArea.tsx b/packages/components/src/components/ColorArea/ColorArea.tsx
new file mode 100644
index 0000000000..8464766f70
--- /dev/null
+++ b/packages/components/src/components/ColorArea/ColorArea.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import {
+ ColorArea as RACColorArea,
+ ColorAreaProps,
+ ColorThumb,
+} from 'react-aria-components';
+
+export function ColorArea(props: ColorAreaProps) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorField/ColorField.stories.tsx b/packages/components/src/components/ColorField/ColorField.stories.tsx
new file mode 100644
index 0000000000..6621ebec66
--- /dev/null
+++ b/packages/components/src/components/ColorField/ColorField.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorField } from './ColorField';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorField.css';
+
+const meta = {
+ title: 'Widgets/ColorField',
+ component: ColorField,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ label: 'Color',
+};
diff --git a/packages/components/src/components/ColorField/ColorField.tsx b/packages/components/src/components/ColorField/ColorField.tsx
new file mode 100644
index 0000000000..fff7c0f912
--- /dev/null
+++ b/packages/components/src/components/ColorField/ColorField.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react';
+import {
+ ColorField as RACColorField,
+ ColorFieldProps as RACColorFieldProps,
+ FieldError,
+ Input,
+ Label,
+ Text,
+ ValidationResult,
+} from 'react-aria-components';
+
+export interface ColorFieldProps extends RACColorFieldProps {
+ label?: string;
+ description?: string;
+ errorMessage?: string | ((validation: ValidationResult) => string);
+}
+
+export function ColorField({
+ label,
+ description,
+ errorMessage,
+ ...props
+}: ColorFieldProps) {
+ return (
+
+ {label && }
+
+ {description && {description}}
+ {errorMessage}
+
+ );
+}
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx b/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx
new file mode 100644
index 0000000000..1f94cc3a6c
--- /dev/null
+++ b/packages/components/src/components/ColorPicker/ColorPicker.stories.tsx
@@ -0,0 +1,27 @@
+import * as React from 'react';
+import { ColorPicker } from './ColorPicker';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorPicker.css';
+
+const meta = {
+ title: 'Widgets/ColorPicker',
+ component: ColorPicker,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ label: 'Fill color',
+ defaultValue: '#f00',
+};
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.tsx b/packages/components/src/components/ColorPicker/ColorPicker.tsx
new file mode 100644
index 0000000000..b5256e24c2
--- /dev/null
+++ b/packages/components/src/components/ColorPicker/ColorPicker.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import {
+ Button,
+ ColorPicker as RACColorPicker,
+ ColorPickerProps as RACColorPickerProps,
+ Dialog,
+ DialogTrigger,
+ Popover,
+} from 'react-aria-components';
+import { ColorSwatch } from '../ColorSwatch/ColorSwatch';
+import { ColorSlider } from '../ColorSlider/ColorSlider';
+import { ColorArea } from '../ColorArea/ColorArea';
+import { ColorField } from '../ColorField/ColorField';
+
+export interface ColorPickerProps extends RACColorPickerProps {
+ label?: string;
+ children?: React.ReactNode;
+}
+
+export function ColorPicker({ label, children, ...props }: ColorPickerProps) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx b/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx
new file mode 100644
index 0000000000..a0a363489e
--- /dev/null
+++ b/packages/components/src/components/ColorSlider/ColorSlider.stories.tsx
@@ -0,0 +1,28 @@
+import * as React from 'react';
+import { ColorSlider } from './ColorSlider';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSlider.css';
+
+const meta = {
+ title: 'Widgets/ColorSlider',
+ component: ColorSlider,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ label: 'Red Opacity',
+ defaultValue: '#f00',
+ channel: 'alpha',
+};
diff --git a/packages/components/src/components/ColorSlider/ColorSlider.tsx b/packages/components/src/components/ColorSlider/ColorSlider.tsx
new file mode 100644
index 0000000000..55b81b9351
--- /dev/null
+++ b/packages/components/src/components/ColorSlider/ColorSlider.tsx
@@ -0,0 +1,30 @@
+import * as React from 'react';
+import {
+ ColorSlider as RACColorSlider,
+ ColorSliderProps as RACColorSliderProps,
+ ColorThumb,
+ Label,
+ SliderOutput,
+ SliderTrack,
+} from 'react-aria-components';
+
+export interface ColorSliderProps extends RACColorSliderProps {
+ label?: string;
+}
+
+export function ColorSlider({ label, ...props }: ColorSliderProps) {
+ return (
+
+
+
+ ({
+ background: `${defaultStyle.background},
+ repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`,
+ })}
+ >
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx b/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx
new file mode 100644
index 0000000000..0168fa94f5
--- /dev/null
+++ b/packages/components/src/components/ColorSwatch/ColorSwatch.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorSwatch } from './ColorSwatch';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSwatch.css';
+
+const meta = {
+ title: 'Widgets/ColorSwatch',
+ component: ColorSwatch,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ color: '#f00a',
+};
diff --git a/packages/components/src/components/ColorSwatch/ColorSwatch.tsx b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
new file mode 100644
index 0000000000..b80a13c31c
--- /dev/null
+++ b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import {
+ ColorSwatch as RACColorSwatch,
+ ColorSwatchProps,
+} from 'react-aria-components';
+
+export function ColorSwatch(props: ColorSwatchProps) {
+ return (
+ ({
+ background: `linear-gradient(${color}, ${color}),
+ repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`,
+ })}
+ />
+ );
+}
diff --git a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx
new file mode 100644
index 0000000000..cba4b2a099
--- /dev/null
+++ b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.stories.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { ColorSwatchPicker, ColorSwatchPickerItem } from './ColorSwatchPicker';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorSwatchPicker.css';
+
+const meta = {
+ title: 'Widgets/ColorSwatchPicker',
+ component: ColorSwatchPicker,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
+
+
+ ),
+};
diff --git a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
new file mode 100644
index 0000000000..9ecdc9dac1
--- /dev/null
+++ b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import {
+ ColorSwatchPicker as AriaColorSwatchPicker,
+ ColorSwatchPickerItem as AriaColorSwatchPickerItem,
+ ColorSwatchPickerItemProps,
+ ColorSwatchPickerProps,
+} from 'react-aria-components';
+
+import { ColorSwatch } from '../ColorSwatch/ColorSwatch';
+
+export function ColorSwatchPicker({
+ children,
+ ...props
+}: ColorSwatchPickerProps) {
+ return {children};
+}
+
+export { ColorSwatchPicker as MyColorSwatchPicker };
+
+export function ColorSwatchPickerItem(props: ColorSwatchPickerItemProps) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx b/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx
new file mode 100644
index 0000000000..5d5d4278c9
--- /dev/null
+++ b/packages/components/src/components/ColorWheel/ColorWheel.stories.tsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { ColorWheel } from './ColorWheel';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/ColorWheel.css';
+
+const meta = {
+ title: 'Widgets/ColorWheel',
+ component: ColorWheel,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => ,
+};
+
+Default.args = {
+ defaultValue: 'hsl(30, 100%, 50%)',
+};
diff --git a/packages/components/src/components/ColorWheel/ColorWheel.tsx b/packages/components/src/components/ColorWheel/ColorWheel.tsx
new file mode 100644
index 0000000000..a9dff1f543
--- /dev/null
+++ b/packages/components/src/components/ColorWheel/ColorWheel.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import {
+ ColorThumb,
+ ColorWheel as AriaColorWheel,
+ ColorWheelProps as AriaColorWheelProps,
+ ColorWheelTrack,
+} from 'react-aria-components';
+
+export interface ColorWheelProps
+ extends Omit {}
+
+export function ColorWheel(props: ColorWheelProps) {
+ return (
+
+
+
+
+ );
+}
+
+export { ColorWheel as MyColorWheel };
diff --git a/packages/components/src/components/Disclosure/Disclosure.stories.tsx b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
new file mode 100644
index 0000000000..6f41082267
--- /dev/null
+++ b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import { Disclosure } from './Disclosure';
+import {
+ Button,
+ UNSTABLE_DisclosurePanel as DisclosurePanel,
+} from 'react-aria-components';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import '../../styles/basic/Disclosure.css';
+
+const meta = {
+ component: Disclosure,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
Details about system requirements here.
+
+
+ ),
+};
diff --git a/packages/components/src/components/Disclosure/Disclosure.tsx b/packages/components/src/components/Disclosure/Disclosure.tsx
new file mode 100644
index 0000000000..8e062a8d61
--- /dev/null
+++ b/packages/components/src/components/Disclosure/Disclosure.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import {
+ UNSTABLE_Disclosure as RACDisclosure,
+ DisclosureProps,
+} from 'react-aria-components';
+
+/**
+ * A Disclosure is used to show or hide content that is not visible by default.
+ *
+ * NOTE: This component is in alpha in RAC thus it's unstable and is subjects of change
+ * in the API, behavior, and appearance.
+ */
+export function Disclosure(props: DisclosureProps) {
+ return ;
+}
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
new file mode 100644
index 0000000000..1871ade147
--- /dev/null
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { DisclosureGroup } from './DisclosureGroup';
+import {
+ Button,
+ UNSTABLE_Disclosure as Disclosure,
+ UNSTABLE_DisclosurePanel as DisclosurePanel,
+} from 'react-aria-components';
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+const meta = {
+ component: DisclosureGroup,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args: any) => (
+
+
+
+
+
+
+
Personal information form here.
+
+
+
+
+
+
+
+
Billing address form here.
+
+
+
+ ),
+};
+
+Default.args = {
+ defaultExpandedKeys: ['personal'],
+};
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
new file mode 100644
index 0000000000..4196e558c6
--- /dev/null
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import {
+ UNSTABLE_DisclosureGroup as RACDisclosureGroup,
+ DisclosureGroupProps,
+} from 'react-aria-components';
+
+/**
+ * A DisclosureGroup is used to group Disclosures together to create an accordion.
+ *
+ * NOTE: This component is in alpha in RAC thus it's unstable and is subjects of change
+ * in the API, behavior, and appearance.
+ */
+export function DisclosureGroup(props: DisclosureGroupProps) {
+ return ;
+}
diff --git a/packages/components/src/components/Link/Link.tsx b/packages/components/src/components/Link/Link.tsx
index 307c6528d3..4923f3218c 100644
--- a/packages/components/src/components/Link/Link.tsx
+++ b/packages/components/src/components/Link/Link.tsx
@@ -1,20 +1 @@
-import React from 'react';
-import { forwardRef, ForwardedRef } from 'react';
-import { Link as RACLink, LinkProps } from 'react-aria-components';
-import { useFlattenToAppURL } from '../../providers/flattenToAppURL';
-
-type forwardRefType = typeof forwardRef;
-
-const Link = (props: LinkProps, ref: ForwardedRef) => {
- const { flattenToAppURL } = useFlattenToAppURL();
- const flattenedURL = flattenToAppURL(props.href);
-
- return (
-
- {props.children}
-
- );
-};
-
-const _Link = /*#__PURE__*/ (forwardRef as forwardRefType)(Link);
-export { _Link as Link };
+export { Link } from 'react-aria-components';
diff --git a/packages/components/src/components/Select/Select.stories.tsx b/packages/components/src/components/Select/Select.stories.tsx
index bec5ec99be..97842eda90 100644
--- a/packages/components/src/components/Select/Select.stories.tsx
+++ b/packages/components/src/components/Select/Select.stories.tsx
@@ -51,14 +51,6 @@ export const Default: Story = {
* Select renders options via render props `(item)=> React.ReactNode`
*/
export const Items: Story = {
- render: (args) => (
- // @ts-ignore I assume this is a storybook bug when passing args
-
- ),
args: {
name: 'field-empty',
label: 'field 1 title',
@@ -81,14 +73,6 @@ export const Items: Story = {
};
export const LotsOfItems: Story = {
- render: (args) => (
- // @ts-ignore I assume this is a storybook bug when passing args
-
- ),
args: {
name: 'field-empty',
label: 'field 1 title',
diff --git a/packages/components/src/components/Select/Select.tsx b/packages/components/src/components/Select/Select.tsx
index fdbe84c784..d57402e238 100644
--- a/packages/components/src/components/Select/Select.tsx
+++ b/packages/components/src/components/Select/Select.tsx
@@ -69,7 +69,15 @@ export function Select({
{description && {description}}
{errorMessage}
- {children}
+ {children ? (
+ {children}
+ ) : (
+
+ {(item: SelectItemObject) => (
+ {item.value}
+ )}
+
+ )}
>
)}
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index fd11f012a8..a7e2df464f 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -2,6 +2,13 @@ export { BlockToolbar } from './components/BlockToolbar/BlockToolbar';
export { Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
export { Button } from './components/Button/Button';
export { Calendar } from './components/Calendar/Calendar';
+export { ColorArea } from './components/ColorArea/ColorArea';
+export { ColorField } from './components/ColorField/ColorField';
+export { ColorPicker } from './components/ColorPicker/ColorPicker';
+export { ColorSlider } from './components/ColorSlider/ColorSlider';
+export { ColorSwatch } from './components/ColorSwatch/ColorSwatch';
+export { ColorSwatchPicker } from './components/ColorSwatchPicker/ColorSwatchPicker';
+export { ColorWheel } from './components/ColorWheel/ColorWheel';
export { Checkbox } from './components/Checkbox/Checkbox';
export { CheckboxField } from './components/CheckboxField/CheckboxField';
export { CheckboxGroup } from './components/CheckboxGroup/CheckboxGroup';
@@ -11,6 +18,8 @@ export { DateField } from './components/DateField/DateField';
export { DatePicker } from './components/DatePicker/DatePicker';
export { DateRangePicker } from './components/DateRangePicker/DateRangePicker';
export { Dialog } from './components/Dialog/Dialog';
+export { Disclosure } from './components/Disclosure/Disclosure';
+export { DisclosureGroup } from './components/DisclosureGroup/DisclosureGroup';
export { Form } from './components/Form/Form';
export { GridList, GridListItem } from './components/GridList/GridList';
export { Icon } from './components/Icon/Icon';
@@ -46,9 +55,6 @@ export { Tooltip } from './components/Tooltip/Tooltip';
export { RenderBlocks } from './views/RenderBlocks/RenderBlocks';
export { DefaultBlockView } from './views/RenderBlocks/DefaultBlockView';
-export { FlattenToAppURLProvider } from './providers/flattenToAppURL';
-export { useFlattenToAppURL } from './providers/flattenToAppURL';
-
// Quanta components
export { QuantaTextField } from './components/quanta/TextField/TextField';
export { QuantaTextAreaField } from './components/quanta/TextAreaField/TextAreaField';
diff --git a/packages/components/src/providers/flattenToAppURL.tsx b/packages/components/src/providers/flattenToAppURL.tsx
deleted file mode 100644
index b1ce32a98d..0000000000
--- a/packages/components/src/providers/flattenToAppURL.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import { createContext, ReactNode, useContext, useMemo } from 'react';
-
-interface FlattenToAppURL {
- flattenToAppURL: (path: string | undefined) => string | undefined;
-}
-
-const FlattenToAppURLContext = createContext({
- flattenToAppURL: (path) => path,
-});
-
-interface FlattenToAppURLProps {
- flattenToAppURL: (path: string | undefined) => string | undefined;
- children: ReactNode;
-}
-
-export function FlattenToAppURLProvider(props: FlattenToAppURLProps) {
- let { children, flattenToAppURL } = props;
-
- let ctx = useMemo(
- () => ({
- flattenToAppURL,
- }),
- [flattenToAppURL],
- );
-
- return (
-
- {children}
-
- );
-}
-
-export function useFlattenToAppURL(): FlattenToAppURL {
- return useContext(FlattenToAppURLContext);
-}
diff --git a/packages/components/src/styles/basic/ColorArea.css b/packages/components/src/styles/basic/ColorArea.css
new file mode 100644
index 0000000000..a241aee10f
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorArea.css
@@ -0,0 +1,34 @@
+@import './ColorSlider.css';
+
+.react-aria-ColorArea {
+ width: 192px;
+ height: 192px;
+ flex-shrink: 0;
+ border-radius: 4px;
+}
+
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorArea {
+ &[data-disabled] {
+ background: gray !important;
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorField.css b/packages/components/src/styles/basic/ColorField.css
new file mode 100644
index 0000000000..bcb29a5273
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorField.css
@@ -0,0 +1,49 @@
+@import './Button.css';
+@import './Form.css';
+@import './theme.css';
+
+.react-aria-ColorField {
+ display: flex;
+ flex-direction: column;
+ color: var(--text-color);
+
+ .react-aria-Input {
+ width: 100%;
+ max-width: 12ch;
+ box-sizing: border-box;
+ padding: 0.286rem;
+ border: 1px solid var(--border-color);
+ border-radius: 6px;
+ margin: 0;
+ background: var(--field-background);
+ color: var(--field-text-color);
+ font-size: 1.143rem;
+
+ &[data-focused] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: -1px;
+ }
+ }
+
+ &[data-invalid] {
+ .react-aria-Input {
+ border-color: var(--color-invalid);
+ }
+ }
+
+ .react-aria-FieldError {
+ color: var(--color-invalid);
+ font-size: 12px;
+ }
+
+ [slot='description'] {
+ font-size: 12px;
+ }
+
+ .react-aria-Input {
+ &[data-disabled] {
+ border-color: var(--border-color-disabled);
+ color: var(--text-color-disabled);
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorPicker.css b/packages/components/src/styles/basic/ColorPicker.css
new file mode 100644
index 0000000000..e8ccca99b8
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorPicker.css
@@ -0,0 +1,43 @@
+@import './Button.css';
+@import './ColorArea.css';
+@import './ColorSlider.css';
+@import './ColorSwatch.css';
+@import './ColorSwatchPicker.css';
+@import './ColorField.css';
+@import './Popover.css';
+@import './Dialog.css';
+@import './ListBox.css';
+@import './Select.css';
+@import './theme.css';
+
+.color-picker {
+ display: flex;
+ align-items: center;
+ padding: 0;
+ border: none;
+ border-radius: 4px;
+ appearance: none;
+ background: none;
+ color: var(--text-color);
+ font-size: 1rem;
+ gap: 8px;
+ outline: none;
+ vertical-align: middle;
+
+ &[data-focus-visible] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: 2px;
+ }
+}
+
+.color-picker-dialog {
+ display: flex;
+ overflow: auto;
+ min-width: 192px;
+ max-height: inherit;
+ box-sizing: border-box;
+ flex-direction: column;
+ padding: 15px;
+ gap: 8px;
+ outline: none;
+}
diff --git a/packages/components/src/styles/basic/ColorSlider.css b/packages/components/src/styles/basic/ColorSlider.css
new file mode 100644
index 0000000000..63f3d432a1
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSlider.css
@@ -0,0 +1,80 @@
+.react-aria-ColorSlider {
+ display: grid;
+ max-width: 300px;
+ gap: 4px;
+ grid-template-areas:
+ 'label output'
+ 'track track';
+ grid-template-columns: 1fr auto;
+
+ .react-aria-Label {
+ grid-area: label;
+ }
+
+ .react-aria-SliderOutput {
+ grid-area: output;
+ }
+
+ .react-aria-SliderTrack {
+ border-radius: 4px;
+ grid-area: track;
+ }
+
+ &[data-orientation='horizontal'] {
+ .react-aria-SliderTrack {
+ height: 28px;
+ }
+
+ .react-aria-ColorThumb {
+ top: 50%;
+ }
+ }
+}
+
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorSlider {
+ &[data-orientation='vertical'] {
+ display: block;
+ height: 150px;
+
+ .react-aria-Label,
+ .react-aria-SliderOutput {
+ display: none;
+ }
+
+ .react-aria-SliderTrack {
+ width: 28px;
+ height: 100%;
+ }
+
+ .react-aria-ColorThumb {
+ left: 50%;
+ }
+ }
+
+ &[data-disabled] {
+ .react-aria-SliderTrack {
+ background: gray !important;
+ }
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ opacity: 0.5;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorSwatch.css b/packages/components/src/styles/basic/ColorSwatch.css
new file mode 100644
index 0000000000..3713b3807b
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSwatch.css
@@ -0,0 +1,8 @@
+@import './ColorSlider.css';
+
+.react-aria-ColorSwatch {
+ width: 32px;
+ height: 32px;
+ border-radius: 4px;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
diff --git a/packages/components/src/styles/basic/ColorSwatchPicker.css b/packages/components/src/styles/basic/ColorSwatchPicker.css
new file mode 100644
index 0000000000..657d836443
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorSwatchPicker.css
@@ -0,0 +1,42 @@
+@import './ColorSwatch.css';
+@import './ColorField.css';
+@import './theme.css';
+
+.react-aria-ColorSwatchPicker {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.react-aria-ColorSwatchPickerItem {
+ position: relative;
+ width: fit-content;
+ border-radius: 4px;
+ forced-color-adjust: none;
+ outline: none;
+
+ &[data-focus-visible] {
+ outline: 2px solid var(--focus-ring-color);
+ outline-offset: 2px;
+ }
+
+ &[data-selected]::after {
+ position: absolute;
+ border: 2px solid black;
+ border-radius: inherit;
+ content: '';
+ inset: 0;
+ outline: 2px solid white;
+ outline-offset: -4px;
+ }
+
+ &[data-disabled] {
+ opacity: 0.2;
+ }
+}
+
+.react-aria-ColorSwatchPicker {
+ &[data-layout='stack'] {
+ flex-direction: column;
+ }
+}
diff --git a/packages/components/src/styles/basic/ColorWheel.css b/packages/components/src/styles/basic/ColorWheel.css
new file mode 100644
index 0000000000..429e59dc9c
--- /dev/null
+++ b/packages/components/src/styles/basic/ColorWheel.css
@@ -0,0 +1,28 @@
+.react-aria-ColorThumb {
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border: 2px solid white;
+ border-radius: 50%;
+ box-shadow:
+ 0 0 0 1px black,
+ inset 0 0 0 1px black;
+
+ &[data-focus-visible] {
+ width: 24px;
+ height: 24px;
+ }
+}
+
+.react-aria-ColorWheel {
+ &[data-disabled] {
+ .react-aria-ColorWheelTrack {
+ background: gray !important;
+ }
+
+ .react-aria-ColorThumb {
+ background: gray !important;
+ opacity: 0.5;
+ }
+ }
+}
diff --git a/packages/components/src/styles/basic/Disclosure.css b/packages/components/src/styles/basic/Disclosure.css
new file mode 100644
index 0000000000..809a49816e
--- /dev/null
+++ b/packages/components/src/styles/basic/Disclosure.css
@@ -0,0 +1,33 @@
+@import './theme.css';
+@import './Button.css';
+
+.react-aria-Disclosure {
+ .react-aria-Button[slot='trigger'] {
+ display: flex;
+ align-items: center;
+ border: none;
+ background: none;
+ box-shadow: none;
+ font-size: 16px;
+ font-weight: bold;
+ gap: 8px;
+
+ svg {
+ width: 12px;
+ height: 12px;
+ fill: none;
+ rotate: 0deg;
+ stroke: currentColor;
+ stroke-width: 3px;
+ transition: rotate 200ms;
+ }
+ }
+
+ &[data-expanded] .react-aria-Button[slot='trigger'] svg {
+ rotate: 90deg;
+ }
+}
+
+.react-aria-DisclosurePanel {
+ margin-left: 32px;
+}
diff --git a/packages/components/src/styles/basic/DisclosureGroup.css b/packages/components/src/styles/basic/DisclosureGroup.css
new file mode 100644
index 0000000000..2c60061a9e
--- /dev/null
+++ b/packages/components/src/styles/basic/DisclosureGroup.css
@@ -0,0 +1,3 @@
+@import './theme.css';
+@import './Button.css';
+@import './Disclosure.css';
diff --git a/packages/components/src/styles/basic/main.css b/packages/components/src/styles/basic/main.css
index 60173bd34b..066feb263c 100644
--- a/packages/components/src/styles/basic/main.css
+++ b/packages/components/src/styles/basic/main.css
@@ -8,7 +8,15 @@
@import './Label.css';
@import './Checkbox.css';
@import './CheckboxGroup.css';
+@import './ColorArea.css';
+@import './ColorField.css';
+@import './ColorPicker.css';
+@import './ColorSlider.css';
+@import './ColorSwatch.css';
+@import './ColorSwatchPicker.css';
@import './ComboBox.css';
+@import './Disclosure.css';
+@import './DisclosureGroup.css';
@import './NumberField.css';
@import './RadioGroup.css';
@import './Switch.css';
diff --git a/packages/components/src/views/RenderBlocks/RenderBlocks.tsx b/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
index e0489596b8..91d6ba4342 100644
--- a/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
+++ b/packages/components/src/views/RenderBlocks/RenderBlocks.tsx
@@ -44,6 +44,7 @@ export const RenderBlocks = (props: RenderBlocksProps) => {
const Block = blocksConfig[blockType]?.view || DefaultBlockView;
return Block ? (
+ // @ts-ignore It's ok to pass the blockData as is
{
exact: false,
},
];
+
+ config.addonRoutes.push({
+ path: '/fallback_login',
+ component: Login,
+ exact: false,
+ });
config.blocks.blocksConfig.testBlock = testBlock;
config.blocks.blocksConfig.inputBlock = inputBlock;
config.blocks.blocksConfig.testBlockConditional = testBlockConditional;
diff --git a/packages/generator-volto/CHANGELOG.md b/packages/generator-volto/CHANGELOG.md
index fb6cf2607a..7107897b68 100644
--- a/packages/generator-volto/CHANGELOG.md
+++ b/packages/generator-volto/CHANGELOG.md
@@ -8,6 +8,27 @@
+## 9.0.0-alpha.20 (2024-10-27)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+- Breaking changes in `.eslintrc.js` and `.storybook/main.js` because of #6399. @sneridagh
+ Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399)
+
+## 9.0.0-alpha.19 (2024-10-18)
+
+### Internal
+
+- Update to use jest 26 @sneridagh [#6406](https://github.com/plone/volto/issues/6406)
+
+## 9.0.0-alpha.18 (2024-10-04)
+
+### Breaking
+
+- Updated `razzle.config.js` to accommodate new argument `paths` passed down to `customModifyWebpackConfig`. @sneridagh [#6368](https://github.com/plone/volto/issues/6368)
+
## 9.0.0-alpha.17 (2024-06-28)
### Bugfix
diff --git a/packages/generator-volto/generators/app/templates/.eslintrc.js b/packages/generator-volto/generators/app/templates/.eslintrc.js
index 6148e2e62f..a5aaf045b1 100644
--- a/packages/generator-volto/generators/app/templates/.eslintrc.js
+++ b/packages/generator-volto/generators/app/templates/.eslintrc.js
@@ -1,6 +1,6 @@
const fs = require('fs');
const projectRootPath = __dirname;
-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
+const { AddonRegistry } = require('@plone/registry/addon-registry');
let voltoPath = './node_modules/@plone/volto';
@@ -17,15 +17,15 @@ if (configFile) {
voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
}
-const reg = new AddonConfigurationRegistry(__dirname);
+const { registry } = AddonRegistry.init(__dirname);
// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
-const addonAliases = Object.keys(reg.packages).map((o) => [
+const addonAliases = Object.keys(registry.packages).map((o) => [
o,
- reg.packages[o].modulePath,
+ registry.packages[o].modulePath,
]);
-const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
+const addonExtenders = registry.getEslintExtenders().map((m) => require(m));
const defaultConfig = {
extends: `${voltoPath}/.eslintrc`,
diff --git a/packages/generator-volto/generators/app/templates/.storybook/main.js b/packages/generator-volto/generators/app/templates/.storybook/main.js
index aa094cb6d7..da6c799e56 100644
--- a/packages/generator-volto/generators/app/templates/.storybook/main.js
+++ b/packages/generator-volto/generators/app/templates/.storybook/main.js
@@ -104,9 +104,9 @@ module.exports = {
[],
defaultRazzleOptions,
);
- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
+ const { AddonRegistry } = require('@plone/registry/addon-registry');
- const registry = new AddonConfigurationRegistry(projectRootPath);
+ const { registry } = AddonRegistry.init(projectRootPath);
config = lessPlugin({ registry }).modifyWebpackConfig({
env: { target: 'web', dev: 'dev' },
diff --git a/packages/generator-volto/generators/app/templates/razzle.config.js b/packages/generator-volto/generators/app/templates/razzle.config.js
index 6bab4ae583..2b4a6a8f57 100644
--- a/packages/generator-volto/generators/app/templates/razzle.config.js
+++ b/packages/generator-volto/generators/app/templates/razzle.config.js
@@ -27,12 +27,14 @@ const customModifyWebpackConfig = ({
webpackConfig,
webpackObject,
options,
+ paths,
}) => {
const config = modifyWebpackConfig({
env: { target, dev },
webpackConfig,
webpackObject,
options,
+ paths,
});
// add custom code here..
return config;
diff --git a/packages/generator-volto/package.json b/packages/generator-volto/package.json
index f61a14b344..cf1b46bc7c 100644
--- a/packages/generator-volto/package.json
+++ b/packages/generator-volto/package.json
@@ -10,7 +10,7 @@
}
],
"license": "MIT",
- "version": "9.0.0-alpha.17",
+ "version": "9.0.0-alpha.20",
"repository": {
"type": "git",
"url": "git+https://github.com/plone/generator-volto.git"
@@ -62,7 +62,7 @@
"testEnvironment": "node"
},
"engines": {
- "node": "^16 || ^18 || ^20"
+ "node": "^16 || ^18 || ^20 || ^22"
},
"dependencies": {
"ansi-escapes": "2.0.0",
@@ -83,7 +83,7 @@
},
"devDependencies": {
"coveralls": "^3.0.7",
- "jest": "^24.8.0",
+ "jest": "^26.6.3",
"release-it": "^16.1.3",
"yeoman-assert": "^3.1.0",
"yeoman-test": "^6.1.0"
diff --git a/packages/helpers/README.md b/packages/helpers/README.md
index 6226029a0f..f9c90bc51b 100644
--- a/packages/helpers/README.md
+++ b/packages/helpers/README.md
@@ -1 +1,6 @@
# @plone/helpers
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/packages/helpers/news/6069.internal b/packages/helpers/news/6069.internal
new file mode 100644
index 0000000000..003eb2470b
--- /dev/null
+++ b/packages/helpers/news/6069.internal
@@ -0,0 +1 @@
+Added experimental notice @sneridagh
diff --git a/packages/helpers/news/6407.internal b/packages/helpers/news/6407.internal
new file mode 100644
index 0000000000..d6fed2b831
--- /dev/null
+++ b/packages/helpers/news/6407.internal
@@ -0,0 +1,2 @@
+Unpin parcel version @sneridagh
+Update `typescript` and `vitest` everywhere @sneridagh
diff --git a/packages/helpers/package.json b/packages/helpers/package.json
index 55e5cd3ff6..db697e84dc 100644
--- a/packages/helpers/package.json
+++ b/packages/helpers/package.json
@@ -54,15 +54,15 @@
},
"dependencies": {},
"devDependencies": {
- "@parcel/packager-ts": "2.12.0",
- "@parcel/transformer-typescript-types": "2.12.0",
+ "@parcel/packager-ts": "^2.12.0",
+ "@parcel/transformer-typescript-types": "^2.12.0",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "2.12.0",
+ "parcel": "^2.12.0",
"release-it": "^17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2",
- "vitest": "^1.3.1"
+ "typescript": "^5.4.5",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/providers/.release-it.json b/packages/providers/.release-it.json
index 2087371cdb..78a8734399 100644
--- a/packages/providers/.release-it.json
+++ b/packages/providers/.release-it.json
@@ -5,7 +5,7 @@
"hooks": {
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft && pipx run towncrier build --yes --version ${version}",
- "pnpm build"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/providers/CHANGELOG.md b/packages/providers/CHANGELOG.md
index 9aad3658e3..c8326e298c 100644
--- a/packages/providers/CHANGELOG.md
+++ b/packages/providers/CHANGELOG.md
@@ -8,6 +8,20 @@
+## 1.0.0-alpha.3 (2024-10-18)
+
+## 1.0.0-alpha.2 (2024-10-18)
+
+### Breaking
+
+- Improve and group providers. @sneridagh
+ Breaking:
+ - The interface of the providers has changed. Please check the new one, and adapt your apps accordingly. [#6069](https://github.com/plone/volto/issues/6069)
+
+### Internal
+
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+
## 1.0.0-alpha.1 (2024-05-23)
### Internal
diff --git a/packages/providers/README.md b/packages/providers/README.md
index 05502673e0..75ba6475a9 100644
--- a/packages/providers/README.md
+++ b/packages/providers/README.md
@@ -1,3 +1,188 @@
# `@plone/providers`
This package contains utility providers for Plone React components.
+The main purpose is to provide dependency injection of common required artifacts needed by any app.
+These artifacts include:
+- Router related
+- Plone Client
+- URL handling methods
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
+
+## `PloneProvider`
+
+It provides all the necessary artifacts that an app can need grouped in a single provider.
+
+```ts
+interface PloneProvider {
+ ploneClient: InstanceType;
+ queryClient: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+```
+
+It should be instantiated at the top of your app.
+You have to provide the required props depending on the framework and the router used.
+This is the example for a Next.js app.
+Please refer to the {file}`apps` folder of the Volto repository for more examples of the usage of `PloneProvider` in different React frameworks.
+
+```tsx
+'use client';
+import React from 'react';
+import {
+ useRouter,
+ usePathname,
+ useSearchParams,
+ useParams,
+} from 'next/navigation';
+import { QueryClient } from '@tanstack/react-query';
+import { PloneProvider } from '@plone/providers';
+import PloneClient from '@plone/client';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { flattenToAppURL } from './utils';
+import config from './config';
+
+// Custom hook to unify the location object between NextJS and Plone
+function useLocation() {
+ let pathname = usePathname();
+ let search = useSearchParams();
+
+ return {
+ pathname,
+ search,
+ searchStr: '',
+ hash: (typeof window !== 'undefined' && window.location.hash) || '',
+ href: (typeof window !== 'undefined' && window.location.href) || '',
+ };
+}
+
+const Providers: React.FC<{
+ children?: React.ReactNode;
+}> = ({ children }) => {
+ // Creating the clients at the file root level makes the cache shared
+ // between all requests and means _all_ data gets passed to _all_ users.
+ // Besides being bad for performance, this also leaks any sensitive data.
+ // We use this pattern to ensure that every client gets its own clients
+ const [queryClient] = React.useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ }),
+ );
+
+ const [ploneClient] = React.useState(() =>
+ PloneClient.initialize({
+ apiPath: config.settings.apiPath,
+ }),
+ );
+
+ const router = useRouter();
+
+ return (
+ {
+ router.push(to);
+ }}
+ useParams={useParams}
+ useHref={(to) => flattenToAppURL(to)}
+ flattenToAppURL={flattenToAppURL}
+ >
+ {children}
+
+
+ );
+};
+
+export default Providers;
+
+```
+
+You can use it anywhere in your app by using the hook `usePloneProvider`.
+
+```tsx
+import { usePloneProvider } from '@plone/providers';
+
+const { ploneClient } = usePloneProvider()
+```
+
+Alternatively, you can use it in any other context property.
+
+```tsx
+const { navigate } = usePloneProvider()
+```
+
+## `PloneClientProvider`
+
+`PloneProvider` in a group of other smaller providers.
+You can also instantiate and use them as standalone providers.
+However, you should do this only if the framework has some limitation on using the bulk `PloneClientProvider`.
+
+The following snippets show its usage.
+First, instantiate the provider.
+
+```ts
+export type PloneClientProviderProps = {
+ client: InstanceType;
+ queryClient: QueryClient;
+ children?: React.ReactNode;
+};
+```
+
+Second, use its related hook through either of the following examples.
+
+```tsx
+import { usePloneClient } from '@plone/providers';
+
+const client = usePloneClient()
+```
+
+or
+
+```tsx
+const { getContentQuery } = usePloneClient()
+```
+
+## `AppRouterProvider`
+
+This provider is included also in `PloneProvider`.
+You can also instantiate and use it as a standalone provider.
+However, you should do this only if the framework has some limitation on using the bulk `PloneClientProvider`.
+
+The following code example shows its usage.
+
+```ts
+interface AppRouterProps {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+```
+
+The following code sample shows its related hook.
+
+```tsx
+import { useAppRouter } from '@plone/providers';
+
+const { useLocation } = useAppRouter()
+```
diff --git a/packages/providers/package.json b/packages/providers/package.json
index d2a84fc790..100c519771 100644
--- a/packages/providers/package.json
+++ b/packages/providers/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "1.0.0-alpha.1",
+ "version": "1.0.0-alpha.3",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -43,7 +43,7 @@
"scripts": {
"watch": "parcel watch",
"build": "parcel build",
- "build:force": "parcel build --no-cache",
+ "build:force": "rm -rf dist && parcel build --no-cache",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -62,7 +62,9 @@
"dependencies": {
"@plone/client": "workspace:*",
"@plone/components": "workspace:*",
- "@plone/registry": "workspace:*"
+ "@plone/registry": "workspace:*",
+ "@tanstack/react-query": "^5.59.0",
+ "react-aria-components": "^1.4.0"
},
"devDependencies": {
"@parcel/config-default": "^2.12.0",
@@ -77,7 +79,7 @@
"parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.2.2",
- "vitest": "^1.3.1"
+ "typescript": "^5.4.5",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/providers/src/AppRouter.tsx b/packages/providers/src/AppRouter.tsx
new file mode 100644
index 0000000000..86b2547114
--- /dev/null
+++ b/packages/providers/src/AppRouter.tsx
@@ -0,0 +1,77 @@
+import React, { createContext, ReactNode, useContext, useMemo } from 'react';
+import { RouterProvider } from 'react-aria-components';
+import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
+
+export type AnySearchSchema = {};
+export interface Location {
+ href?: string; // TSR
+ searchStr?: string; // TSR
+ pathname: string; // TSR, RR and Remix
+ search: TSearchObj; // TSR, RR and Remix
+ hash: string; // TSR, RR and Remix
+ state?: any; // TSR, RR and Remix
+ key?: string; // RR and Remix
+}
+
+interface AppRouter {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+
+const AppRouterContext = createContext({
+ useLocation: () => ({
+ href: '',
+ pathname: '',
+ search: {},
+ searchStr: '',
+ hash: '',
+ }),
+ useParams: () => ({}),
+ navigate: () => {},
+ useHref: () => '',
+ flattenToAppURL: defaultFlattenToAppURL,
+});
+
+interface AppRouterProps {
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+
+export function AppRouterProvider(props: AppRouterProps) {
+ let { children, navigate, useLocation, useParams, useHref, flattenToAppURL } =
+ props;
+
+ if (!flattenToAppURL) {
+ flattenToAppURL = defaultFlattenToAppURL;
+ }
+
+ let ctx = useMemo(
+ () => ({
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ }),
+ [useLocation, useParams, navigate, useHref, flattenToAppURL],
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+export function useAppRouter() {
+ return useContext(AppRouterContext);
+}
diff --git a/packages/providers/src/PloneClient.tsx b/packages/providers/src/PloneClient.tsx
index 621f797746..50a319a6f4 100644
--- a/packages/providers/src/PloneClient.tsx
+++ b/packages/providers/src/PloneClient.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import PloneClient from '@plone/client';
@@ -18,16 +19,18 @@ export const usePloneClient = () => {
export type PloneClientProviderProps = {
client: InstanceType;
+ queryClient: QueryClient;
children?: React.ReactNode;
};
export const PloneClientProvider = ({
client,
+ queryClient,
children,
}: PloneClientProviderProps): JSX.Element => {
return (
- {children}
+ {children}
);
};
diff --git a/packages/providers/src/PloneProvider.tsx b/packages/providers/src/PloneProvider.tsx
new file mode 100644
index 0000000000..31bae3d27f
--- /dev/null
+++ b/packages/providers/src/PloneProvider.tsx
@@ -0,0 +1,101 @@
+import React, { createContext, ReactNode, useContext, useMemo } from 'react';
+import { QueryClient } from '@tanstack/react-query';
+import { AppRouterProvider, Location } from './AppRouter';
+import { PloneClientProvider } from './PloneClient';
+import PloneClient from '@plone/client';
+import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
+
+interface PloneProvider {
+ ploneClient: InstanceType;
+ queryClient: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref: (to: string, options?: any) => string;
+ flattenToAppURL: (path: string | undefined) => string | undefined;
+}
+
+const PloneProviderContext = createContext({
+ ploneClient: null,
+ queryClient: null,
+ useLocation: () => ({
+ href: '',
+ pathname: '',
+ search: {},
+ searchStr: '',
+ hash: '',
+ }),
+ useParams: () => ({}),
+ navigate: () => {},
+ useHref: () => '',
+ flattenToAppURL: defaultFlattenToAppURL,
+});
+
+interface PloneProviderProps {
+ ploneClient: InstanceType;
+ queryClient: QueryClient;
+ useLocation: () => Location | undefined;
+ useParams: (opts?: any) => Record;
+ navigate: (path: string) => void;
+ useHref?: (to: string, options?: any) => string;
+ flattenToAppURL?: (path: string | undefined) => string | undefined;
+ children: ReactNode;
+}
+
+export function PloneProvider(props: PloneProviderProps) {
+ let {
+ children,
+ navigate,
+ useLocation,
+ useParams,
+ useHref,
+ ploneClient,
+ queryClient,
+ flattenToAppURL,
+ } = props;
+
+ if (!flattenToAppURL) {
+ flattenToAppURL = defaultFlattenToAppURL;
+ }
+
+ let ctx = useMemo(
+ () => ({
+ ploneClient,
+ queryClient,
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ }),
+ [
+ ploneClient,
+ queryClient,
+ useLocation,
+ useParams,
+ navigate,
+ useHref,
+ flattenToAppURL,
+ ],
+ );
+
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
+
+export function usePloneProvider() {
+ return useContext(PloneProviderContext);
+}
diff --git a/packages/providers/src/RouterLocation.tsx b/packages/providers/src/RouterLocation.tsx
deleted file mode 100644
index f2881957d4..0000000000
--- a/packages/providers/src/RouterLocation.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import React, { createContext, ReactNode, useContext, useMemo } from 'react';
-
-export type AnySearchSchema = {};
-export interface Location {
- href: string;
- pathname: string;
- search: TSearchObj;
- searchStr: string;
- hash: string;
-}
-
-interface RouterLocation {
- useLocation: () => Location | undefined;
-}
-
-const RouterLocationContext = createContext({
- useLocation: () => ({
- href: '',
- pathname: '',
- search: {},
- searchStr: '',
- hash: '',
- }),
-});
-
-interface RouterLocationProps {
- useLocation: () => Location | undefined;
- children: ReactNode;
-}
-
-export function RouterLocationProvider(props: RouterLocationProps) {
- let { children, useLocation } = props;
-
- let ctx = useMemo(
- () => ({
- useLocation,
- }),
- [useLocation],
- );
-
- return (
-
- {children}
-
- );
-}
-
-export function useRouterLocation() {
- const { useLocation } = useContext(RouterLocationContext);
- return useLocation();
-}
diff --git a/packages/providers/src/index.ts b/packages/providers/src/index.ts
index 37d404ef95..5978c8c821 100644
--- a/packages/providers/src/index.ts
+++ b/packages/providers/src/index.ts
@@ -1,6 +1,6 @@
-export * from './RouterLocation';
+export * from './AppRouter';
export * from './PloneClient';
-// FlattenToAppURL provider should live on the components
-// but @plone/components can't depend on other packages
-// we are proxying it here for convenience and centralization
-export { FlattenToAppURLProvider, useFlattenToAppURL } from '@plone/components';
+export * from './PloneProvider';
+
+// Proxying RouterProvider from react-aria-components
+export { RouterProvider } from 'react-aria-components';
diff --git a/packages/registry/.eslintrc.js b/packages/registry/.eslintrc.cjs
similarity index 88%
rename from packages/registry/.eslintrc.js
rename to packages/registry/.eslintrc.cjs
index b60637a973..8614b4cd25 100644
--- a/packages/registry/.eslintrc.js
+++ b/packages/registry/.eslintrc.cjs
@@ -16,6 +16,8 @@ module.exports = {
// Base config
extends: ['eslint:recommended'],
+ ignorePatterns: ['docs/_static/searchtools.js'],
+
overrides: [
// React
{
@@ -61,7 +63,11 @@ module.exports = {
// Node
{
- files: ['.eslintrc.js', 'src/*.js'],
+ files: [
+ '.eslintrc.cjs',
+ 'src/addon-registry/**/*.{js,ts}',
+ '__tests__/**/*.{js,ts}',
+ ],
env: {
node: true,
es6: true,
diff --git a/packages/registry/.gitignore b/packages/registry/.gitignore
index d32327c9cd..2c29928dd0 100644
--- a/packages/registry/.gitignore
+++ b/packages/registry/.gitignore
@@ -1,6 +1,7 @@
.parcel-cache/
dist
+/bin
+/lib
+/include
-# yarn 3
-.pnp.*
-.yarn/*
+docs/_build/
diff --git a/packages/registry/.readthedocs.yaml b/packages/registry/.readthedocs.yaml
new file mode 100644
index 0000000000..b1f7970727
--- /dev/null
+++ b/packages/registry/.readthedocs.yaml
@@ -0,0 +1,34 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.12"
+ # You can also specify other tool versions:
+ # nodejs: "19"
+ # rust: "1.64"
+ # golang: "1.19"
+ commands:
+ - cd ./packages/registry && make docs-rtd-registry
+
+# Build documentation in the "docs/" directory with Sphinx
+#sphinx:
+# configuration: packages/registry/docs/conf.py
+
+# Optionally build your docs in additional formats such as PDF and ePub
+# formats:
+# - pdf
+# - epub
+
+# Optional but recommended, declare the Python requirements required
+# to build your documentation
+# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+#python:
+# install:
+# - requirements: requirements-docs.txt
diff --git a/packages/registry/.release-it.json b/packages/registry/.release-it.json
index f2720cb9f0..a63e1ce21e 100644
--- a/packages/registry/.release-it.json
+++ b/packages/registry/.release-it.json
@@ -6,7 +6,7 @@
"after:bump": [
"pipx run towncrier build --draft --yes --version ${version} > .changelog.draft",
"pipx run towncrier build --yes --version ${version}",
- "pnpm build --no-cache"
+ "pnpm build:force"
],
"after:release": "rm .changelog.draft"
},
diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md
index bfc4bc4705..dd27bf57e5 100644
--- a/packages/registry/CHANGELOG.md
+++ b/packages/registry/CHANGELOG.md
@@ -8,6 +8,31 @@
+## 2.0.0-alpha.0 (2024-10-27)
+
+### Breaking
+
+- Moved the package to ESM and refactored the add-on registry scripts to TypeScript. @sneridagh
+ Breaking:
+ - For maximum compatibility with CommonJS builds, the default exports have been moved to named exports.
+ - The modules affected are now built, and the import paths have changed, too.
+ - These changes force the modification in imports in a couple of files.
+ Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399)
+
+### Feature
+
+- Added an experimental Vite plugin. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+
+### Bugfix
+
+- Return empty array when `getUtilities` does not match anything. @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+
+### Internal
+
+- Update typescript @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/issues/6373)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+
## 1.8.0 (2024-07-30)
### Feature
diff --git a/packages/registry/Makefile b/packages/registry/Makefile
new file mode 100644
index 0000000000..d0e9c89f6d
--- /dev/null
+++ b/packages/registry/Makefile
@@ -0,0 +1,90 @@
+### Defensive settings for make:
+# https://tech.davis-hansson.com/p/make/
+SHELL:=bash
+.ONESHELL:
+.SHELLFLAGS:=-eu -o pipefail -c
+.SILENT:
+.DELETE_ON_ERROR:
+MAKEFLAGS+=--warn-undefined-variables
+MAKEFLAGS+=--no-builtin-rules
+
+# Sphinx variables
+# You can set these variables from the command line.
+SPHINXOPTS ?=
+VALEOPTS ?=
+# Internal variables.
+SPHINXBUILD = "$(realpath bin/sphinx-build)"
+SPHINXAUTOBUILD = "$(realpath bin/sphinx-autobuild)"
+DOCS_DIR = ./docs/
+BUILDDIR = ./_build/
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
+VALEFILES := $(shell find $(DOCS_DIR) -type f -name "*.md" -print)
+
+# We like colors
+# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
+RED=`tput setaf 1`
+GREEN=`tput setaf 2`
+RESET=`tput sgr0`
+YELLOW=`tput setaf 3`
+
+## Docs
+
+bin/python: ## Create a Python virtual environment with the latest pip, and install documentation requirements
+ python3 -m venv . || virtualenv --clear --python=python3 .
+ bin/python -m pip install --upgrade pip
+ @echo "Python environment created."
+ bin/pip install -r ../../requirements-docs.txt
+ @echo "Requirements installed."
+
+.PHONY: docs-clean
+docs-clean: ## Clean current and legacy docs build directories, and Python virtual environment
+ rm -rf bin include lib
+ rm -rf docs/_build
+ cd $(DOCS_DIR) && rm -rf $(BUILDDIR)/
+
+.PHONY: docs-html
+docs-html: bin/python ## Build html
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: docs-livehtml
+docs-livehtml: bin/python ## Rebuild Sphinx documentation on changes, with live-reload in the browser
+ cd "$(DOCS_DIR)" && ${SPHINXAUTOBUILD} \
+ --ignore "*.swp" \
+ -b html . "$(BUILDDIR)/html" $(SPHINXOPTS)
+
+.PHONY: docs-linkcheck
+docs-linkcheck: bin/python ## Run linkcheck
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/ ."
+
+.PHONY: docs-linkcheckbroken
+docs-linkcheckbroken: bin/python ## Run linkcheck and show only broken links
+ cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck | GREP_COLORS='0;31' grep -wi "broken\|redirect" --color=always | GREP_COLORS='0;31' grep -vi "https://github.com/plone/volto/issues/" --color=always && if test $$? -eq 0; then exit 1; fi || test $$? -ne 0
+
+.PHONY: docs-vale
+docs-vale: bin/python ## Install (once) and run Vale style, grammar, and spell checks
+ bin/vale sync
+ bin/vale --no-wrap $(VALEOPTS) $(VALEFILES)
+ @echo
+ @echo "Vale is finished; look for any errors in the above output."
+
+.PHONY: docs-rtd-pr-preview
+docs-rtd-pr-preview: ## Build previews of pull requests that have documentation changes on Read the Docs via CI
+ pip install -r requirements-docs.txt
+ cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/
+
+.PHONY: docs-rtd-registry
+docs-rtd-registry: ## Build Plone Registry docs on RTD
+ pip install -r ../../requirements-docs.txt && cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/
+
+## Build
+
+.PHONY: rename-to-cjs
+rename-to-cjs: ## Rename the built files js -> cjs
+ mv dist/cjs/addon-registry.js dist/cjs/addon-registry.cjs
+ mv dist/cjs/create-addons-loader.js dist/cjs/create-addons-loader.cjs
+ mv dist/cjs/create-theme-loader.js dist/cjs/create-theme-loader.cjs
diff --git a/packages/registry/README.md b/packages/registry/README.md
index 552133573c..0c4ae8865b 100644
--- a/packages/registry/README.md
+++ b/packages/registry/README.md
@@ -10,198 +10,10 @@ That means you have to build something that has very specific requirements, beha
Sometimes you need to build something generic that is pluggable and extensible.
In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this.
-## Add-on registry
+`@plone/registry` provides tools to facilitate pluggability for your app.
-An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable.
+## Documentation
-The add-on registry is a store where you can register a number of add-ons that your app consumes.
+You can find the detailed documentation of this package by visiting the following link.
-Add-on packages are just CommonJS packages.
-The only requirement is that they point the `main` key of their `package.json` to a module that exports as a default function, which acts as a configuration loader.
-
-An add-on can be published in an npm registry, just as any other package.
-However, add-ons are meant to not be transpiled.
-They should be released as source packages.
-
-## Register an add-on
-
-You should declare your add-on in your project.
-This is done in your app's `package.json`'s `addons` key:
-
-```json
-{
- "name": "my-app-project",
- "addons": [
- "acme-volto-foo-addon",
- "@plone/some-addon",
- "collective-another-volto-addon"
- ]
-}
-```
-
-The `addons` key ensures the add-on's main default export function is executed, being passed the configuration registry.
-In that function, the add-on can customize the registry.
-The function needs to return the `config` object (the configuration registry), so that it's passed further along to the other add-ons.
-
-The add-ons are registered in the order they are found in the `addons` key.
-The last add-on takes precedence over the others.
-This means that if you configure something in `acme-volto-foo-addon`, then the same thing later in `collective-another-volto-addon`, the latter configured thing will win and its configuration will be applied.
-
-The default export of any add-on main `index.js` file should be a function with the signature `config => config`.
-That is, it should take the configuration registry object and return it, possibly mutated or changed.
-
-## Configuration registry
-
-The configuration registry supplements the add-on registry.
-
-It is a facility that stores app configuration to be shared in the app.
-
-Let's say that your app is the user interface of a content management system (CMS).
-This CMS uses blocks as its main fundamental unit of content.
-The pages that the CMS builds are made up of these blocks.
-The CMS has some basic available blocks, but it's a requirement that integrators are able to register more blocks in a pluggable way.
-
-This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to the basic CMS ones.
-
-Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry:
-
-```js
- config.blocks.blocksConfig.faq_viewer = {
- id: 'faq_viewer',
- title: 'FAQ Viewer',
- edit: FAQBlockEdit,
- view: FAQBlockView,
- icon: chartIcon,
- group: 'common',
- restricted: false,
- mostUsed: true,
- sidebarTab: 1,
- };
-```
-
-The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS.
-Then the CMS will properly populate the available blocks in the user interface.
-
-The add-on is meant to extend the initial configuration.
-From the default export function of our add-on, we should provide the configuration of the new block:
-
-```ts
-export default function applyConfig(config: ConfigData) {
- config.blocks.blocksConfig.faq_viewer = {
- id: 'faq_viewer',
- title: 'FAQ Viewer',
- edit: FAQBlockEdit,
- view: FAQBlockView,
- icon: chartIcon,
- group: 'common',
- restricted: false,
- mostUsed: true,
- sidebarTab: 1,
- };
-
- return config;
-}
-```
-
-Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block.
-The add-on will then become available to the CMS when it asks the configuration registry for it.
-
-## Accessing the configuration registry
-
-The configuration registry can be accessed by:
-
-```ts
-import config from '@plone/registry'
-
-const blocksConfig = config.blocks.blocksConfig
-```
-
-## Component registry
-
-The configuration registry also stores a components registry in itself.
-The components registry is a mapping of name to component.
-You can look up a name, and receive a component that you can reference in your code.
-This provides an alternative, and more convenient, way to customize components.
-You can override programmatically such registrations from your add-on or projects because it's stored in the configuration registry.
-You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather then import it directly.
-You can even have modifiers to the component registrations through dependencies.
-Thus you can adapt the call, given an array of such dependencies.
-
-## Register components by name using `config.registerComponent`
-
-You can register components by name, typically from an add-on or project configuration:
-
-```js
-import MyToolbarComponent from './MyToolbarComponent'
-
-config.registerComponent({
- name: 'Toolbar',
- component: MyToolbarComponent,
-});
-```
-
-## Retrieve a component from the component registry
-
-You can programmatically retrieve a component from the registry using `config.getComponent`:
-
-```js
-const Toolbar = config.getComponent('Toolbar').component
-```
-
-Or you can retrieve a component by using the convenience component `Component`, if you want to use it in JSX code directly.
-
-```jsx
-import Component from '@plone/volto/components/theme/Component/Component';
-
-
-```
-
-Note that you can pass `props` down to the retrieved component.
-
-## Adapt the component using the `dependencies` array
-
-You can register components, then retrieve them, given a list of modifiers using `dependencies`.
-
-```js
-import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserNewsItemComponent,
- dependencies: 'News Item',
- });
-```
-
-And then retrieve the component:
-
-```js
-config.getComponent({
- name: 'Teaser',
- dependencies: ['News Item'],
- }).component
-```
-
-You can have both, either with or without dependencies:
-
-```js
-import MyTeaserDefaultComponent from './MyTeaserDefaultComponent'
-import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserDefaultComponent,
- });
-
-config.registerComponent({
- name: 'Teaser',
- component: MyTeaserNewsItemComponent,
- dependencies: 'News Item',
- });
-```
-
-Then retrieve them both, depending on the use case.
-In the example, given a content type value coming from the `content` prop, you would retrieve them as shown:
-
-```jsx
-
-```
+https://plone-registry.readthedocs.io
diff --git a/packages/registry/__tests__/addon-registry-app.test.js b/packages/registry/__tests__/addon-registry-app.test.js
new file mode 100644
index 0000000000..7c3d7d7e04
--- /dev/null
+++ b/packages/registry/__tests__/addon-registry-app.test.js
@@ -0,0 +1,18 @@
+import path from 'path';
+import { AddonRegistry } from '../src/addon-registry/addon-registry';
+import { describe, it, expect } from 'vitest';
+
+describe('AddonRegistry - get()', () => {
+ it('Basic information', () => {
+ const base = path.join(import.meta.dirname, 'fixtures', 'test-app');
+ const { addons, shadowAliases, theme } = AddonRegistry.init(base);
+ expect(addons).toStrictEqual(['@plone/slots', 'my-addon']);
+ expect(shadowAliases).toStrictEqual([
+ {
+ find: '@plone/slots/components/Logo/Logo.svg',
+ replacement: `${base}/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg`,
+ },
+ ]);
+ expect(theme).toStrictEqual(undefined);
+ });
+});
diff --git a/packages/registry/__tests__/addon-registry.test.js b/packages/registry/__tests__/addon-registry.test.js
new file mode 100644
index 0000000000..cc21df88fa
--- /dev/null
+++ b/packages/registry/__tests__/addon-registry.test.js
@@ -0,0 +1,366 @@
+import path from 'path';
+import {
+ AddonRegistry,
+ buildDependencyGraph,
+ getAddonsLoaderChain,
+} from '../src/addon-registry/addon-registry';
+import { vi, describe, it, expect, test, beforeEach, afterEach } from 'vitest';
+
+vi.mock(
+ 'fixtures/test-volto-project/node_modules/@plone/volto/package.json',
+ () => ({
+ // TODO: mock the packages folder inside the mocked @plone/volto to work with resolves
+ coreAddons: {},
+ }),
+ { virtual: true },
+);
+
+describe('AddonRegistry - get()', () => {
+ it('Basic information', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { addons, shadowAliases, theme } = AddonRegistry.init(base);
+ expect(addons).toStrictEqual([
+ 'test-released-unmentioned:extra1,extra2',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon:extra',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ ]);
+ expect(shadowAliases).toStrictEqual([
+ {
+ find: 'test-released-source-addon/index',
+ replacement: `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`,
+ },
+ {
+ find: '@plone/volto/server',
+ replacement: `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`,
+ },
+ {
+ find: '@root/marker',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`,
+ },
+ {
+ find: '@plone/volto/routes',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`,
+ },
+ {
+ find: '@plone/volto/client',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/client.js`,
+ },
+ {
+ find: '@plone/volto/TSComponent',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`,
+ },
+ {
+ find: '@plone/volto/LanguageSwitcher',
+ replacement: `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`,
+ },
+ ]);
+ expect(theme).toStrictEqual(undefined);
+ });
+});
+
+describe('AddonRegistry - Project', () => {
+ it('works in a mock project directory', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+
+ const voltoPath = `${base}/node_modules/@plone/volto`;
+
+ expect(registry.projectRootPath).toStrictEqual(base);
+ expect(registry.voltoPath).toStrictEqual(voltoPath);
+
+ expect(registry.addonNames).toStrictEqual([
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ 'test-released-dummy',
+ 'test-released-unmentioned',
+ ]);
+
+ expect(registry.packages).toEqual({
+ 'test-addon': {
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/test-addon/src`,
+ name: 'test-addon',
+ packageJson: `${base}/addons/test-addon/package.json`,
+ addons: ['test-released-dummy'],
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ 'test-released-addon': {
+ basePath: `${base}/node_modules/test-released-addon`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-addon`,
+ name: 'test-released-addon',
+ packageJson: `${base}/node_modules/test-released-addon/package.json`,
+ addons: ['test-released-unmentioned:extra1,extra2'],
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'test-released-source-addon': {
+ basePath: `${base}/node_modules/test-released-source-addon`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-source-addon/src`,
+ name: 'test-released-source-addon',
+ packageJson: `${base}/node_modules/test-released-source-addon/package.json`,
+ razzleExtender: `${base}/node_modules/test-released-source-addon/razzle.extend.js`,
+ addons: [],
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'test-released-unmentioned': {
+ addons: [],
+ basePath: `${base}/node_modules/test-released-unmentioned`,
+ isPublishedPackage: true,
+ modulePath: `${base}/node_modules/test-released-unmentioned`,
+ name: 'test-released-unmentioned',
+ packageJson: `${base}/node_modules/test-released-unmentioned/package.json`,
+ isRegisteredAddon: true,
+ tsConfigPaths: null,
+ version: '0.0.0',
+ },
+ 'my-volto-config-addon': {
+ addons: ['test-released-dummy'],
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/my-volto-config-addon/src`,
+ name: 'my-volto-config-addon',
+ packageJson: `${base}/addons/my-volto-config-addon/package.json`,
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ 'test-released-dummy': {
+ addons: ['test-released-unmentioned'],
+ isPublishedPackage: false,
+ modulePath: `${base}/addons/test-released-dummy`,
+ name: 'test-released-dummy',
+ packageJson: `${base}/addons/test-released-dummy/package.json`,
+ isRegisteredAddon: true,
+ version: '0.0.0',
+ },
+ });
+ });
+
+ it('provides aliases for addons', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getResolveAliases()).toStrictEqual({
+ 'my-volto-config-addon': `${base}/addons/my-volto-config-addon/src`,
+ 'test-addon': `${base}/addons/test-addon/src`,
+ 'test-released-addon': `${base}/node_modules/test-released-addon`,
+ 'test-released-dummy': `${base}/addons/test-released-dummy`,
+ 'test-released-source-addon': `${base}/node_modules/test-released-source-addon/src`,
+ 'test-released-unmentioned': `${base}/node_modules/test-released-unmentioned`,
+ });
+ });
+
+ it('provides addon extenders', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonExtenders().length).toBe(1);
+ });
+
+ it('provides a list of addon records ordered by initial package declaration', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon',
+ ]);
+ });
+
+ it('provides customization paths declared in a Volto project', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getProjectCustomizationPaths()).toStrictEqual({
+ '@plone/volto/LanguageSwitcher': `${base}/src/customizations/LanguageSwitcher.js`,
+ '@plone/volto/TSComponent': `${base}/src/customizations/TSComponent.jsx`,
+ '@plone/volto/client': `${base}/src/customizations/client.js`,
+ '@plone/volto/routes': `${base}/src/customizations/routes.tsx`,
+ 'test-addon/testaddon': `${base}/src/custom-addons/test-addon/testaddon.js`,
+ '@plone/volto/server': `${base}/src/customizations/server.jsx`,
+ });
+ });
+
+ it('provides customization paths declared in addons', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonCustomizationPaths()).toStrictEqual({
+ '@plone/volto/LanguageSwitcher': `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`,
+ '@plone/volto/TSComponent': `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`,
+ '@plone/volto/client': `${base}/node_modules/test-released-source-addon/src/customizations/client.js`,
+ '@plone/volto/routes': `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`,
+ '@plone/volto/server': `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`,
+ '@root/marker': `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`,
+ 'test-released-source-addon/index': `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`,
+ });
+ });
+});
+
+describe('Addon chain loading dependencies', () => {
+ const depTree = {
+ add0: ['add1'],
+ add1: ['add2:e0', 'add4'],
+ add2: ['add3:e6', 'add5', 'add6'],
+ add3: ['add0'],
+ add4: ['add2:e1,e3'],
+ add5: ['add6'],
+ };
+ const extractor = (name) => depTree[name] || [];
+
+ test('no addons', () => {
+ const graph = buildDependencyGraph([], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual([]);
+ });
+
+ test('one addon', () => {
+ const graph = buildDependencyGraph(['volto-addon1'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['volto-addon1']);
+ });
+
+ test('two addons', () => {
+ const graph = buildDependencyGraph(
+ ['volto-addon1', 'volto-addon2'],
+ extractor,
+ );
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['volto-addon1', 'volto-addon2']);
+ });
+
+ test('one addon with dependency', () => {
+ const graph = buildDependencyGraph(['add5'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual(['add6', 'add5']);
+ });
+
+ test('one addon with circular dependencies', () => {
+ const graph = buildDependencyGraph(['add0'], extractor);
+ const deps = getAddonsLoaderChain(graph);
+ expect(deps).toEqual([
+ 'add3:e6',
+ 'add6',
+ 'add5',
+ 'add2:e0,e1,e3',
+ 'add4',
+ 'add1',
+ 'add0',
+ ]);
+ });
+});
+
+describe('Addon via env var - Released addon (same as dev add-on, when resolved via workspaces)', () => {
+ const originalEnv = process.env;
+
+ beforeEach(() => {
+ vi.resetModules();
+ process.env = {
+ ...originalEnv,
+ ADDONS: 'test-released-via-addons-env-var',
+ };
+ });
+
+ afterEach(() => {
+ process.env = originalEnv;
+ });
+
+ it('addons can be specified on the fly using ADDONS env var - Released addon', () => {
+ const base = path.join(
+ import.meta.dirname,
+ 'fixtures',
+ 'test-volto-project',
+ );
+ const { registry } = AddonRegistry.init(base);
+ expect(
+ Object.keys(registry.packages).includes(
+ 'test-released-via-addons-env-var',
+ ),
+ ).toBe(true);
+ });
+});
+
+describe('Add-on via config file provided using an env var', () => {
+ const originalEnv = process.env;
+ const base = path.join(import.meta.dirname, 'fixtures', 'test-volto-project');
+
+ beforeEach(() => {
+ vi.resetModules();
+ });
+
+ afterEach(() => {
+ process.env = originalEnv;
+ });
+
+ it('VOLTOCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => {
+ process.env = {
+ ...originalEnv,
+ VOLTOCONFIG: `${base}/volto.config.envvar.js`,
+ };
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon-via-env-var',
+ ]);
+ });
+
+ it('[REGISTRYCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => {
+ process.env = {
+ ...originalEnv,
+ REGISTRYCONFIG: `${base}/volto.config.envvar.js`,
+ };
+
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
+ expect(addons.map((a) => a.name)).toStrictEqual([
+ 'test-released-unmentioned',
+ 'test-released-dummy',
+ 'test-addon',
+ 'test-released-addon',
+ 'test-released-source-addon',
+ 'my-volto-config-addon-via-env-var',
+ ]);
+ });
+});
diff --git a/packages/registry/__tests__/create-addons-loader.test.js b/packages/registry/__tests__/create-addons-loader.test.js
new file mode 100644
index 0000000000..60b611ad67
--- /dev/null
+++ b/packages/registry/__tests__/create-addons-loader.test.js
@@ -0,0 +1,164 @@
+import {
+ getAddonsLoaderCode,
+ nameFromPackage,
+} from '../src/addon-registry/create-addons-loader';
+import { describe, expect, test } from 'vitest';
+
+describe('create-addons-loader code generation', () => {
+ test('no addon creates simple loader, default = no loadProjectConfig', () => {
+ const code = getAddonsLoaderCode([]);
+ expect(code).toBe(`/*
+This file is autogenerated. Don't change it directly.
+Instead, change the "addons" setting in your package.json file.
+*/
+
+
+const addonsInfo = {};
+export { addonsInfo };
+
+const safeWrapper = (func) => (config) => {
+ const res = func(config);
+ if (typeof res === 'undefined') {
+ throw new Error("Configuration function doesn't return config");
+ }
+ return res;
+}
+
+const projectConfigLoader = false;
+const projectConfig = (config) => {
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+}
+
+const load = (config) => {
+ const addonLoaders = [];
+ if(!addonLoaders.every((el) => typeof el === "function")) {
+ throw new TypeError(
+ 'Each addon has to provide a function applying its configuration to the projects configuration.',
+ );
+ }
+ return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config));
+};
+export default load;
+`);
+ });
+
+ test('no addon creates simple loader, loadProjectConfig set to true', () => {
+ const code = getAddonsLoaderCode([], {}, true);
+ expect(code).toBe(`/*
+This file is autogenerated. Don't change it directly.
+Instead, change the "addons" setting in your package.json file.
+*/
+
+const projectConfigLoader = require('@root/config');
+
+const addonsInfo = {};
+export { addonsInfo };
+
+const safeWrapper = (func) => (config) => {
+ const res = func(config);
+ if (typeof res === 'undefined') {
+ throw new Error("Configuration function doesn't return config");
+ }
+ return res;
+}
+
+
+const projectConfig = (config) => {
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+}
+
+const load = (config) => {
+ const addonLoaders = [];
+ if(!addonLoaders.every((el) => typeof el === "function")) {
+ throw new TypeError(
+ 'Each addon has to provide a function applying its configuration to the projects configuration.',
+ );
+ }
+ return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config));
+};
+export default load;
+`);
+ });
+
+ test('one addon creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1']);
+ expect(code.indexOf("import voltoAddon1 from 'volto-addon1';") > 0).toBe(
+ true,
+ );
+ });
+
+ test('two addons create loaders', () => {
+ const code = getAddonsLoaderCode(['volto-addon1', 'volto-addon2']);
+ expect(
+ code.indexOf(`
+import voltoAddon1 from 'volto-addon1';
+import voltoAddon2 from 'volto-addon2';`) > 0,
+ ).toBe(true);
+ });
+
+ test('one addons plus one extra creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1']);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1';
+`) > 0,
+ ).toBe(true);
+ });
+
+ test('one addons plus two extras creates loader', () => {
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1,loadExtra2']);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1';
+`) > 0,
+ ).toBe(true);
+ });
+
+ test('two addons plus extras creates loader', () => {
+ const code = getAddonsLoaderCode([
+ 'volto-addon1:loadExtra1,loadExtra2',
+ 'volto-addon2:loadExtra3,loadExtra4',
+ ]);
+ expect(
+ code.indexOf(`
+import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1';
+import voltoAddon2, { loadExtra3 as loadExtra32, loadExtra4 as loadExtra43 } from 'volto-addon2';
+`) > 0,
+ ).toBe(true);
+ });
+});
+
+describe('create-addons-loader default name generation', () => {
+ const getName = nameFromPackage;
+
+ test('passing a simple word returns a word', () => {
+ expect(getName('something')).toBe('something');
+ });
+
+ test('passing a kebab-name returns a word', () => {
+ expect(getName('volto-something-else')).toBe('voltoSomethingElse');
+ });
+
+ test('passing a simple relative path returns random string', () => {
+ const rand = getName('../../');
+ expect(rand.length).toBe(10);
+ expect(new RegExp(/[abcdefghjk]+/).exec(rand)[0].length > 0).toBe(true);
+ });
+ test('passing a tilda relative path with addon strips tilda', () => {
+ const name = getName('~/addons/volto-addon1');
+ expect(name).toBe('addonsvoltoAddon1');
+ });
+ test('passing a namespace package strips @', () => {
+ const name = getName('@plone/volto-addon1');
+ expect(name).toBe('plonevoltoAddon1');
+ });
+ test('passing a tilda relative path strips tilda', () => {
+ const name = getName('~/../');
+ expect(name.length).toBe(10);
+ expect(new RegExp(/[abcdefghjk]+/).exec(name)[0].length > 0).toBe(true);
+ });
+ test('passing a backspaced path strips backspace', () => {
+ const name = getName('c:\\nodeprojects');
+ expect(name).toBe('cnodeprojects');
+ });
+});
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg
new file mode 100644
index 0000000000..1aa6adfdf3
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg
@@ -0,0 +1 @@
+
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json
new file mode 100644
index 0000000000..68a86c4d37
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "@plone/slots",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg
new file mode 100644
index 0000000000..3e8fcacea6
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg
@@ -0,0 +1 @@
+
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json
new file mode 100644
index 0000000000..ccf0c501c1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "my-addon",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/package.json b/packages/registry/__tests__/fixtures/test-app/package.json
new file mode 100644
index 0000000000..24de726e14
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/package.json
@@ -0,0 +1,3 @@
+{
+ "name": "test-app"
+}
diff --git a/packages/registry/__tests__/fixtures/test-app/registry.config.js b/packages/registry/__tests__/fixtures/test-app/registry.config.js
new file mode 100644
index 0000000000..f520684acb
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-app/registry.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ addons: ['@plone/slots', 'my-addon'],
+};
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json
new file mode 100644
index 0000000000..87e8d2ad96
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "test-addon",
+ "version": "0.0.0",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/apps/plone/cypress/.gitkeep b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js
similarity index 100%
rename from apps/plone/cypress/.gitkeep
rename to packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json
new file mode 100644
index 0000000000..c81be50ab7
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "non-volto-addon-lib",
+ "version": "0.0.0",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json
new file mode 100644
index 0000000000..87e8d2ad96
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "test-addon",
+ "version": "0.0.0",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx
@@ -0,0 +1 @@
+//
diff --git a/apps/plone/cypress/tests/.gitkeep b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js
similarity index 100%
rename from apps/plone/cypress/tests/.gitkeep
rename to packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json
new file mode 100644
index 0000000000..18c940d721
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-released-dummy",
+ "version": "0.0.0",
+ "main": "index.js",
+ "addons": [
+ "test-released-unmentioned"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json
new file mode 100644
index 0000000000..4c3beb25ec
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "paths": {
+ "test-addon": ["test-addon/src"],
+ "test-released-dummy": ["test-released-dummy"],
+ "my-volto-config-addon": ["my-volto-config-addon/src"],
+ "non-volto-addon-lib": ["non-volto-addon-lib/src"]
+ },
+ "baseUrl": "addons"
+ }
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js
new file mode 100644
index 0000000000..0dae97301d
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js
@@ -0,0 +1 @@
+//dummy file
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/pnpm-lock.yaml b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx
similarity index 100%
rename from packages/registry/pnpm-lock.yaml
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json
new file mode 100644
index 0000000000..ac0652a9b0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "my-volto-config-addon-via-env-var",
+ "version": "0.0.0",
+ "main": "index.js",
+ "customizationPaths": [
+ "src/custom-addons"
+ ],
+ "addons": [
+ "test-released-dummy"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json
new file mode 100644
index 0000000000..4f1f61b465
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-released-addon",
+ "version": "0.0.0",
+ "main": "index.js",
+ "addons": [
+ "test-released-unmentioned:extra1,extra2"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json
new file mode 100644
index 0000000000..4640873bbf
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-dummy",
+ "main": "index.js",
+ "addons": []
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json
new file mode 100644
index 0000000000..3220bd17cc
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-source-addon",
+ "version": "0.0.0",
+ "main": "src/index.js"
+}
diff --git a/styles/Vocab/Base/accept.txt b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js
similarity index 100%
rename from styles/Vocab/Base/accept.txt
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js
diff --git a/styles/Vocab/Base/reject.txt b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js
similarity index 100%
rename from styles/Vocab/Base/reject.txt
rename to packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json
new file mode 100644
index 0000000000..b3b3c399f1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "test-released-unmentioned",
+ "version": "0.0.0",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js
new file mode 100644
index 0000000000..53e61140f2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js
@@ -0,0 +1,2 @@
+export default (config) => config;
+
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json
new file mode 100644
index 0000000000..c6a4424310
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "test-released-via-addons-env-var",
+ "main": "index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/package.json b/packages/registry/__tests__/fixtures/test-volto-project/package.json
new file mode 100644
index 0000000000..bb67637bb2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "test-volto-project",
+ "addons": [
+ "test-addon",
+ "test-released-addon:extra",
+ "test-released-source-addon"
+ ],
+ "customizationPaths": [
+ "src/custom-addons",
+ "src/customizations/"
+ ]
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json
new file mode 100644
index 0000000000..7e6c4a11fb
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "test-local-packages-via-addons-env-var",
+ "main": "src/index.js"
+}
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js
new file mode 100644
index 0000000000..8cb61d300a
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js
@@ -0,0 +1 @@
+export default (config) => config;
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js
new file mode 100644
index 0000000000..4d389677f0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js
@@ -0,0 +1 @@
+// another customization
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js
new file mode 100644
index 0000000000..b262fa10a3
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js
@@ -0,0 +1 @@
+// dummy file
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx
new file mode 100644
index 0000000000..8337712ea5
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx
@@ -0,0 +1 @@
+//
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js b/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js
new file mode 100644
index 0000000000..2e37cd37c2
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js
@@ -0,0 +1,3 @@
+module.exports = {
+ addons: ['my-volto-config-addon-via-env-var'],
+};
diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js
new file mode 100644
index 0000000000..ec05e8e085
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ addons: ['my-volto-config-addon'],
+};
diff --git a/packages/registry/__tests__/fixtures/volto-addon1.js b/packages/registry/__tests__/fixtures/volto-addon1.js
new file mode 100644
index 0000000000..b406bacbd8
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon1.js
@@ -0,0 +1,14 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en'],
+ navDepth: 1,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
diff --git a/packages/registry/__tests__/fixtures/volto-addon2.js b/packages/registry/__tests__/fixtures/volto-addon2.js
new file mode 100644
index 0000000000..62ec3d2cf0
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon2.js
@@ -0,0 +1,29 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en', 'de'],
+ navDepth: 3,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const additionalConfig = (config) => {
+ const settings = {
+ navDepth: 6,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+export { additionalConfig };
diff --git a/packages/registry/__tests__/fixtures/volto-addon3.js b/packages/registry/__tests__/fixtures/volto-addon3.js
new file mode 100644
index 0000000000..2231f8a8b1
--- /dev/null
+++ b/packages/registry/__tests__/fixtures/volto-addon3.js
@@ -0,0 +1,41 @@
+export default (config) => {
+ const settings = {
+ nonContentRoutes: [],
+ supportedLanguages: ['en', 'de'],
+ navDepth: 3,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const additionalConfig = (config) => {
+ const settings = {
+ navDepth: 6,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+
+const alternateAdditionalConfig = (config) => {
+ const settings = {
+ navDepth: 10,
+ };
+
+ config.settings = {
+ ...config.settings,
+ ...settings,
+ };
+
+ return config;
+};
+export { additionalConfig, alternateAdditionalConfig };
diff --git a/packages/registry/docs/_static/Plone_logo_square.png b/packages/registry/docs/_static/Plone_logo_square.png
new file mode 100644
index 0000000000..f42a1220d7
Binary files /dev/null and b/packages/registry/docs/_static/Plone_logo_square.png differ
diff --git a/packages/registry/docs/_static/favicon.ico b/packages/registry/docs/_static/favicon.ico
new file mode 100644
index 0000000000..ae7bb0cd8b
Binary files /dev/null and b/packages/registry/docs/_static/favicon.ico differ
diff --git a/packages/registry/docs/_static/logo.svg b/packages/registry/docs/_static/logo.svg
new file mode 100644
index 0000000000..6bfdc1ee90
--- /dev/null
+++ b/packages/registry/docs/_static/logo.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/packages/registry/docs/_static/print.css b/packages/registry/docs/_static/print.css
new file mode 100644
index 0000000000..8dbc2d5794
--- /dev/null
+++ b/packages/registry/docs/_static/print.css
@@ -0,0 +1,3 @@
+.tooltip {
+ display: none;
+}
diff --git a/packages/registry/docs/conceptual-guides/add-on-registry.md b/packages/registry/docs/conceptual-guides/add-on-registry.md
new file mode 100644
index 0000000000..763d9f54d9
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/add-on-registry.md
@@ -0,0 +1,38 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the add-on registry in @plone/registry"
+ "property=og:description": "An explanation of the add-on registry in @plone/registry"
+ "property=og:title": "Add-on registry"
+ "keywords": "@plone/registry, registry, add-on"
+---
+
+# Add-on registry
+
+An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable.
+
+The add-on registry is a store where you can register a number of add-ons that your app consumes.
+
+The add-on registry can be queried, so it can provide a list of add-ons installed in the registry and their properties.
+
+
+## What is an add-on?
+
+Add-on packages are just CommonJS or ESM packages.
+Their main purpose is to encapsulate logic, configuration and customizations in a reusable way.
+The only requirement is that their primary entry point (the `main` key of their `package.json`) points to a module that exports a default function, which acts as a default configuration loader for that package.
+
+Add-ons are applied in the order they are declared in the `addons` key of {file}`package.json` or programatically via a provided configuration file.
+Add-ons can override configuration coming from other add-ons, providing a hierarchy of configuration stacks.
+
+An add-on can be published in an npm registry, just as any other package.
+However, add-ons are meant to not be transpiled, but built along with your app code.
+They can be released as "source" packages or used directly in your app as local code.
+
+Add-ons can define shadowed components.
+"Component shadowing" is a technique for overriding modules of other packages at build time.
+This technique builds upon the `resolve.aliases` facilities of bundlers, so modules can be replaced when the app is being built.
+
+Add-ons can be chained, where each one can configure the app in some way.
+If needed, each add-on in the chain can override or extend the previous configuration that other add-ons set.
+Thus, the order in which you register add-ons matters.
diff --git a/packages/registry/docs/conceptual-guides/component-registry.md b/packages/registry/docs/conceptual-guides/component-registry.md
new file mode 100644
index 0000000000..08fb9226d9
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/component-registry.md
@@ -0,0 +1,20 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the component registry in @plone/registry"
+ "property=og:description": "An explanation of the component registry in @plone/registry"
+ "property=og:title": "Component registry"
+ "keywords": "@plone/registry, registry, component"
+---
+
+# Component registry
+
+The configuration registry stores a component registry in itself.
+The component registry is a mapping of names to components.
+You can look up a name, and receive a component that you can reference in your code.
+This provides an alternative, and more convenient, way to customize components in a pluggable way.
+
+You can programmatically override such registrations from your add-on or projects because it's stored in the configuration registry.
+You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather than import it directly.
+You can even have modifiers to the component registrations through dependencies.
+Thus you can adapt the call, given an array of such dependencies.
diff --git a/packages/registry/docs/conceptual-guides/configuration-registry.md b/packages/registry/docs/conceptual-guides/configuration-registry.md
new file mode 100644
index 0000000000..c1fd19db20
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/configuration-registry.md
@@ -0,0 +1,78 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the configuration registry in @plone/registry"
+ "property=og:description": "An explanation of the configuration registry in @plone/registry"
+ "property=og:title": "Configuration registry"
+ "keywords": "@plone/registry, registry, configuration"
+---
+
+# Configuration registry
+
+The configuration registry supplements the add-on registry.
+They both work together to provide extensibility and pluggability capabilities.
+The configuration registry is a facility that stores app configuration to share in the app.
+The add-ons save configuration from the registry using their default export function on app bootstrap time.
+They retrieve this configuration as needed by the functionality and components they expose.
+
+## Example use case - Pluggable block system
+
+Let's say that your app is the user interface of a content management system (CMS).
+This CMS uses blocks as its main fundamental unit of content.
+The pages that the CMS builds are made up of these blocks.
+The CMS has some basic available blocks, yet it's a requirement that integrators can register more blocks in a pluggable way.
+This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to those in the basic CMS.
+
+Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry:
+
+```js
+ config.blocks.blocksConfig.faq_viewer = {
+ id: 'faq_viewer',
+ title: 'FAQ Viewer',
+ edit: FAQBlockEdit,
+ view: FAQBlockView,
+ icon: chartIcon,
+ group: 'common',
+ restricted: false,
+ mostUsed: true,
+ sidebarTab: 1,
+ };
+```
+
+The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS.
+Then the CMS will populate the available blocks in the user interface.
+
+The add-on is meant to extend the initial configuration.
+From the default export function of our add-on, you should provide the configuration of the new block:
+
+```ts
+export default function applyConfig(config: ConfigData) {
+ config.blocks.blocksConfig.faq_viewer = {
+ id: 'faq_viewer',
+ title: 'FAQ Viewer',
+ edit: FAQBlockEdit,
+ view: FAQBlockView,
+ icon: chartIcon,
+ group: 'common',
+ restricted: false,
+ mostUsed: true,
+ sidebarTab: 1,
+ };
+
+ return config;
+}
+```
+
+Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block.
+The add-on will then become available to the CMS when it asks the configuration registry for it.
+
+
+## Configuration registry artifacts
+
+The configuration registry also stores special elements that can be queried and retrieved in a pluggable way.
+
+- Components
+- Slots
+- Utilities
+
+Some of the components are particular to the use case of a CMS, such as slots, but the abstraction can be ported and applied to different scenarios.
diff --git a/packages/registry/docs/conceptual-guides/utility-registry.md b/packages/registry/docs/conceptual-guides/utility-registry.md
new file mode 100644
index 0000000000..5c0d284bae
--- /dev/null
+++ b/packages/registry/docs/conceptual-guides/utility-registry.md
@@ -0,0 +1,14 @@
+---
+myst:
+ html_meta:
+ "description": "An explanation of the utility registry in @plone/registry"
+ "property=og:description": "An explanation of the utility registry in @plone/registry"
+ "property=og:title": "Utility registry"
+ "keywords": "@plone/registry, registry, utility"
+---
+
+# Utility registry
+
+The configuration registry stores a utility registry in itself.
+The component registry is a mapping of a `name` and a `type` to a method or function.
+The utility registry works similarly, but for methods and functions, and with additional query argument `type`.
diff --git a/packages/registry/docs/conf.py b/packages/registry/docs/conf.py
new file mode 100644
index 0000000000..796fc4b620
--- /dev/null
+++ b/packages/registry/docs/conf.py
@@ -0,0 +1,297 @@
+# Configuration file for the Sphinx documentation builder.
+# Plone Documentation build configuration file
+
+
+# -- Path setup --------------------------------------------------------------
+
+from datetime import datetime
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath("."))
+
+import os
+import json
+
+
+# -- Project information -----------------------------------------------------
+
+project = "@plone/registry documentation"
+copyright = "Plone Foundation"
+author = "Plone Community"
+trademark_name = "Plone"
+now = datetime.now()
+year = str(now.year)
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+with open(
+ os.path.join(os.path.abspath("."), "../package.json"), "r"
+) as package_json:
+ data = package_json.read()
+
+version_from_package_json = json.loads(data)["version"]
+
+if version_from_package_json:
+ # The short X.Y version.
+ version = version_from_package_json
+ # The full version, including alpha/beta/rc tags.
+ release = version_from_package_json
+else:
+ version = "1.8.0"
+ release = "1.8.0"
+
+
+# -- General configuration ----------------------------------------------------
+
+# Add any paths that contain templates here, relative to this directory.
+# templates_path = ["_templates"]
+
+# Add any Sphinx extension module names here, as strings.
+# They can be extensions coming with Sphinx (named "sphinx.ext.*")
+# or your custom ones.
+extensions = [
+ "myst_parser",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.todo",
+ "sphinx_copybutton",
+ "sphinxext.opengraph",
+]
+
+
+# If true, the Docutils Smart Quotes transform, originally based on SmartyPants
+# (limited to English) and currently applying to many languages, will be used
+# to convert quotes and dashes to typographically correct entities.
+# Note to maintainers: setting this to `True` will cause contractions and
+# hyphenated words to be marked as misspelled by spellchecker.
+smartquotes = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+# pygments_style = "sphinx.pygments_styles.PyramidStyle"
+pygments_style = "sphinx"
+
+# Options for the linkcheck builder
+# Ignore localhost
+linkcheck_ignore = [
+ # TODO: Before release, clean up any links to ignore
+ r"http://127.0.0.1",
+ r"http://localhost",
+ # Ignore pages that require authentication
+ r"https://github.com/plone/volto/issues/new/choose", # requires auth
+ # Ignore github.com pages with anchors
+ r"https://github.com/.*#.*",
+ # Ignore other specific anchors
+]
+linkcheck_anchors = True
+linkcheck_timeout = 5
+linkcheck_retries = 1
+
+# The suffix of source filenames.
+source_suffix = {
+ ".md": "markdown",
+ ".bugfix": "markdown",
+ ".breaking": "markdown",
+ ".documentation": "markdown",
+ ".feature": "markdown",
+ ".internal": "markdown",
+}
+
+# The master toctree document.
+master_doc = "index"
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = [
+]
+
+suppress_warnings = [
+ # "toc.excluded", # Suppress `WARNING: document isn't included in any toctree`
+ "toc.not_readable", # Suppress `WARNING: toctree contains reference to nonexisting document 'news*'`
+]
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = "plone_sphinx_theme"
+html_logo = "_static/logo.svg"
+html_favicon = "_static/favicon.ico"
+html_theme_options = {
+ "article_header_start": ["toggle-primary-sidebar"],
+ "extra_footer": """
The text and illustrations in this website are licensed by the Plone Foundation under a Creative Commons Attribution 4.0 International license. Plone and the Plone® logo are registered trademarks of the Plone Foundation, registered in the United States and other countries. For guidelines on the permitted uses of the Plone trademarks, see https://plone.org/foundation/logo. All other trademarks are owned by their respective owners.
""",
+ "footer_end": ["version.html"],
+ "icon_links": [
+ {
+ "name": "GitHub",
+ "url": "https://github.com/plone/volto",
+ "icon": "fa-brands fa-square-github",
+ "type": "fontawesome",
+ "attributes": {
+ "target": "_blank",
+ "rel": "noopener me",
+ "class": "nav-link custom-fancy-css"
+ }
+ },
+ {
+ "name": "Mastodon",
+ "url": "https://plone.social/@plone",
+ "icon": "fa-brands fa-mastodon",
+ "type": "fontawesome",
+ "attributes": {
+ "target": "_blank",
+ "rel": "noopener me",
+ "class": "nav-link custom-fancy-css"
+ }
+ },
+ {
+ "name": "X (formerly Twitter)",
+ "url": "https://x.com/plone",
+ "icon": "fa-brands fa-square-x-twitter",
+ "type": "fontawesome",
+ "attributes": {
+ "target": "_blank",
+ "rel": "noopener me",
+ "class": "nav-link custom-fancy-css"
+ }
+ },
+ ],
+ "logo": {
+ "text": "@plone/registry Documentation",
+ },
+ "navigation_with_keys": True,
+ "path_to_docs": "docs",
+ "repository_branch": "main",
+ "repository_url": "https://github.com/plone/volto/tree/main/packages/registry",
+ "search_bar_text": "Search", # TODO: Confirm usage of search_bar_text in plone-sphinx-theme
+ "use_edit_page_button": True,
+ "use_issues_button": True,
+ "use_repository_button": True,
+}
+
+# Announce that we have an opensearch plugin
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch
+html_use_opensearch = "https://plone-registry.readthedocs.io/" # TODO: Confirm usage of opensearch in theme
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+html_title = "%(project)s v%(release)s" % {"project": project, "release": release}
+
+html_css_files = ["custom.css", ("print.css", {"media": "print"})]
+
+# If false, no index is generated.
+html_use_index = True
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+html_title = "%(project)s v%(release)s" % {"project": project, "release": release}
+
+html_extra_path = [
+ "robots.txt",
+]
+
+html_static_path = [
+ "_static",
+]
+
+
+# -- Options for MyST markdown conversion to HTML -----------------------------
+
+# For more information see:
+# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html
+myst_enable_extensions = [
+ "deflist", # Support definition lists.
+ # https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#definition-lists
+ "linkify", # Identify "bare" web URLs and add hyperlinks.
+ "colon_fence", # You can also use ::: delimiters to denote code fences,\
+ # instead of ```.
+ "html_image", # For inline images. See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#html-images
+]
+
+
+# -- Intersphinx configuration ----------------------------------
+
+# This extension can generate automatic links to the documentation of objects
+# in other projects. Usage is simple: whenever Sphinx encounters a
+# cross-reference that has no matching target in the current documentation set,
+# it looks for targets in the documentation sets configured in
+# intersphinx_mapping. A reference like :py:class:`zipfile.ZipFile` can then
+# linkto the Python documentation for the ZipFile class, without you having to
+# specify where it is located exactly.
+#
+# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
+#
+intersphinx_mapping = {
+ "plone": ("https://6.docs.plone.org/", None),
+ "python": ("https://docs.python.org/3/", None),
+ "training": ("https://training.plone.org/", None),
+}
+
+
+# -- OpenGraph configuration ----------------------------------
+
+ogp_site_url = "https://plone-registry.readthedocs.io/"
+ogp_description_length = 200
+ogp_image = "https://plone-registry.readthedocs.io/en/latest/_static/Plone_logo_square.png"
+ogp_site_name = "@plone/registry Documentation"
+ogp_type = "website"
+ogp_custom_meta_tags = [
+ '',
+]
+
+
+# -- Options for sphinx.ext.todo -----------------------
+# See http://sphinx-doc.org/ext/todo.html#confval-todo_include_todos
+todo_include_todos = True
+
+
+# -- Options for HTML help output -------------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = "PloneRegistryDocumentation"
+
+
+# -- Options for LaTeX output -------------------------------------------------
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual])
+latex_documents = [
+ (
+ "index",
+ "PloneRegistryDocumentation.tex",
+ "@plone/registry Documentation",
+ "Plone Community",
+ "manual",
+ ),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = "_static/logo_2x.png"
+
+
+# An extension that allows replacements for code blocks that
+# are not supported in `rst_epilog` or other substitutions.
+# https://stackoverflow.com/a/56328457/2214933
+def source_replace(app, docname, source):
+ result = source[0]
+ for key in app.config.source_replacements:
+ result = result.replace(key, app.config.source_replacements[key])
+ source[0] = result
+
+
+# Dict of replacements.
+source_replacements = {
+ "{NVM_VERSION}": "0.39.5",
+}
+
+
+def setup(app):
+ app.add_config_value("source_replacements", {}, True)
+ app.connect("source-read", source_replace)
diff --git a/packages/registry/docs/how-to-guides/access-registry.md b/packages/registry/docs/how-to-guides/access-registry.md
new file mode 100644
index 0000000000..cbe6923633
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/access-registry.md
@@ -0,0 +1,20 @@
+---
+myst:
+ html_meta:
+ "description": "How to access the configuration registry in @plone/registry"
+ "property=og:description": "How to access the configuration registry in @plone/registry"
+ "property=og:title": "Access the configuration registry"
+ "keywords": "@plone/registry, registry, configuration, guide"
+---
+
+# Access the configuration registry
+
+You can access the configuration registry as follows.
+
+```ts
+import config from '@plone/registry'
+
+const blocksConfig = config.blocks.blocksConfig
+```
+
+This method assumes that either you previously created a `blocksConfig` key in `blocks` in your add-on, or another add-on sets it.
diff --git a/packages/registry/docs/how-to-guides/instantiate-registry.md b/packages/registry/docs/how-to-guides/instantiate-registry.md
new file mode 100644
index 0000000000..f915e983fe
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/instantiate-registry.md
@@ -0,0 +1,56 @@
+---
+myst:
+ html_meta:
+ "description": "How to instantiate the registry in @plone/registry"
+ "property=og:description": "How to instantiate the registry in @plone/registry"
+ "property=og:title": "Instantiate the registry"
+ "keywords": "@plone/registry, registry, instantiate"
+---
+
+# Instantiate the registry
+
+The registry is instantiated in the context of your app folder.
+It gets your app folder path as argument.
+
+```js
+import path from 'path';
+import { AddonRegistry } from '@plone/registry/addon-registry';
+
+const appRootPath = path.resolve('.');
+const { registry } = AddonRegistry.init(appRootPath)
+```
+
+You have full access to the add-on registry API in the `registry` object.
+
+By default, you also get these objects after calling `init`.
+
+```js
+const { registry, addons, theme, shadowAliases } = AddonRegistry.init(appRootPath)
+```
+
+This can be useful for configuring your build process.
+
+
+## Initialization
+
+By default, the configuration registry is empty.
+It only contains the base object keys which are required for it to work properly.
+These are the keys present on initialization.
+The optional keys are excluded.
+
+```ts
+export type ConfigData = {
+ settings: SettingsConfig | Record;
+ blocks: BlocksConfig | Record;
+ views: ViewsConfig | Record;
+ widgets: WidgetsConfig | Record;
+ addonReducers?: AddonReducersConfig;
+ addonRoutes?: AddonRoutesConfig;
+ slots: SlotsConfig | Record;
+ components: ComponentsConfig | Record;
+ utilities: UtilitiesConfig | Record;
+ experimental?: ExperimentalConfig;
+};
+```
+
+In the context of a Volto app, the registry gets initialized by Volto by default.
diff --git a/packages/registry/docs/how-to-guides/register-an-add-on.md b/packages/registry/docs/how-to-guides/register-an-add-on.md
new file mode 100644
index 0000000000..874b35471d
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/register-an-add-on.md
@@ -0,0 +1,94 @@
+---
+myst:
+ html_meta:
+ "description": "How to register an add-on in @plone/registry"
+ "property=og:description": "How to register an add-on in @plone/registry"
+ "property=og:title": "Register an add-on"
+ "keywords": "@plone/registry, registry, add-on"
+---
+
+# Register an add-on
+
+You have two ways available to register an add-on in your app.
+You can do so either through the `addons` key in your {file}`package.json` or by using a configuration file.
+
+```{note}
+Using a configuration file is useful when you want to add some logic to the `addons` list, if you want it to be dynamic.
+For example, you can make the list dynamic given an environment variable.
+```
+
+
+## Via `addons` key in `package.json`
+
+The following code sample shows how to register your add-on in your app through the `addons` key in your {file}`package.json`.
+
+```json
+{
+ "name": "my-app-project",
+ "addons": [
+ "acme-volto-foo-addon",
+ "@plone/some-addon",
+ "collective-another-volto-addon"
+ ]
+}
+```
+
+The add-ons are registered in the order they are found in the `addons` key.
+The last add-on takes precedence over the others.
+This means that if you configure something in `acme-volto-foo-addon`, then the same thing later in `collective-another-volto-addon`, the latter configured thing will win and its configuration will be applied.
+
+All add-ons should set the value for the `main` entry module, such as `src/index.js`, in {file}`package.json`.
+This function should have a signature of `config => config`.
+That is, it should take the configuration registry object and return it, possibly mutated or changed.
+
+```ts
+import type { ConfigType } from '@plone/registry'
+
+export default function applyConfig(config: ConfigType) {
+ return config
+};
+```
+
+The `addons` key ensures the add-on's main default export function is executed, being passed the configuration registry.
+
+
+## Via a configuration file
+
+The configuration file can be provided via an environment variable.
+You can use one of these two environment variables.
+
+- `REGISTRYCONFIG`
+- `VOLTOCONFIG`
+
+The value of the environment variable must point to a file that exists relative to the app folder, that is, the one you pass to the instantiation of the add-on registry.
+You can also pass the full path of the file.
+
+For example, if your configuration file is named {file}`my-add-on-registry-config.js` and is located at the root of your add-on package, you would set your environment variable as shown.
+
+```shell
+set REGISTRYCONFIG="my-add-on-registry.config.js"
+```
+
+```{note}
+This is useful when you want to provide different `addon` configuration files under different scenarios.
+```
+
+If the file that you specify in the environment variable exists, then `@plone/registry` uses it to configure your add-on.
+If it does not exist, then `@plone/registry` looks for the configuration file in the following locations in the root of your app in order.
+The first found configuration file wins.
+
+- {file}`registry.config.js`
+- {file}`volto.config.js`
+
+This is an example of a configuration file.
+You must define it in [CommonJS](https://en.wikipedia.org/wiki/CommonJS) format.
+
+```js
+module.exports = {
+ addons: ['my-volto-config-addon'],
+};
+```
+
+If your app is in ESM (`"type": "module"` in {file}`package.json`), then you should use the `.cjs` suffix for the configuration file to mark it as a proper `CommonJS` file.
+
+If `@plone/registry` finds no configuration file, then it only relies on the configuration, if any, in the `addons` key in {file}`package.json`.
diff --git a/packages/registry/docs/how-to-guides/register-and-retrieve-components.md b/packages/registry/docs/how-to-guides/register-and-retrieve-components.md
new file mode 100644
index 0000000000..08e8623e0b
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/register-and-retrieve-components.md
@@ -0,0 +1,114 @@
+---
+myst:
+ html_meta:
+ "description": "How to register, retrieve, adapt, and load components in @plone/registry"
+ "property=og:description": "How to register, retrieve, adapt, and load components in @plone/registry"
+ "property=og:title": "Register and retrieve components"
+ "keywords": "@plone/registry, registry, register, retrieve, components"
+---
+
+# Register and retrieve components
+
+This section of the documentation describes how to register, retrieve, adapt, and load components.
+
+
+## Register components by name
+
+You can register components by name, typically from an add-on or project configuration, using `config.registerComponent`.
+
+```js
+import MyToolbarComponent from './MyToolbarComponent'
+
+config.registerComponent({
+ name: 'Toolbar',
+ component: MyToolbarComponent,
+});
+```
+
+
+## Retrieve a component from the component registry
+
+You can programmatically retrieve a component from the component registry using `config.getComponent`.
+
+```js
+const Toolbar = config.getComponent('Toolbar').component
+```
+
+Alternatively, you can retrieve a component by using the convenience component `Component`, if you want to use it in JSX code directly.
+
+```jsx
+import Component from '@plone/volto/components/theme/Component/Component';
+
+
+```
+
+Note that you can pass `props` down to the retrieved component.
+
+
+## Adapt the component
+
+You can register components, then retrieve them, given a list of modifiers using the `dependencies` array.
+
+```js
+import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
+
+config.registerComponent({
+ name: 'Teaser',
+ component: MyTeaserNewsItemComponent,
+ dependencies: ['News Item'],
+ });
+```
+
+To retrieve the component, use `getComponent`.
+
+```js
+config.getComponent({
+ name: 'Teaser',
+ dependencies: ['News Item'],
+ }).component
+```
+
+If you have a single dependency, you can use a string instead of an array.
+
+You can have both, either with or without dependencies.
+
+```js
+import MyTeaserDefaultComponent from './MyTeaserDefaultComponent'
+import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent'
+
+config.registerComponent({
+ name: 'Teaser',
+ component: MyTeaserDefaultComponent,
+ });
+
+config.registerComponent({
+ name: 'Teaser',
+ component: MyTeaserNewsItemComponent,
+ dependencies: 'News Item',
+ });
+```
+
+After you register them, you can retrieve both or either of them, depending on your use case.
+In the next example, given a content type value coming from the `content` prop, you can retrieve them.
+
+```jsx
+
+```
+
+
+## Lazy load components
+
+```{todo}
+Test it properly.
+```
+
+You can lazy load the component in the registry, too, if you need it.
+
+```js
+const MyTeaserDefaultComponent = lazy(()=> import(./MyTeaserDefaultComponent))
+
+config.registerComponent({
+ name: 'Teaser',
+ component: MyTeaserDefaultComponent,
+ });
+```
diff --git a/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md b/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md
new file mode 100644
index 0000000000..d58fc92d4c
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md
@@ -0,0 +1,113 @@
+---
+myst:
+ html_meta:
+ "description": "How to register and retrieve utilities in @plone/registry"
+ "property=og:description": "How to register and retrieve utilities in @plone/registry"
+ "property=og:title": "Register and retrieve utilities"
+ "keywords": "@plone/registry, registry, utilities, register, retrieve"
+---
+
+# Register and retrieve utilities
+
+This section of the documentation describes how to register and retrieve utilities.
+
+
+## Register utilities by `name` and `type`
+
+You can register a utility using `config.registerUtility` by using its specific `name` and `type` arguments.
+
+```js
+config.registerUtility({
+ name: 'url',
+ type: 'validator',
+ method: () => 'this is a simple validator utility',
+});
+```
+
+For utilities of the same `type`, you can register different `name` utilities.
+
+```js
+config.registerUtility({
+ name: 'url',
+ type: 'validator',
+ method: () => 'this is a simple URL validator utility',
+});
+
+config.registerUtility({
+ name: 'email',
+ type: 'validator',
+ method: () => 'this is a simple email validator utility',
+});
+```
+
+However, if you register two utilities under the same `name`, then the latter will override the former.
+Thus you can override existing utilities in your add-ons.
+
+```js
+config.registerUtility({
+ name: 'url',
+ type: 'validator',
+ method: () => 'this is a simple URL validator utility',
+});
+
+config.registerUtility({
+ name: 'url',
+ type: 'validator',
+ method: () => 'this registered URL validator utility is the last defined, and therefore overrides all previous utilities with the same `name`',
+});
+```
+
+
+## Register utilities using a `dependencies` object
+
+It is possible to register utilities using a `dependencies` object.
+This is useful to further specify the utility.
+
+```js
+config.registerUtility({
+ name: 'email',
+ type: 'validator',
+ dependencies: { fieldType: 'email' },
+ method: () => 'this is an email validator utility with dependencies for email',
+});
+```
+
+
+## Retrieve a specific utility
+
+You can retrieve one specific utility using `config.getUtility`, given the `name` and `type`.
+
+```js
+config.getUtility({ name: 'url', type: 'validator' }).method()
+```
+
+You can do the same using a `dependencies` object.
+
+```js
+config.getUtility({
+ name: 'email',
+ dependencies: { fieldType: 'string' },
+ type: 'validator',
+}).method()
+```
+
+
+### Retrieve groups of utilities
+
+You can retrieve all utilities registered under the same `type`.
+
+```js
+config.getUtilities({ type: 'validator' })
+```
+
+You can do the same using a `dependencies` object.
+
+```js
+config.getUtilities({
+ type: 'validator',
+ dependencies: { fieldType: 'string' },
+}).length
+```
+
+This is useful when building pluggable systems, so you can query all the utilities present in the registry.
+For example, retrieve all validator utilities with the `fieldType` of `string`.
diff --git a/packages/registry/docs/how-to-guides/shadow-a-component.md b/packages/registry/docs/how-to-guides/shadow-a-component.md
new file mode 100644
index 0000000000..90179a1f04
--- /dev/null
+++ b/packages/registry/docs/how-to-guides/shadow-a-component.md
@@ -0,0 +1,61 @@
+---
+myst:
+ html_meta:
+ "description": "How to shadow a component or module in @plone/registry"
+ "property=og:description": "How to shadow a component or module in @plone/registry"
+ "property=og:title": "Shadow a component or module"
+ "keywords": "@plone/registry, registry, shadow, component"
+---
+
+# Shadow a component or module
+
+Component or module shadowing is a technique that allows you to define an alternative module for a specific module.
+You normally would want to override a module from another add-on.
+This add-on should not be transpiled.
+
+This technique relies on the `resolve.alias` feature of the bundlers, so the module is effectively being replaced by the alternative one supplied.
+You will need to modify some imports in the alternative module to comply with the new placement and convert relative imports to absolute ones.
+
+To override the component, first, you should identify the component you want to shadow in the package.
+Then, replicate the same folder structure that the original component has in the source code and place it inside the `customizations` folder of your add-on.
+
+Start by using the name of the package you want to shadow.
+If the package has a namespace, then use a folder to define it.
+
+To identify a component to shadow, you can use several approaches.
+The primary method uses [React Developer Tools](https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi).
+You can inspect the app and find out the name of the component (the name
+of the tag), then search for it in the source code.
+Alternatively, you can browse the contents of the source package you want to shadow by searching for it inside {file}`node_modules` of your app folder.
+
+
+## Example: customize the `Logo` resource
+
+To replace the `Logo` resource, your folder structure needs to match the folder structure of the package in the `customizations` folder.
+The `Logo` resource is located in the `@plone/slots` package in the {file}`components/Logo/Logo.svg` file.
+├── slots
+
+```text
+node_modules
+└── @plone
+ └── slots
+ └── components
+ └── Logo.svg
+```
+
+The structure inside your `customizations` of the component shadowing the original should be {file}`src/customizations/@plone/slots/components/Logo/Logo.svg`.
+
+```text
+src
+└── customizations
+ └── @plone
+ └── slots
+ └── components
+ └── Logo.svg
+```
+
+```{warning}
+When upgrading add-ons in your project, it's important to review any shadowed components from the updated add-on.
+Changes in the add-on's public API could potentially break your application.
+Ensure that your shadowed components are updated to align with the new specifications of the original module.
+```
diff --git a/packages/registry/docs/index.md b/packages/registry/docs/index.md
new file mode 100644
index 0000000000..ad433cc9b6
--- /dev/null
+++ b/packages/registry/docs/index.md
@@ -0,0 +1,43 @@
+---
+myst:
+ html_meta:
+ "description": "@plone/registry provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps."
+ "property=og:description": "@plone/registry provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps."
+ "property=og:title": "@plone/registry"
+ "keywords": "@plone/registry, registry, add-on, configuration, component, utility, JavaScript, TypeScript, app"
+---
+
+# `@plone/registry`
+
+`@plone/registry` provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps.
+
+As a developer when you build an app, regardless of the framework and technologies used, it's a one-off app.
+That means you have to build something that has very specific requirements, behavior, and look and feel.
+
+Sometimes you need to build something generic that is pluggable and extensible.
+In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this.
+`@plone/registry` helps developers extend their apps in a pluggable way.
+
+
+```{toctree}
+:maxdepth: 1
+:hidden:
+:caption: How-to guides
+how-to-guides/instantiate-registry
+how-to-guides/register-an-add-on
+how-to-guides/access-registry
+how-to-guides/register-and-retrieve-components
+how-to-guides/register-and-retrieve-utilities
+how-to-guides/shadow-a-component
+```
+
+
+```{toctree}
+:maxdepth: 1
+:hidden:
+:caption: Conceptual guides
+conceptual-guides/add-on-registry
+conceptual-guides/configuration-registry
+conceptual-guides/component-registry
+conceptual-guides/utility-registry
+```
diff --git a/packages/registry/docs/robots.txt b/packages/registry/docs/robots.txt
new file mode 100644
index 0000000000..fa4599903a
--- /dev/null
+++ b/packages/registry/docs/robots.txt
@@ -0,0 +1,8 @@
+# Disallow all user agents from the following directories and files
+User-agent: *
+Disallow: /_sources/
+Disallow: /.doctrees/
+Disallow: /*.txt$
+Disallow: /.buildinfo$
+Disallow: /objects.inv$
+Sitemap: https://plone-registry.readthedocs.io/sitemap.xml
diff --git a/packages/registry/package.json b/packages/registry/package.json
index df81edb35b..27a12e4a7f 100644
--- a/packages/registry/package.json
+++ b/packages/registry/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "1.8.0",
+ "version": "2.0.0-alpha.0",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -31,16 +31,36 @@
"publishConfig": {
"access": "public"
},
+ "type": "module",
"source": "src/index.ts",
- "main": "dist/main.js",
- "module": "dist/module.js",
+ "main": "dist/index.cjs",
+ "module": "dist/index.js",
"types": "dist/types.d.ts",
"exports": {
- "./src/*": "./src/*.js",
+ "./src/*": "./src/*.cjs",
+ "./addon-registry": {
+ "require": "./dist/cjs/addon-registry.cjs",
+ "import": "./dist/esm/addon-registry.js",
+ "types": "./dist/esm/addon-registry.d.ts"
+ },
+ "./create-addons-loader": {
+ "require": "./dist/cjs/create-addons-loader.cjs",
+ "import": "./dist/esm/create-addons-loader.js",
+ "types": "./dist/esm/create-addons-loader.d.ts"
+ },
+ "./create-theme-loader": {
+ "require": "./dist/cjs/create-theme-loader.cjs",
+ "import": "./dist/esm/create-theme-loader.js",
+ "types": "./dist/esm/create-theme-loader.d.ts"
+ },
+ "./vite-plugin": {
+ "import": "./vite-plugin.js",
+ "types": "./vite-plugin.d.ts"
+ },
".": {
"types": "./dist/types.d.ts",
- "import": "./dist/module.js",
- "require": "./dist/main.js"
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
}
},
"targets": {
@@ -50,9 +70,12 @@
},
"scripts": {
"watch": "parcel watch",
- "build": "parcel build",
- "build:force": "parcel build --no-cache",
+ "build": "parcel build && pnpm build:node:esm && pnpm build:node:cjs",
+ "build:force": "rm -rf dist && parcel build --no-cache && pnpm build:node:esm && pnpm build:node:cjs",
+ "build:node:esm": "tsc --project tsconfig.node.json || true",
+ "build:node:cjs": "tsc --project tsconfig.node.json --module commonjs --moduleResolution Node --outDir dist/cjs || true && make rename-to-cjs",
"test": "vitest",
+ "test:debug": "vitest --inspect-brk --no-file-parallelism registry",
"dry-release": "release-it --dry-run",
"release": "release-it",
"release-major-alpha": "release-it major --preRelease=alpha",
@@ -71,20 +94,26 @@
"crypto-random-string": "3.2.0",
"debug": "4.3.2",
"dependency-graph": "0.10.0",
- "glob": "7.1.6"
+ "glob": "^10.4.5",
+ "tmp": "0.2.1"
},
"devDependencies": {
"@parcel/packager-ts": "^2.12.0",
"@parcel/transformer-typescript-types": "^2.12.0",
"@plone/types": "workspace:*",
+ "@types/debug": "^4.1.12",
+ "@types/glob": "^8.1.0",
+ "@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "@types/tmp": "^0.2.6",
"parcel": "^2.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"release-it": "16.2.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2",
- "vitest": "^0.34.6"
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/registry/src/addon-registry.js b/packages/registry/src/addon-registry/addon-registry.ts
similarity index 65%
rename from packages/registry/src/addon-registry.js
rename to packages/registry/src/addon-registry/addon-registry.ts
index 26ae57555c..576d439d4d 100644
--- a/packages/registry/src/addon-registry.js
+++ b/packages/registry/src/addon-registry/addon-registry.ts
@@ -1,64 +1,120 @@
/* eslint no-console: 0 */
-const glob = require('glob').sync;
-const path = require('path');
-const fs = require('fs');
-const debug = require('debug')('shadowing');
-const { DepGraph } = require('dependency-graph');
-
-function getPackageBasePath(base) {
+import { globSync as glob } from 'glob';
+import path from 'path';
+import fs from 'fs';
+import _debug from 'debug';
+import { DepGraph } from 'dependency-graph';
+
+const debug = _debug('shadowing');
+
+export type Package = {
+ name: string;
+ version: string;
+ isPublishedPackage: boolean;
+ isRegisteredAddon: boolean;
+ modulePath: string;
+ packageJson: string;
+ basePath?: string;
+ tsConfigPaths?: [string, any] | null;
+ addons: Array;
+ razzleExtender?: string;
+ eslintExtender?: string;
+};
+type VoltoConfigJS = {
+ addons: Array;
+ theme: string;
+};
+type Aliases = Record;
+type AliasesObject = { find: string; replacement: string }[];
+type CoreAddons = { [x: string]: { package: string } };
+type PackageJsonObject = {
+ type: 'module' | 'commonjs';
+ addons: Array;
+ coreAddons: CoreAddons;
+ theme: string;
+ customizationPaths: string[];
+};
+
+type flatAliases = Record;
+
+type AddonRegistryGet = {
+ /** The ordered list of addons */
+ addons: Array;
+ /** The theme name */
+ theme: string;
+ /** The customizations (shadows) aliases */
+ shadowAliases: AliasesObject;
+ /** The add-ons aliases - Only for Volto add-ons for which code lives inside `src` */
+ addonAliases: AliasesObject;
+};
+
+function getPackageBasePath(base: string) {
while (!fs.existsSync(`${base}/package.json`)) {
base = path.join(base, '../');
}
return path.resolve(base);
}
-function fromEntries(pairs) {
- const res = {};
+function fromEntries(pairs: [string, any][]) {
+ const res: { [key: string]: any } = {};
pairs.forEach((p) => {
res[p[0]] = p[1];
});
return res;
}
-function buildDependencyGraph(addons, extractDependency) {
+function flatAliasesToObject(flatAliases: flatAliases): AliasesObject {
+ return Object.entries(flatAliases).map(([key, value]) => ({
+ find: key,
+ replacement: value,
+ }));
+}
+
+function buildDependencyGraph(
+ addons: Array,
+ extractDependency: (name: string) => Array,
+) {
// getAddonsLoaderChain
- const graph = new DepGraph({ circular: true });
+ const graph = new DepGraph({ circular: true });
graph.addNode('@root');
const seen = ['@root'];
- const stack = [['@root', addons]];
+ const stack: Array<[string, Array]> = [['@root', addons]];
while (stack.length > 0) {
- const [pkgName, addons] = stack.shift();
- if (!graph.hasNode(pkgName)) {
- graph.addNode(pkgName, []);
- }
-
- if (!seen.includes(pkgName)) {
- stack.push([pkgName, extractDependency(pkgName)]);
- seen.push(pkgName);
- }
-
- addons.forEach((loaderString) => {
- const [name, extra] = loaderString.split(':');
- if (!graph.hasNode(name)) {
- graph.addNode(name, []);
+ const [pkgName, addons] = stack.shift() || [];
+ if (pkgName && addons) {
+ if (!graph.hasNode(pkgName)) {
+ graph.addNode(pkgName, []);
}
- const data = graph.getNodeData(name) || [];
- if (extra) {
- extra.split(',').forEach((funcName) => {
- if (!data.includes(funcName)) data.push(funcName);
- });
+ if (!seen.includes(pkgName)) {
+ stack.push([pkgName, extractDependency(pkgName)]);
+ seen.push(pkgName);
}
- graph.setNodeData(name, data);
- graph.addDependency(pkgName, name);
+ addons.forEach((loaderString) => {
+ const [name, extra] = loaderString.split(':');
+ if (!graph.hasNode(name)) {
+ graph.addNode(name, []);
+ }
- if (!seen.includes(name)) {
- stack.push([name, extractDependency(name)]);
- }
- });
+ const data = graph.getNodeData(name) || [];
+ if (extra) {
+ extra.split(',').forEach((funcName) => {
+ // @ts-expect-error TODO: fix this
+ if (!data.includes(funcName)) data.push(funcName);
+ });
+ }
+ graph.setNodeData(name, data);
+
+ graph.addDependency(pkgName, name);
+
+ if (!seen.includes(name)) {
+ stack.push([name, extractDependency(name)]);
+ }
+ });
+ }
}
return graph;
@@ -68,7 +124,7 @@ function buildDependencyGraph(addons, extractDependency) {
* Given an addons loader string, it generates an addons loader string with
* a resolved chain of dependencies
*/
-function getAddonsLoaderChain(graph) {
+function getAddonsLoaderChain(graph: DepGraph) {
return graph.dependenciesOf('@root').map((name) => {
const extras = graph.getNodeData(name) || [].join(',');
return extras.length ? `${name}:${extras}` : name;
@@ -92,26 +148,29 @@ function getAddonsLoaderChain(graph) {
* addons to customize the webpack configuration)
*
*/
-class AddonConfigurationRegistry {
- constructor(projectRootPath) {
- const packageJson = (this.packageJson = require(
- path.join(projectRootPath, 'package.json'),
+class AddonRegistry {
+ public packageJson: PackageJsonObject;
+ public voltoConfigJS: VoltoConfigJS;
+ public projectRootPath: string;
+ public isVoltoProject: boolean;
+ public voltoPath: string;
+ public coreAddons: CoreAddons;
+ public resultantMergedAddons: Array;
+ public addonNames: Array;
+ public packages: Record;
+ public customizations: any;
+ public theme: any;
+ public dependencyGraph: DepGraph;
+
+ constructor(projectRootPath: string) {
+ const packageJson = (this.packageJson = JSON.parse(
+ fs.readFileSync(path.join(projectRootPath, 'package.json'), {
+ encoding: 'utf-8',
+ }),
));
- this.voltoConfigJS = {};
+
// Loads the dynamic config, if any
- if (process.env.VOLTOCONFIG) {
- if (fs.existsSync(path.resolve(process.env.VOLTOCONFIG))) {
- const voltoConfigPath = path.resolve(process.env.VOLTOCONFIG);
- console.log(`Using volto.config.js in: ${voltoConfigPath}`);
- this.voltoConfigJS = require(voltoConfigPath);
- }
- } else if (fs.existsSync(path.join(projectRootPath, 'volto.config.js'))) {
- this.voltoConfigJS = require(
- path.join(projectRootPath, 'volto.config.js'),
- );
- } else {
- this.voltoConfigJS = {};
- }
+ this.voltoConfigJS = this.getRegistryConfig(projectRootPath);
this.projectRootPath = projectRootPath;
this.isVoltoProject = packageJson.name !== '@plone/volto';
@@ -123,15 +182,21 @@ class AddonConfigurationRegistry {
this.coreAddons =
packageJson.name === '@plone/volto'
? packageJson.coreAddons || {}
- : require(`${getPackageBasePath(this.voltoPath)}/package.json`)
- .coreAddons || {};
+ : JSON.parse(
+ fs.readFileSync(
+ `${getPackageBasePath(this.voltoPath)}/package.json`,
+ 'utf-8',
+ ),
+ ).coreAddons || {};
this.resultantMergedAddons = [
...(packageJson.addons || []),
...(this.voltoConfigJS.addons || []),
];
- this.addonNames = this.resultantMergedAddons.map((s) => s.split(':')[0]);
+ this.addonNames = this.resultantMergedAddons.map(
+ (s: string) => s.split(':')[0],
+ );
this.packages = {};
this.customizations = new Map();
@@ -161,22 +226,77 @@ class AddonConfigurationRegistry {
this.initAddonExtenders();
}
+ public get(): AddonRegistryGet {
+ return {
+ addons: this.getAddonDependencies(),
+ theme: this.theme,
+ addonAliases: flatAliasesToObject(this.getResolveAliases()),
+ shadowAliases: flatAliasesToObject(this.getAddonCustomizationPaths()),
+ };
+ }
+
+ public static init(projectRootPath: string) {
+ const registry = new AddonRegistry(projectRootPath);
+ return {
+ registry,
+ addons: registry.getAddonDependencies(),
+ theme: registry.theme,
+ shadowAliases: flatAliasesToObject(registry.getAddonCustomizationPaths()),
+ };
+ }
+
+ isESM = () => this.packageJson.type === 'module';
+
+ getRegistryConfig(projectRootPath: string) {
+ let config: VoltoConfigJS = {
+ addons: [],
+ theme: '',
+ };
+ const CONFIGMAP = {
+ REGISTRYCONFIG: this.isESM()
+ ? 'registry.config.cjs'
+ : 'registry.config.js',
+ VOLTOCONFIG: this.isESM() ? 'volto.config.cjs' : 'volto.config.js',
+ };
+
+ for (const key in CONFIGMAP) {
+ if (process.env[key]) {
+ const resolvedPath = path.resolve(process.env[key]);
+ if (fs.existsSync(resolvedPath)) {
+ const voltoConfigPath = resolvedPath;
+ console.log(`Using configuration file in: ${voltoConfigPath}`);
+ config = require(voltoConfigPath);
+ break;
+ }
+ } else if (fs.existsSync(path.join(projectRootPath, CONFIGMAP[key]))) {
+ config = require(path.join(projectRootPath, CONFIGMAP[key]));
+ break;
+ }
+ }
+
+ return config;
+ }
+
/**
* Gets the `tsconfig.json` `compilerOptions.baseUrl` and `compilerOptions.paths`
* Returns a tuple `[baseUrl, pathsConfig]`
*
*/
- getTSConfigPaths(rootPath = this.projectRootPath) {
- let configFile;
+ getTSConfigPaths(
+ rootPath = this.projectRootPath,
+ ): [string, Record | undefined] {
+ let configFile: string | undefined;
if (fs.existsSync(`${rootPath}/tsconfig.json`))
configFile = `${rootPath}/tsconfig.json`;
else if (fs.existsSync(`${rootPath}/jsconfig.json`))
configFile = `${rootPath}/jsconfig.json`;
- let pathsConfig;
- let baseUrl;
+ let pathsConfig: Record | undefined;
+ let baseUrl: string = '';
if (configFile) {
- const jsConfig = require(configFile).compilerOptions;
+ const jsConfig = JSON.parse(
+ fs.readFileSync(configFile, 'utf-8'),
+ ).compilerOptions;
pathsConfig = jsConfig.paths;
baseUrl = jsConfig.baseUrl;
}
@@ -209,28 +329,31 @@ class AddonConfigurationRegistry {
* Given an add-on name, it registers it as a development package
*
*/
- initDevelopmentPackage(name) {
+ initDevelopmentPackage(name: string) {
const [baseUrl, pathsConfig] = this.getTSConfigPaths();
- const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name][0]}`;
- const packageJsonPath = `${getPackageBasePath(packagePath)}/package.json`;
- const innerAddons = require(packageJsonPath).addons || [];
- const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]);
- if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) {
- innerAddonsNormalized.forEach((name) => {
- if (!this.addonNames.includes(name)) this.addonNames.push(name);
- });
- }
- const pkg = {
- modulePath: packagePath,
- packageJson: packageJsonPath,
- version: require(packageJsonPath).version,
- isPublishedPackage: false,
- isRegisteredAddon: this.addonNames.includes(name),
- name,
- addons: require(packageJsonPath).addons || [],
- };
+ if (pathsConfig && pathsConfig.hasOwnProperty(name)) {
+ const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name][0]}`;
+ const packageJsonPath = `${getPackageBasePath(packagePath)}/package.json`;
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
+ const innerAddons: Array = packageJson.addons || [];
+ const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]);
+ if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) {
+ innerAddonsNormalized.forEach((name) => {
+ if (!this.addonNames.includes(name)) this.addonNames.push(name);
+ });
+ }
+ const pkg = {
+ modulePath: packagePath,
+ packageJson: packageJsonPath,
+ version: packageJson.version,
+ isPublishedPackage: false,
+ isRegisteredAddon: this.addonNames.includes(name),
+ name,
+ addons: packageJson.addons || [],
+ };
- this.packages[name] = Object.assign(this.packages[name] || {}, pkg);
+ this.packages[name] = Object.assign(this.packages[name] || {}, pkg);
+ }
}
/**
@@ -243,11 +366,11 @@ class AddonConfigurationRegistry {
this.addonNames.forEach(this.initPublishedPackage.bind(this));
}
- initPublishedPackage(name) {
+ initPublishedPackage(name: string) {
// I am in the paths list, if so, register it as a development package
// instead than a published one
const [, pathsConfig] = this.getTSConfigPaths();
- if (pathsConfig.hasOwnProperty(name)) {
+ if (pathsConfig && pathsConfig.hasOwnProperty(name)) {
return this.initDevelopmentPackage(name);
}
@@ -255,10 +378,10 @@ class AddonConfigurationRegistry {
const resolved = require.resolve(name, { paths: [this.projectRootPath] });
const basePath = getPackageBasePath(resolved);
const packageJson = path.join(basePath, 'package.json');
- const pkg = require(packageJson);
+ const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8'));
const main = pkg.main || 'src/index.js';
const modulePath = path.dirname(require.resolve(`${basePath}/${main}`));
- const innerAddons = pkg.addons || [];
+ const innerAddons: Array = pkg.addons || [];
const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]);
if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) {
innerAddonsNormalized.forEach((name) => {
@@ -290,7 +413,7 @@ class AddonConfigurationRegistry {
}
// An add-on from the ADDONS env var can only be a published one
- initAddonFromEnvVar(name) {
+ initAddonFromEnvVar(name: string) {
const normalizedAddonName = name.split(':')[0];
this.initPublishedPackage(normalizedAddonName);
}
@@ -341,7 +464,10 @@ class AddonConfigurationRegistry {
}
getCustomThemeAddons() {
- const customThemeAddonsInfo = {
+ const customThemeAddonsInfo: {
+ variables: string[];
+ main: string[];
+ } = {
variables: [],
main: [],
};
@@ -372,11 +498,11 @@ class AddonConfigurationRegistry {
/**
* Returns a list of aliases given the defined paths in `tsconfig.json`
*/
- getAliasesFromTSConfig(basePath, tsConfig) {
+ getAliasesFromTSConfig(basePath: string, tsConfig: [string, any]) {
const [baseUrl, options] = tsConfig;
const fullPathsPath = baseUrl ? `${basePath}/${baseUrl}` : basePath;
- let aliases = {};
+ const aliases: { [x: string]: string } = {};
Object.keys(options || {}).forEach((item) => {
const name = item.replace(/\/\*$/, '');
// webpack5 allows arrays here, fix later
@@ -397,12 +523,10 @@ class AddonConfigurationRegistry {
* defined in the `tsconfig.json` files of the add-ons.
*/
getResolveAliases() {
- const pairs = [
- ...Object.keys(this.packages).map((o) => [
- o,
- this.packages[o].modulePath,
- ]),
- ];
+ const pairs: [string, string][] = Object.keys(this.packages).map((o) => [
+ o,
+ this.packages[o].modulePath,
+ ]);
let aliasesFromTSPaths = {};
Object.keys(this.packages).forEach((o) => {
@@ -410,7 +534,7 @@ class AddonConfigurationRegistry {
aliasesFromTSPaths = {
...aliasesFromTSPaths,
...this.getAliasesFromTSConfig(
- this.packages[o].basePath,
+ this.packages[o].basePath || '',
this.packages[o].tsConfigPaths,
),
};
@@ -435,21 +559,25 @@ class AddonConfigurationRegistry {
* convention, we use a folder called "volto" inside customizations folder
* and separate folder for each addon, identified by its addon (package) name.
*/
- getCustomizationPaths(packageJson, rootPath) {
- const aliases = {};
+ getCustomizationPaths(packageJson: PackageJsonObject, rootPath: string) {
+ const aliases: Aliases = {};
let { customizationPaths } = packageJson;
if (!customizationPaths) {
- customizationPaths = ['src/customizations'];
+ customizationPaths = ['src/customizations', 'customizations'];
}
customizationPaths.forEach((customizationPath) => {
customizationPath = customizationPath.endsWith('/')
? customizationPath.slice(0, customizationPath.length - 1)
: customizationPath;
const base = path.join(rootPath, customizationPath);
- const reg = [];
-
- // All registered addon packages (in tsconfig.json/jsconfig.json and package.json:addons)
- // can be customized
+ const reg: Array<{
+ customPath: string;
+ sourcePath: string;
+ name: string;
+ }> = [];
+
+ // All registered addon packages (in tsconfig.json/jsconfig.json and
+ // package.json:addons) can be customized by other addons
Object.values(this.packages).forEach((addon) => {
const { name, modulePath } = addon;
if (fs.existsSync(path.join(base, name))) {
@@ -491,12 +619,14 @@ class AddonConfigurationRegistry {
glob(
`${customPath}/**/*.*(svg|png|jpg|jpeg|gif|ico|less|js|jsx|ts|tsx)`,
).map((filename) => {
- function changeFileExtension(filePath) {
+ function changeFileExtension(filePath: string) {
// Extract the current file extension
- const currentExtension = filePath.split('.').pop();
+ const currentExtension = filePath.split('.').pop() || '';
// Define the mapping between file extensions
- const extensionMapping = {
+ const extensionMapping: {
+ [key: string]: string;
+ } = {
jsx: 'tsx',
tsx: 'jsx',
js: 'ts',
@@ -523,7 +653,7 @@ class AddonConfigurationRegistry {
*
* @param {*} filePath
*/
- function simpleSwapFileExtension(filePath) {
+ function simpleSwapFileExtension(filePath: string) {
// Extract the current file extension
const currentExtension = filePath.split('.').pop();
return filePath.replace(`.${currentExtension}`, '.jsx');
@@ -569,12 +699,12 @@ class AddonConfigurationRegistry {
* `addons:volto-addonA,volto-addonB`
*/
getAddonCustomizationPaths() {
- let aliases = {};
+ let aliases: Aliases = {};
this.getAddons().forEach((addon) => {
aliases = {
...aliases,
...this.getCustomizationPaths(
- require(addon.packageJson),
+ JSON.parse(fs.readFileSync(addon.packageJson, 'utf-8')),
getPackageBasePath(addon.modulePath),
),
};
@@ -586,9 +716,13 @@ class AddonConfigurationRegistry {
* Allow packages from addons set in env vars to customize Volto and other addons.
*
* Same as the above one, but specific for Volto addons coming from env vars
+ *
+ * This is no longer necessary in the pnpm setup, as all valid packages have to be
+ * released or declared as a workspace
+ *
*/
getAddonsFromEnvVarCustomizationPaths() {
- let aliases = {};
+ let aliases: Aliases = {};
if (process.env.ADDONS) {
process.env.ADDONS.split(';').forEach((addon) => {
const normalizedAddonName = addon.split(':')[0];
@@ -598,7 +732,10 @@ class AddonConfigurationRegistry {
const packageJson = path.join(basePath, 'package.json');
aliases = {
...aliases,
- ...this.getCustomizationPaths(require(packageJson), basePath),
+ ...this.getCustomizationPaths(
+ JSON.parse(fs.readFileSync(packageJson, 'utf-8')),
+ basePath,
+ ),
};
}
});
@@ -626,11 +763,11 @@ class AddonConfigurationRegistry {
"@root" [color = red fillcolor=yellow style=filled]
`;
- let queue = ['@root'];
- let name;
+ const queue = ['@root'];
+ let name: string;
while (queue.length > 0) {
- name = queue.pop();
+ name = queue.pop() || '';
const deps = this.dependencyGraph.directDependenciesOf(name);
for (let i = 0; i < deps.length; i++) {
@@ -648,6 +785,4 @@ class AddonConfigurationRegistry {
}
}
-module.exports = AddonConfigurationRegistry;
-module.exports.getAddonsLoaderChain = getAddonsLoaderChain;
-module.exports.buildDependencyGraph = buildDependencyGraph;
+export { AddonRegistry, getAddonsLoaderChain, buildDependencyGraph };
diff --git a/packages/registry/src/create-addons-loader.js b/packages/registry/src/addon-registry/create-addons-loader.ts
similarity index 66%
rename from packages/registry/src/create-addons-loader.js
rename to packages/registry/src/addon-registry/create-addons-loader.ts
index 7eb5f55757..f03349693f 100644
--- a/packages/registry/src/create-addons-loader.js
+++ b/packages/registry/src/addon-registry/create-addons-loader.ts
@@ -1,13 +1,16 @@
-const fs = require('fs');
-const tmp = require('tmp');
-const cryptoRandomString = require('crypto-random-string');
+import fs from 'fs';
+import path from 'path';
+import tmp from 'tmp';
+import cryptoRandomString from 'crypto-random-string';
+import type { Package } from './addon-registry';
-const titleCase = (w) => w.slice(0, 1).toUpperCase() + w.slice(1, w.length);
+const titleCase = (w: string) =>
+ w.slice(0, 1).toUpperCase() + w.slice(1, w.length);
/*
* Transforms a package name to javascript variable name
*/
-function nameFromPackage(name) {
+function nameFromPackage(name: string) {
name =
name.replace(/[@~./\\:\s]/gi, '') ||
cryptoRandomString({ length: 10, characters: 'abcdefghijk' });
@@ -21,19 +24,26 @@ function nameFromPackage(name) {
* Creates a static file with code necessary to load the addons configuration
*
*/
-function getAddonsLoaderCode(addons = [], addonsInfo) {
+function getAddonsLoaderCode(
+ addons: string[],
+ addonsInfo: Package[],
+ loadProjectConfig?: boolean,
+) {
let buf = `/*
This file is autogenerated. Don't change it directly.
Instead, change the "addons" setting in your package.json file.
*/
-const projectConfigLoader = require('@root/config');
`;
- let configsToLoad = [],
+ if (loadProjectConfig) {
+ buf += `const projectConfigLoader = require('@root/config');\n`;
+ }
+
+ let configsToLoad: string[] = [],
counter = 0;
addons.forEach((addonConfigString) => {
- let extras = [];
+ let extras: string[] | string[][] = []; // TODO: Improve this typing
const addonConfigLoadInfo = addonConfigString.split(':');
const pkgName = addonConfigLoadInfo[0];
const defaultImport = nameFromPackage(pkgName);
@@ -41,7 +51,6 @@ const projectConfigLoader = require('@root/config');
extras = addonConfigLoadInfo[1].split(',');
}
extras = extras.map((name) => [name, `${name}${counter++}`]);
-
const line = `import ${defaultImport}${
extras.length
? `, { ${extras
@@ -77,8 +86,9 @@ const safeWrapper = (func) => (config) => {
return res;
}
+${loadProjectConfig ? '' : 'const projectConfigLoader = false;'}
const projectConfig = (config) => {
- return typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
}
const load = (config) => {
@@ -96,21 +106,27 @@ export default load;
return buf;
}
-module.exports = (addons, addonsInfo, { tempInProject } = {}) => {
+export function createAddonsLoader(
+ addons: string[],
+ addonsInfo: Package[],
+ {
+ tempInProject,
+ loadProjectConfig = false,
+ }: { tempInProject?: boolean; loadProjectConfig?: boolean } = {},
+) {
// Some frameworks do not allow to load code from outside the project.
// the `tempInProject` allows to place it inside
- let addonsLoaderPath;
+ let addonsLoaderPath: string;
if (tempInProject) {
- const path = require('path');
addonsLoaderPath = path.join(process.cwd(), 'src', '.addons-loader.js');
} else {
addonsLoaderPath = tmp.tmpNameSync({ postfix: '.js' });
}
- const code = getAddonsLoaderCode(addons, addonsInfo);
+ const code = getAddonsLoaderCode(addons, addonsInfo, loadProjectConfig);
+ // @ts-expect-error No clue why it's complaining
fs.writeFileSync(addonsLoaderPath, Buffer.from(code));
return addonsLoaderPath;
-};
+}
-module.exports.getAddonsLoaderCode = getAddonsLoaderCode;
-module.exports.nameFromPackage = nameFromPackage;
+export { getAddonsLoaderCode, nameFromPackage };
diff --git a/packages/registry/src/create-theme-addons-loader.js b/packages/registry/src/addon-registry/create-theme-loader.ts
similarity index 50%
rename from packages/registry/src/create-theme-addons-loader.js
rename to packages/registry/src/addon-registry/create-theme-loader.ts
index 6b46257744..d5f4dbbc24 100644
--- a/packages/registry/src/create-theme-addons-loader.js
+++ b/packages/registry/src/addon-registry/create-theme-loader.ts
@@ -1,13 +1,14 @@
-const fs = require('fs');
-const tmp = require('tmp');
-const cryptoRandomString = require('crypto-random-string');
+import fs from 'fs';
+import tmp from 'tmp';
+import cryptoRandomString from 'crypto-random-string';
-const titleCase = (w) => w.slice(0, 1).toUpperCase() + w.slice(1, w.length);
+const titleCase = (w: string) =>
+ w.slice(0, 1).toUpperCase() + w.slice(1, w.length);
/*
* Transforms a package name to javascript variable name
*/
-function nameFromPackage(name) {
+function nameFromPackage(name: string) {
name =
name.replace(/[@~./\\:\s]/gi, '') ||
cryptoRandomString({ length: 10, characters: 'abcdefghijk' });
@@ -21,7 +22,7 @@ function nameFromPackage(name) {
* Creates a static file with code necessary to load the addons configuration
*
*/
-function getAddonsLoaderCode(name, customThemeAddons = []) {
+function getThemeLoaderCode(name, customThemeAddons = []) {
let buf = `/*
This file is autogenerated. Don't change it directly.
Add a ./theme/_${name}.scss in your add-on to load your theme customizations in the current theme.
@@ -37,42 +38,27 @@ Add a ./theme/_${name}.scss in your add-on to load your theme customizations in
return buf;
}
-module.exports = ({ main, variables }) => {
- // const addonsThemeLoaderVariablesPath = path.join(
- // process.cwd(),
- // 'src',
- // '_variables.scss',
- // );
- // const addonsThemeLoaderMainPath = path.join(
- // process.cwd(),
- // 'src',
- // '_main.scss',
- // );
-
- // const addonsThemeLoaderVariablesPath = path.join(
- // process.cwd(),
- // 'src',
- // '_variables.scss',
- // );
- // const addonsThemeLoaderMainPath = path.join(
- // process.cwd(),
- // 'src',
- // '_main.scss',
- // );
-
+export function createThemeAddonsLoader({
+ main,
+ variables,
+}: {
+ main: string[];
+ variables: string[];
+}) {
const addonsThemeLoaderVariablesPath = tmp.tmpNameSync({ postfix: '.scss' });
const addonsThemeLoaderMainPath = tmp.tmpNameSync({ postfix: '.scss' });
fs.writeFileSync(
addonsThemeLoaderVariablesPath,
- new Buffer.from(getAddonsLoaderCode('variables', variables)),
+ //@ts-expect-error No clue why this is erroring
+ new Buffer.from(getThemeLoaderCode('variables', variables)),
);
fs.writeFileSync(
addonsThemeLoaderMainPath,
- new Buffer.from(getAddonsLoaderCode('main', main)),
+ //@ts-expect-error No clue why this is erroring
+ new Buffer.from(getThemeLoaderCode('main', main)),
);
return [addonsThemeLoaderVariablesPath, addonsThemeLoaderMainPath];
-};
+}
-module.exports.getAddonsLoaderCode = getAddonsLoaderCode;
-module.exports.nameFromPackage = nameFromPackage;
+export { getThemeLoaderCode, nameFromPackage };
diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts
index 830202f797..5394254281 100644
--- a/packages/registry/src/index.ts
+++ b/packages/registry/src/index.ts
@@ -16,16 +16,16 @@ import type {
} from '@plone/types';
export type ConfigData = {
- settings: SettingsConfig;
- blocks: BlocksConfig;
- views: ViewsConfig;
- widgets: WidgetsConfig;
- addonReducers: AddonReducersConfig;
- addonRoutes: AddonRoutesConfig;
- slots: SlotsConfig;
- components: ComponentsConfig;
- utilities: UtilitiesConfig;
- experimental: ExperimentalConfig;
+ settings: SettingsConfig | Record;
+ blocks: BlocksConfig | Record;
+ views: ViewsConfig | Record;
+ widgets: WidgetsConfig | Record;
+ addonReducers?: AddonReducersConfig;
+ addonRoutes?: AddonRoutesConfig;
+ slots: SlotsConfig | Record;
+ components: ComponentsConfig | Record;
+ utilities: UtilitiesConfig | Record;
+ experimental?: ExperimentalConfig;
};
type GetComponentResult = {
@@ -44,7 +44,15 @@ class Config {
constructor() {
if (!Config.instance) {
- this._data = {};
+ this._data = {
+ settings: {},
+ blocks: {},
+ views: {},
+ widgets: {},
+ slots: {},
+ components: {},
+ utilities: {},
+ };
Config.instance = this;
}
@@ -205,7 +213,10 @@ class Config {
return;
}
const { slots, data } = this._data.slots[name];
- const slotComponents = [];
+ const slotComponents: {
+ component: SlotComponent['component'];
+ name: string;
+ }[] = [];
// For all enabled slots
for (const slotName of slots) {
// For all registered components for that slot, inversed, since the last one registered wins
@@ -474,7 +485,7 @@ class Config {
.join('+');
const utilityName = `${depsString ? `|${depsString}` : ''}`;
- const utilitiesKeys = Object.keys(this._data.utilities[type]).filter(
+ const utilitiesKeys = Object.keys(this._data.utilities[type] || {}).filter(
(key) => key.startsWith(utilityName),
);
const utilities = utilitiesKeys.map(
diff --git a/packages/registry/src/registry.test.tsx b/packages/registry/src/registry.test.tsx
index ab189a9368..90ad418249 100644
--- a/packages/registry/src/registry.test.tsx
+++ b/packages/registry/src/registry.test.tsx
@@ -1063,4 +1063,13 @@ describe('Utilities registry', () => {
.method(),
).toEqual('this is a validator for maxLength');
});
+
+ it('getUtilities - registers two utilities with the same dependencies and different names', () => {
+ expect(
+ config.getUtilities({
+ dependencies: { fieldType: 'string' },
+ type: 'validator',
+ }),
+ ).toEqual([]);
+ });
});
diff --git a/packages/registry/tsconfig.json b/packages/registry/tsconfig.json
index c25bedd978..1319106020 100644
--- a/packages/registry/tsconfig.json
+++ b/packages/registry/tsconfig.json
@@ -17,7 +17,7 @@
"jsx": "react-jsx",
"paths": {}
},
- "include": ["src", "src/**/*.js"],
+ "include": ["src/index.ts", "src/**/*.js", "src/**/*.cjs"],
"exclude": [
"node_modules",
"build",
diff --git a/packages/registry/tsconfig.node.json b/packages/registry/tsconfig.node.json
new file mode 100644
index 0000000000..a25380a557
--- /dev/null
+++ b/packages/registry/tsconfig.node.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ /* Base Options: */
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "resolveJsonModule": true,
+ "moduleDetection": "force",
+ "isolatedModules": true,
+ /* Strictness */
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ /* If transpiling with TypeScript: */
+ "moduleResolution": "NodeNext",
+ "module": "NodeNext",
+ "outDir": "dist/esm",
+ "sourceMap": true,
+ /* If your code doesn't run in the DOM: */
+ "lib": ["es2022"],
+ "declaration": true
+ },
+ "include": ["src/addon-registry"]
+}
diff --git a/packages/registry/vite-plugin.d.ts b/packages/registry/vite-plugin.d.ts
new file mode 100644
index 0000000000..2af076c87a
--- /dev/null
+++ b/packages/registry/vite-plugin.d.ts
@@ -0,0 +1,5 @@
+import * as vite from 'vite';
+
+declare function PloneRegistryVitePlugin(): vite.Plugin;
+
+export { PloneRegistryVitePlugin };
diff --git a/packages/registry/vite-plugin.js b/packages/registry/vite-plugin.js
new file mode 100644
index 0000000000..6ec97dbe64
--- /dev/null
+++ b/packages/registry/vite-plugin.js
@@ -0,0 +1,60 @@
+import path from 'path';
+import { AddonRegistry } from '@plone/registry/addon-registry';
+import { createAddonsLoader } from '@plone/registry/create-addons-loader';
+import { createThemeAddonsLoader } from '@plone/registry/create-theme-loader';
+
+export const PloneRegistryVitePlugin = () => {
+ const projectRootPath = path.resolve('.');
+ const { registry, shadowAliases } = AddonRegistry.init(projectRootPath);
+
+ const addonsLoaderPath = createAddonsLoader(
+ registry.getAddonDependencies(),
+ registry.getAddons(),
+ );
+
+ const [addonsThemeLoaderVariablesPath, addonsThemeLoaderMainPath] =
+ createThemeAddonsLoader(registry.getCustomThemeAddons());
+
+ const addOns = Object.keys(registry.packages);
+
+ return {
+ name: 'plone-registry',
+ enforce: 'pre',
+ config: () => ({
+ ssr: {
+ optimizeDeps: {
+ exclude: addOns,
+ },
+ },
+ esbuild: {
+ supported: {
+ 'top-level-await': true, //browsers can handle top-level-await features
+ },
+ },
+ optimizeDeps: {
+ exclude: addOns,
+ },
+ resolve: {
+ alias: [
+ ...shadowAliases,
+ // Remove in case that we have addons aliases (Volto add-ons which need the `src` path hack)
+ // ...addonAliases,
+ ...(registry.theme
+ ? // Load the theme aliases from the theme config
+ [
+ {
+ find: 'addonsThemeCustomizationsVariables',
+ replacement: addonsThemeLoaderVariablesPath,
+ },
+ {
+ find: 'addonsThemeCustomizationsMain',
+ replacement: addonsThemeLoaderMainPath,
+ },
+ ]
+ : []),
+ { find: 'load-plone-registry-addons', replacement: addonsLoaderPath },
+ ],
+ },
+ }),
+ };
+};
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index 34cce11565..f1c4f0278b 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -8,6 +8,16 @@
+## 3.7.0 (2024-10-27)
+
+### Feature
+
+- Support for the new `@plone/registry` ESM format. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+
+### Documentation
+
+- Added the configuration for VSCode not to reformat Markdown and MyST files. @aadityaforwork [#6354](https://github.com/plone/volto/issues/6354)
+
## 3.6.2 (2024-06-06)
### Bugfix
diff --git a/packages/scripts/i18n.cjs b/packages/scripts/i18n.cjs
index bd4a1a1f87..439c860e67 100755
--- a/packages/scripts/i18n.cjs
+++ b/packages/scripts/i18n.cjs
@@ -276,10 +276,25 @@ function main({ addonMode }) {
console.log('Synchronizing messages to po files...');
syncPoByPot();
if (!addonMode) {
- let AddonConfigurationRegistry;
+ let AddonRegistry, AddonConfigurationRegistry, registry;
try {
// Detect where is the registry (if we are in Volto 18 or above for either core and projects)
if (
+ fs.existsSync(
+ path.join(
+ projectRootPath,
+ '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs',
+ ),
+ )
+ ) {
+ AddonRegistry = require(
+ path.join(
+ projectRootPath,
+ '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs',
+ ),
+ ).AddonRegistry;
+ // Detect where is the registry (if we are in Volto 18-alpha.46 or below)
+ } else if (
fs.existsSync(
path.join(
projectRootPath,
@@ -324,7 +339,11 @@ function main({ addonMode }) {
process.exit();
}
console.log('Generating the language JSON files...');
- const registry = new AddonConfigurationRegistry(projectRootPath);
+ if (AddonConfigurationRegistry) {
+ registry = new AddonConfigurationRegistry(projectRootPath);
+ } else if (AddonRegistry) {
+ registry = AddonRegistry.init(projectRootPath).registry;
+ }
poToJson({ registry, addonMode });
}
console.log('done!');
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 66d18276c1..412e88fcee 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -9,7 +9,7 @@
}
],
"license": "MIT",
- "version": "3.6.2",
+ "version": "3.7.0",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
diff --git a/packages/scripts/vscodesettings.js b/packages/scripts/vscodesettings.js
index ebcc8cec08..2df03c41b8 100644
--- a/packages/scripts/vscodesettings.js
+++ b/packages/scripts/vscodesettings.js
@@ -13,9 +13,17 @@ if (fs.existsSync('.vscode')) {
if (!vscodeSettingsJSON['eslint.workingDirectories']) {
vscodeSettingsJSON['eslint.workingDirectories'] = [{ mode: 'auto' }];
+}
- fs.writeFileSync(
- '.vscode/settings.json',
- `${stringify(vscodeSettingsJSON, null, 2)}`,
- );
+if (!vscodeSettingsJSON['[markdown]']) {
+ vscodeSettingsJSON['[markdown]'] = {
+ 'editor.formatOnSave': false,
+ };
+} else {
+ vscodeSettingsJSON['[markdown]']['editor.formatOnSave'] = false;
}
+
+fs.writeFileSync(
+ '.vscode/settings.json',
+ `${stringify(vscodeSettingsJSON, null, 2)}`,
+);
diff --git a/packages/slots/README.md b/packages/slots/README.md
index c53c030b59..c77e2bb2de 100644
--- a/packages/slots/README.md
+++ b/packages/slots/README.md
@@ -1,3 +1,8 @@
# `@plone/blocks`
This package contains the default blocks provided by Plone.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/packages/slots/package.json b/packages/slots/package.json
index 96c2d4b13b..2daece72e1 100644
--- a/packages/slots/package.json
+++ b/packages/slots/package.json
@@ -65,7 +65,7 @@
"parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.2.2",
- "vitest": "^1.3.1"
+ "typescript": "^5.6.3",
+ "vitest": "^2.1.3"
}
}
diff --git a/packages/tsconfig/README.md b/packages/tsconfig/README.md
index c94118473f..840f722880 100644
--- a/packages/tsconfig/README.md
+++ b/packages/tsconfig/README.md
@@ -27,3 +27,8 @@ In `package.json`:
]
}
```
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md
index 2b31ad63ca..8be199b528 100644
--- a/packages/types/CHANGELOG.md
+++ b/packages/types/CHANGELOG.md
@@ -8,6 +8,22 @@
+## 1.0.0-alpha.21 (2024-10-18)
+
+### Bugfix
+
+- Some improvements and fixes in blocks and settings types. @sneridagh [#6412](https://github.com/plone/volto/issues/6412)
+
+### Internal
+
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+
+## 1.0.0-alpha.20 (2024-10-08)
+
+### Bugfix
+
+- Fixed types for image fields and BlocksFormData @pnicolli @deodorhunter [#6382](https://github.com/plone/volto/issues/6382)
+
## 1.0.0-alpha.19 (2024-07-30)
### Feature
diff --git a/packages/types/news/6445.feature b/packages/types/news/6445.feature
new file mode 100644
index 0000000000..818f3ad7b7
--- /dev/null
+++ b/packages/types/news/6445.feature
@@ -0,0 +1 @@
+`StyleDefinitions` in types. @sneridagh
diff --git a/packages/types/package.json b/packages/types/package.json
index b4e06bc361..46471747ab 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "1.0.0-alpha.19",
+ "version": "1.0.0-alpha.21",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -56,6 +56,6 @@
"react-intl": "3.12.1",
"release-it": "^17.1.1",
"tsconfig": "workspace:*",
- "typescript": "5.4.2"
+ "typescript": "^5.6.3"
}
}
diff --git a/packages/types/src/blocks/index.d.ts b/packages/types/src/blocks/index.d.ts
index 2c0a2e65de..149af1143e 100644
--- a/packages/types/src/blocks/index.d.ts
+++ b/packages/types/src/blocks/index.d.ts
@@ -10,6 +10,7 @@ import type { Location, History } from 'history';
export interface BlocksFormData {
'@type': AvailableBlocks;
variation?: string;
+ [x: string]: unknown;
}
export interface BlockViewProps {
@@ -119,3 +120,15 @@ export interface BlockEditProps {
errors: Record>;
blocksErrors: Record>>;
}
+
+export type StyleDefinition =
+ | {
+ name: string;
+ label: string;
+ style: Record<`--${string}`, string>;
+ }
+ | {
+ name: string;
+ label: string;
+ style: undefined;
+ };
diff --git a/packages/types/src/config/Blocks.d.ts b/packages/types/src/config/Blocks.d.ts
index cec3e97491..70bf61c5cf 100644
--- a/packages/types/src/config/Blocks.d.ts
+++ b/packages/types/src/config/Blocks.d.ts
@@ -1,6 +1,8 @@
import type { Content } from '../content';
import type { BlockViewProps, BlockEditProps } from '../blocks';
import type { IntlShape } from 'react-intl';
+import { User } from '../services';
+import { StyleDefinition } from '../blocks';
export interface BlocksConfig {
blocksConfig: BlocksConfigData;
@@ -8,6 +10,8 @@ export interface BlocksConfig {
requiredBlocks: string[];
initialBlocks: Record | Record;
initialBlocksFocus: Record;
+ themes: StyleDefinition[];
+ widths: StyleDefinition[];
}
export interface BlocksConfigData {
@@ -79,6 +83,7 @@ export interface BlockConfigBase {
block: BlockConfigBase; // TODO: This has to be extendable
navRoot: Content;
contentType: string;
+ user: User;
}) => boolean)
| boolean;
@@ -124,6 +129,7 @@ export interface BlockExtension {
title: string;
template?: React.ComponentType;
render?: React.ComponentType;
+ view?: React.ComponentType;
fullobjects?: boolean;
}
diff --git a/packages/types/src/config/Settings.d.ts b/packages/types/src/config/Settings.d.ts
index 68df1ab205..68419e2db2 100644
--- a/packages/types/src/config/Settings.d.ts
+++ b/packages/types/src/config/Settings.d.ts
@@ -1,5 +1,5 @@
import { Content } from '../content';
-import { BlocksConfigData } from './Blocks';
+import { BlocksFormData } from '../blocks/index';
type apiExpandersType =
| { match: string; GET_CONTENT: string[] }
@@ -19,7 +19,7 @@ type styleClassNameExtendersType = ({
}: {
block: string;
content: Content;
- data: BlocksConfigData;
+ data: BlocksFormData;
classNames: string[];
}) => string[];
diff --git a/packages/types/src/content/common.d.ts b/packages/types/src/content/common.d.ts
index be73df373a..1d7f4118af 100644
--- a/packages/types/src/content/common.d.ts
+++ b/packages/types/src/content/common.d.ts
@@ -23,8 +23,8 @@ export interface ContainedItem {
'@id': string;
'@type': string;
description: string;
- image_field: null;
- image_scales: null;
+ image_field: string;
+ image_scales: Record | null;
review_state: string;
title: string;
}
diff --git a/packages/types/src/content/index.d.ts b/packages/types/src/content/index.d.ts
index 5595751536..4a9083d0d4 100644
--- a/packages/types/src/content/index.d.ts
+++ b/packages/types/src/content/index.d.ts
@@ -1,4 +1,5 @@
import type { Expanders, ContainedItem, Image, RelatedItem } from './common';
+import type { BlocksFormData } from '../blocks';
export interface Content {
'@components': Expanders;
@@ -12,10 +13,7 @@ export interface Content {
token: boolean;
};
blocks: {
- [k in string]: {
- '@id': string;
- '@type': string;
- } & Record;
+ [k in string]: BlocksFormData;
};
blocks_layout: {
items: string[];
diff --git a/packages/volto-slate/CHANGELOG.md b/packages/volto-slate/CHANGELOG.md
index 554619a18d..e8c7953223 100644
--- a/packages/volto-slate/CHANGELOG.md
+++ b/packages/volto-slate/CHANGELOG.md
@@ -8,6 +8,22 @@
+## 18.0.0-alpha.19 (2024-10-03)
+
+### Feature
+
+- Update Brazilian Portuguese translations. @ericof [#6292](https://github.com/plone/volto/issues/6292)
+
+### Bugfix
+
+- Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
+
+## 18.0.0-alpha.18 (2024-09-13)
+
+### Feature
+
+- Pass `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
+
## 18.0.0-alpha.17 (2024-07-05)
### Feature
diff --git a/packages/volto-slate/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto-slate/locales/pt_BR/LC_MESSAGES/volto.po
new file mode 100644
index 0000000000..867d80478b
--- /dev/null
+++ b/packages/volto-slate/locales/pt_BR/LC_MESSAGES/volto.po
@@ -0,0 +1,205 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Plone\n"
+"POT-Creation-Date: 2022-07-20T12:45:09.681Z\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Érico Andrei \n"
+"Language-Team: Plone i18n \n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"Language-Code: en\n"
+"Language-Name: English\n"
+"Preferred-Encodings: utf-8\n"
+"Domain: volto\n"
+"X-Generator: Poedit 3.5\n"
+
+# defaultMessage: Add link
+#: editor/plugins/Link/index
+msgid "Add link"
+msgstr "Adicionar link"
+
+# defaultMessage: An error has occurred while editing "{name}" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator.
+#: widgets/HtmlSlateWidget
+#, fuzzy
+msgid "An error has occurred while editing \"{name}\" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator."
+msgstr "Ocorreu um erro ao editar o campo \"{name}\". Fomos notificados e estamos analisando o problema. Salve seu trabalho e tente novamente. Se o problema persistir, entre em contato com o administrador do site."
+
+# defaultMessage: An error has occurred while rendering "{name}" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator.
+#: widgets/RichTextWidgetView
+#, fuzzy
+msgid "An error has occurred while rendering \"{name}\" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator."
+msgstr "Ocorreu um erro ao renderizar o campo \"{name}\". Fomos notificados e estamos analisando o problema. Se o problema persistir, entre em contato com o administrador do site."
+
+# defaultMessage: Bottom
+#: blocks/Table/TableBlockEdit
+msgid "Bottom"
+msgstr "Base"
+
+# defaultMessage: Center
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Center"
+msgstr "Centro"
+
+# defaultMessage: Delete col
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Delete col"
+msgstr "Excluir coluna"
+
+# defaultMessage: Delete row
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Delete row"
+msgstr "Excluir linha"
+
+# defaultMessage: Delete table
+#: editor/plugins/Table/index
+#, fuzzy
+msgid "Delete table"
+msgstr "Excluir tabela"
+
+# defaultMessage: Divide each row into separate cells
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Divide each row into separate cells"
+msgstr "Dividir cada linha em células separadas"
+
+# defaultMessage: Edit element
+#: elementEditor/messages
+#, fuzzy
+msgid "Edit element"
+msgstr "Editar elemento"
+
+# defaultMessage: Edit link
+#: editor/plugins/AdvancedLink/index editor/plugins/Link/index
+#, fuzzy
+msgid "Edit link"
+msgstr "Editar link"
+
+# defaultMessage: Fixed width table cells
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Fixed width table cells"
+msgstr "Células de tabela de largura fixa"
+
+# defaultMessage: Hide headers
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Hide headers"
+msgstr "Ocultar cabeçalhos"
+
+# defaultMessage: Insert col after
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Insert col after"
+msgstr "Inserir coluna após"
+
+# defaultMessage: Insert col before
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Insert col before"
+msgstr "Inserir coluna antes"
+
+# defaultMessage: Insert row after
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Insert row after"
+msgstr "Inserir linha após"
+
+# defaultMessage: Insert row before
+#: blocks/Table/TableBlockEdit editor/plugins/Table/index
+#, fuzzy
+msgid "Insert row before"
+msgstr "Inserir linha antes"
+
+# defaultMessage: Left
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Left"
+msgstr "Esquerda"
+
+# defaultMessage: Make the table compact
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Make the table compact"
+msgstr "Compactar a tabela"
+
+# defaultMessage: Make the table sortable
+#: blocks/Table/TableBlockEdit
+msgid "Make the table sortable"
+msgstr "Tornar a tabela ordenável"
+
+# defaultMessage: Middle
+#: blocks/Table/TableBlockEdit
+msgid "Middle"
+msgstr "Meio"
+
+# defaultMessage: No matching blocks
+#: blocks/Text/SlashMenu
+#, fuzzy
+msgid "No matching blocks"
+msgstr "Não há blocos correspondentes"
+
+# defaultMessage: Reduce complexity
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Reduce complexity"
+msgstr "Reduzir complexidade"
+
+# defaultMessage: Remove element
+#: elementEditor/messages
+#, fuzzy
+msgid "Remove element"
+msgstr "Remover elemento"
+
+# defaultMessage: Remove link
+#: editor/plugins/AdvancedLink/index
+#, fuzzy
+msgid "Remove link"
+msgstr "Remover link"
+
+# defaultMessage: Right
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Right"
+msgstr "Direita"
+
+# defaultMessage: Stripe alternate rows with color
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Stripe alternate rows with color"
+msgstr "Alternar cores das linhas"
+
+# defaultMessage: Table
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Table"
+msgstr "Tabela"
+
+# defaultMessage: Table color inverted
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Table color inverted"
+msgstr "Cor da tabela invertida"
+
+# defaultMessage: Top
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Top"
+msgstr "Topo"
+
+# defaultMessage: Type text…
+#: blocks/Text/DefaultTextBlockEditor blocks/Text/DetachedTextBlockEditor
+#, fuzzy
+msgid "Type text…"
+msgstr "Digite texto…"
+
+# defaultMessage: Visible only in view mode
+#: blocks/Table/TableBlockEdit
+#, fuzzy
+msgid "Visible only in view mode"
+msgstr "Visível apenas no modo de visualização"
diff --git a/packages/volto-slate/news/6264.feature b/packages/volto-slate/news/6264.feature
deleted file mode 100644
index 02fa752b0f..0000000000
--- a/packages/volto-slate/news/6264.feature
+++ /dev/null
@@ -1 +0,0 @@
-Pass `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl
diff --git a/packages/volto-slate/news/6444.bugfix b/packages/volto-slate/news/6444.bugfix
new file mode 100644
index 0000000000..82e8145547
--- /dev/null
+++ b/packages/volto-slate/news/6444.bugfix
@@ -0,0 +1 @@
+Fix slight CSS lint violation in volto-slate @sneridagh
diff --git a/packages/volto-slate/package.json b/packages/volto-slate/package.json
index c64a51372f..4b62cf0f94 100644
--- a/packages/volto-slate/package.json
+++ b/packages/volto-slate/package.json
@@ -1,6 +1,6 @@
{
"name": "@plone/volto-slate",
- "version": "18.0.0-alpha.17",
+ "version": "18.0.0-alpha.19",
"description": "Slate.js integration with Volto",
"main": "src/index.js",
"author": "European Environment Agency: IDM2 A-Team",
diff --git a/packages/volto-slate/src/blocks/Text/SlashMenu.jsx b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx
index 2a26c39cfe..9ec1612304 100644
--- a/packages/volto-slate/src/blocks/Text/SlashMenu.jsx
+++ b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx
@@ -4,7 +4,7 @@ import { filter, isEmpty } from 'lodash';
import { Menu } from 'semantic-ui-react';
import { useIntl, FormattedMessage } from 'react-intl';
import { Icon } from '@plone/volto/components';
-import { useSelector } from 'react-redux';
+import { useUser } from '@plone/volto/hooks';
const emptySlateBlock = () => ({
value: [
@@ -111,7 +111,7 @@ const PersistentSlashMenu = ({ editor }) => {
} = props;
const disableNewBlocks = data?.disableNewBlocks || detached;
- const user = useSelector((state) => state.users?.user);
+ const user = useUser();
const [slashMenuSelected, setSlashMenuSelected] = React.useState(0);
diff --git a/packages/volto-slate/src/editor/less/editor.less b/packages/volto-slate/src/editor/less/editor.less
index a1a51d8b23..e6006e3c19 100644
--- a/packages/volto-slate/src/editor/less/editor.less
+++ b/packages/volto-slate/src/editor/less/editor.less
@@ -28,7 +28,7 @@
.link-form-container {
margin-left: 2px;
- display: flex;
+ display: flex;
align-items: center;
}
}
diff --git a/packages/volto/.eslintrc b/packages/volto/.eslintrc
index 3e28cbddef..9b98fbf685 100644
--- a/packages/volto/.eslintrc
+++ b/packages/volto/.eslintrc
@@ -6,29 +6,30 @@
"browser": true,
"node": true,
"mocha": true,
- "jasmine": true
+ "jasmine": true,
},
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
- "legacyDecorators": true
- }
+ "legacyDecorators": true,
+ },
},
"rules": {
"import/no-unresolved": 1,
+ "react/jsx-key": [2, { "checkFragmentShorthand": true }],
"no-alert": 1,
"no-console": 1,
"no-debugger": 1,
"prettier/prettier": [
"error",
- { "trailingComma": "all", "singleQuote": true }
+ { "trailingComma": "all", "singleQuote": true },
],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/react-in-jsx-scope": "off",
- "jsx-a11y/label-has-associated-control": "off"
+ "jsx-a11y/label-has-associated-control": "off",
},
"settings": {
"import/resolver": {
@@ -36,21 +37,20 @@
"map": [
["@plone/volto", "./src"],
["@plone/volto-slate", "../volto-slate/src"],
- ["@plone/registry", "../registry/src"],
["@plone/types", "../types"],
["@package", "./src"],
- ["@root", "./src"]
+ ["@root", "./src"],
],
- "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"]
+ "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"],
},
"babel-plugin-root-import": {
- "rootPathSuffix": "src"
- }
+ "rootPathSuffix": "src",
+ },
},
"import/core-modules": ["load-volto-addons"],
"react": {
- "version": "detect"
- }
+ "version": "detect",
+ },
},
"overrides": [
{
@@ -60,17 +60,14 @@
// Re-add it if at some point, we stop relying on it
"extends": ["react-app", "prettier", "plugin:jsx-a11y/recommended"],
"plugins": ["prettier", "react-hooks", "jsx-a11y"],
- "parser": "@typescript-eslint/parser"
+ "parser": "@typescript-eslint/parser",
},
{
- "files": [
- "**/*.stories.js",
- "**/*.stories.jsx"
- ],
+ "files": ["**/*.stories.js", "**/*.stories.jsx"],
"rules": {
- "import/no-anonymous-default-export": "off"
- }
- }
+ "import/no-anonymous-default-export": "off",
+ },
+ },
],
"globals": {
"root": true,
@@ -86,6 +83,6 @@
"Cypress": true,
"jest": true,
"socket": true,
- "webpackIsomorphicTools": true
- }
+ "webpackIsomorphicTools": true,
+ },
}
diff --git a/packages/volto/.storybook/main.js b/packages/volto/.storybook/main.js
index c745006748..36cb9a3017 100644
--- a/packages/volto/.storybook/main.js
+++ b/packages/volto/.storybook/main.js
@@ -102,8 +102,8 @@ module.exports = {
[],
defaultRazzleOptions,
);
- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
- const registry = new AddonConfigurationRegistry(projectRootPath);
+ const { AddonRegistry } = require('@plone/registry/addon-registry');
+ const { registry } = AddonRegistry.init(projectRootPath);
config = lessPlugin({
registry,
}).modifyWebpackConfig({
diff --git a/packages/volto/CHANGELOG.md b/packages/volto/CHANGELOG.md
index 286d1fe3f7..a1ff0c4d00 100644
--- a/packages/volto/CHANGELOG.md
+++ b/packages/volto/CHANGELOG.md
@@ -377,6 +377,189 @@ myst:
- Reduce the severity level of `Microsoft.Contractions` and `Microsoft.Units` from `error` to `suggestion` when running `make docs-vale` in preparation for requiring Vale passing without errors. @stevepiercy [#5371](https://github.com/plone/volto/issues/5371)
- Improved wording in branching policy. @sneridagh [#5372](https://github.com/plone/volto/issues/5372)
+## 18.0.0-alpha.47 (2024-10-27)
+
+### Breaking
+
+- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh
+ Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371)
+
+### Feature
+
+- URL Management control panel: add a filter for redirects created after a given date.
+ **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later.
+ @davisagli [#6414](https://github.com/plone/volto/issues/6414)
+- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: Add feature to bulk upload redirects in a CSV file.
+ **Note:** This requires `plone.restapi` 9.8.0 or later.
+ @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422)
+- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425)
+
+### Bugfix
+
+- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355)
+- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419)
+- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421)
+- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436)
+
+### Internal
+
+- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308)
+- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371)
+- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399)
+- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403)
+- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407)
+- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408)
+- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418)
+- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424)
+
+### Documentation
+
+- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426)
+- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433)
+- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438)
+
+## 18.0.0-alpha.46 (2024-10-10)
+
+### Breaking
+
+- Added `react/jsx-key` rule for ESlint to detect missing key property in iterators. @sneridagh [#6387](https://github.com/plone/volto/issues/6387)
+
+### Bugfix
+
+- Revert "Fix block chooser search is not focusable when clicked on add button" #5867 @sneridagh
+ It was causing a regression described in #6389 [#6390](https://github.com/plone/volto/issues/6390)
+
+### Internal
+
+- Added missing improvement to the `build-deps` Makefile command. @sneridagh [#6383](https://github.com/plone/volto/issues/6383)
+
+### Documentation
+
+- Add reference to Translate Volto in the main documentation. @stevepiercy [#6386](https://github.com/plone/volto/issues/6386)
+
+## 18.0.0-alpha.45 (2024-10-08)
+
+### Bugfix
+
+- Added missing arg for `buildStyleClassNamesExtenders` @sneridagh [#6381](https://github.com/plone/volto/issues/6381)
+
+### Documentation
+
+- Removed pin on Vale for documentation spelling, grammar, and style checks, and upgrade to v3.x configuration.
+ Updated CI and `Makefile` to allow `VALEOPTS=--no-exit`. @stevepiercy [#6376](https://github.com/plone/volto/issues/6376)
+
+## 18.0.0-alpha.44 (2024-10-03)
+
+### Feature
+
+- Added `config.settings.nonContentRoutesPublic` to avoid `isCmsUi` issues in these public routes. @giuliaghisini [#6173](https://github.com/plone/volto/issues/6173)
+- Add language independent field icon. @iRohitSingh [#6297](https://github.com/plone/volto/issues/6297)
+- Improve DiffField.jsx to render only the blocks on the page with better support for displaying HTML elements such as images. @dobri1408 [#6309](https://github.com/plone/volto/issues/6309)
+- Updated Italian Italian translations. @gianniftp [#6342](https://github.com/plone/volto/issues/6342)
+- Update Brazilian Portuguese translations. @ericof
+
+### Bugfix
+
+- Increase specificity of table header style selector to properly override colors for better contrast @jackahl [#2487](https://github.com/plone/volto/issues/2487)
+- Change Form input:focus text color to the `textColor` value for a11y.
+ Add Cypress test for contact form inputs. @ThomasKindermann @tedw87 [#2570](https://github.com/plone/volto/issues/2570)
+- Add missing `for` attribute to checkbox label to improve accessibility. @gomez [#6249](https://github.com/plone/volto/issues/6249)
+- - Fixed build style classnames in edit mode. Also use buildStyleClassNamesExtenders. @giuliaghisini [#6259](https://github.com/plone/volto/issues/6259)
+- Fetch `user` before pass it to the `restricted` function of the block settings. @wesleybl [#6293](https://github.com/plone/volto/issues/6293)
+- - Join validation errors in one single toast and update errors from response. @cekk
+ - Toast content now has a
wrapper instead of a
. @cekk [#6295](https://github.com/plone/volto/issues/6295)
+- Fixed findBlocks when no blocks are passed. @giuliaghisini [#6299](https://github.com/plone/volto/issues/6299)
+- Fixed toolbar buttons not having a focus outline. @JeffersonBledsoe [#6313](https://github.com/plone/volto/issues/6313)
+- fix: SidebarPopup close on ESC keypress @nileshgulia1 [#6315](https://github.com/plone/volto/issues/6315)
+- Changed sidebar accordion text colour from @teal to @textColor. @JeffersonBledsoe [#6330](https://github.com/plone/volto/issues/6330)
+- Labels accessibility for ArrayWidget, SelectWidget, TokenWidget. @folix-01 [#6332](https://github.com/plone/volto/issues/6332)
+- Use lighter blue as link color in inverted tables to improve contrast for a11y @jackahl [#6334](https://github.com/plone/volto/issues/6334)
+
+### Internal
+
+- Added a pull request template as a checklist before submitting a PR. @stevepiercy [#6365](https://github.com/plone/volto/issues/6365)
+
+### Documentation
+
+- Update references to cookiecutter-plone-starter in docs. @davisagli [#6289](https://github.com/plone/volto/issues/6289)
+- - Revised Cookieplone section in the Upgrade Guide. @stevepiercy [#6290](https://github.com/plone/volto/issues/6290)
+- - Fix the MyST syntax for the label `upgrade-18-cookieplone-label`. @stevepiercy [#6360](https://github.com/plone/volto/issues/6360)
+- Fixed spelling of prerequisites. @stevepiercy [#6362](https://github.com/plone/volto/issues/6362)
+- Fix links to Vite website. @stevepiercy [#6366](https://github.com/plone/volto/issues/6366)
+
+## 18.0.0-alpha.43 (2024-09-13)
+
+### Breaking
+
+- Move `Tags` component to the slot `belowContent`. @wesleybl [#6269](https://github.com/plone/volto/issues/6269)
+
+### Feature
+
+- Refactor AlbumView from class to functional component. @Tishasoumya-02 [#4077](https://github.com/plone/volto/issues/4077)
+- Add setting `unwantedControlPanelsFields` and use it in the function `filterControlPanelsSchema`. @wesleybl [#4819](https://github.com/plone/volto/issues/4819)
+- Refactor the `Register` component from class-based to functional. @Tishasoumya-02 [#4861](https://github.com/plone/volto/issues/4861)
+- Refactor Toolbar/More component from class to functional component. @Tishasoumya-02 [#4955](https://github.com/plone/volto/issues/4955)
+- Refactor Blocks/LeadImage/Edit component from class to functional component. @Tishasoumya-02 [#4959](https://github.com/plone/volto/issues/4959)
+- Refactor Blocks/Video/Edit component from class to functional component. @Tishasoumya-02 [#4960](https://github.com/plone/volto/issues/4960)
+- Refactor ControlPanel/Users/Aliases component , replacing class component lifecycle methods with functional hooks. @Tishasoumya-02 [#4985](https://github.com/plone/volto/issues/4985)
+- Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02 [#4986](https://github.com/plone/volto/issues/4986)
+- Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02 [#4993](https://github.com/plone/volto/issues/4993)
+- Refactor AddonsControlpanel from class to functional components. @Tishasoumya [#4995](https://github.com/plone/volto/issues/4995)
+- Refactor Preference/Change Password from class to functional component. @Tishasoumya-02 [#5044](https://github.com/plone/volto/issues/5044)
+- Refactor Preference/PersonalPreferences from class to functional component. @Tishasoumya-02 [#5045](https://github.com/plone/volto/issues/5045)
+- Refactor Preference/PersonalInformation from Class to Functional component. @Tishasoumya-02 [#5046](https://github.com/plone/volto/issues/5046)
+- Refactor Contents/ContentsUploadModal & Storybook-@Tishasoumya-02 [#5047](https://github.com/plone/volto/issues/5047)
+- Storybook test for ContentBreadcrumbs component. @Tishasoumya [#5048](https://github.com/plone/volto/issues/5048)
+- Storybook Actions component test. @Tishasoumya-02 [#5049](https://github.com/plone/volto/issues/5049)
+- Refactor Display from class to functional component. @Tishasoumya-02 [#5066](https://github.com/plone/volto/issues/5066)
+- Refactor `ReferenceWidget` from class-based to functional component. @Tishasoumya [#5093](https://github.com/plone/volto/issues/5093)
+- Refactor the table of contents block component from a class component to a functional component. @Prince0906 [#6167](https://github.com/plone/volto/issues/6167)
+- Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico [#6186](https://github.com/plone/volto/issues/6186)
+- Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach [#6213](https://github.com/plone/volto/issues/6213)
+- The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
+ Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli [#6248](https://github.com/plone/volto/issues/6248)
+- Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl [#6264](https://github.com/plone/volto/issues/6264)
+
+### Bugfix
+
+- Fix search block showing no option select in sort on property @iRohitSingh [#5055](https://github.com/plone/volto/issues/5055)
+- Displays validation error messages on control panel forms. @wesleybl [#5274](https://github.com/plone/volto/issues/5274)
+- Fix `initialValue` block setting. @wesleybl [#5971](https://github.com/plone/volto/issues/5971)
+- When user changes location, set the `userSession.token` value based on cookie. This fixes the login status not being properly determined by the application. @tiberiu-ichim [#6071](https://github.com/plone/volto/issues/6071)
+- Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02 [#6147](https://github.com/plone/volto/issues/6147)
+- Add Cypress test for search block sort on property. @iRohitSingh [#6226](https://github.com/plone/volto/issues/6226)
+- Remove unused i18n message for SortOn component. @davisagli [#6230](https://github.com/plone/volto/issues/6230)
+- Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli [#6235](https://github.com/plone/volto/issues/6235)
+- Fix loading of .cjs in webpack. @davisagli [#6237](https://github.com/plone/volto/issues/6237)
+- fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one,
+ onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini [#6240](https://github.com/plone/volto/issues/6240)
+- Changed imports from relative to absolute to avoid type errors in add-on tests. @wesleybl [#6244](https://github.com/plone/volto/issues/6244)
+- In the URL Management control panel, allow external URLs as targets. @davisagli [#6247](https://github.com/plone/volto/issues/6247)
+- Disable save button when loading POST query @sabrina-bongiovanni [#6252](https://github.com/plone/volto/issues/6252)
+- Fix `TypeError: values[0] is undefined` in Contents properties modal. @davisagli [#6258](https://github.com/plone/volto/issues/6258)
+- Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
+- Place actions storybook in correct path. @Tishasoumya-02 [#6275](https://github.com/plone/volto/issues/6275)
+- Remove `Generate tabs for items other than folders` field from navigation control panel. @wesleybl [#6278](https://github.com/plone/volto/issues/6278)
+- Fix error rendering empty grid blocks. @MAX-786 [#6279](https://github.com/plone/volto/issues/6279)
+- Fixed instant error on click on an item of the "Order" tab @sneridagh [#6284](https://github.com/plone/volto/issues/6284)
+
+### Internal
+
+- Refactored the `SelectWidget` component to a functional component. @lorstenoplo [#5570](https://github.com/plone/volto/issues/5570)
+- Allow setting the language for `make backend-docker-start`. Default is `en`. Added usage documentation in Contributing > Develop Volto core, and reorganized it. Cleaned up Configuration > Multilingual. @stevepiercy [#6231](https://github.com/plone/volto/issues/6231)
+- Revert rename of `.gitkeep`. @stevepiercy [#6232](https://github.com/plone/volto/issues/6232)
+- Upgrade Cypress to 13.13.2. @wesleybl [#6241](https://github.com/plone/volto/issues/6241)
+- Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy [#6242](https://github.com/plone/volto/issues/6242)
+- Add `make i18n` command. @Faakhir30 [#6274](https://github.com/plone/volto/issues/6274)
+- Update to Plone 6.0.13 @sneridagh [#6285](https://github.com/plone/volto/issues/6285)
+
+### Documentation
+
+- Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy [#6239](https://github.com/plone/volto/issues/6239)
+- Fix redirect to Docker documentation. @stevepiercy [#6262](https://github.com/plone/volto/issues/6262)
+- Added upgrade guide notice about CookiePlone as the recommended way for generating projects and add-on boilerplate @sneridagh [#6286](https://github.com/plone/volto/issues/6286)
## 18.0.0-alpha.42 (2024-07-30)
diff --git a/packages/volto/Makefile b/packages/volto/Makefile
index 8139f5b8a5..1a346e111f 100644
--- a/packages/volto/Makefile
+++ b/packages/volto/Makefile
@@ -72,10 +72,10 @@ install: ## Set up development environment
cypress-install: ## Install Cypress for acceptance tests
$(NODEBIN)/cypress install
-../registry/dist: ../registry/src
+../registry/dist: $(shell find ../registry/src -type f)
(cd ../../ && pnpm build:registry)
-../components/dist: ../components/src
+../components/dist: $(shell find ../components/src -type f)
(cd ../../ && pnpm build:components)
.PHONY: build-deps
diff --git a/packages/volto/__tests__/addon-registry-project.test.js b/packages/volto/__tests__/addon-registry-project.test.js
index 08d4bad832..fad432bc88 100644
--- a/packages/volto/__tests__/addon-registry-project.test.js
+++ b/packages/volto/__tests__/addon-registry-project.test.js
@@ -1,9 +1,11 @@
-const path = require('path');
-const AddonConfigurationRegistry = require('../../registry/src/addon-registry');
-const { buildDependencyGraph, getAddonsLoaderChain } =
- AddonConfigurationRegistry;
+import path from 'path';
+import {
+ AddonRegistry,
+ buildDependencyGraph,
+ getAddonsLoaderChain,
+} from '@plone/registry/addon-registry';
-describe('AddonConfigurationRegistry - Project', () => {
+describe('AddonRegistry - Project', () => {
jest.mock(
`${path.join(
__dirname,
@@ -19,14 +21,14 @@ describe('AddonConfigurationRegistry - Project', () => {
it('works in a mock project directory', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
+ const { registry } = AddonRegistry.init(base);
const voltoPath = `${base}/node_modules/@plone/volto`;
- expect(reg.projectRootPath).toStrictEqual(base);
- expect(reg.voltoPath).toStrictEqual(voltoPath);
+ expect(registry.projectRootPath).toStrictEqual(base);
+ expect(registry.voltoPath).toStrictEqual(voltoPath);
- expect(reg.addonNames).toStrictEqual([
+ expect(registry.addonNames).toStrictEqual([
'test-addon',
'test-released-addon',
'test-released-source-addon',
@@ -35,7 +37,7 @@ describe('AddonConfigurationRegistry - Project', () => {
'test-released-unmentioned',
]);
- expect(reg.packages).toEqual({
+ expect(registry.packages).toEqual({
'test-addon': {
isPublishedPackage: false,
modulePath: `${base}/addons/test-addon/src`,
@@ -102,8 +104,8 @@ describe('AddonConfigurationRegistry - Project', () => {
it('provides aliases for addons', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
- expect(reg.getResolveAliases()).toStrictEqual({
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getResolveAliases()).toStrictEqual({
'my-volto-config-addon': `${base}/addons/my-volto-config-addon/src`,
'test-addon': `${base}/addons/test-addon/src`,
'test-released-addon': `${base}/node_modules/test-released-addon`,
@@ -115,14 +117,14 @@ describe('AddonConfigurationRegistry - Project', () => {
it('provides addon extenders', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
- expect(reg.getAddonExtenders().length).toBe(1);
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonExtenders().length).toBe(1);
});
it('provides a list of addon records ordered by initial package declaration', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
- const addons = reg.getAddons();
+ const { registry } = AddonRegistry.init(base);
+ const addons = registry.getAddons();
expect(addons.map((a) => a.name)).toStrictEqual([
'test-released-unmentioned',
'test-released-dummy',
@@ -135,8 +137,8 @@ describe('AddonConfigurationRegistry - Project', () => {
it('provides customization paths declared in a Volto project', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
- expect(reg.getProjectCustomizationPaths()).toStrictEqual({
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getProjectCustomizationPaths()).toStrictEqual({
'@plone/volto/LanguageSwitcher': `${base}/src/customizations/LanguageSwitcher.js`,
'@plone/volto/TSComponent': `${base}/src/customizations/TSComponent.jsx`,
'@plone/volto/client': `${base}/src/customizations/client.js`,
@@ -148,8 +150,8 @@ describe('AddonConfigurationRegistry - Project', () => {
it('provides customization paths declared in addons', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
- expect(reg.getAddonCustomizationPaths()).toStrictEqual({
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.getAddonCustomizationPaths()).toStrictEqual({
'@plone/volto/LanguageSwitcher': `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`,
'@plone/volto/TSComponent': `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`,
'@plone/volto/client': `${base}/node_modules/test-released-source-addon/src/customizations/client.js`,
@@ -231,9 +233,11 @@ describe('Addon via env var - Released addon', () => {
it('addons can be specified on the fly using ADDONS env var - Released addon', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
+ const { registry } = AddonRegistry.init(base);
expect(
- Object.keys(reg.packages).includes('test-released-via-addons-env-var'),
+ Object.keys(registry.packages).includes(
+ 'test-released-via-addons-env-var',
+ ),
).toBe(true);
});
});
@@ -255,9 +259,9 @@ describe('Addon via env var - local packages folder addon', () => {
it('addons can be specified on the fly using ADDONS env var - local packages folder addon', () => {
const base = path.join(__dirname, 'fixtures', 'test-volto-project');
- const reg = new AddonConfigurationRegistry(base);
+ const { registry } = AddonRegistry.init(base);
expect(
- Object.keys(reg.packages).includes(
+ Object.keys(registry.packages).includes(
'test-local-packages-via-addons-env-var',
),
).toBe(true);
diff --git a/packages/volto/__tests__/addon-registry-volto.test.js b/packages/volto/__tests__/addon-registry-volto.test.js
index 5bea088c21..79fc58af0d 100644
--- a/packages/volto/__tests__/addon-registry-volto.test.js
+++ b/packages/volto/__tests__/addon-registry-volto.test.js
@@ -1,14 +1,14 @@
-const path = require('path');
-const AddonConfigurationRegistry = require('../../registry/src/addon-registry');
+import path from 'path';
+import { AddonRegistry } from '@plone/registry/addon-registry';
-describe('AddonConfigurationRegistry - Volto', () => {
+describe('AddonRegistry - Volto', () => {
it('works in Volto', () => {
const base = path.join(__dirname, '..');
- const reg = new AddonConfigurationRegistry(base);
- expect(reg.projectRootPath).toStrictEqual(base);
+ const { registry } = AddonRegistry.init(base);
+ expect(registry.projectRootPath).toStrictEqual(base);
// TODO: rename initPackagesFolder to proper name after the refactor
- // expect(reg.addonNames).toStrictEqual(['@plone/volto-slate']);
- expect(reg.packages['@plone/volto-slate'].name).toStrictEqual(
+ // expect(registry.addonNames).toStrictEqual(['@plone/volto-slate']);
+ expect(registry.packages['@plone/volto-slate'].name).toStrictEqual(
'@plone/volto-slate',
);
});
diff --git a/packages/volto/__tests__/create-addons-loader.test.js b/packages/volto/__tests__/create-addons-loader.test.js
index 570fa10a18..dfe9d174cb 100644
--- a/packages/volto/__tests__/create-addons-loader.test.js
+++ b/packages/volto/__tests__/create-addons-loader.test.js
@@ -1,11 +1,51 @@
-const fs = require('fs');
-const transform = require('@babel/core').transform;
-
-const getLoader = require('../../registry/src/create-addons-loader');
+import fs from 'fs';
+import { transform } from '@babel/core';
+import {
+ createAddonsLoader,
+ getAddonsLoaderCode,
+ nameFromPackage,
+} from '@plone/registry/create-addons-loader';
describe('create-addons-loader code generation', () => {
- test('no addon creates simple loader', () => {
- const code = getLoader.getAddonsLoaderCode([]);
+ test('no addon creates simple loader, default = no loadProjectConfig', () => {
+ const code = getAddonsLoaderCode([]);
+ expect(code).toBe(`/*
+This file is autogenerated. Don't change it directly.
+Instead, change the "addons" setting in your package.json file.
+*/
+
+
+const addonsInfo = {};
+export { addonsInfo };
+
+const safeWrapper = (func) => (config) => {
+ const res = func(config);
+ if (typeof res === 'undefined') {
+ throw new Error("Configuration function doesn't return config");
+ }
+ return res;
+}
+
+const projectConfigLoader = false;
+const projectConfig = (config) => {
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+}
+
+const load = (config) => {
+ const addonLoaders = [];
+ if(!addonLoaders.every((el) => typeof el === "function")) {
+ throw new TypeError(
+ 'Each addon has to provide a function applying its configuration to the projects configuration.',
+ );
+ }
+ return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config));
+};
+export default load;
+`);
+ });
+
+ test('no addon creates simple loader, loadProjectConfig set to true', () => {
+ const code = getAddonsLoaderCode([], {}, true);
expect(code).toBe(`/*
This file is autogenerated. Don't change it directly.
Instead, change the "addons" setting in your package.json file.
@@ -24,8 +64,9 @@ const safeWrapper = (func) => (config) => {
return res;
}
+
const projectConfig = (config) => {
- return typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
+ return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config;
}
const load = (config) => {
@@ -42,17 +83,14 @@ export default load;
});
test('one addon creates loader', () => {
- const code = getLoader.getAddonsLoaderCode(['volto-addon1']);
+ const code = getAddonsLoaderCode(['volto-addon1']);
expect(code.indexOf("import voltoAddon1 from 'volto-addon1';") > 0).toBe(
true,
);
});
test('two addons create loaders', () => {
- const code = getLoader.getAddonsLoaderCode([
- 'volto-addon1',
- 'volto-addon2',
- ]);
+ const code = getAddonsLoaderCode(['volto-addon1', 'volto-addon2']);
expect(
code.indexOf(`
import voltoAddon1 from 'volto-addon1';
@@ -61,7 +99,7 @@ import voltoAddon2 from 'volto-addon2';`) > 0,
});
test('one addons plus one extra creates loader', () => {
- const code = getLoader.getAddonsLoaderCode(['volto-addon1:loadExtra1']);
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1']);
expect(
code.indexOf(`
import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1';
@@ -70,9 +108,7 @@ import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1';
});
test('one addons plus two extras creates loader', () => {
- const code = getLoader.getAddonsLoaderCode([
- 'volto-addon1:loadExtra1,loadExtra2',
- ]);
+ const code = getAddonsLoaderCode(['volto-addon1:loadExtra1,loadExtra2']);
expect(
code.indexOf(`
import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1';
@@ -81,7 +117,7 @@ import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } fro
});
test('two addons plus extras creates loader', () => {
- const code = getLoader.getAddonsLoaderCode([
+ const code = getAddonsLoaderCode([
'volto-addon1:loadExtra1,loadExtra2',
'volto-addon2:loadExtra3,loadExtra4',
]);
@@ -95,7 +131,7 @@ import voltoAddon2, { loadExtra3 as loadExtra32, loadExtra4 as loadExtra43 } fro
});
describe('create-addons-loader default name generation', () => {
- const getName = getLoader.nameFromPackage;
+ const getName = nameFromPackage;
test('passing a simple word returns a word', () => {
expect(getName('something')).toBe('something');
@@ -149,7 +185,7 @@ function makeAddonLoader(addons, load = true) {
: require.resolve(name),
);
- const loaderPath = getLoader(addons);
+ const loaderPath = createAddonsLoader(addons);
transpile(loaderPath);
if (load) {
diff --git a/packages/volto/__tests__/webpack-relative-resolver.test.js b/packages/volto/__tests__/webpack-relative-resolver.test.js
index 429946a216..cc91dbfb4f 100644
--- a/packages/volto/__tests__/webpack-relative-resolver.test.js
+++ b/packages/volto/__tests__/webpack-relative-resolver.test.js
@@ -1,10 +1,10 @@
const path = require('path');
-const AddonConfigurationRegistry = require('../../registry/src/addon-registry');
+const { AddonRegistry } = require('@plone/registry/addon-registry');
const WebpackRelativeResolver = require('../../volto/webpack-plugins/webpack-relative-resolver');
const base = path.join(__dirname, '..');
const makeRegistry = () => {
- const registry = new AddonConfigurationRegistry(base);
+ const { registry } = AddonRegistry.init(base);
registry.packages = {
'@plone/volto-addon': {
modulePath: '/somewhere/volto-addon/src',
diff --git a/packages/volto/cypress/tests/core/basic/a11y.js b/packages/volto/cypress/tests/core/basic/a11y.js
index 9c21668d8f..593c2b4889 100644
--- a/packages/volto/cypress/tests/core/basic/a11y.js
+++ b/packages/volto/cypress/tests/core/basic/a11y.js
@@ -10,6 +10,10 @@ describe('Accessibility Tests', () => {
it('Contact form has not a11y violations', () => {
cy.navigate('/contact-form');
+ cy.get('#field-name').click().type('input');
+ cy.get('#field-from').click().type('something@domain.com');
+ cy.get('#field-subject').click().type('input');
+ cy.get('#field-message').click().type('input');
cy.checkA11y();
});
diff --git a/packages/volto/cypress/tests/core/basic/locking.js b/packages/volto/cypress/tests/core/basic/locking.js
index d414667777..63e47823eb 100644
--- a/packages/volto/cypress/tests/core/basic/locking.js
+++ b/packages/volto/cypress/tests/core/basic/locking.js
@@ -41,8 +41,8 @@ describe('Document locking', () => {
cy.visit('/document');
cy.wait('@content');
- cy.findByRole('alert')
- .get('.toast-inner-content')
+ cy.get('.Toastify')
+ .findByRole('alert')
.contains('This item was locked by Editor 1 on');
});
diff --git a/packages/volto/cypress/tests/core/basic/metadata.js b/packages/volto/cypress/tests/core/basic/metadata.js
index 70c30e31a9..6d5b414aad 100644
--- a/packages/volto/cypress/tests/core/basic/metadata.js
+++ b/packages/volto/cypress/tests/core/basic/metadata.js
@@ -34,8 +34,8 @@ describe('Add Content Tests', () => {
cy.get('.ui.basic.icon.button.image').contains('Image').click();
cy.get('#toolbar-save').click();
- cy.findByRole('alert')
- .get('.toast-inner-content')
+ cy.get('.Toastify')
+ .findByRole('alert')
.contains('Required input is missing');
cy.get('.sidebar-container .tabs-wrapper .active.item').contains('Page');
});
diff --git a/packages/volto/cypress/tests/core/blocks/blocks-image.js b/packages/volto/cypress/tests/core/blocks/blocks-image.js
index c0c79d2eae..7c9c84b39b 100644
--- a/packages/volto/cypress/tests/core/blocks/blocks-image.js
+++ b/packages/volto/cypress/tests/core/blocks/blocks-image.js
@@ -35,7 +35,8 @@ describe('Blocks Tests', () => {
.last()
.focus()
.should('have.css', 'outline', 'rgb(16, 16, 16) auto 1px');
- cy.get('.block.image .ui.input input[type="text"]').type(
+ cy.findByLabelText('Enter a URL to an image').click();
+ cy.get('.ui.input.editor-link.input-anchorlink-theme input').type(
`https://github.com/plone/volto/raw/main/logos/volto-colorful.png{enter}`,
);
cy.get('#toolbar-save').click();
@@ -162,7 +163,7 @@ describe('Blocks Tests', () => {
});
});
- it.only('Create an image block and initially alt attr is empty', () => {
+ it('Create an image block and initially alt attr is empty', () => {
// when I add an image block via upload
cy.get('.content-area .slate-editor [contenteditable=true]', {
timeout: 10000,
diff --git a/packages/volto/cypress/tests/core/blocks/blocks-table.js b/packages/volto/cypress/tests/core/blocks/blocks-table.js
index 43c5f3ee26..97f00622e1 100644
--- a/packages/volto/cypress/tests/core/blocks/blocks-table.js
+++ b/packages/volto/cypress/tests/core/blocks/blocks-table.js
@@ -29,7 +29,7 @@ describe('Table Block Tests', () => {
cy.get('.block-editor-slateTable [role=textbox]')
.first()
.click()
- .should('have.css', 'outline', 'rgb(135, 143, 147) none 0px');
+ .should('have.css', 'outline', 'rgba(0, 0, 0, 0.87) none 0px');
cy.get(
'.celled.fixed.table thead tr th:first-child() [contenteditable="true"]',
diff --git a/packages/volto/cypress/tests/core/controlpanels/aliasesControlPanel.js b/packages/volto/cypress/tests/core/controlpanels/aliasesControlPanel.js
index d944c24c3c..5d079b429b 100644
--- a/packages/volto/cypress/tests/core/controlpanels/aliasesControlPanel.js
+++ b/packages/volto/cypress/tests/core/controlpanels/aliasesControlPanel.js
@@ -1,4 +1,4 @@
-describe('Add a new alias from control panel interface', () => {
+describe('URL Management control panel', () => {
beforeEach(() => {
cy.intercept('GET', `/**/*?expand*`).as('content');
cy.autologin();
@@ -9,11 +9,28 @@ describe('Add a new alias from control panel interface', () => {
});
cy.visit('/my-page');
cy.wait('@content');
+
+ cy.visit('/controlpanel/aliases');
+ cy.get('#add-alt-url').click();
+ cy.get('#field-altUrlPath').type('/alturl');
+ cy.get('#field-targetUrlPath').type('/my-page');
+ cy.get('button[aria-label="Save"]').click();
});
it('adds a new alias', () => {
+ cy.get('.toast-inner-content').contains('Alias has been added');
+ });
+ it('edits an existing new alias', () => {
+ cy.visit('/controlpanel/aliases');
+ cy.get('button[aria-label="Edit Alternative URL"]').click();
+ cy.get('#field-altUrlPath').clear().type('/alturl2');
+ cy.get('#field-targetUrlPath').clear().type('/my-page');
+ cy.get('button[aria-label="Save"]').click();
+ cy.get('.toast-inner-content').contains('Alias has been added');
+ });
+ it('removes an alias', () => {
cy.visit('/controlpanel/aliases');
- cy.get('#alternative-url-input').type('/alturl');
- cy.get('#target-url-input').type('/my-page');
- cy.get('#submit-alias').click();
+ cy.get('input[type="checkbox"][value="/alturl"]').click({ force: true });
+ cy.get('#remove-alt-urls').click();
+ cy.get('.toast-inner-content').contains('Aliases have been removed');
});
});
diff --git a/packages/volto/cypress/tests/coresandbox/login.js b/packages/volto/cypress/tests/coresandbox/login.js
new file mode 100644
index 0000000000..ed6c933198
--- /dev/null
+++ b/packages/volto/cypress/tests/coresandbox/login.js
@@ -0,0 +1,17 @@
+import { ploneAuth } from '../../support/constants';
+
+describe('Login Tests', () => {
+ beforeEach(() => {
+ cy.visit('/fallback_login');
+ });
+ it('Check login redirect when the login is at another root', function () {
+ const user = ploneAuth[0];
+ const password = ploneAuth[1];
+
+ cy.get('#login').type(user).should('have.value', user);
+ cy.get('#password').type(password).should('have.value', password);
+ cy.get('#login-form-submit').click();
+ cy.wait(3000);
+ cy.location('pathname').should('eq', '/');
+ });
+});
diff --git a/packages/volto/cypress/tests/workingCopy/create.js b/packages/volto/cypress/tests/workingCopy/create.js
index 9d72f4c2c5..55b737e07f 100644
--- a/packages/volto/cypress/tests/workingCopy/create.js
+++ b/packages/volto/cypress/tests/workingCopy/create.js
@@ -19,8 +19,11 @@ describe('Working Copy Tests - Create', () => {
it('Basic create operation', function () {
cy.get('#toolbar-more').click();
cy.findByLabelText('Create working copy').click();
- cy.findByRole('alert').contains('This is a working copy of');
- cy.findByRole('alert')
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .contains('This is a working copy of');
+ cy.get('.Toastify')
+ .findByRole('alert')
.get('.toast-inner-content a')
.should('have.attr', 'href')
.and('include', '/document');
@@ -37,11 +40,17 @@ describe('Working Copy Tests - Create', () => {
it('Navigation through baseline-working copy', function () {
cy.get('#toolbar-more').click();
cy.findByLabelText('Create working copy').click();
- cy.findByRole('alert').get('.toast-inner-content a').click();
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .get('.toast-inner-content a')
+ .click();
cy.url().should('eq', Cypress.config().baseUrl + '/document');
- cy.findByRole('alert').contains('This has an ongoing working copy in');
- cy.findByRole('alert')
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .contains('This has an ongoing working copy in');
+ cy.get('.Toastify')
+ .findByRole('alert')
.get('.toast-inner-content a')
.should('have.attr', 'href')
.and('include', '/working_copy_of_document');
diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po
index 69687ab2b8..e9e7fe90a2 100644
--- a/packages/volto/locales/ca/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po
@@ -74,7 +74,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -94,6 +93,11 @@ msgstr ""
msgid "Add Addons"
msgstr "Afegeix complements"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -310,6 +314,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -329,7 +343,7 @@ msgstr "Tots"
msgid "All content"
msgstr "Tot el contingut"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -356,7 +370,7 @@ msgstr "Deixeu el camp buit si la imatge és purament decorativa"
msgid "Alt text hint link text"
msgstr "Descriu el propòsit de la imatge"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -367,7 +381,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -418,6 +432,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -477,6 +496,11 @@ msgstr "Base de cerca"
msgid "Block"
msgstr "Bloc"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -503,11 +527,21 @@ msgstr "Navegui"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Navegueu pel lloc, deixeu anar una imatge o escriviu un URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Per defecte, els permisos del contenidor d'aquest element són heretats. Si es desactiva, només seran vàlids els permisos de compartició definits explícitament. A la vista general, el símbol ${inherited} indica un valor heretat. De la mateixa manera, el símbol ${global} indica un rol global, que és gestionat per l'administrador del lloc."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -836,6 +870,16 @@ msgstr ""
msgid "Create working copy"
msgstr "Crea una còpia de treball"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1182,6 +1226,11 @@ msgstr "Les adreces de correu electrònic no coincideixen."
msgid "Edit"
msgstr "Edita"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1293,14 +1342,18 @@ msgstr "Introduïu el nom complet, per exemple John Smith."
msgid "Enter map Embed Code"
msgstr "Introduïu el codi d'inserció del mapa"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1346,11 +1399,6 @@ msgstr ""
msgid "Error"
msgstr "Error"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1376,6 +1424,11 @@ msgstr ""
msgid "Event view"
msgstr "Esdeveniment"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1487,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Fitxers pujats: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1496,7 +1550,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1938,6 +1992,11 @@ msgstr "Llenguatge"
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2142,6 +2201,11 @@ msgstr "Gestiona les traduccions per a {title}"
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3558,8 +3622,7 @@ msgid "Table"
msgstr "Taula"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Taula de continguts"
@@ -3586,7 +3649,6 @@ msgstr "Etiquetes per eliminar"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4427,6 +4489,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Has oblidat la teva contrasenya?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4537,6 +4604,11 @@ msgstr "quan"
msgid "event_where"
msgstr "On?"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po
index d35ff24b4c..07723ded35 100644
--- a/packages/volto/locales/de/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/de/LC_MESSAGES/volto.po
@@ -73,7 +73,6 @@ msgstr "Aktivierte Inhaltsregeln auf dieser Seite"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -93,6 +92,11 @@ msgstr "Hinzufügen"
msgid "Add Addons"
msgstr "Hier werden verfügbare und installierte Backend-Add-ons gezeigt. Informationen wie Weitere hinzugefügt werden können, finden sich hier:"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr "Quell URL hinzufügen"
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -171,7 +175,7 @@ msgstr "Datum hinzufügen"
#. Default: "Add element to container"
#: components/manage/Blocks/Container/SimpleContainerToolbar
msgid "Add element to container"
-msgstr ""
+msgstr "Element zum Container hinzufügen"
#. Default: "Add field"
#: components/manage/Widgets/SchemaWidget
@@ -309,6 +313,16 @@ msgstr "Alias"
msgid "Alias has been added"
msgstr "Alias wurde hinzugefügt"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr "Weiterleitungen wurden entfernt."
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr "Weiterleitungen wurden hochgeladen."
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -328,7 +342,7 @@ msgstr "Alle"
msgid "All content"
msgstr "Alle Inhalte"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Alle existierenden, alternativen Pfade für diese Seite"
@@ -355,7 +369,7 @@ msgstr "Das Feld kann leer bleiben, wenn das Bild rein dekorativ ist."
msgid "Alt text hint link text"
msgstr "Beschreiben Sie, was auf dem Bild zu sehen ist."
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Alternativer Pfad (erforderlich)"
@@ -366,7 +380,7 @@ msgstr "Alternativer Pfad (erforderlich)"
msgid "Alternative url path must start with a slash."
msgstr "Alternativer Pfad muss mit einem Schrägstrich beginnen"
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Alternativer URL Pfad → Zielpfad (Datum und Zeit der Erstellung, manuell erstellt ja/nein)"
@@ -410,13 +424,18 @@ msgstr "Aufsteigend"
#. Default: "Assign the {role} role to {entry}"
#: components/manage/Sharing/Sharing
msgid "Assign the {role} role to {entry}"
-msgstr ""
+msgstr "Rolle {role} zum {entry} zugewiesen."
#. Default: "Assignments"
#: components/manage/Controlpanels/Rules/ConfigureRule
msgid "Assignments"
msgstr "Zuweisungen"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr "automatisch"
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -476,6 +495,11 @@ msgstr "Basis Suchfilter"
msgid "Block"
msgstr "Block"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr "beide"
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -502,11 +526,21 @@ msgstr "Durchsuchen"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Seite durchsuchen, Bild ablegen oder URL eingeben"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr "CSV Massenupload"
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Standardmäßig werden die Berechtigungen von einem Ordner auf die in ihm befindlichen Artikel vererbt. Wenn Sie dies deaktivieren, sind nur die explizit definierten Zugriffsberechtigungen gültig. In der Übersicht zeigt das Symbol ${image_confirm_icon} einen ererbten Wert an. Das Symbol ${image_link_icon} zeigt eine globale Funktion an, die vom Administrator verwaltet wird."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr "CSV Datei"
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -835,6 +869,16 @@ msgstr "Relationen mit diesem Ziel erstellen oder löschen"
msgid "Create working copy"
msgstr "Arbeitskopie erstellen"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr "Erstellt nach"
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr "Erstellt vor"
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1181,6 +1225,11 @@ msgstr "E-Mail-Adressen stimmen nicht überein."
msgid "Edit"
msgstr "Bearbeiten"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr "Quell URL bearbeiten"
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1292,14 +1341,18 @@ msgstr "Tragen Sie bitte Ihren vollen Namen ein."
msgid "Enter map Embed Code"
msgstr "Karten-Einbettungscode eingeben"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Geben Sie den absoluten Pfad des Ziels ein. Das Ziel muss existieren oder ein existierender, alternativer Pfad zum Ziel sein."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr "Geben Sie den absoluten Pfad an wo die Quell URL hinterlegt werden sollte. Der Pfad muss mit einem '/' beginnen. Nur URLs welche einen 404 (Not Found) Fehler auslösen werden weitergeleitet."
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Geben Sie den absoluten Pfad ein, an dem die alternative URL existieren soll. Der Pfad muss mit '/' beginnen. Es werden nur URLs die in einem '404 Nicht Gefunden' enden, weitergeleitet."
@@ -1327,7 +1380,7 @@ msgstr "Geben Sie Ihren Nutzernamen zur Verifikation ein."
#. Default: "Entries"
#: components/manage/Blocks/ToC/Schema
msgid "Entries"
-msgstr ""
+msgstr "Einträge"
#. Default: "Error"
#: components/manage/Add/Add
@@ -1345,11 +1398,6 @@ msgstr ""
msgid "Error"
msgstr "Fehler"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Fehler"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1375,6 +1423,11 @@ msgstr ""
msgid "Event view"
msgstr "Termin"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr "Beispiel"
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1486,19 +1539,20 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Hochgeladene Dateien: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
-msgstr ""
+msgstr "Filtern"
#. Default: "Filter Rules:"
#: components/manage/Controlpanels/Rules/Rules
msgid "Filter Rules:"
msgstr "Filterregeln:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
-msgstr "Nach Präfix filtern"
+msgstr "Nach Pfad filtern"
#. Default: "Filter users by groups"
#: helpers/MessageLabels/MessageLabels
@@ -1937,6 +1991,11 @@ msgstr "Sprache"
msgid "Language independent field."
msgstr "Sprachunabhängiges Feld."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr "Dies ist ein sprachunabhängiges Feld. Jeder Wert, den Sie hier eingeben, überschreibt das entsprechende Feld aller Mitglieder der Übersetzungsgruppe, wenn Sie dieses Formular speichern."
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2141,6 +2200,11 @@ msgstr "Übersetzungen für {} verwalten"
msgid "Manual"
msgstr "Manuell"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr "manuell"
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3557,8 +3621,7 @@ msgid "Table"
msgstr "Tabelle"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Inhaltsverzeichnis"
@@ -3585,7 +3648,6 @@ msgstr "Zu entfernende Tags"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4426,6 +4488,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Passwort vergessen?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr "Fügen Sie mehrere alternative URLs auf einmal hinzu, indem Sie eine CSV-Datei hochladen. Die erste Spalte sollte den Pfad enthalten, von dem die Weiterleitung erfolgen soll; die zweite den Pfad, zu dem die Weiterleitung erfolgen soll. Beide Pfade müssen relativ zur Plone-Site sein und mit einem Schrägstrich (/) beginnen. Eine optionale dritte Spalte kann ein Datum und eine Uhrzeit enthalten. Eine optionale vierte Spalte kann einen Booleschen Wert enthalten, um eine manuelle Weiterleitung zu kennzeichnen (Standard: „true“)."
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4536,6 +4603,11 @@ msgstr "Datum"
msgid "event_where"
msgstr "Ort"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr "/beispiel"
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po
index a5b40a7988..2ad5450f89 100644
--- a/packages/volto/locales/en/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/en/LC_MESSAGES/volto.po
@@ -68,7 +68,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -88,6 +87,11 @@ msgstr ""
msgid "Add Addons"
msgstr ""
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -304,6 +308,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -323,7 +337,7 @@ msgstr ""
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -350,7 +364,7 @@ msgstr ""
msgid "Alt text hint link text"
msgstr ""
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -361,7 +375,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -412,6 +426,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -471,6 +490,11 @@ msgstr ""
msgid "Block"
msgstr ""
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -497,11 +521,21 @@ msgstr ""
msgid "Browse the site, drop an image, or type a URL"
msgstr ""
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr ""
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -830,6 +864,16 @@ msgstr ""
msgid "Create working copy"
msgstr ""
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1176,6 +1220,11 @@ msgstr ""
msgid "Edit"
msgstr ""
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1287,14 +1336,18 @@ msgstr ""
msgid "Enter map Embed Code"
msgstr ""
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1340,11 +1393,6 @@ msgstr ""
msgid "Error"
msgstr ""
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1370,6 +1418,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1481,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr ""
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1490,7 +1544,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1932,6 +1986,11 @@ msgstr ""
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2136,6 +2195,11 @@ msgstr ""
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3552,8 +3616,7 @@ msgid "Table"
msgstr ""
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr ""
@@ -3580,7 +3643,6 @@ msgstr ""
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4421,6 +4483,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr ""
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4531,6 +4598,11 @@ msgstr ""
msgid "event_where"
msgstr ""
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po
index 6e284bfd75..669fa842da 100644
--- a/packages/volto/locales/es/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/es/LC_MESSAGES/volto.po
@@ -75,7 +75,6 @@ msgstr "Reglas de contenido activas en esta página"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -95,6 +94,11 @@ msgstr "Añadir"
msgid "Add Addons"
msgstr "Añadir complementos"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -311,6 +315,16 @@ msgstr "Alias"
msgid "Alias has been added"
msgstr "Alias añadido correctamente"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -330,7 +344,7 @@ msgstr "Todo"
msgid "All content"
msgstr "Todo el contenido"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Todas los alias disponibles para este sitio"
@@ -357,7 +371,7 @@ msgstr "Dejar vacío si la imagen es puramente decorativa"
msgid "Alt text hint link text"
msgstr "Describir el propósito de la imagen"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Ruta de url alternativa (obligatorio)"
@@ -368,7 +382,7 @@ msgstr "Ruta de url alternativa (obligatorio)"
msgid "Alternative url path must start with a slash."
msgstr "La url alternativa debe comenzar con un carácter especial '/'."
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Url alternativa → url destino (fecha y hora de creación, creado de forma manual si/no)"
@@ -419,6 +433,11 @@ msgstr ""
msgid "Assignments"
msgstr "Tareas"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -478,6 +497,11 @@ msgstr "Consulta base de la búsqueda"
msgid "Block"
msgstr "Bloque"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -504,11 +528,21 @@ msgstr "Navegue"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Navegue el contenido, arrastre un imagen o ingrese una dirección URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Por defecto, los permisos desde el contenedor de este elemento son heredados. Si usted deshabilita esta opción, solamente los permisos compartidos definidos explícitamente serán validos. En la vista general, el símbolo {inherited} indica un valor heredado. Similarmente, el símbolo {global} indica un rol global, el cual es administrado por el administrador del sitio."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -837,6 +871,16 @@ msgstr "Crear o eliminar relaciones con el destino"
msgid "Create working copy"
msgstr "Crear copia de trabajo"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1183,6 +1227,11 @@ msgstr "La dirección de correo electrónico no coincide."
msgid "Edit"
msgstr "Editar"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1294,14 +1343,18 @@ msgstr "Introduzca el nombre completo, por ejemplo Leonardo Caballero."
msgid "Enter map Embed Code"
msgstr "Introduzca el código embebido de un mapa"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Introduzca la ruta absoluta del destino. El destino debe existir o ser una url alternativa de otro destino."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Introduzca la ruta de la url alternativa. La ruta debe comenzar por '/'. Solo tendrán redirección aquellas urls que creen errores 404 No encontrado."
@@ -1347,11 +1400,6 @@ msgstr "Entradas"
msgid "Error"
msgstr "Error"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Error"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1377,6 +1425,11 @@ msgstr ""
msgid "Event view"
msgstr "Vista de evento"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1488,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Archivos subidos: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr "Filtrar"
@@ -1497,7 +1551,7 @@ msgstr "Filtrar"
msgid "Filter Rules:"
msgstr "Reglas de filtrado:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Filtrar por prefijo"
@@ -1939,6 +1993,11 @@ msgstr "Idioma"
msgid "Language independent field."
msgstr "Campo independiente de idioma."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2143,6 +2202,11 @@ msgstr "Administrar traducciones de {title}"
msgid "Manual"
msgstr "Manual"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3559,8 +3623,7 @@ msgid "Table"
msgstr "Tabla"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Tabla de contenidos"
@@ -3587,7 +3650,6 @@ msgstr "Categoría a remover"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4428,6 +4490,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "¿Olvidaste tu contraseña?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4538,6 +4605,11 @@ msgstr "Cuándo"
msgid "event_where"
msgstr "Dónde"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po
index b7c57b8ee1..504f03b20d 100644
--- a/packages/volto/locales/eu/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po
@@ -75,7 +75,6 @@ msgstr "Orrialde honetan aktibo dauden eduki-erregelak"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -95,6 +94,11 @@ msgstr "Gehitu"
msgid "Add Addons"
msgstr "Gehitu gehigarriak"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -311,6 +315,16 @@ msgstr "Aliasa"
msgid "Alias has been added"
msgstr "Aliasa ondo gehitu da"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -330,7 +344,7 @@ msgstr "Guztiak"
msgid "All content"
msgstr "Eduki guztia"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Atari honetako ordezko helbide guztiak"
@@ -357,7 +371,7 @@ msgstr "Utzi hutsik irudia dekoratiboa bada"
msgid "Alt text hint link text"
msgstr "Deskribatu irudia zertarako den"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Ordezko helbidearen bidea (derrigorrezkoa)"
@@ -368,7 +382,7 @@ msgstr "Ordezko helbidearen bidea (derrigorrezkoa)"
msgid "Alternative url path must start with a slash."
msgstr "Ordezko helbidearen bidea / karakterearekin hasi behar da"
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Ordezko helbidearen bidea → helburu helbidearen bidea (sorrera data eta ordua, eskuz sortu den bai/ez)"
@@ -419,6 +433,11 @@ msgstr ""
msgid "Assignments"
msgstr "Esleipenak"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -478,6 +497,11 @@ msgstr "Oinarrizko bilaketa kontsulta"
msgid "Block"
msgstr "Blokea"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -504,11 +528,21 @@ msgstr "Arakatu"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Atarian bilatu, irudi bat hona arrastatu edo URL bat idatzi"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Defektuz, elementu hau barnean duen karpetaren baimenak heredatu egiten dira. Hau desaktibatzen baduzu, hemen zehaztutako baimenak bakarrik aplikatuko dira. {inherited} ikurrak heredatutako balioa adierazten du. Era berean, {global} ikurrak rol globala adierazten du, atariaren kudeatzaileak kudeatzen duen rola."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -837,6 +871,16 @@ msgstr ""
msgid "Create working copy"
msgstr "Sortu lan-bertsioa"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1183,6 +1227,11 @@ msgstr "Eposta helbideak ez datoz bat"
msgid "Edit"
msgstr "Editatu"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1294,14 +1343,18 @@ msgstr "Idatzi izen osoa, adb. Jon Garmendia."
msgid "Enter map Embed Code"
msgstr "Sartu itsasteko kodea"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Idatzi helburuaren bide absolutua. Helburua existitu egin behar da edo ordezko helbide bat izan behar da."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Idatzi ordezko helbidearen bide absolutua. Bidea '/' karakterearekin hasi behar da. Orrialde bat aurkitzen ez denean sortzen den 404 errorea ematen duten helbideekin bakarrik gertatuko dira berbideraketak."
@@ -1347,11 +1400,6 @@ msgstr ""
msgid "Error"
msgstr "Erroea"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Errorea"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1377,6 +1425,11 @@ msgstr ""
msgid "Event view"
msgstr "Hitzorduaren bista"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1488,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Igotako fitxategiak: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1497,7 +1551,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr "Filtratu erregelak:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Filtratu aurrizkiaren arabera"
@@ -1939,6 +1993,11 @@ msgstr "Hizkuntza"
msgid "Language independent field."
msgstr "Hizkuntzarekiko Independentea den eremua."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2143,6 +2202,11 @@ msgstr "Kudeatu honen itzulpenak {title}"
msgid "Manual"
msgstr "Eskuz"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3559,8 +3623,7 @@ msgid "Table"
msgstr "Taula"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Edukien taula"
@@ -3587,7 +3650,6 @@ msgstr "Kendu beharreko etiketak"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4428,6 +4490,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Pasahitza ahaztuta?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4538,6 +4605,11 @@ msgstr "Noiz"
msgid "event_where"
msgstr "Non"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po
index 16a81a822c..99ad9cfb65 100644
--- a/packages/volto/locales/fi/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po
@@ -73,7 +73,6 @@ msgstr "Aktiiviset sisältösäännöt tällä sivulla"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -93,6 +92,11 @@ msgstr "Lisää (objektilista)"
msgid "Add Addons"
msgstr "Lisää laajennoksia"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -309,6 +313,16 @@ msgstr "Alias"
msgid "Alias has been added"
msgstr "Alias on lisätty"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -328,7 +342,7 @@ msgstr "Kaikki"
msgid "All content"
msgstr "Kaikki sisältö"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Kaikki vaihtoehtoiset URL:t tälle sivustolle"
@@ -355,7 +369,7 @@ msgstr "Kuvaustekstin vihje"
msgid "Alt text hint link text"
msgstr "Kuvaustekstin vihjeen linkkiteksti"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Vaihtoehtoinen URL-polku (pakollinen)"
@@ -366,7 +380,7 @@ msgstr "Vaihtoehtoinen URL-polku (pakollinen)"
msgid "Alternative url path must start with a slash."
msgstr "Vaihtoehtoisen URL-polun pitää alkaa kauttaviivalla"
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Vaihtoehtoinen URL-polku -> kohteen URL-polku (päivämäärä ja luontiaika, käsin luotu kyllä/ei)"
@@ -417,6 +431,11 @@ msgstr ""
msgid "Assignments"
msgstr "Tehtävät"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -476,6 +495,11 @@ msgstr ""
msgid "Block"
msgstr "Palikka"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -502,11 +526,21 @@ msgstr "Selaa"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Selaa sivustoa, raahaa ja pudota kuva, tai syötä URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Oletuksena käyttöoikeudet peritään aina edeltävältä sisältötasolta. Jos tämä valinta poistetaan, vain tällä tasolla erikseen määritellyt käyttöoikeudet ovat voimassa. {inherited} tarkoittaa perittyä käyttöoikeutta. {global} tarkoittaa ylläpitäjän myöntämää sivustonlaajuista käyttöoikeutta."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -835,6 +869,16 @@ msgstr ""
msgid "Create working copy"
msgstr "Luo työkopio"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1181,6 +1225,11 @@ msgstr "Sähköpostiosoitteet eivät olleet identtiset."
msgid "Edit"
msgstr "Muokkaa"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1292,14 +1341,18 @@ msgstr "Syötä koko nimi, esimerkiksi Lumi Vuorinen."
msgid "Enter map Embed Code"
msgstr "Syötä Google Maps -upotuskoodi"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Syötä kohteen osoite. Kohteen tulee olla olemassa tai sen tulee olla olemassaoleva vaihtoehtoinen URL-polku kohteelle."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1345,11 +1398,6 @@ msgstr ""
msgid "Error"
msgstr "Virhe"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1375,6 +1423,11 @@ msgstr ""
msgid "Event view"
msgstr "Tapahtuman näkymä"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1486,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Lähetetyt tiedostot: {uploadedFiles} "
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1495,7 +1549,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr "Suodata säännöt:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Suodata alkutunnisteen perusteella"
@@ -1937,6 +1991,11 @@ msgstr "Kieli"
msgid "Language independent field."
msgstr "Kieliriippumaton kenttä"
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2141,6 +2200,11 @@ msgstr "Hallitse käännöksiä: {title}"
msgid "Manual"
msgstr "Manuaalinen"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3557,8 +3621,7 @@ msgid "Table"
msgstr "Taulukkoa"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Sisällys"
@@ -3585,7 +3648,6 @@ msgstr "Poistettavat tagit"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4426,6 +4488,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Unohditko salasanasi?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4536,6 +4603,11 @@ msgstr "Milloin"
msgid "event_where"
msgstr "Missä"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po
index b405214240..fb52823be1 100644
--- a/packages/volto/locales/fr/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po
@@ -75,7 +75,6 @@ msgstr "Règles de contenu actives dans cette page"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -95,6 +94,11 @@ msgstr "Ajouter (liste d'objets)"
msgid "Add Addons"
msgstr "Ajouter des modules"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -311,6 +315,16 @@ msgstr "Alias"
msgid "Alias has been added"
msgstr "L'alias a été ajouté"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -330,7 +344,7 @@ msgstr "Tout"
msgid "All content"
msgstr "Tout le contenu"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Toutes les URLs alternatives existantes pour ce site"
@@ -357,7 +371,7 @@ msgstr "Laisser vide si l'image est uniquement décorative"
msgid "Alt text hint link text"
msgstr "Texte du lien de la suggestion de texte alternatif"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Autre chemin d'URL (obligatoire)"
@@ -368,7 +382,7 @@ msgstr "Autre chemin d'URL (obligatoire)"
msgid "Alternative url path must start with a slash."
msgstr "Le chemin d'URL alternatif doit commencer par une barre oblique."
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Chemin d'URL alternatif → chemin URL cible (date et heure de création, créé manuellement oui/non)"
@@ -419,6 +433,11 @@ msgstr ""
msgid "Assignments"
msgstr "Affectations"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -478,6 +497,11 @@ msgstr "Requête de base de la recherche"
msgid "Block"
msgstr "Bloc"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -504,11 +528,21 @@ msgstr "Explore"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Explorer le site, déposer l'image ou écriver l'URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Par défaut, les autorisations du conteneur de cet élément sont héritées. Si vous désactivez cette option, seules les autorisations de partage explicitement définies seront valides. Dans l'aperçu, le symbole {inherited} indique une valeur héritée. De même, le symbole {global} indique un rôle global, qui est géré par l'administrateur du site."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -837,6 +871,16 @@ msgstr ""
msgid "Create working copy"
msgstr "Créer une copie de travail"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1183,6 +1227,11 @@ msgstr "Les adresses e-mails ne correspondent pas."
msgid "Edit"
msgstr "Modifier"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1294,14 +1343,18 @@ msgstr "Saisissez votre nom complet (par exemple : John Smith)"
msgid "Enter map Embed Code"
msgstr "Saisissez le code intégré de la carte"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Saisissez le chemin absolu de la cible. La cible doit exister ou être un chemin d'URL alternatif existant vers la cible."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Saisissez le chemin absolu où l'URL alternative doit exister. Le chemin doit commencer par '/'. Seules les URL qui aboutissent à une page 404 introuvable entraîneront une redirection."
@@ -1347,11 +1400,6 @@ msgstr ""
msgid "Error"
msgstr "Erreur"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Erreur"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1377,6 +1425,11 @@ msgstr ""
msgid "Event view"
msgstr "Vue d'événement"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1488,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Fichiers téléchargés : {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1497,7 +1551,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr "Règles de filtre :"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Filtrer par préfixe"
@@ -1939,6 +1993,11 @@ msgstr "Langage"
msgid "Language independent field."
msgstr "Champ indépendant de la langue."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2143,6 +2202,11 @@ msgstr "Gérer les traductions pour {title}"
msgid "Manual"
msgstr "Manuel"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3559,8 +3623,7 @@ msgid "Table"
msgstr "Table"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Table des matières"
@@ -3587,7 +3650,6 @@ msgstr "Mots clés à supprimer"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4428,6 +4490,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Mot de passe oublié"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4538,6 +4605,11 @@ msgstr "Quand"
msgid "event_where"
msgstr "Où"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po
index c21aa3608f..0028b6e413 100644
--- a/packages/volto/locales/hi/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po
@@ -68,7 +68,6 @@ msgstr "पेज में सक्रिए कंटेंट रूल्
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -88,6 +87,11 @@ msgstr "जोड़े (वस्तु सूची)"
msgid "Add Addons"
msgstr "नए ऐड-ऑन को यहां दिखाने के लिए, उन्हें अपने कॉन्फ़िगरेशन में जोड़ें, बनाएं और सर्वर प्रक्रिया को पुनरारंभ करें। विस्तृत निर्देशों के लिए देखें"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -304,6 +308,16 @@ msgstr "उपनाम"
msgid "Alias has been added"
msgstr "उपनाम जोड़ा गया है"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -323,7 +337,7 @@ msgstr "सभी"
msgid "All content"
msgstr "सभी कंटेंट"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "इस साइट के लिए सभी मौजूदा वैकल्पिक यूआरएल"
@@ -350,7 +364,7 @@ msgstr "अगर छवि केवल सजावटी है तो खा
msgid "Alt text hint link text"
msgstr "छवि का उद्देश्य वर्णन करें।"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "वैकल्पिक URL पथ (आवश्यक)"
@@ -361,7 +375,7 @@ msgstr "वैकल्पिक URL पथ (आवश्यक)"
msgid "Alternative url path must start with a slash."
msgstr "वैकल्पिक URL पथ को स्लैश के साथ शुरू करना चाहिए।"
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "वैकल्पिक URL पथ → लक्ष्य URL पथ (निर्माण की तारीख और समय, मैन्युअल रूप से बनाया गया है/नहीं)"
@@ -412,6 +426,11 @@ msgstr "{entry} को {role} भूमिका का काम दें"
msgid "Assignments"
msgstr "असाइनमेंट"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -471,6 +490,11 @@ msgstr "मूल खोज क्वेरी"
msgid "Block"
msgstr "ब्लॉक"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -497,11 +521,21 @@ msgstr "ब्राउज़ करें"
msgid "Browse the site, drop an image, or type a URL"
msgstr "साइट ब्राउज़ करें, एक छवि छोड़ें, या एक URL टाइप करें"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "डिफ़ॉल्ट रूप से, इस आइटम के कंटेनर से अनुमतियाँ विरासत में होती हैं। यदि आप इसे अक्षम करते हैं, तो केवल स्पष्ट रूप से परिभागित अनुमतियाँ मान्य होंगी। अवलोकन में, प्रतीक {inherited} एक विरासत मान को दर्शाता है। उसी तरह, प्रतीक {global} एक वैश्विक भूमिका को दर्शाता है, जो साइट प्रशासक द्वारा प्रबंधित की जाती है।"
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -830,6 +864,16 @@ msgstr "लक्ष्य के साथ संबंध बनाएं य
msgid "Create working copy"
msgstr "काम की प्रतिलिपि बनाएं"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1176,6 +1220,11 @@ msgstr "ईमेल पते मेल नहीं खाते।"
msgid "Edit"
msgstr "संपादित करें"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1287,14 +1336,18 @@ msgstr "पूरा नाम दर्ज करें, उदाहरण क
msgid "Enter map Embed Code"
msgstr "नक्शे का एम्बेड कोड दर्ज करें"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "लक्ष्य का पूर्ण रास्ता दर्ज करें। लक्ष्य मौजूद होना चाहिए या लक्ष्य के लिए मौजूदा वैकल्पिक URL पथ होना चाहिए।"
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "वैकल्पिक URL होनी चाहिए जहां का पूर्ण रास्ता दर्ज करें। पथ '/ ' से प्रारंभ होना चाहिए। केवल वे URL जिनके परिणामस्वरूप 404 नहीं मिला पेज एक रिडायरेक्ट होगा।"
@@ -1340,11 +1393,6 @@ msgstr "एन्ट्रीज़"
msgid "Error"
msgstr "त्रुटि"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "त्रुटि"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1370,6 +1418,11 @@ msgstr ""
msgid "Event view"
msgstr "आयोजन दृश्य"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1481,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr ""फ़ाइलें अपलोड की गईं: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr "फ़िल्टर"
@@ -1490,7 +1544,7 @@ msgstr "फ़िल्टर"
msgid "Filter Rules:"
msgstr "फ़िल्टर नियम:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "उपसर्ग द्वारा फ़िल्टर करें"
@@ -1932,6 +1986,11 @@ msgstr "भाषा"
msgid "Language independent field."
msgstr "भाषा स्वतंत्र क्षेत्र।"
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2136,6 +2195,11 @@ msgstr "{title} के लिए अनुवाद प्रबंधित क
msgid "Manual"
msgstr "मैनुअल"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3552,8 +3616,7 @@ msgid "Table"
msgstr "तालिका"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "सारांश की तालिका"
@@ -3580,7 +3643,6 @@ msgstr "हटाने के लिए टैग"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4421,6 +4483,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "पासवर्ड भूल गए?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4531,6 +4598,11 @@ msgstr "कब"
msgid "event_where"
msgstr "कहाँ"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po
index fbdee8436e..d8f9c6b6ec 100644
--- a/packages/volto/locales/it/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/it/LC_MESSAGES/volto.po
@@ -68,7 +68,6 @@ msgstr "Regole di contenuto attive in questa Pagina"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -88,6 +87,11 @@ msgstr "Aggiungi"
msgid "Add Addons"
msgstr "Aggiungi Add-ons"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -304,6 +308,16 @@ msgstr "Alias"
msgid "Alias has been added"
msgstr "L'alias è stato aggiunto"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -323,7 +337,7 @@ msgstr "Tutti"
msgid "All content"
msgstr "Tutti i contenuti"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Tutti gli url alternativi per questo sito"
@@ -350,7 +364,7 @@ msgstr "Lascia vuoto se l'immagine è decorativa."
msgid "Alt text hint link text"
msgstr "Descrivi lo scopo dell'immagine."
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Percorso url alternativo (Obbligatorio)"
@@ -361,7 +375,7 @@ msgstr "Percorso url alternativo (Obbligatorio)"
msgid "Alternative url path must start with a slash."
msgstr "Il percorso url alternativo comincia con uno slash "
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Percorso url alternativo → percorso url di destinazione (data e ora di creazione, creata manualmente sì/no)"
@@ -412,6 +426,11 @@ msgstr "Assegnare il ruolo di {role} a {entry}"
msgid "Assignments"
msgstr "Assegnazione"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -471,6 +490,11 @@ msgstr "Ricerca iniziale"
msgid "Block"
msgstr "Blocco"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -497,11 +521,21 @@ msgstr "Sfoglia"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Sfoglia i contenuti, rilascia un'immagine o digita un URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Di norma, i permessi di questo elemento vengono ereditati dal contenitore. Se disabiliti questa opzione, verranno considerati solo i permessi di condivisione definiti esplicitamente. Nel sommario, il simbolo {inherited} indica una impostazione ereditata. Analogamente, il simbolo {global} indica un ruolo globale, che è gestito dall'amministratore del sito"
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -589,7 +623,7 @@ msgstr "Modifiche salvate."
#. Default: "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later."
#: components/manage/Blocks/Teaser/schema
msgid "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later."
-msgstr ""
+msgstr "Seleziona questa casella per personalizzare il titolo, la descrizione o l'immagine dell'oggetto di riferimento per questo teaser. Lascia la casella vuota per mostrare automaticamente gli aggiornamenti del contenuto se viene modificato in seguito."
#. Default: "Checkbox"
#: components/manage/Widgets/SchemaWidget
@@ -830,6 +864,16 @@ msgstr "Crea o elimina relazioni con la destinazione"
msgid "Create working copy"
msgstr "Crea copia di lavoro"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -874,7 +918,7 @@ msgstr "Password corrente"
#. Default: "Customize teaser content"
#: components/manage/Blocks/Teaser/schema
msgid "Customize teaser content"
-msgstr ""
+msgstr "Personalizza il contenuto del teaser"
#. Default: "Cut"
#: components/manage/Actions/Actions
@@ -986,7 +1030,7 @@ msgstr "Elimina utente"
#. Default: "Action deleted"
#: components/manage/Controlpanels/Rules/ConfigureRule
msgid "Delete action"
-msgstr "Cancella azione"
+msgstr "Elimina azione"
#. Default: "Delete blocks"
#: helpers/MessageLabels/MessageLabels
@@ -1011,12 +1055,12 @@ msgstr "Elimina riga"
#. Default: "Delete selected items?"
#: components/manage/Contents/Contents
msgid "Delete selected items?"
-msgstr ""
+msgstr "Vuoi cancellare l'elemento selezionato?"
#. Default: "Delete this item?"
#: components/manage/Contents/Contents
msgid "Delete this item?"
-msgstr ""
+msgstr "Vuoi cancellare questo elemento?"
#. Default: "Deleted"
#: components/manage/Controlpanels/Rules/Rules
@@ -1176,6 +1220,11 @@ msgstr "Gli indirizzi e-mail non corrispondono"
msgid "Edit"
msgstr "Modifica"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1287,14 +1336,18 @@ msgstr "Inserisci il tuo nome completo, ad esempio Mario Rossi."
msgid "Enter map Embed Code"
msgstr "Inserisci il codice di incorporamento della mappa"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Inserisci il path assoluto per la destinazione. La destinazione deve già esistere o essere un url alternativo per la destinazione."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Inserisci il path assoluto per cui esiste un url alternativo. Il path deve iniziare con '/'. Solo gli url che portano a una pagina '404 not found' causeranno una redirect."
@@ -1322,7 +1375,7 @@ msgstr "Inserisci il tuo username per la verifica."
#. Default: "Entries"
#: components/manage/Blocks/ToC/Schema
msgid "Entries"
-msgstr ""
+msgstr "Elementi"
#. Default: "Error"
#: components/manage/Add/Add
@@ -1340,11 +1393,6 @@ msgstr ""
msgid "Error"
msgstr "Errore"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Errore"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1353,7 +1401,7 @@ msgstr "Evento"
#. Default: "Event end date must be on or after {startDateValueOrStartFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event end date must be on or after {startDateValueOrStartFieldName}"
-msgstr ""
+msgstr "La data di fine evento deve essere uguale o successiva al {startDateValueOrStartFieldName}"
#. Default: "Event listing"
#: config/Views
@@ -1363,13 +1411,18 @@ msgstr "Elenco eventi"
#. Default: "Event start date must be on or before {endDateValueOrEndFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event start date must be on or before {endDateValueOrEndFieldName}"
-msgstr ""
+msgstr "La data di inizio evento essere uguale o precedente al {endDateValueOrEndFieldName}"
#. Default: "Event view"
#: config/Views
msgid "Event view"
msgstr "Vista evento"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1481,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "File caricati: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr "Filtra"
@@ -1490,7 +1544,7 @@ msgstr "Filtra"
msgid "Filter Rules:"
msgstr "Regole di filtro:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Filtra per prefisso"
@@ -1605,7 +1659,7 @@ msgstr "Gruppo creato"
#. Default: "Group deleted"
#: helpers/MessageLabels/MessageLabels
msgid "Group deleted"
-msgstr ""
+msgstr "Gruppo eliminato"
#. Default: "Group roles updated"
#: helpers/MessageLabels/MessageLabels
@@ -1670,7 +1724,7 @@ msgstr "Nascondi i filtri"
#. Default: "Hide title"
#: components/manage/Blocks/ToC/Schema
msgid "Hide title"
-msgstr ""
+msgstr "Nascondi il titolo"
#. Default: "History"
#: components/manage/History/History
@@ -1863,12 +1917,12 @@ msgstr "Blocco non valido - Salvando, verrà rimosso"
#. Default: "Invalid teaser source"
#: components/manage/Blocks/Teaser/Data
msgid "Invalid teaser source"
-msgstr ""
+msgstr "Sorgente non valida per il teaser"
#. Default: "It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them."
#: helpers/MessageLabels/MessageLabels
msgid "It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them."
-msgstr ""
+msgstr "Non è consentito effettuare contemporaneamente l'inserimento della password e la richiesta dell'invio del messaggio di reimpostazione della password tramite e-mail. È necessario selezionare una di esse."
#. Default: "Item batch size"
#: components/manage/Widgets/QuerystringWidget
@@ -1932,6 +1986,11 @@ msgstr "Lingua"
msgid "Language independent field."
msgstr "Campo indipendete dalla lingua"
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2007,7 +2066,7 @@ msgstr "Vista collegamento"
#. Default: "Link settings"
#: components/manage/Blocks/Image/schema
msgid "Link settings"
-msgstr ""
+msgstr "Impostazioni Link"
#. Default: "Link Title"
#: components/manage/Blocks/Listing/schema
@@ -2098,7 +2157,7 @@ msgstr "Nome utente"
#. Default: "Logo of"
#: components/theme/Logo/Logo
msgid "Logo of"
-msgstr ""
+msgstr "Logo di"
#. Default: "Logout"
#: components/manage/Toolbar/PersonalTools
@@ -2136,6 +2195,11 @@ msgstr "Gestisci le traduzioni per {title}"
msgid "Manual"
msgstr "Manuale"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -2518,7 +2582,7 @@ msgstr "Ordine"
#. Default: "Ordered"
#: components/manage/Blocks/ToC/Schema
msgid "Ordered"
-msgstr ""
+msgstr "Ordinato"
#. Default: "Origin"
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -2782,7 +2846,7 @@ msgstr "Elementi collegati a questo contenuto in {relationship}"
#. Default: "Refresh source content"
#: components/manage/Blocks/Teaser/Data
msgid "Refresh source content"
-msgstr ""
+msgstr "Ricarica il sorgente del contenuto"
#. Default: "Register"
#: components/theme/Anontools/Anontools
@@ -3224,7 +3288,7 @@ msgstr "Invia una mail di conferma con un link per impostare la password."
#. Default: "Server Error"
#: components/theme/Error/ServerError
msgid "Server Error"
-msgstr ""
+msgstr "Errore del Server"
#. Default: "Set my password"
#: components/theme/PasswordReset/PasswordReset
@@ -3370,7 +3434,7 @@ msgstr "Piccolo"
#. Default: "Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
#: components/manage/Contents/Contents
msgid "Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
-msgstr ""
+msgstr "Alcuni elementi sono anche una cartella. Eliminandoli eliminerai {containedItemsToDelete} {variation} dentro le cartelle."
#. Default: "Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
#: components/manage/Contents/Contents
@@ -3422,7 +3486,7 @@ msgstr "Ordinato"
#. Default: "Sorted on"
#: components/manage/Blocks/Search/components/SortOn
msgid "Sorted on"
-msgstr ""
+msgstr "Ordina in base a"
#. Default: "Source"
#: components/manage/Blocks/HTML/Edit
@@ -3472,7 +3536,7 @@ msgstr "Status"
#. Default: "Sticky"
#: components/manage/Blocks/ToC/Schema
msgid "Sticky"
-msgstr ""
+msgstr "Persistente"
#. Default: "Stop compare"
#: components/manage/Multilingual/CompareLanguages
@@ -3502,7 +3566,7 @@ msgstr "Oggetto"
#. Default: "Submit"
#: components/manage/AnchorPlugin/components/LinkButton/AddLinkForm
msgid "Submit"
-msgstr ""
+msgstr "Invia"
#. Default: "Success"
#: components/manage/Actions/Actions
@@ -3552,8 +3616,7 @@ msgid "Table"
msgstr "Tabella"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Indice dei contenuti"
@@ -3580,7 +3643,6 @@ msgstr "Categorie da rimuovere"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -3668,12 +3730,12 @@ msgstr "L'indirizzo del collegamento è:"
#. Default: "The number of items must be greater than or equal to {minItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be greater than or equal to {minItems}"
-msgstr ""
+msgstr "Il numero di elementi deve essere maggiore o uguale a {minItems}"
#. Default: "The number of items must be less than or equal to {maxItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be less than or equal to {maxItems}"
-msgstr ""
+msgstr "Il numero di elementi deve essere minore o uguale a {maxItems}"
#. Default: "The provided alternative url already exists!"
#: components/manage/Aliases/Aliases
@@ -3694,7 +3756,7 @@ msgstr "La configurazione del sito è obsoleta e deve essere aggiornata."
#. Default: "The value does not match the pattern {pattern}"
#: helpers/MessageLabels/MessageLabels
msgid "The value does not match the pattern {pattern}"
-msgstr ""
+msgstr "I valori non corrispondono al pattern {pattern}"
#. Default: "The working copy was discarded"
#: components/manage/Toolbar/More
@@ -3709,18 +3771,18 @@ msgstr "{plonecms} è {copyright} 2000-{current_year} della {plonefoundation} ed
#. Default: "There are no groups with the searched criteria"
#: helpers/MessageLabels/MessageLabels
msgid "There are no groups with the searched criteria"
-msgstr ""
+msgstr "Non ci sono gruppi con i criteri ricercati"
#. Default: "There are no users with the searched criteria"
#: helpers/MessageLabels/MessageLabels
msgid "There are no users with the searched criteria"
-msgstr ""
+msgstr "Non ci sono utenti con i criteri ricercati"
#. Default: "There are some errors."
#: components/manage/Add/Add
#: components/manage/Edit/Edit
msgid "There are some errors."
-msgstr ""
+msgstr "Ci sono degli errori."
#. Default: "There is a configuration problem on the backend"
#: components/theme/CorsError/CorsError
@@ -4003,7 +4065,7 @@ msgstr "Aggiorna"
#. Default: "Update User"
#: helpers/MessageLabels/MessageLabels
msgid "Update User"
-msgstr ""
+msgstr "Aggiorna Utente"
#. Default: "Update installed addons"
#: components/manage/Controlpanels/AddonsControlpanel
@@ -4101,7 +4163,7 @@ msgstr "Utente creato"
#. Default: "User deleted"
#: helpers/MessageLabels/MessageLabels
msgid "User deleted"
-msgstr ""
+msgstr "Utente eliminato"
#. Default: "User name"
#: components/manage/Controlpanels/Users/UsersControlpanel
@@ -4116,7 +4178,7 @@ msgstr "Ruoli utente aggiornati"
#. Default: "User updated successfuly"
#: helpers/MessageLabels/MessageLabels
msgid "User updated successfuly"
-msgstr ""
+msgstr "Utente aggiornato con successo"
#. Default: "Username"
#: helpers/MessageLabels/MessageLabels
@@ -4175,7 +4237,7 @@ msgstr "Mostra le modifiche"
#. Default: "View links and references to this item"
#: components/manage/Contents/Contents
msgid "View links and references to this item"
-msgstr ""
+msgstr "Visualizza i collegamenti e i riferimenti a questo elemento"
#. Default: "View this revision"
#: components/manage/History/History
@@ -4421,6 +4483,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Hai dimenticato la tua password?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4531,6 +4598,11 @@ msgstr "Quando"
msgid "event_where"
msgstr "Dove"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
@@ -4544,7 +4616,7 @@ msgstr "elimina intIds e ricrea le relazioni"
#. Default: "
Regenerate intIds (tokens of relations in relation catalog)
Rebuild relations
Check the log for details!
Warning: If you have add-ons relying on intIds, you should not flush them.
Rigenera intIds (i token delle relazioni nel catalogo delle relazioni)
Ricrea le relazioni
Controlla il log per i dettagli!
Attenzione: Se ci sono prodotti aggiuntivi che hanno bisogno degli intIds, non dovresti eliminarli.
"
#. Default: "Head title"
#: components/manage/Blocks/Teaser/schema
@@ -4594,12 +4666,12 @@ msgstr "Pubblicato internamente"
#. Default: "item"
#: components/manage/Contents/Contents
msgid "item"
-msgstr ""
+msgstr "elemento"
#. Default: "items"
#: components/manage/Contents/Contents
msgid "items"
-msgstr ""
+msgstr "elementi"
#. Default: "My email is"
#: components/theme/PasswordReset/RequestPasswordReset
@@ -4743,12 +4815,12 @@ msgstr "ricrea le relazioni"
#. Default: "reference"
#: components/manage/Contents/Contents
msgid "reference"
-msgstr ""
+msgstr "riferimento"
#. Default: "references"
#: components/manage/Contents/Contents
msgid "references"
-msgstr ""
+msgstr "riferimenti"
#. Default: "results"
#: components/theme/Search/Search
@@ -4968,7 +5040,7 @@ msgstr "percorso di destinazione"
#. Default: "Text"
#: config/Blocks
msgid "text"
-msgstr ""
+msgstr "Testo"
#. Default: "Title"
#: config/Blocks
diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po
index 60d63a02e7..c6ba730668 100644
--- a/packages/volto/locales/ja/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po
@@ -73,7 +73,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -93,6 +92,11 @@ msgstr ""
msgid "Add Addons"
msgstr "以下のリストに新しいアドオンが表示されるようにするには、それをビルドアウト設定(buildout.cfg)に追加し、ビルドアウトコマンド(bin/buildout)を実行した後に、サーバプロセスを再起動します。"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -309,6 +313,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -328,7 +342,7 @@ msgstr "すべて"
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -355,7 +369,7 @@ msgstr ""
msgid "Alt text hint link text"
msgstr ""
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -366,7 +380,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -417,6 +431,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -476,6 +495,11 @@ msgstr ""
msgid "Block"
msgstr "ブロック"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -502,11 +526,21 @@ msgstr "参照"
msgid "Browse the site, drop an image, or type a URL"
msgstr "参照"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "デフォルトでは、このアイテムのコンテナからのパーミッションが継承されます。これを無効にすると、明示的に定義されたパーミッションだけが有効になります。概要画面で {inherited} シンボルが継承された値を示します。同様に、{global} シンボルはグローバルロールを示します。これはサイト管理者によって管理されるものです。"
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -835,6 +869,16 @@ msgstr ""
msgid "Create working copy"
msgstr ""
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1181,6 +1225,11 @@ msgstr "メールアドレスが見つかりません。"
msgid "Edit"
msgstr "編集"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1292,14 +1341,18 @@ msgstr "氏名(フルネーム)を入力。例: Hanako Suzuki や 山田
msgid "Enter map Embed Code"
msgstr "地図のEmbedコードを入力"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1345,11 +1398,6 @@ msgstr ""
msgid "Error"
msgstr "エラー"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1375,6 +1423,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1486,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "アップロードされたファイル: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1495,7 +1549,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1937,6 +1991,11 @@ msgstr "言語"
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2141,6 +2200,11 @@ msgstr "{title}の翻訳を管理する"
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3557,8 +3621,7 @@ msgid "Table"
msgstr "テーブル"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "目次"
@@ -3585,7 +3648,6 @@ msgstr "削除をするタグ"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4426,6 +4488,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "パスワードを忘れた?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4536,6 +4603,11 @@ msgstr "日時"
msgid "event_where"
msgstr "場所"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po
index c6330673c2..ab60d22d27 100644
--- a/packages/volto/locales/nl/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po
@@ -72,7 +72,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -92,6 +91,11 @@ msgstr ""
msgid "Add Addons"
msgstr ""
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -308,6 +312,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -327,7 +341,7 @@ msgstr "Alles"
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -354,7 +368,7 @@ msgstr ""
msgid "Alt text hint link text"
msgstr ""
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -365,7 +379,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -416,6 +430,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -475,6 +494,11 @@ msgstr ""
msgid "Block"
msgstr ""
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -501,11 +525,21 @@ msgstr "Bladeren"
msgid "Browse the site, drop an image, or type a URL"
msgstr ""
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Standaard worden de rechten overgenomen van de bovenliggende map. Als u dit uitgeschakeld, worden alleen de expliciet toegekende rechten gebruikt. In het overzicht geeft het symbool ${image_confirm_icon} een overgenomen waarde aan. Vergelijkbaar geeft het icoon ${image_link_icon} een globale rol aan, die wordt beheerd door de websitebeheerder."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -834,6 +868,16 @@ msgstr ""
msgid "Create working copy"
msgstr ""
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1180,6 +1224,11 @@ msgstr ""
msgid "Edit"
msgstr "Bewerken"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1291,14 +1340,18 @@ msgstr "Vul de volledige naam in, bijvoorbeeld Jan Smit."
msgid "Enter map Embed Code"
msgstr ""
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1344,11 +1397,6 @@ msgstr ""
msgid "Error"
msgstr ""
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1374,6 +1422,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1485,6 +1538,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Geüploade bestanden: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1494,7 +1548,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1936,6 +1990,11 @@ msgstr "Taal"
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2140,6 +2199,11 @@ msgstr ""
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3556,8 +3620,7 @@ msgid "Table"
msgstr ""
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr ""
@@ -3584,7 +3647,6 @@ msgstr "Te verwijderen tags"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4425,6 +4487,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr ""
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4535,6 +4602,11 @@ msgstr ""
msgid "event_where"
msgstr ""
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po
index f7350c0098..075a03f3d3 100644
--- a/packages/volto/locales/pt/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po
@@ -73,7 +73,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -93,6 +92,11 @@ msgstr ""
msgid "Add Addons"
msgstr ""
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -309,6 +313,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -328,7 +342,7 @@ msgstr "Tudo"
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -355,7 +369,7 @@ msgstr ""
msgid "Alt text hint link text"
msgstr ""
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -366,7 +380,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -417,6 +431,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -476,6 +495,11 @@ msgstr ""
msgid "Block"
msgstr "Bloco"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -502,11 +526,21 @@ msgstr "Navegar"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Navegue no website, largue uma imagem ou escreva um URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Por predefinição, as permissões do contentor deste item são herdadas. Se desactivar isto, apenas serão válidas as permissões de partilha explicitamente definidas. No resumo, o símbolo {inherited} indica um valor herdado. Do mesmo modo, o símbolo {global} indica um papel global, que é gerido pelo administrador do sítio."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -835,6 +869,16 @@ msgstr ""
msgid "Create working copy"
msgstr ""
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1181,6 +1225,11 @@ msgstr "Os endereços de e-mail não coincidem."
msgid "Edit"
msgstr "Editar"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1292,14 +1341,18 @@ msgstr "Escreva o nome completo. Por exemplo, José Silva."
msgid "Enter map Embed Code"
msgstr ""
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1345,11 +1398,6 @@ msgstr ""
msgid "Error"
msgstr "Erro"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1375,6 +1423,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1486,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Arquivos enviados: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1495,7 +1549,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1937,6 +1991,11 @@ msgstr "Idioma"
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2141,6 +2200,11 @@ msgstr ""
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3557,8 +3621,7 @@ msgid "Table"
msgstr "Tabela"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr ""
@@ -3585,7 +3648,6 @@ msgstr "Etiquetas a remover"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4426,6 +4488,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "box_forgot_password_option"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4536,6 +4603,11 @@ msgstr ""
msgid "event_where"
msgstr ""
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po
index 22e61c7f06..e7fee30166 100644
--- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: Plone\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-04-27T19:30:59.079Z\n"
-"PO-Revision-Date: 2022-11-14 19:36-0300\n"
+"PO-Revision-Date: 2024-09-15 14:20-0300\n"
"Last-Translator: Érico Andrei \n"
"Language: pt_BR\n"
"Language-Team: Portuguese (https://www.transifex.com/plone/teams/14552/pt/)\n"
@@ -15,7 +15,7 @@ msgstr ""
"Language-Name: Português do Brasil\n"
"Preferred-Encodings: utf-8\n"
"Domain: volto\n"
-"X-Generator: Poedit 3.2\n"
+"X-Generator: Poedit 3.5\n"
#. Default: "
Add some HTML here
"
#: components/manage/Blocks/HTML/Edit
@@ -74,7 +74,6 @@ msgstr "Regras de conteúdo ativas nesta página"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -94,6 +93,11 @@ msgstr "Adicionar"
msgid "Add Addons"
msgstr "Adicionar complementos"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -142,7 +146,7 @@ msgstr "Adicionar bloco"
#. Default: "Add block in position {index}"
#: components/manage/Blocks/Container/NewBlockAddButton
msgid "Add block in position {index}"
-msgstr ""
+msgstr "Adicionar bloco na posição {index}"
#. Default: "Add block…"
#: helpers/MessageLabels/MessageLabels
@@ -310,6 +314,16 @@ msgstr "Alternativa"
msgid "Alias has been added"
msgstr "Alternativa foi adicionada"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -329,7 +343,7 @@ msgstr "Todos"
msgid "All content"
msgstr "Todo o conteúdo"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "Todas as URLs alternativas para este site"
@@ -356,7 +370,7 @@ msgstr "Deixe em branco se a imagem for puramente decorativa"
msgid "Alt text hint link text"
msgstr "Descreve o propósito da imagem"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "Caminho da URL alternativa (Obrigatório)"
@@ -367,7 +381,7 @@ msgstr "Caminho da URL alternativa (Obrigatório)"
msgid "Alternative url path must start with a slash."
msgstr "Caminho da URL alternativa deve iniciar com uma barra."
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "Caminho da URL alternativa → caminho da URL de destino (data e hora da criação, criado manualmente sim/não)"
@@ -418,6 +432,11 @@ msgstr "Atribuir o papel de {role} à {entry}"
msgid "Assignments"
msgstr "Atribuições"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -477,6 +496,11 @@ msgstr "Base da consulta para busca"
msgid "Block"
msgstr "Bloco"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -503,11 +527,21 @@ msgstr "Procurar"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Navegue no site, arraste uma imagem ou digite uma URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "Por padrão, as permissões do container deste item são herdadas. Se você desabilitar isso, apenas as permissões de compartilhamento definidas explicitamente serão válidas. Na visão geral, o símbolo {inherited} indica um valor herdado. Da mesma forma, o símbolo {global} indica uma função global que é gerenciada pelo administrador do site."
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -595,7 +629,7 @@ msgstr "Alterações guardadas."
#. Default: "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later."
#: components/manage/Blocks/Teaser/schema
msgid "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later."
-msgstr ""
+msgstr "Marque essa caixa para personalizar o título, a descrição ou a imagem do item de conteúdo de destino para esse teaser. Deixe-a desmarcada para mostrar atualizações no item de conteúdo de destino se ele for editado posteriormente."
#. Default: "Checkbox"
#: components/manage/Widgets/SchemaWidget
@@ -750,7 +784,7 @@ msgstr "Itens contidos"
#. Default: "Container settings"
#: components/manage/Blocks/Container/SimpleContainerToolbar
msgid "Container settings"
-msgstr ""
+msgstr "Configurações de contêineres"
#. Default: "Content"
#: components/manage/Controlpanels/Controlpanels
@@ -781,7 +815,7 @@ msgstr "Regras de conteúdo de pastas pai"
#. Default: "Content that links to or references {title}"
#: components/manage/LinksToItem/LinksToItem
msgid "Content that links to or references {title}"
-msgstr ""
+msgstr "Conteúdo com links ou referências para {title}"
#. Default: "Content type created"
#: components/manage/Controlpanels/ContentTypes
@@ -836,6 +870,16 @@ msgstr "Crie ou remova relacionamentos para o destino"
msgid "Create working copy"
msgstr "Criar cópia de trabalho"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -880,7 +924,7 @@ msgstr "Senha atual"
#. Default: "Customize teaser content"
#: components/manage/Blocks/Teaser/schema
msgid "Customize teaser content"
-msgstr ""
+msgstr "Personalizar o conteúdo do destaque"
#. Default: "Cut"
#: components/manage/Actions/Actions
@@ -1017,12 +1061,12 @@ msgstr "Excluir linha"
#. Default: "Delete selected items?"
#: components/manage/Contents/Contents
msgid "Delete selected items?"
-msgstr ""
+msgstr "Excluir os itens selecionados?"
#. Default: "Delete this item?"
#: components/manage/Contents/Contents
msgid "Delete this item?"
-msgstr ""
+msgstr "Apagar este item?"
#. Default: "Deleted"
#: components/manage/Controlpanels/Rules/Rules
@@ -1032,7 +1076,7 @@ msgstr "Removida"
#. Default: "Deleting this item breaks {brokenReferences} {variation}."
#: components/manage/Contents/Contents
msgid "Deleting this item breaks {brokenReferences} {variation}."
-msgstr ""
+msgstr "A exclusão desse item quebra {brokenReferences} {variation}."
#. Default: "Depth"
#: components/manage/Widgets/QuerystringWidget
@@ -1182,6 +1226,11 @@ msgstr "Os endereços de e-mail não coincidem."
msgid "Edit"
msgstr "Editar"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1293,14 +1342,18 @@ msgstr "Digite o nome completo. Por exemplo, José da Silva."
msgid "Enter map Embed Code"
msgstr "Informe o código Embed do mapa"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "Informe o caminho absoluto do destino. O destino deve existir ou ser um caminho da URL alternativa existente para o destino."
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "Informe o caminho absoluto onde a URL alternativa deve existir. O caminho deve começar com '/'. Somente URLs que resultam em uma página 404 não encontrada resultarão em um redirecionamento ocorrendo."
@@ -1328,7 +1381,7 @@ msgstr "Informe seu nome de usuário para verificação."
#. Default: "Entries"
#: components/manage/Blocks/ToC/Schema
msgid "Entries"
-msgstr ""
+msgstr "Entradas"
#. Default: "Error"
#: components/manage/Add/Add
@@ -1346,11 +1399,6 @@ msgstr ""
msgid "Error"
msgstr "Erro"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr "Erro"
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1359,7 +1407,7 @@ msgstr "Evento"
#. Default: "Event end date must be on or after {startDateValueOrStartFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event end date must be on or after {startDateValueOrStartFieldName}"
-msgstr ""
+msgstr "A data de término do evento deve ser igual ou posterior a {startDateValueOrStartFieldName}"
#. Default: "Event listing"
#: config/Views
@@ -1369,13 +1417,18 @@ msgstr "Listagem de Evento"
#. Default: "Event start date must be on or before {endDateValueOrEndFieldName}"
#: helpers/MessageLabels/MessageLabels
msgid "Event start date must be on or before {endDateValueOrEndFieldName}"
-msgstr ""
+msgstr "A data de início do evento deve ser igual ou anterior a {endDateValueOrEndFieldName}"
#. Default: "Event view"
#: config/Views
msgid "Event view"
msgstr "Visão de Evento"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1487,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Arquivos carregados: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr "Filtrar"
@@ -1496,7 +1550,7 @@ msgstr "Filtrar"
msgid "Filter Rules:"
msgstr "Filtrar regras:"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "Filtrar por prefixo"
@@ -1596,7 +1650,7 @@ msgstr "Bloco Google Maps"
#. Default: "Grid"
#: components/manage/Blocks/Grid/schema
msgid "Grid"
-msgstr ""
+msgstr "Grade"
#. Default: "Group"
#: components/manage/Sharing/Sharing
@@ -1611,7 +1665,7 @@ msgstr "Grupo criado"
#. Default: "Group deleted"
#: helpers/MessageLabels/MessageLabels
msgid "Group deleted"
-msgstr ""
+msgstr "Grupo Excluído"
#. Default: "Group roles updated"
#: helpers/MessageLabels/MessageLabels
@@ -1676,7 +1730,7 @@ msgstr "Ocultar filtros"
#. Default: "Hide title"
#: components/manage/Blocks/ToC/Schema
msgid "Hide title"
-msgstr ""
+msgstr "Ocultar título"
#. Default: "History"
#: components/manage/History/History
@@ -1712,7 +1766,7 @@ msgstr "ID"
#. Default: "Icon View"
#: components/manage/Sidebar/ObjectBrowserBody
msgid "Icon View"
-msgstr ""
+msgstr "Visualização de ícone"
#. Default: "If all of the following conditions are met:"
#: components/manage/Controlpanels/Rules/ConfigureRule
@@ -1869,12 +1923,12 @@ msgstr "Bloco inválido"
#. Default: "Invalid teaser source"
#: components/manage/Blocks/Teaser/Data
msgid "Invalid teaser source"
-msgstr ""
+msgstr "Fonte do destaque inválida"
#. Default: "It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them."
#: helpers/MessageLabels/MessageLabels
msgid "It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them."
-msgstr ""
+msgstr "Não é permitido definir a senha e solicitar o envio da mensagem de redefinição de senha por e-mail. Por favor, selecione um deles."
#. Default: "Item batch size"
#: components/manage/Widgets/QuerystringWidget
@@ -1938,6 +1992,11 @@ msgstr "Idioma"
msgid "Language independent field."
msgstr "Campo independente da linguagem."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -1998,7 +2057,7 @@ msgstr "Link"
#. Default: "Anchor link copied to the clipboard"
#: helpers/MessageLabels/MessageLabels
msgid "Link copied to clipboard"
-msgstr ""
+msgstr "Link copiado para área de transferência"
#. Default: "Link more"
#: components/manage/Blocks/Listing/schema
@@ -2013,7 +2072,7 @@ msgstr "Visão de redireção de Link"
#. Default: "Link settings"
#: components/manage/Blocks/Image/schema
msgid "Link settings"
-msgstr ""
+msgstr "Configurações do link"
#. Default: "Link Title"
#: components/manage/Blocks/Listing/schema
@@ -2035,18 +2094,18 @@ msgstr "Linkar tradução para"
#. Default: "Linking this item with hyperlink in text"
#: components/manage/LinksToItem/LinksToItem
msgid "Linking this item with hyperlink in text"
-msgstr ""
+msgstr "Vinculação deste item com hiperlink no texto"
#. Default: "Links and references"
#: components/manage/LinksToItem/LinksToItem
#: components/manage/Toolbar/More
msgid "Links and references"
-msgstr ""
+msgstr "Links e referências"
#. Default: "List View"
#: components/manage/Sidebar/ObjectBrowserBody
msgid "List View"
-msgstr ""
+msgstr "Exibição de lista"
#. Default: "Listing"
#: components/manage/Blocks/Listing/schema
@@ -2104,7 +2163,7 @@ msgstr "Nome de usuário"
#. Default: "Logo of"
#: components/theme/Logo/Logo
msgid "Logo of"
-msgstr ""
+msgstr "Logotipo da"
#. Default: "Logout"
#: components/manage/Toolbar/PersonalTools
@@ -2142,6 +2201,11 @@ msgstr "Gerenciar traduções para {title}"
msgid "Manual"
msgstr "Manual"
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -2390,7 +2454,7 @@ msgstr "Nenhum item selecionado"
#. Default: "No links to this item found."
#: components/manage/LinksToItem/LinksToItem
msgid "No links to this item found."
-msgstr ""
+msgstr "Não foram encontrados links para esse item."
#. Default: "No map selected"
#: components/manage/Blocks/Maps/MapsSidebar
@@ -2496,7 +2560,7 @@ msgstr "Ok"
#. Default: "Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: <, >, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines."
#: components/manage/Widgets/IdWidget
msgid "Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: <, >, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines."
-msgstr ""
+msgstr "Somente caracteres de bytes de 7 bits são permitidos. Não pode conter letras maiúsculas, caracteres especiais: <, >, &, #, /, ?, ou outros que são ilegais em URLs. Não pode começar com: , aq, @@, ++. Não pode terminar com __. Não pode ser: request, contributors, ., .., “”. Não pode conter quebras de linha."
#. Default: "Open in a new tab"
#: components/manage/Blocks/Image/schema
@@ -2519,12 +2583,12 @@ msgstr "Abrir navegador de objetos"
#. Default: "Order"
#: components/manage/Sidebar/Sidebar
msgid "Order"
-msgstr ""
+msgstr "Ordem"
#. Default: "Ordered"
#: components/manage/Blocks/ToC/Schema
msgid "Ordered"
-msgstr ""
+msgstr "Ordenado"
#. Default: "Origin"
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -2534,7 +2598,7 @@ msgstr "Origem"
#. Default: "Overview of relations of all content items"
#: components/manage/LinksToItem/LinksToItem
msgid "Overview of relations of all content items"
-msgstr ""
+msgstr "Visão geral das relações de todos os itens de conteúdo"
#. Default: "Page"
#: components/manage/Toolbar/Toolbar
@@ -2778,17 +2842,17 @@ msgstr "Reduzir complexidade"
#. Default: "Referencing this item as related item"
#: components/manage/LinksToItem/LinksToItem
msgid "Referencing this item as related item"
-msgstr ""
+msgstr "Referenciando este item como item relacionado"
#. Default: "Referencing this item with {relationship}"
#: components/manage/LinksToItem/LinksToItem
msgid "Referencing this item with {relationship}"
-msgstr ""
+msgstr "Referenciando este item via {relationship}"
#. Default: "Refresh source content"
#: components/manage/Blocks/Teaser/Data
msgid "Refresh source content"
-msgstr ""
+msgstr "Atualizar o conteúdo de origem"
#. Default: "Register"
#: components/theme/Anontools/Anontools
@@ -2838,7 +2902,7 @@ msgstr "Remover"
#. Default: "Remove element {index}"
#: components/manage/Blocks/Container/EditBlockWrapper
msgid "Remove element {index}"
-msgstr ""
+msgstr "Remover elemento {index}"
#. Default: "Remove item"
#: components/manage/Widgets/ObjectListWidget
@@ -2927,7 +2991,7 @@ msgstr "Falta informação obrigatória."
#. Default: "Reset element {index}"
#: components/manage/Blocks/Container/EditBlockWrapper
msgid "Reset element {index}"
-msgstr ""
+msgstr "Redefinir elemento {index}"
#. Default: "Reset title"
#: components/manage/Widgets/VocabularyTermsWidget
@@ -3230,7 +3294,7 @@ msgstr "Envie um e-mail de confirmação com um link para definir a senha."
#. Default: "Server Error"
#: components/theme/Error/ServerError
msgid "Server Error"
-msgstr ""
+msgstr "Erro de servidor"
#. Default: "Set my password"
#: components/theme/PasswordReset/PasswordReset
@@ -3376,12 +3440,12 @@ msgstr "Pequeno"
#. Default: "Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
#: components/manage/Contents/Contents
msgid "Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
-msgstr ""
+msgstr "Alguns itens também são uma pasta. Ao excluí-los, você excluirá {containedItemsToDelete} {variation} dentro destas pastas."
#. Default: "Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
#: components/manage/Contents/Contents
msgid "Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
-msgstr ""
+msgstr "Alguns itens são referenciados por outros conteúdos. Ao excluí-los, {brokenReferences} {variation} podem ser quebrados."
#. Default: "Some relations are broken. Please fix."
#: components/manage/Controlpanels/Relations/Relations
@@ -3428,7 +3492,7 @@ msgstr "Ordenado"
#. Default: "Sorted on"
#: components/manage/Blocks/Search/components/SortOn
msgid "Sorted on"
-msgstr ""
+msgstr "Ordenado por"
#. Default: "Source"
#: components/manage/Blocks/HTML/Edit
@@ -3478,7 +3542,7 @@ msgstr "Situação"
#. Default: "Sticky"
#: components/manage/Blocks/ToC/Schema
msgid "Sticky"
-msgstr ""
+msgstr "Fixo"
#. Default: "Stop compare"
#: components/manage/Multilingual/CompareLanguages
@@ -3508,7 +3572,7 @@ msgstr "Assunto"
#. Default: "Submit"
#: components/manage/AnchorPlugin/components/LinkButton/AddLinkForm
msgid "Submit"
-msgstr ""
+msgstr "Enviar"
#. Default: "Success"
#: components/manage/Actions/Actions
@@ -3558,8 +3622,7 @@ msgid "Table"
msgstr "Tabela"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Tabela de conteúdos"
@@ -3586,7 +3649,6 @@ msgstr "Tags a serem removidas"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -3674,12 +3736,12 @@ msgstr "O endereço do link é:"
#. Default: "The number of items must be greater than or equal to {minItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be greater than or equal to {minItems}"
-msgstr ""
+msgstr "O número de itens deve ser maior ou igual a {minItems}"
#. Default: "The number of items must be less than or equal to {maxItems}"
#: helpers/MessageLabels/MessageLabels
msgid "The number of items must be less than or equal to {maxItems}"
-msgstr ""
+msgstr "O número de itens deve ser menor ou igual a {maxItems}"
#. Default: "The provided alternative url already exists!"
#: components/manage/Aliases/Aliases
@@ -3700,7 +3762,7 @@ msgstr "A configuração do site está desatualizada e precisa ser atualizada."
#. Default: "The value does not match the pattern {pattern}"
#: helpers/MessageLabels/MessageLabels
msgid "The value does not match the pattern {pattern}"
-msgstr ""
+msgstr "O valor não corresponde ao padrão {pattern}"
#. Default: "The working copy was discarded"
#: components/manage/Toolbar/More
@@ -3715,12 +3777,12 @@ msgstr "O {plonecms} tem {copyright} de 2000-{current_year} pela {plonefoundatio
#. Default: "There are no groups with the searched criteria"
#: helpers/MessageLabels/MessageLabels
msgid "There are no groups with the searched criteria"
-msgstr ""
+msgstr "Não há grupos com os critérios pesquisados"
#. Default: "There are no users with the searched criteria"
#: helpers/MessageLabels/MessageLabels
msgid "There are no users with the searched criteria"
-msgstr ""
+msgstr "Não há usuários com os critérios pesquisados"
#. Default: "There are some errors."
#: components/manage/Add/Add
@@ -3772,7 +3834,7 @@ msgstr "Esta é uma cópia de trabalho de {title}"
#. Default: "This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
#: components/manage/Contents/Contents
msgid "This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
-msgstr ""
+msgstr "Esse item também é uma pasta. Ao excluí-lo, você excluirá {containedItemsToDelete} {variation} dentro da pasta."
#. Default: "This item was locked by {creator} on {date}"
#: components/manage/LockingToastsFactory/LockingToastsFactory
@@ -4009,7 +4071,7 @@ msgstr "Atualizar"
#. Default: "Update User"
#: helpers/MessageLabels/MessageLabels
msgid "Update User"
-msgstr ""
+msgstr "Atualizar o usuário"
#. Default: "Update installed addons"
#: components/manage/Controlpanels/AddonsControlpanel
@@ -4107,7 +4169,7 @@ msgstr "Usuário criado"
#. Default: "User deleted"
#: helpers/MessageLabels/MessageLabels
msgid "User deleted"
-msgstr ""
+msgstr "Usuário excluído"
#. Default: "User name"
#: components/manage/Controlpanels/Users/UsersControlpanel
@@ -4122,7 +4184,7 @@ msgstr "Papéis do grupo atualizados"
#. Default: "User updated successfuly"
#: helpers/MessageLabels/MessageLabels
msgid "User updated successfuly"
-msgstr ""
+msgstr "Usuário atualizado com sucesso"
#. Default: "Username"
#: helpers/MessageLabels/MessageLabels
@@ -4181,12 +4243,12 @@ msgstr "Ver mudanças"
#. Default: "View links and references to this item"
#: components/manage/Contents/Contents
msgid "View links and references to this item"
-msgstr ""
+msgstr "Exibir links e referências a este item"
#. Default: "View this revision"
#: components/manage/History/History
msgid "View this revision"
-msgstr "Ver esta revisão"
+msgstr "Visualizar esta revisão"
#. Default: "View working copy"
#: components/manage/Toolbar/More
@@ -4231,7 +4293,7 @@ msgstr "Pedimos desculpas pelo inconveniente, mas a página que você estava ten
#. Default: "We apologize for the inconvenience, but there was an unexpected error on the server."
#: components/theme/Error/ServerError
msgid "We apologize for the inconvenience, but there was an unexpected error on the server."
-msgstr ""
+msgstr "Pedimos desculpas pelo inconveniente, mas ocorreu um erro inesperado no servidor."
#. Default: "We apologize for the inconvenience, but you don't have permissions on this resource."
#: components/theme/Forbidden/Forbidden
@@ -4419,7 +4481,7 @@ msgstr "Visões disponíveis"
#. Default: "Error in the block field {errorField}."
#: helpers/MessageLabels/MessageLabels
msgid "blocksFieldsErrorTitle"
-msgstr ""
+msgstr "Erro no campo de blocos {errorField}"
#. Default: "Forgot your password?"
#: components/theme/Login/Login
@@ -4427,6 +4489,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Esqueceu sua senha?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4435,12 +4502,12 @@ msgstr "Caixa de seleção"
#. Default: "column"
#: components/manage/Blocks/Grid/templates
msgid "column"
-msgstr ""
+msgstr "coluna"
#. Default: "columns"
#: components/manage/Blocks/Grid/templates
msgid "columns"
-msgstr ""
+msgstr "colunas"
#. Default: "Common"
#: config/Blocks
@@ -4537,10 +4604,15 @@ msgstr "Quando"
msgid "event_where"
msgstr "Onde"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
-msgstr ""
+msgstr "Este site não aceita arquivos maiores que {limit}"
#. Default: "flush intIds and rebuild relations"
#: helpers/MessageLabels/MessageLabels
@@ -4550,7 +4622,7 @@ msgstr "descarrega os intIds e recontrua os relacionamentos"
#. Default: "
Regenerate intIds (tokens of relations in relation catalog)
Rebuild relations
Check the log for details!
Warning: If you have add-ons relying on intIds, you should not flush them.
Regerar os intIds (tokens das relações no catálogo de relações)
Reconstruir relações
Verifique o log para detalhes!
Aviso: Se você tem complementos que dependem de intIds, não deve excluí-los.
"
#. Default: "Head title"
#: components/manage/Blocks/Teaser/schema
@@ -4580,12 +4652,12 @@ msgstr "Imagem"
#. Default: "Clear image"
#: components/manage/Blocks/Image/ImageSidebar
msgid "image_block_clear"
-msgstr ""
+msgstr "Remover imagem"
#. Default: "Image preview"
#: components/manage/Blocks/Image/ImageSidebar
msgid "image_block_preview"
-msgstr ""
+msgstr "Pré-visualização da imagem"
#. Default: "Input must be integer"
#: helpers/MessageLabels/MessageLabels
@@ -4600,12 +4672,12 @@ msgstr "Intranet"
#. Default: "item"
#: components/manage/Contents/Contents
msgid "item"
-msgstr ""
+msgstr "Item"
#. Default: "items"
#: components/manage/Contents/Contents
msgid "items"
-msgstr ""
+msgstr "Itens"
#. Default: "My email is"
#: components/theme/PasswordReset/RequestPasswordReset
@@ -4749,12 +4821,12 @@ msgstr "reconstruir relacionamentos"
#. Default: "reference"
#: components/manage/Contents/Contents
msgid "reference"
-msgstr ""
+msgstr "referência"
#. Default: "references"
#: components/manage/Contents/Contents
msgid "references"
-msgstr ""
+msgstr "referências"
#. Default: "results"
#: components/theme/Search/Search
@@ -4974,7 +5046,7 @@ msgstr "caminho do destino"
#. Default: "Text"
#: config/Blocks
msgid "text"
-msgstr ""
+msgstr "texto"
#. Default: "Title"
#: config/Blocks
@@ -5047,7 +5119,7 @@ msgstr "Sim"
#. Default: "{count, plural, one {Upload {count} file} other {Upload {count} files}}"
#: components/manage/Contents/ContentsUploadModal
msgid "{count, plural, one {Upload {count} file} other {Upload {count} files}}"
-msgstr "{count, plural, one {Enviar {count} arquivo} other {Enviar {count} arquivos}}"
+msgstr "{count, plural, one {Enviar {count} arquivo} ou {Enviar {count} arquivos}}"
#. Default: "{count} selected"
#: components/manage/Contents/Contents
diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po
index 4ccc25cc34..3219e49600 100644
--- a/packages/volto/locales/ro/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po
@@ -68,7 +68,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -88,6 +87,11 @@ msgstr "Adăugare (listă de obiecte)"
msgid "Add Addons"
msgstr "Adăugare add-on-uri"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -304,6 +308,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -323,7 +337,7 @@ msgstr "Toate"
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -350,7 +364,7 @@ msgstr "Sugestie text alternativ"
msgid "Alt text hint link text"
msgstr "Link sugestie text alternativ"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -361,7 +375,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -412,6 +426,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -471,6 +490,11 @@ msgstr "Interogare căutare de bază"
msgid "Block"
msgstr "Bloc"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -497,11 +521,21 @@ msgstr "Răsfoiește"
msgid "Browse the site, drop an image, or type a URL"
msgstr "Navigați site-ul, adaugați o imagine sau introduceți o adresă URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "În mod implicit, permisiunile din containerul acestui articol sunt moștenite. Dacă dezactivați acest lucru, numai permisiunile de partajare definite în mod explicit vor fi valide. În imagine, simbolul {inherited} indică o valoare moștenită. În mod similar, simbolul {global} indică un rol global, care este gestionat de administratorul site-ului. "
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -830,6 +864,16 @@ msgstr ""
msgid "Create working copy"
msgstr "Creați o copie de lucru"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1176,6 +1220,11 @@ msgstr "Adresele de e-mail nu se potrivesc."
msgid "Edit"
msgstr "Editează"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1287,14 +1336,18 @@ msgstr "Introduceți numele complet, de exemplu, Valentin Popescu."
msgid "Enter map Embed Code"
msgstr "Introduceți codul de integrare a hărții"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1340,11 +1393,6 @@ msgstr ""
msgid "Error"
msgstr "Eroare"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1370,6 +1418,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1481,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "Fișiere încărcate: {uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1490,7 +1544,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1932,6 +1986,11 @@ msgstr "Limba"
msgid "Language independent field."
msgstr "Câmp independent de limbă."
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2136,6 +2195,11 @@ msgstr "Gestionați traducerile pentru {title}"
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3552,8 +3616,7 @@ msgid "Table"
msgstr "Tabel"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "Cuprins"
@@ -3580,7 +3643,6 @@ msgstr "Etichete de eliminat"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4421,6 +4483,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "Ați uitat parola?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4531,6 +4598,11 @@ msgstr "Data"
msgid "event_where"
msgstr "Locație"
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot
index 7e870490fc..3d048e5ef7 100644
--- a/packages/volto/locales/volto.pot
+++ b/packages/volto/locales/volto.pot
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2024-08-10T17:00:27.117Z\n"
+"POT-Creation-Date: 2024-10-24T16:41:21.354Z\n"
"Last-Translator: Plone i18n \n"
"Language-Team: Plone i18n \n"
"Content-Type: text/plain; charset=utf-8\n"
@@ -70,7 +70,6 @@ msgstr ""
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -90,6 +89,11 @@ msgstr ""
msgid "Add Addons"
msgstr ""
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -306,6 +310,16 @@ msgstr ""
msgid "Alias has been added"
msgstr ""
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -325,7 +339,7 @@ msgstr ""
msgid "All content"
msgstr ""
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr ""
@@ -352,7 +366,7 @@ msgstr ""
msgid "Alt text hint link text"
msgstr ""
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr ""
@@ -363,7 +377,7 @@ msgstr ""
msgid "Alternative url path must start with a slash."
msgstr ""
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr ""
@@ -414,6 +428,11 @@ msgstr ""
msgid "Assignments"
msgstr ""
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -473,6 +492,11 @@ msgstr ""
msgid "Block"
msgstr ""
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -499,11 +523,21 @@ msgstr ""
msgid "Browse the site, drop an image, or type a URL"
msgstr ""
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr ""
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -832,6 +866,16 @@ msgstr ""
msgid "Create working copy"
msgstr ""
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1178,6 +1222,11 @@ msgstr ""
msgid "Edit"
msgstr ""
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1289,14 +1338,18 @@ msgstr ""
msgid "Enter map Embed Code"
msgstr ""
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr ""
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr ""
@@ -1342,11 +1395,6 @@ msgstr ""
msgid "Error"
msgstr ""
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1372,6 +1420,11 @@ msgstr ""
msgid "Event view"
msgstr ""
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1483,6 +1536,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr ""
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1492,7 +1546,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr ""
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr ""
@@ -1934,6 +1988,11 @@ msgstr ""
msgid "Language independent field."
msgstr ""
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2138,6 +2197,11 @@ msgstr ""
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3554,8 +3618,7 @@ msgid "Table"
msgstr ""
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr ""
@@ -3582,7 +3645,6 @@ msgstr ""
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4423,6 +4485,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr ""
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4533,6 +4600,11 @@ msgstr ""
msgid "event_where"
msgstr ""
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po
index cddef470f6..e1948dea26 100644
--- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po
@@ -74,7 +74,6 @@ msgstr "在此页面中活动的内容规则"
#. Default: "Add"
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ContentTypes
#: components/manage/Controlpanels/Rules/ConfigureRule
#: components/manage/Rules/Rules
@@ -94,6 +93,11 @@ msgstr "添加(对象列表)"
msgid "Add Addons"
msgstr "添加附件"
+#. Default: "Add Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Add Alternative URL"
+msgstr ""
+
#. Default: "Add Content…"
#: components/manage/Toolbar/Types
msgid "Add Content"
@@ -310,6 +314,16 @@ msgstr "别名"
msgid "Alias has been added"
msgstr "别名已添加"
+#. Default: "Aliases have been removed."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been removed."
+msgstr ""
+
+#. Default: "Aliases have been uploaded."
+#: components/manage/Controlpanels/Aliases
+msgid "Aliases have been uploaded."
+msgstr ""
+
#. Default: "Alignment"
#: components/manage/Blocks/Image/schema
#: components/manage/Blocks/LeadImage/LeadImageSidebar
@@ -329,7 +343,7 @@ msgstr "所有"
msgid "All content"
msgstr "所有内容"
-#. Default: "All existing alternative urls for this site"
+#. Default: "Existing alternative URLs for this site"
#: components/manage/Controlpanels/Aliases
msgid "All existing alternative urls for this site"
msgstr "此网站所有的替代 urls"
@@ -356,7 +370,7 @@ msgstr "替换文本提示"
msgid "Alt text hint link text"
msgstr "替换文本提示链接文本"
-#. Default: "Alternative url path (Required)"
+#. Default: "Alternative URL path (Required)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path (Required)"
msgstr "替代 URL 路径(必选)"
@@ -367,7 +381,7 @@ msgstr "替代 URL 路径(必选)"
msgid "Alternative url path must start with a slash."
msgstr "替代 URL 路径必须以斜杠开头。"
-#. Default: "Alternative url path → target url path (date and time of creation, manually created yes/no)"
+#. Default: "Alternative URL path → target URL path (date and time of creation, manually created yes/no)"
#: components/manage/Controlpanels/Aliases
msgid "Alternative url path → target url path (date and time of creation, manually created yes/no)"
msgstr "替代 URL 路径 → 目标 URL 路径(创建的日期和时间,手动创建 yes/no)"
@@ -418,6 +432,11 @@ msgstr ""
msgid "Assignments"
msgstr "分配"
+#. Default: "Automatically"
+#: components/manage/Controlpanels/Aliases
+msgid "Automatically"
+msgstr ""
+
#. Default: "Available"
#: components/manage/Controlpanels/AddonsControlpanel
msgid "Available"
@@ -477,6 +496,11 @@ msgstr "基本搜索查询"
msgid "Block"
msgstr "块"
+#. Default: "Both"
+#: components/manage/Controlpanels/Aliases
+msgid "Both"
+msgstr ""
+
#. Default: "Both email address and password are case sensitive, check that caps lock is not enabled."
#: components/theme/Login/Login
msgid "Both email address and password are case sensitive, check that caps lock is not enabled."
@@ -503,11 +527,21 @@ msgstr "浏览"
msgid "Browse the site, drop an image, or type a URL"
msgstr "浏览网站,放入一张图片,或者输入一个URL"
+#. Default: "Bulk upload CSV"
+#: components/manage/Controlpanels/Aliases
+msgid "BulkUploadAltUrls"
+msgstr ""
+
#. Default: "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
#: components/manage/Sharing/Sharing
msgid "By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
msgstr "默认情况下,条目的权限从父文件夹继承,您可以禁用继承,明确设置权限。总体来说,${inherited}符号表示这是一个继承的值,${global}符号表示这是一个由网站管理员管理的全局角色。"
+#. Default: "CSV file"
+#: components/manage/Controlpanels/Aliases
+msgid "CSVFile"
+msgstr ""
+
#. Default: "Cache Name"
#: components/manage/Controlpanels/DatabaseInformation
msgid "Cache Name"
@@ -836,6 +870,16 @@ msgstr ""
msgid "Create working copy"
msgstr "创建工作副本"
+#. Default: "Created after"
+#: components/manage/Controlpanels/Aliases
+msgid "Created after"
+msgstr ""
+
+#. Default: "Created before"
+#: components/manage/Controlpanels/Aliases
+msgid "Created before"
+msgstr ""
+
#. Default: "Created by {creator} on {date}"
#: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory
msgid "Created by {creator} on {date}"
@@ -1182,6 +1226,11 @@ msgstr "邮箱地址不匹配"
msgid "Edit"
msgstr "编辑"
+#. Default: "Edit Alternative URL"
+#: components/manage/Controlpanels/Aliases
+msgid "Edit Alternative URL"
+msgstr ""
+
#. Default: "Edit Rule"
#: components/manage/Controlpanels/Rules/EditRule
msgid "Edit Rule"
@@ -1293,14 +1342,18 @@ msgstr "输入您的姓名,如:张三"
msgid "Enter map Embed Code"
msgstr "输入 map 嵌入代码"
-#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
+#. Default: "Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target."
#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target."
msgstr "输入目标的绝对路径。目标必须存在,或者是指向目标的现有可选的url路径。"
+#. Default: "Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+#: components/manage/Controlpanels/Aliases
+msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring."
+msgstr ""
+
#. Default: "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
#: components/manage/Aliases/Aliases
-#: components/manage/Controlpanels/Aliases
msgid "Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
msgstr "输入已存在替代 URL 的绝对路径。路径必须以 '/' 开头。只有导致404 未找到页面的url才会发生重定向。"
@@ -1346,11 +1399,6 @@ msgstr ""
msgid "Error"
msgstr "错误"
-#. Default: "Error"
-#: components/manage/Controlpanels/Aliases
-msgid "ErrorHeader"
-msgstr ""
-
#. Default: "Event"
#: components/manage/Controlpanels/Rules/Rules
msgid "Event"
@@ -1376,6 +1424,11 @@ msgstr ""
msgid "Event view"
msgstr "事件视图"
+#. Default: "Example"
+#: components/manage/Controlpanels/Aliases
+msgid "Example"
+msgstr ""
+
#. Default: "Exclude from navigation"
#: components/manage/Contents/ContentsPropertiesModal
msgid "Exclude from navigation"
@@ -1487,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}"
msgstr "已上载的文件:{uploadedFiles}"
#. Default: "Filter"
+#: components/manage/Controlpanels/Aliases
#: helpers/MessageLabels/MessageLabels
msgid "Filter"
msgstr ""
@@ -1496,7 +1550,7 @@ msgstr ""
msgid "Filter Rules:"
msgstr "过滤规则"
-#. Default: "Filter by prefix"
+#. Default: "Filter by path"
#: components/manage/Controlpanels/Aliases
msgid "Filter by prefix"
msgstr "按前缀过滤"
@@ -1938,6 +1992,11 @@ msgstr "语言"
msgid "Language independent field."
msgstr "语言独立字段"
+#. Default: "This is a language independent field. Any value you enter here will overwrite the corresponding field of all members of the translation group when you save this form."
+#: components/manage/Widgets/FormFieldWrapper
+msgid "Language independent icon title"
+msgstr ""
+
#. Default: "Large"
#: components/manage/Widgets/ImageSizeWidget
msgid "Large"
@@ -2142,6 +2201,11 @@ msgstr "管理{title}的翻译"
msgid "Manual"
msgstr ""
+#. Default: "Manually"
+#: components/manage/Controlpanels/Aliases
+msgid "Manually"
+msgstr ""
+
#. Default: "Manually or automatically added?"
#: components/manage/Controlpanels/Aliases
msgid "Manually or automatically added?"
@@ -3558,8 +3622,7 @@ msgid "Table"
msgstr "表格"
#. Default: "Table of Contents"
-#: components/manage/Blocks/ToC/variations/DefaultTocRenderer
-#: components/manage/Blocks/ToC/variations/HorizontalMenu
+#: components/manage/Blocks/ToC/View
msgid "Table of Contents"
msgstr "表格内容"
@@ -3586,7 +3649,6 @@ msgstr "移除关键词"
#. Default: "Target"
#: components/manage/Blocks/Teaser/schema
-#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/Relations/BrokenRelations
#: components/manage/Controlpanels/Relations/RelationsMatrix
msgid "Target"
@@ -4427,6 +4489,11 @@ msgstr ""
msgid "box_forgot_password_option"
msgstr "忘记了密码?"
+#. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)."
+#: components/manage/Controlpanels/Aliases
+msgid "bulkUploadUrlsHelp"
+msgstr ""
+
#. Default: "Checkbox"
#: config/Blocks
msgid "checkboxFacet"
@@ -4537,6 +4604,11 @@ msgstr ""
msgid "event_where"
msgstr ""
+#. Default: "/example"
+#: components/manage/Controlpanels/Aliases
+msgid "examplePath"
+msgstr ""
+
#. Default: "This website does not accept files larger than {limit}"
#: helpers/MessageLabels/MessageLabels
msgid "fileTooLarge"
diff --git a/packages/volto/news/3839.bugfix b/packages/volto/news/3839.bugfix
new file mode 100644
index 0000000000..bdafdb23dc
--- /dev/null
+++ b/packages/volto/news/3839.bugfix
@@ -0,0 +1 @@
+Fix error "Exception: No query supplied" in the listing block, when there is no criteria selected. @tedw87
\ No newline at end of file
diff --git a/packages/volto/news/4077.feature b/packages/volto/news/4077.feature
deleted file mode 100644
index 062a72de76..0000000000
--- a/packages/volto/news/4077.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor AlbumView from class to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4819.feature b/packages/volto/news/4819.feature
deleted file mode 100644
index fe12773f69..0000000000
--- a/packages/volto/news/4819.feature
+++ /dev/null
@@ -1 +0,0 @@
-Add setting `unwantedControlPanelsFields` and use it in the function `filterControlPanelsSchema`. @wesleybl
diff --git a/packages/volto/news/4861.feature b/packages/volto/news/4861.feature
deleted file mode 100644
index c5521a45f1..0000000000
--- a/packages/volto/news/4861.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor the `Register` component from class-based to functional. @Tishasoumya-02
diff --git a/packages/volto/news/4955.feature b/packages/volto/news/4955.feature
deleted file mode 100644
index 4884c96572..0000000000
--- a/packages/volto/news/4955.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Toolbar/More component from class to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4959.feature b/packages/volto/news/4959.feature
deleted file mode 100644
index 81e63b0f64..0000000000
--- a/packages/volto/news/4959.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Blocks/LeadImage/Edit component from class to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4960.feature b/packages/volto/news/4960.feature
deleted file mode 100644
index 6b28eeee61..0000000000
--- a/packages/volto/news/4960.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Blocks/Video/Edit component from class to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4985.feature b/packages/volto/news/4985.feature
deleted file mode 100644
index 1ba74856d9..0000000000
--- a/packages/volto/news/4985.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor ControlPanel/Users/Aliases component , replacing class component lifecycle methods with functional hooks. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4986.feature b/packages/volto/news/4986.feature
deleted file mode 100644
index caaac5aa19..0000000000
--- a/packages/volto/news/4986.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Controlpanel databaseInformation from class component to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4993.feature b/packages/volto/news/4993.feature
deleted file mode 100644
index f5f831e506..0000000000
--- a/packages/volto/news/4993.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor ControlPanels/Groups RenderGroups from class components to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/4995.feature b/packages/volto/news/4995.feature
deleted file mode 100644
index 9e38ddfcec..0000000000
--- a/packages/volto/news/4995.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor AddonsControlpanel from class to functional components. @Tishasoumya
\ No newline at end of file
diff --git a/packages/volto/news/5044.feature b/packages/volto/news/5044.feature
deleted file mode 100644
index 60833498c6..0000000000
--- a/packages/volto/news/5044.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Preference/Change Password from class to functional component. @Tishasoumya-02
diff --git a/packages/volto/news/5045.feature b/packages/volto/news/5045.feature
deleted file mode 100644
index b5385619ac..0000000000
--- a/packages/volto/news/5045.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Preference/PersonalPreferences from class to functional component. @Tishasoumya-02
diff --git a/packages/volto/news/5046.feature b/packages/volto/news/5046.feature
deleted file mode 100644
index acf69da56f..0000000000
--- a/packages/volto/news/5046.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Preference/PersonalInformation from Class to Functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/5047.feature b/packages/volto/news/5047.feature
deleted file mode 100644
index 0e0d81f8ff..0000000000
--- a/packages/volto/news/5047.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Contents/ContentsUploadModal & Storybook-@Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/5048.feature b/packages/volto/news/5048.feature
deleted file mode 100644
index 756b8f5891..0000000000
--- a/packages/volto/news/5048.feature
+++ /dev/null
@@ -1 +0,0 @@
-Storybook test for ContentBreadcrumbs component. @Tishasoumya
\ No newline at end of file
diff --git a/packages/volto/news/5049.feature b/packages/volto/news/5049.feature
deleted file mode 100644
index 8dc75b2192..0000000000
--- a/packages/volto/news/5049.feature
+++ /dev/null
@@ -1 +0,0 @@
-Storybook Actions component test. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/5055.bugfix b/packages/volto/news/5055.bugfix
deleted file mode 100644
index d089f7d0b4..0000000000
--- a/packages/volto/news/5055.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix search block showing no option select in sort on property @iRohitSingh
\ No newline at end of file
diff --git a/packages/volto/news/5066.feature b/packages/volto/news/5066.feature
deleted file mode 100644
index 347026cf08..0000000000
--- a/packages/volto/news/5066.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor Display from class to functional component. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/5093.feature b/packages/volto/news/5093.feature
deleted file mode 100644
index cb0a0ab525..0000000000
--- a/packages/volto/news/5093.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor `ReferenceWidget` from class-based to functional component. @Tishasoumya
\ No newline at end of file
diff --git a/packages/volto/news/5274.bugfix b/packages/volto/news/5274.bugfix
deleted file mode 100644
index da9be01184..0000000000
--- a/packages/volto/news/5274.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Displays validation error messages on control panel forms. @wesleybl
diff --git a/packages/volto/news/5288.bugfix b/packages/volto/news/5288.bugfix
new file mode 100644
index 0000000000..e53a45483d
--- /dev/null
+++ b/packages/volto/news/5288.bugfix
@@ -0,0 +1 @@
+Fixed page changes not being announced to screen reader users. @JeffersonBledsoe
\ No newline at end of file
diff --git a/packages/volto/news/5570.internal b/packages/volto/news/5570.internal
deleted file mode 100644
index 7b26c18db0..0000000000
--- a/packages/volto/news/5570.internal
+++ /dev/null
@@ -1 +0,0 @@
-Refactored the `SelectWidget` component to a functional component. @lorstenoplo
\ No newline at end of file
diff --git a/packages/volto/news/5971.bugfix b/packages/volto/news/5971.bugfix
deleted file mode 100644
index 71d9814b79..0000000000
--- a/packages/volto/news/5971.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix `initialValue` block setting. @wesleybl
diff --git a/packages/volto/news/6071.bugfix b/packages/volto/news/6071.bugfix
deleted file mode 100644
index a6ac978c83..0000000000
--- a/packages/volto/news/6071.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-When user changes location, set the `userSession.token` value based on cookie. This fixes the login status not being properly determined by the application. @tiberiu-ichim
diff --git a/packages/volto/news/6082.breaking b/packages/volto/news/6082.breaking
new file mode 100644
index 0000000000..11bd0cc5a5
--- /dev/null
+++ b/packages/volto/news/6082.breaking
@@ -0,0 +1 @@
+Wrap Table of Contents block in a `nav` tag, instead of a `div` tag, for better representation improving the block accessibility. @Tishasoumya-02 @ichim-david
diff --git a/packages/volto/news/6147.bugfix b/packages/volto/news/6147.bugfix
deleted file mode 100644
index f8636db7f6..0000000000
--- a/packages/volto/news/6147.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix `Teaser` block image override option to render external images and internal images pointing to image scales. @Tishasoumya-02
diff --git a/packages/volto/news/6167.feature b/packages/volto/news/6167.feature
deleted file mode 100644
index bfa7da310f..0000000000
--- a/packages/volto/news/6167.feature
+++ /dev/null
@@ -1 +0,0 @@
-Refactor the table of contents block component from a class component to a functional component. @Prince0906
\ No newline at end of file
diff --git a/packages/volto/news/6186.feature b/packages/volto/news/6186.feature
deleted file mode 100644
index bd02ea020f..0000000000
--- a/packages/volto/news/6186.feature
+++ /dev/null
@@ -1 +0,0 @@
-Improved URL regex to allow non-public or intranet URLs, such as `https://intranet/` or `file://server/share`. @mamico
diff --git a/packages/volto/news/6213.feature b/packages/volto/news/6213.feature
deleted file mode 100644
index 76fbceecdc..0000000000
--- a/packages/volto/news/6213.feature
+++ /dev/null
@@ -1,2 +0,0 @@
-
-Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach
diff --git a/packages/volto/news/6226.bugfix b/packages/volto/news/6226.bugfix
deleted file mode 100644
index a571bd1d1e..0000000000
--- a/packages/volto/news/6226.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Add Cypress test for search block sort on property. @iRohitSingh
diff --git a/packages/volto/news/6230.bugfix b/packages/volto/news/6230.bugfix
deleted file mode 100644
index 7e8bc151cb..0000000000
--- a/packages/volto/news/6230.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Remove unused i18n message for SortOn component. @davisagli
diff --git a/packages/volto/news/6231.internal b/packages/volto/news/6231.internal
deleted file mode 100644
index 99beab7a0c..0000000000
--- a/packages/volto/news/6231.internal
+++ /dev/null
@@ -1 +0,0 @@
-Allow setting the language for `make backend-docker-start`. Default is `en`. Added usage documentation in Contributing > Develop Volto core, and reorganized it. Cleaned up Configuration > Multilingual. @stevepiercy
diff --git a/packages/volto/news/6232.internal b/packages/volto/news/6232.internal
deleted file mode 100644
index d9d32916c7..0000000000
--- a/packages/volto/news/6232.internal
+++ /dev/null
@@ -1 +0,0 @@
-Revert rename of `.gitkeep`. @stevepiercy
diff --git a/packages/volto/news/6235.bugfix b/packages/volto/news/6235.bugfix
deleted file mode 100644
index 8967eedb2a..0000000000
--- a/packages/volto/news/6235.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli
diff --git a/packages/volto/news/6237.bugfix b/packages/volto/news/6237.bugfix
deleted file mode 100644
index 7bb075eac5..0000000000
--- a/packages/volto/news/6237.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix loading of .cjs in webpack. @davisagli
diff --git a/packages/volto/news/6239.documentation b/packages/volto/news/6239.documentation
deleted file mode 100644
index 84013472bd..0000000000
--- a/packages/volto/news/6239.documentation
+++ /dev/null
@@ -1 +0,0 @@
-Fix redirect of link in documentation to testing Redux store blog post. @stevepiercy
diff --git a/packages/volto/news/6240.bugfix b/packages/volto/news/6240.bugfix
deleted file mode 100644
index f69fbc8a6c..0000000000
--- a/packages/volto/news/6240.bugfix
+++ /dev/null
@@ -1,2 +0,0 @@
-fixed change of form.ui.hovered when editing blocks, because if you have a FormBlock component inside another one,
-onMouseOver fires for all stacked blocks and you cannot use the nested form. @giuliaghisini
diff --git a/packages/volto/news/6241.internal b/packages/volto/news/6241.internal
deleted file mode 100644
index d157a14bc8..0000000000
--- a/packages/volto/news/6241.internal
+++ /dev/null
@@ -1 +0,0 @@
-Upgrade Cypress to 13.13.2. @wesleybl
diff --git a/packages/volto/news/6242.internal b/packages/volto/news/6242.internal
deleted file mode 100644
index f362b022e0..0000000000
--- a/packages/volto/news/6242.internal
+++ /dev/null
@@ -1 +0,0 @@
-Complete upgrade Cypress to 13.13.2. Bump actions/upload-artifact@v1 to v4. @stevepiercy
diff --git a/packages/volto/news/6244.bugfix b/packages/volto/news/6244.bugfix
deleted file mode 100644
index 4b9b29e635..0000000000
--- a/packages/volto/news/6244.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Changed imports from relative to absolute to avoid type errors in add-on tests. @wesleybl
diff --git a/packages/volto/news/6247.bugfix b/packages/volto/news/6247.bugfix
deleted file mode 100644
index c74a667f3b..0000000000
--- a/packages/volto/news/6247.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-In the URL Management control panel, allow external URLs as targets. @davisagli
diff --git a/packages/volto/news/6248.feature b/packages/volto/news/6248.feature
deleted file mode 100644
index 8f809c135e..0000000000
--- a/packages/volto/news/6248.feature
+++ /dev/null
@@ -1,2 +0,0 @@
-The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
-Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli
diff --git a/packages/volto/news/6252.bugfix b/packages/volto/news/6252.bugfix
deleted file mode 100644
index ac84782906..0000000000
--- a/packages/volto/news/6252.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Disable save button when loading POST query @sabrina-bongiovanni
diff --git a/packages/volto/news/6258.bugfix b/packages/volto/news/6258.bugfix
deleted file mode 100644
index 44a9fe4b9a..0000000000
--- a/packages/volto/news/6258.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix `TypeError: values[0] is undefined` in Contents properties modal. @davisagli
diff --git a/packages/volto/news/6262.documentation b/packages/volto/news/6262.documentation
deleted file mode 100644
index 0018dd38ef..0000000000
--- a/packages/volto/news/6262.documentation
+++ /dev/null
@@ -1 +0,0 @@
-Fix redirect to Docker documentation. @stevepiercy
diff --git a/packages/volto/news/6264.feature b/packages/volto/news/6264.feature
deleted file mode 100644
index e8a31aaf20..0000000000
--- a/packages/volto/news/6264.feature
+++ /dev/null
@@ -1 +0,0 @@
-Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl
diff --git a/packages/volto/news/6269.breaking b/packages/volto/news/6269.breaking
deleted file mode 100644
index a361dd27fc..0000000000
--- a/packages/volto/news/6269.breaking
+++ /dev/null
@@ -1 +0,0 @@
-Move `Tags` component to the slot `belowContent`. @wesleybl
diff --git a/packages/volto/news/6273.bugfix b/packages/volto/news/6273.bugfix
deleted file mode 100644
index 6caa1e8e5e..0000000000
--- a/packages/volto/news/6273.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix error in `SortOn` component when no sort is selected. @davisagli
diff --git a/packages/volto/news/6274.internal b/packages/volto/news/6274.internal
deleted file mode 100644
index d56fc85f27..0000000000
--- a/packages/volto/news/6274.internal
+++ /dev/null
@@ -1 +0,0 @@
-Add `make i18n` command. @Faakhir30
diff --git a/packages/volto/news/6275.bugfix b/packages/volto/news/6275.bugfix
deleted file mode 100644
index 47f0ea53ba..0000000000
--- a/packages/volto/news/6275.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Place actions storybook in correct path. @Tishasoumya-02
\ No newline at end of file
diff --git a/packages/volto/news/6278.bugfix b/packages/volto/news/6278.bugfix
deleted file mode 100644
index df2d41d358..0000000000
--- a/packages/volto/news/6278.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Remove `Generate tabs for items other than folders` field from navigation control panel. @wesleybl
diff --git a/packages/volto/news/6279.bugfix b/packages/volto/news/6279.bugfix
deleted file mode 100644
index 598e7ba4e3..0000000000
--- a/packages/volto/news/6279.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix error rendering empty grid blocks. @MAX-786
\ No newline at end of file
diff --git a/packages/volto/news/6445.feature b/packages/volto/news/6445.feature
new file mode 100644
index 0000000000..213e80ce46
--- /dev/null
+++ b/packages/volto/news/6445.feature
@@ -0,0 +1 @@
+Enhance the `buildStyleObjectFromData` helper. @sneridagh
diff --git a/packages/volto/news/6446.documentation b/packages/volto/news/6446.documentation
new file mode 100644
index 0000000000..e03a0087d3
--- /dev/null
+++ b/packages/volto/news/6446.documentation
@@ -0,0 +1 @@
+Added upgrade guide fix for HMR. @sneridagh
diff --git a/packages/volto/news/6449.bugfix b/packages/volto/news/6449.bugfix
new file mode 100644
index 0000000000..63992f787a
--- /dev/null
+++ b/packages/volto/news/6449.bugfix
@@ -0,0 +1 @@
+Fix useSelector is returned a different result when called with the same parameters in IdWidget. @iFlameing
\ No newline at end of file
diff --git a/packages/volto/news/6451.bugfix b/packages/volto/news/6451.bugfix
new file mode 100644
index 0000000000..f4f9865d63
--- /dev/null
+++ b/packages/volto/news/6451.bugfix
@@ -0,0 +1 @@
+Moved `applyBlockDefaults` one component up so the style is computed correctly. @sneridagh
diff --git a/packages/volto/package.json b/packages/volto/package.json
index 03ee312ad8..809e554ea0 100644
--- a/packages/volto/package.json
+++ b/packages/volto/package.json
@@ -9,7 +9,7 @@
}
],
"license": "MIT",
- "version": "18.0.0-alpha.42",
+ "version": "18.0.0-alpha.47",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
@@ -85,6 +85,8 @@
"@plone/volto/babel": "/babel.js",
"@plone/volto/(.*)$": "/src/$1",
"@plone/volto-slate/(.*)$": "/../volto-slate/src/$1",
+ "@plone/registry/addon-registry$": "/node_modules/@plone/registry/dist/esm/addon-registry.js",
+ "@plone/registry/create-addons-loader$": "/node_modules/@plone/registry/dist/esm/create-addons-loader.js",
"@plone/registry": "/../registry/src",
"@plone/registry/(.*)$": "/../registry/src/$1",
"@plone/volto": "/src/index.js",
@@ -173,7 +175,7 @@
"not dead"
],
"engines": {
- "node": "^16 || ^18 || ^20"
+ "node": "^20 || ^22"
},
"dependencies": {
"@loadable/component": "5.14.1",
@@ -280,11 +282,11 @@
"@babel/plugin-syntax-export-namespace-from": "7.8.3",
"@babel/runtime": "7.20.6",
"@babel/types": "7.20.5",
- "@fiverr/afterbuild-webpack-plugin": "^1.0.0",
- "@jest/globals": "^29.7.0",
"@dnd-kit/core": "6.0.8",
"@dnd-kit/sortable": "7.0.2",
"@dnd-kit/utilities": "3.2.2",
+ "@fiverr/afterbuild-webpack-plugin": "^1.0.0",
+ "@jest/globals": "^29.7.0",
"@loadable/babel-plugin": "5.13.2",
"@loadable/webpack-plugin": "5.15.2",
"@plone/types": "workspace:*",
@@ -381,7 +383,7 @@
"tmp": "0.2.1",
"ts-jest": "^26.4.2",
"ts-loader": "9.4.4",
- "typescript": "^5.4.2",
+ "typescript": "^5.6.3",
"use-trace-update": "1.3.2",
"wait-on": "6.0.0",
"webpack": "5.90.1",
diff --git a/packages/volto/razzle.config.js b/packages/volto/razzle.config.js
index b2ec16d7f7..a37df657ac 100644
--- a/packages/volto/razzle.config.js
+++ b/packages/volto/razzle.config.js
@@ -8,9 +8,11 @@ const fs = require('fs');
const RootResolverPlugin = require('./webpack-plugins/webpack-root-resolver');
const RelativeResolverPlugin = require('./webpack-plugins/webpack-relative-resolver');
const { poToJson } = require('@plone/scripts/i18n.cjs');
-const createAddonsLoader = require('@plone/registry/src/create-addons-loader');
-const createThemeAddonsLoader = require('@plone/registry/src/create-theme-addons-loader');
-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry');
+const { createAddonsLoader } = require('@plone/registry/create-addons-loader');
+const {
+ createThemeAddonsLoader,
+} = require('@plone/registry/create-theme-loader');
+const { AddonRegistry } = require('@plone/registry/addon-registry');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
@@ -23,8 +25,7 @@ const projectRootPath = path.resolve('.');
const languages = require('./src/constants/Languages.cjs');
const packageJson = require(path.join(projectRootPath, 'package.json'));
-
-const registry = new AddonConfigurationRegistry(projectRootPath);
+const { registry } = AddonRegistry.init(projectRootPath);
const defaultModify = ({
env: { target, dev },
@@ -290,6 +291,8 @@ const defaultModify = ({
const addonsLoaderPath = createAddonsLoader(
registry.getAddonDependencies(),
registry.getAddons(),
+ // The load of the project config is deprecated and will be removed in Volto 19.
+ { loadProjectConfig: true },
);
config.resolve.plugins = [
diff --git a/packages/volto/src/actions/aliases/aliases.js b/packages/volto/src/actions/aliases/aliases.js
index 368353f2d8..9c972be2eb 100644
--- a/packages/volto/src/actions/aliases/aliases.js
+++ b/packages/volto/src/actions/aliases/aliases.js
@@ -7,6 +7,7 @@ import {
GET_ALIASES,
ADD_ALIASES,
REMOVE_ALIASES,
+ UPLOAD_ALIASES,
} from '@plone/volto/constants/ActionTypes';
/**
@@ -16,17 +17,19 @@ import {
* @param {Object} options Options data.
* @returns {Object} Get aliases action.
*/
-export function getAliases(url, options) {
- const { query, manual, datetime, batchSize, batchStart } = options || {};
+export function getAliases(url, options = {}) {
+ const { query, batchSize, batchStart, ...rest } = options;
+ const params = new URLSearchParams({
+ q: query ?? '',
+ b_start: batchStart ?? 0,
+ b_size: batchSize ?? 99999999999,
+ ...rest,
+ });
return {
type: GET_ALIASES,
request: {
op: 'get',
- path: `${url}/@aliases?q=${query ? query : ''}&manual=${
- manual ? manual : ''
- }&datetime=${datetime !== null ? datetime : ''}&b_size=${
- batchSize ? batchSize : 99999999999
- }&b_start=${batchStart ? batchStart : 0}`,
+ path: `${url}/@aliases?${params.toString()}`,
},
};
}
@@ -66,3 +69,20 @@ export function removeAliases(url, data) {
},
};
}
+
+/**
+ * Upload aliases function.
+ * @function uploadAliases
+ * @param {Object} file CSV file.
+ * @returns {Object} Upload aliases action.
+ */
+export function uploadAliases(file) {
+ return {
+ type: UPLOAD_ALIASES,
+ request: {
+ op: 'post',
+ path: '/@aliases',
+ attach: [['file', file]],
+ },
+ };
+}
diff --git a/packages/volto/src/actions/aliases/aliases.test.js b/packages/volto/src/actions/aliases/aliases.test.js
index e419b58a6a..e04783cead 100644
--- a/packages/volto/src/actions/aliases/aliases.test.js
+++ b/packages/volto/src/actions/aliases/aliases.test.js
@@ -14,7 +14,7 @@ describe('Aliases action', () => {
expect(action.type).toEqual(GET_ALIASES);
expect(action.request.op).toEqual('get');
expect(action.request.path).toEqual(
- '/news/@aliases?q=&manual=&datetime=undefined&b_size=99999999999&b_start=0',
+ '/news/@aliases?q=&b_start=0&b_size=99999999999',
);
});
});
diff --git a/packages/volto/src/components/manage/BlockChooser/BlockChooser.jsx b/packages/volto/src/components/manage/BlockChooser/BlockChooser.jsx
index 80c5142e81..2ae878e5ba 100644
--- a/packages/volto/src/components/manage/BlockChooser/BlockChooser.jsx
+++ b/packages/volto/src/components/manage/BlockChooser/BlockChooser.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { useSelector } from 'react-redux';
+import { useUser } from '@plone/volto/hooks';
import PropTypes from 'prop-types';
import { filter, map, groupBy, isEmpty } from 'lodash';
import { Accordion, Button } from 'semantic-ui-react';
@@ -36,7 +36,7 @@ const BlockChooser = ({
contentType,
}) => {
const intl = useIntl();
- const user = useSelector((state) => state.users?.user);
+ const user = useUser();
const hasAllowedBlocks = !isEmpty(allowedBlocks);
const filteredBlocksConfig = filter(blocksConfig, (item) => {
diff --git a/packages/volto/src/components/manage/BlockChooser/BlockChooser.test.jsx b/packages/volto/src/components/manage/BlockChooser/BlockChooser.test.jsx
index 447c20f61d..d2bd1bbe5a 100644
--- a/packages/volto/src/components/manage/BlockChooser/BlockChooser.test.jsx
+++ b/packages/volto/src/components/manage/BlockChooser/BlockChooser.test.jsx
@@ -4,6 +4,7 @@ import { Provider } from 'react-intl-redux';
import configureStore from 'redux-mock-store';
import BlockChooser from './BlockChooser';
import config from '@plone/volto/registry';
+import jwt from 'jsonwebtoken';
const blockSVG = {};
@@ -121,6 +122,9 @@ const store = mockStore({
locale: 'en',
messages: {},
},
+ userSession: {
+ token: jwt.sign({ fullname: 'John Doe' }, 'secret'),
+ },
});
describe('BlocksChooser', () => {
diff --git a/packages/volto/src/components/manage/BlockChooser/BlockChooserSearch.jsx b/packages/volto/src/components/manage/BlockChooser/BlockChooserSearch.jsx
index 346447087e..73875ea0e7 100644
--- a/packages/volto/src/components/manage/BlockChooser/BlockChooserSearch.jsx
+++ b/packages/volto/src/components/manage/BlockChooser/BlockChooserSearch.jsx
@@ -35,7 +35,6 @@ const BlockChooserSearch = ({ onChange, searchValue }) => {
placeholder={intl.formatMessage(messages.search)}
title={intl.formatMessage(messages.search)}
ref={searchInput}
- autoFocus
/>
{searchValue && (
{this.props.manage && (
{
onInsertBlock,
onSelectBlock,
onMutateBlock,
- data,
+ data: originalData,
editable,
properties,
showBlockChooser,
@@ -55,14 +57,22 @@ const EditBlockWrapper = (props) => {
contentType,
} = blockProps;
+ const data = applyBlockDefaults({ data: originalData, ...blockProps, intl });
+
const visible = selected && !hideHandler(data);
const required = isBoolean(data.required)
? data.required
: includes(config.blocks.requiredBlocks, type);
- const classNames = buildStyleClassNamesFromData(data.styles);
- const style = buildStyleObjectFromData(data.styles);
+ let classNames = buildStyleClassNamesFromData(data.styles);
+ classNames = buildStyleClassNamesExtenders({
+ block,
+ content: properties,
+ data,
+ classNames,
+ });
+ const style = buildStyleObjectFromData(data);
// We need to merge the StyleWrapper styles with the draggable props from b-D&D
const styleMergedWithDragProps = {
diff --git a/packages/volto/src/components/manage/Blocks/Block/Order/Item.jsx b/packages/volto/src/components/manage/Blocks/Block/Order/Item.jsx
index 68e20edcc6..5580fc416f 100644
--- a/packages/volto/src/components/manage/Blocks/Block/Order/Item.jsx
+++ b/packages/volto/src/components/manage/Blocks/Block/Order/Item.jsx
@@ -98,7 +98,9 @@ export const Item = forwardRef(
0 })}
+ className={cx('text', {
+ errored: errors && Object.keys(errors).length > 0,
+ })}
>
{config.blocks.blocksConfig[data?.['@type']]?.icon && (
{
classNames,
});
- style = buildStyleObjectFromData(data.styles);
+ style = buildStyleObjectFromData(data);
const rewrittenChildren = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
diff --git a/packages/volto/src/components/manage/Blocks/Listing/getAsyncData.js b/packages/volto/src/components/manage/Blocks/Listing/getAsyncData.js
index bc125bb1c7..e7c4996cc5 100644
--- a/packages/volto/src/components/manage/Blocks/Listing/getAsyncData.js
+++ b/packages/volto/src/components/manage/Blocks/Listing/getAsyncData.js
@@ -24,6 +24,14 @@ export default function getListingBlockAsyncData(props) {
const subrequestID = content?.UID ? `${content?.UID}-${id}` : id;
const currentPage = getCurrentPage(location, id);
+ if (!data.querystring) {
+ return [
+ async () => {
+ return null;
+ },
+ ];
+ }
+
return [
dispatch(
getQueryStringResults(
diff --git a/packages/volto/src/components/manage/Blocks/Search/components/CheckboxFacet.jsx b/packages/volto/src/components/manage/Blocks/Search/components/CheckboxFacet.jsx
index c4b86c1147..7d0665054f 100644
--- a/packages/volto/src/components/manage/Blocks/Search/components/CheckboxFacet.jsx
+++ b/packages/volto/src/components/manage/Blocks/Search/components/CheckboxFacet.jsx
@@ -21,6 +21,7 @@ const CheckboxFacet = (props) => {
{choices.map(({ label, value }, i) => (
-
@@ -47,11 +50,14 @@ exports[`CheckboxFacet renders a facet component with checkboxes 1`] = `
-
+
Second
- Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring.
-
-
-
-
-
-
-
- Target Path (Required)
-
-
- Enter the absolute path of the target. Target must exist or be an existing alternative url path to the target.
-
-
-
-
-
-
-
-
-
-
+
+
+
-
- All existing alternative urls for this site
+ Existing alternative URLs for this site
- Filter by prefix
-
-
-
+
+
+
+
+
+
+ Filter by path
+
+
+
+
+
+
+
+
+
+
+
-
-
- Manually or automatically added?
-
-
-
-
- Both
-
+
+
+
+
+
+
+ Manually or automatically added?
+
+
+
+
+
+
+
+
+
+ Both
+
+
+
+
+
+
+
+ Automatically
+
+
+
+
+
+
+
+ Manually
+
+
+
+
+
+
+
+
-
-
-
-
- Automatically
-
-
-
-
+ class="field"
+ />
+
-
-
-
-
- Alternative url path → target url path (date and time of creation, manually created yes/no)
+ Filter
+
-
+
+
+
+ Alternative URL path → target URL path (date and time of creation, manually created yes/no)
+