From 004247a94245aa84eed207a6c26c3f9b5f6292bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 17 Mar 2024 20:06:54 +0100 Subject: [PATCH 001/226] Test also inflection, wildcard, exact, compounded words --- Makefile | 14 -- README.md | 6 +- TESTING.md | 10 -- TODO.md | 2 +- acceptance/ci.yml | 1 - ...ks-searchkit.cy.js => create_search.cy.js} | 34 ++--- acceptance/cypress/tests/search.cy.js | 130 ++++++++++++++++++ acceptance/docker-compose.yml | 40 +++--- docker-opensearch/docker-compose.yml | 117 ---------------- src/components/Views/FacetedSearch.jsx | 5 +- 10 files changed, 167 insertions(+), 192 deletions(-) rename acceptance/cypress/tests/{blocks-searchkit.cy.js => create_search.cy.js} (60%) create mode 100644 acceptance/cypress/tests/search.cy.js delete mode 100644 docker-opensearch/docker-compose.yml diff --git a/Makefile b/Makefile index dddee065..32c81c45 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,9 @@ YELLOW=`tput setaf 3` BACKEND_ADDONS='collective.elastic.plone ${KGS} $(TESTING_ADDONS)' DEV_COMPOSE=dockerfiles/docker-compose.yml ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml -# OPENSEARCH_COMPOSE=docker-opensearch/docker-compose.yml CMD=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} BACKEND_ADDONS=${BACKEND_ADDONS} docker compose DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${DEV_COMPOSE} ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE} -# OPENSEARCH=CURRENT_DIR=${CURRENT_DIR} docker compose -p ${ADDON_PATH}-opensearch -f ${OPENSEARCH_COMPOSE} .PHONY: all @@ -94,17 +92,6 @@ test: ## Run unit tests test-ci: ## Run unit tests in CI ${DOCKER_COMPOSE} run -e CI=1 addon-dev test -# Acceptance opensearch -# # TODO marry OPENSEARCH with backend acceptance -# .PHONY: build-acceptance-opensearch -# build-acceptance-opensearch: ## build opensearch containers -# (cd docker-opensearch) -# ${OPENSEARCH} --profile dev build - -# .PHONY: start-test-acceptance-server -# start-test-acceptance-server-opensearch: ## Start acceptance opensearch containers -# ${OPENSEARCH} --profile dev up -d - # Acceptance backend and frontend .PHONY: build-acceptance build-acceptance: ## Install Cypress, build containers @@ -115,7 +102,6 @@ build-acceptance: ## Install Cypress, build containers start-acceptance: ## Start acceptance server-containers ${ACCEPTANCE} --profile dev up -d --force-recreate -# TODO Maybe depend on build and start? .PHONY: test-acceptance test-acceptance: ## Start Cypress (cd acceptance && ./node_modules/.bin/cypress open) diff --git a/README.md b/README.md index 37ca0d9b..17b40dea 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# Features of **@rohberg/volto-searchkit-block** +# @rohberg/volto-searchkit-block – the search block + +[![NPM](https://img.shields.io/npm/v/@rohberg/volto-searchkit-block.svg)](https://www.npmjs.com/package/@rohberg/volto-searchkit-block) +[![Unit Tests](https://github.com/rohberg/volto-searchkit-block/actions/workflows/unit.yml/badge.svg)](https://github.com/rohberg/volto-searchkit-block/actions/workflows/unit.yml) +[![Acceptance Tests](https://github.com/rohberg/volto-searchkit-block/actions/workflows/acceptance.yml/badge.svg)](https://github.com/rohberg/volto-searchkit-block/actions/workflows/acceptance.yml) Search block with highly overridable components for searching, filtering and displaying search results. Sometimes also called faceted navigation. diff --git a/TESTING.md b/TESTING.md index 8091a177..058fa217 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,15 +1,5 @@ # Testing -## TODO - -- block can be added -- search fuzzy -- search flexation -- search wildcard -- search exact -- search compounded word - - ## Build and run acceptance tests Build acceptance server: diff --git a/TODO.md b/TODO.md index 75d59800..6a54d48b 100644 --- a/TODO.md +++ b/TODO.md @@ -3,4 +3,4 @@ - Restrict block creation to Site Admins. In future, blocks creation can be restricted by permission / role. Then change attribute "restricted" to false. Until then: download add-on, change restricted to false, add block, switch back to restricted true. - `config.blocks.blocksConfig.searchkitblock.restricted` \ No newline at end of file + `config.blocks.blocksConfig.searchkitblock.restricted` diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 2ec75b38..03b2fc46 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -20,7 +20,6 @@ services: - prod backend-acceptance: - # TODO Create content build: context: ../ dockerfile: ./dockerfiles/backend/Dockerfile.acceptance diff --git a/acceptance/cypress/tests/blocks-searchkit.cy.js b/acceptance/cypress/tests/create_search.cy.js similarity index 60% rename from acceptance/cypress/tests/blocks-searchkit.cy.js rename to acceptance/cypress/tests/create_search.cy.js index 1035ba3c..d91a935d 100644 --- a/acceptance/cypress/tests/blocks-searchkit.cy.js +++ b/acceptance/cypress/tests/create_search.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit Block Tests', () => { +describe('Searchkit block tests- create search ', () => { before(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); @@ -43,16 +43,13 @@ describe('Searchkit Block Tests', () => { }); after(() => { - // cy.removeContent({ path: 'garden-february' }); - // cy.removeContent({ path: 'garden-march' }); - // cy.removeContent({ path: 'garden-blog' }); - // cy.removeContent({ path: 'searching' }); - // TODO Update index server - // @@update-elasticsearch - + cy.removeContent({ path: 'garden-blog/garden-february' }); + cy.removeContent({ path: 'garden-blog/garden-march' }); + cy.removeContent({ path: 'garden-blog' }); + cy.removeContent({ path: 'searching' }); }); - it('As manager I can add a searchkit-block', function () { + it('As manager I can add a searchkit-block and find a documunt', function () { cy.navigate('/searching/edit'); cy.wait('@schema'); @@ -62,19 +59,6 @@ describe('Searchkit Block Tests', () => { cy.get('.blocks-chooser .common .button.searchkitblock').click({ force: true, }); - - // // block configuration index server - // cy.get('input#field-backend_url').type('http://localhost:55001/plone') - // cy.get('input#field-frontend_url').type('http://localhost:3000') - // // block configuration allowed types and states - // cy.get("#field-allowed_content_types").click(); - // cy.get("#field-allowed_content_types .react-select__option") - // .contains('Page') - // .click(); - // cy.get("#field-allowed_review_states").click(); - // cy.get("#field-allowed_review_states .react-select__option") - // .contains('Private') - // .click(); cy.get('#toolbar-save').click(); cy.visit('/searching'); @@ -84,7 +68,9 @@ describe('Searchkit Block Tests', () => { cy.get('.block.searchkitsearch') .contains('The garden in february'); - - // cy.visit('/garden-blog/contents'); + cy.get('.searchbar-wrapper input').type('Februar{enter}'); + cy.get('.block.searchkitsearch') + .contains('The garden in february'); }); + }); diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.cy.js new file mode 100644 index 00000000..87375fee --- /dev/null +++ b/acceptance/cypress/tests/search.cy.js @@ -0,0 +1,130 @@ +describe('Searchkit block tests – search', () => { + before(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'suche', + contentTitle: 'Suche', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garten-blog', + contentTitle: 'Garten-Blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'februar', + contentTitle: 'Der Garten im Februar', + path: '/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'marz', + contentTitle: 'Der Garten im März', + path: '/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-mann', + contentTitle: 'Testseite Mann', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-manner', + contentTitle: 'Testseite Männer', + }); + + + cy.visit('/suche/edit'); + cy.wait('@schema'); + + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('.blocks-chooser .common .button.searchkitblock').click({ + force: true, + }); + + cy.get('#toolbar-save').click(); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/suche'); + cy.wait(5000); + // cy.wait('@kitsearch'); + }); + + after(() => { + cy.removeContent({ path: 'garten-blog/februar' }); + cy.removeContent({ path: 'garten-blog/marz' }); + cy.removeContent({ path: 'garten-blog' }); + cy.removeContent({ path: 'suche' }); + cy.removeContent({ path: 'testseite-mann' }); + cy.removeContent({ path: 'testseite-manner' }); + }); + + it('I see all if no filter selected', function() { + cy.get('.block.searchkitsearch') + .contains('Der Garten im Februar'); + }); + + + it('I can search fuzzy', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch') + .contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch') + .should('not.contain', 'März'); + }); + + it('I can search with inflection', function () { + cy.get('.searchbar-wrapper input').type('Schnelltestkit{enter}'); + cy.get('.block.searchkitsearch') + .contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); + cy.get('.block.searchkitsearch') + .contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); + cy.get('.block.searchkitsearch') + .contains('Testseite Männer'); + }); + + it('I can search with decompounding', function () { + cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.get('.block.searchkitsearch') + .contains('Garten-Blog'); + + cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.get('.block.searchkitsearch') + .contains('Februar'); + }); + + it('I can search with wildcard', function () { + cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.get('.block.searchkitsearch') + .contains('Der Garten im Februar'); + }); + + it('I can search for an exact match', function () { + cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.get('.block.searchkitsearch') + .contains('Testseite Mann'); + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.get('.block.searchkitsearch') + .should('not.contain', 'Männer'); + }); + +}); diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index e13720f7..e5b963c0 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -26,32 +26,31 @@ services: - dev backend-acceptance: - # TODO Create content build: context: ../ dockerfile: ./dockerfiles/backend/Dockerfile.acceptance args: PLONE_VERSION: ${PLONE_VERSION:-6.0} environment: - ZSERVER_HOST: "0.0.0.0" - ZSERVER_PORT: "55001" - # CORS_ALLOW_ORIGIN: "*" - # DELETE_EXISTING: "true" - # SETUP_CONTENT: "true" - # ADDONS: ${BACKEND_ADDONS} - # TYPE: volto - # # Profiles to be added to the created site - # ADDITIONAL_PROFILES: "collective.elastic.plone:default" - # # Packages to be used in configuration - # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto,plone.volto.cors" - # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto" - # plone.elastic 2.x - CELERY_BROKER: ${CELERY_BROKER?unset} - INDEX_SERVER: ${INDEX_SERVER?unset} - INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} - INDEX_USE_SSL: ${INDEX_USE_SSL?unset} - INDEX_LOGIN: ${INDEX_LOGIN?unset} - INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + ZSERVER_HOST: "0.0.0.0" + ZSERVER_PORT: "55001" + # CORS_ALLOW_ORIGIN: "*" + # DELETE_EXISTING: "true" + # SETUP_CONTENT: "true" + # ADDONS: ${BACKEND_ADDONS} + # TYPE: volto + # # Profiles to be added to the created site + # ADDITIONAL_PROFILES: "collective.elastic.plone:default" + # # Packages to be used in configuration + # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto,plone.volto.cors" + # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto" + # plone.elastic 2.x + CELERY_BROKER: ${CELERY_BROKER?unset} + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} ports: - 55001:55001 profiles: @@ -100,6 +99,7 @@ services: - discovery.type=single-node - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: memlock: soft: -1 diff --git a/docker-opensearch/docker-compose.yml b/docker-opensearch/docker-compose.yml deleted file mode 100644 index 5323d79b..00000000 --- a/docker-opensearch/docker-compose.yml +++ /dev/null @@ -1,117 +0,0 @@ -# ***************** -# OBSOLETE -# Merged with docker-compose for backend and frontend -# ********************* - -# opensearch needs sysctl -w vm.max_map_count=262144 -# see https://stackoverflow.com/questions/66444027/max-virtual-memory-areas-vm-max-map-count-65530-is-too-low-increase-to-at-lea -version: '3' -services: - - ingest: - image: ghcr.io/collective/collective.elastic.ingest:latest - environment: - MAPPINGS_FILE: ${MAPPINGS_FILE} - ANALYSIS_FILE: ${ANALYSIS_FILE} - PREPROCESSINGS_FILE: ${PREPROCESSINGS_FILE} - INDEX_SERVER: ${INDEX_SERVER?unset} - INDEX_USE_SSL: ${INDEX_USE_SSL?unset} - INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} - INDEX_LOGIN: ${INDEX_LOGIN?unset} - INDEX_PASSWORD: ${INDEX_PASSWORD?unset} - CELERY_BROKER: ${CELERY_BROKER?unset} - CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} - PLONE_SERVICE: ${PLONE_SERVICE?unset} - PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} - PLONE_USER: ${PLONE_USER?unset} - PLONE_PASSWORD: ${PLONE_PASSWORD?unset} - SENTRY_DSN: ${SENTRY_DSN} - volumes: - - ingest-configuration:/configuration - networks: - - opensearch-net - - redis: - image: 'redis:latest' - networks: - - opensearch-net - ports: - - 6379:6379 - - opensearch: - build: - context: ../ - dockerfile: ./docker-opensearch/Dockerfile.opensearch - # args: - # ADDON_NAME: "${ADDON_NAME}" - # ADDON_PATH: "${ADDON_PATH}" - # VOLTO_VERSION: ${VOLTO_VERSION:-17} - environment: - - cluster.name=opensearch-cluster - - node.name=opensearch - - discovery.type=single-node - - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - volumes: - - opensearch-data:/usr/share/opensearch/data - - "./opensearch-configuration/keywords.txt:/usr/share/opensearch/config/keywords.txt" - - "./opensearch-configuration/lexicon.txt:/usr/share/opensearch/config/lexicon.txt" - ports: - - 9200:9200 # REST API - - 9600:9600 # Performance Analyzer - networks: - - opensearch-net - tty: true - profiles: - - dev - - prod - - - opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:latest - ports: - - 5601:5601 - expose: - - "5601" - environment: - OPENSEARCH_HOSTS: '["http://opensearch:9200"]' - networks: - - opensearch-net - profiles: - - dev - - # dejavu: - # image: appbaseio/dejavu:latest - # container_name: dejavu - # ports: - # - "1358:1358" - # expose: - # - "1358" - # links: - # - opensearch - -volumes: - opensearch-data: - ingest-configuration: - driver: local - driver_opts: - type: none - device: ./ingest-configuration - o: bind - # opensearch-configuration: - # driver: local - # driver_opts: - # type: none - # device: ./opensearch-configuration - # o: bind -networks: - opensearch-net: - diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 0c8ec0a0..5182376b 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -65,9 +65,6 @@ export const ploneSearchApi = (data) => { url: expandToBackendURL('/@kitsearch'), timeout: 5000, headers: { - // TODO Fix CORS - // Host: localhost:55001 - // Origin: http://localhost:3000 Accept: 'application/json', Authorization: `Bearer ${authToken}`, }, @@ -76,7 +73,7 @@ export const ploneSearchApi = (data) => { requestSerializer: CustomESRequestSerializer, responseSerializer: CustomESResponseSerializer, }, - searchedFields: data.searchedFields, + searchedFields: data.searchedFields || ['title'], facet_fields: data.facet_fields, allowed_content_types: data.allowed_content_types, allowed_review_states: data.allowed_review_states, From cac684058dd1dd0fd0baf125c34b836b6d03b4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 17 Mar 2024 20:19:02 +0100 Subject: [PATCH 002/226] lint --- acceptance/cypress/tests/create_search.cy.js | 10 ++--- acceptance/cypress/tests/search.cy.js | 38 ++++++------------- .../Searchkit/CustomESRequestSerializer.jsx | 29 +++++++------- src/components/Searchkit/ESSearchApi.jsx | 4 +- src/components/Searchkit/Error.jsx | 2 - src/components/Searchkit/Error.test.js | 7 +++- src/components/Views/FacetedSearch.jsx | 9 ++++- .../springisnow-volto-searchkit-block.less | 4 +- 8 files changed, 45 insertions(+), 58 deletions(-) diff --git a/acceptance/cypress/tests/create_search.cy.js b/acceptance/cypress/tests/create_search.cy.js index d91a935d..fdc71625 100644 --- a/acceptance/cypress/tests/create_search.cy.js +++ b/acceptance/cypress/tests/create_search.cy.js @@ -63,14 +63,10 @@ describe('Searchkit block tests- create search ', () => { cy.get('#toolbar-save').click(); cy.visit('/searching'); - cy.get('.block.searchkitsearch') - .should('not.contain', 'No results') - cy.get('.block.searchkitsearch') - .contains('The garden in february'); + cy.get('.block.searchkitsearch').should('not.contain', 'No results'); + cy.get('.block.searchkitsearch').contains('The garden in february'); cy.get('.searchbar-wrapper input').type('Februar{enter}'); - cy.get('.block.searchkitsearch') - .contains('The garden in february'); + cy.get('.block.searchkitsearch').contains('The garden in february'); }); - }); diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.cy.js index 87375fee..3802c4a8 100644 --- a/acceptance/cypress/tests/search.cy.js +++ b/acceptance/cypress/tests/search.cy.js @@ -38,7 +38,6 @@ describe('Searchkit block tests – search', () => { contentTitle: 'Testseite Männer', }); - cy.visit('/suche/edit'); cy.wait('@schema'); @@ -74,57 +73,44 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'testseite-manner' }); }); - it('I see all if no filter selected', function() { - cy.get('.block.searchkitsearch') - .contains('Der Garten im Februar'); + it('I see all if no filter selected', function () { + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); - it('I can search fuzzy', function () { cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch') - .contains('Der Garten im Februar'); - cy.get('.block.searchkitsearch') - .should('not.contain', 'März'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').should('not.contain', 'März'); }); it('I can search with inflection', function () { cy.get('.searchbar-wrapper input').type('Schnelltestkit{enter}'); - cy.get('.block.searchkitsearch') - .contains('Testseite Mann'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); - cy.get('.block.searchkitsearch') - .contains('Testseite Mann'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); - cy.get('.block.searchkitsearch') - .contains('Testseite Männer'); + cy.get('.block.searchkitsearch').contains('Testseite Männer'); }); it('I can search with decompounding', function () { cy.get('.searchbar-wrapper input').type('Garten{enter}'); - cy.get('.block.searchkitsearch') - .contains('Garten-Blog'); + cy.get('.block.searchkitsearch').contains('Garten-Blog'); cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - cy.get('.block.searchkitsearch') - .contains('Februar'); + cy.get('.block.searchkitsearch').contains('Februar'); }); it('I can search with wildcard', function () { cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - cy.get('.block.searchkitsearch') - .contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); it('I can search for an exact match', function () { cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - cy.get('.block.searchkitsearch') - .contains('Testseite Mann'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - cy.get('.block.searchkitsearch') - .should('not.contain', 'Männer'); + cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); }); - }); diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index c35b03d0..bcd350af 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -258,16 +258,18 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; - this.allowed_content_types?.length > 0 && terms.push({ - terms: { - portal_type: this.allowed_content_types, - }, - }); - this.allowed_review_states?.length > 0 && terms.push({ - terms: { - review_state: this.allowed_review_states, - }, - }); + this.allowed_content_types?.length > 0 && + terms.push({ + terms: { + portal_type: this.allowed_content_types, + }, + }); + this.allowed_review_states?.length > 0 && + terms.push({ + terms: { + review_state: this.allowed_review_states, + }, + }); const filters_dict = keyBy(filters, (e) => { return e[0]; @@ -330,10 +332,9 @@ export class CustomESRequestSerializer { const filter = (fieldName) => { let myAggsFilter = terms; // Add selected filters - const terms_of_selected_options_without_self = - terms_of_selected_options.filter( - (el) => !Object.keys(el.terms).includes(fieldName), - ); + const terms_of_selected_options_without_self = terms_of_selected_options.filter( + (el) => !Object.keys(el.terms).includes(fieldName), + ); myAggsFilter = myAggsFilter.concat( terms_of_selected_options_without_self, ); diff --git a/src/components/Searchkit/ESSearchApi.jsx b/src/components/Searchkit/ESSearchApi.jsx index 8de9f63c..16dd1b95 100644 --- a/src/components/Searchkit/ESSearchApi.jsx +++ b/src/components/Searchkit/ESSearchApi.jsx @@ -64,14 +64,12 @@ export class PloneSearchApi { // let results = await this.responseSerializer.serialize(response.data); let results = await response.json(); if (results.message) { - throw results + throw results; // throw new Error(`${results.type} ${results.message}`); } results = this.responseSerializer.serialize(results); return results; } catch (error) { - console.debug('ESSearchApi. error:'); - console.debug(error); throw error; } } diff --git a/src/components/Searchkit/Error.jsx b/src/components/Searchkit/Error.jsx index 97e756d7..cf1ba620 100644 --- a/src/components/Searchkit/Error.jsx +++ b/src/components/Searchkit/Error.jsx @@ -1,9 +1,7 @@ -import { useSelector } from 'react-redux'; import { Header, Segment } from 'semantic-ui-react'; import { FormattedMessage } from 'react-intl'; const Error = ({ error }) => { - return error?.type ? (
diff --git a/src/components/Searchkit/Error.test.js b/src/components/Searchkit/Error.test.js index 7d54361b..2608ece0 100644 --- a/src/components/Searchkit/Error.test.js +++ b/src/components/Searchkit/Error.test.js @@ -21,7 +21,12 @@ describe('Generic Error', () => { const component = renderer.create( - + , ); diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 5182376b..89e56c11 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -562,8 +562,13 @@ const customSort = ({ }; const customPaginationElement = (props) => { - const { currentPage, currentSize, totalResults, onPageChange, options} = - props; + const { + currentPage, + currentSize, + totalResults, + onPageChange, + options, + } = props; const pages = Math.ceil(totalResults / currentSize); const boundaryRangeCount = options.boundaryRangeCount; const siblingRangeCount = options.siblingRangeCount; diff --git a/src/components/Views/less/springisnow-volto-searchkit-block.less b/src/components/Views/less/springisnow-volto-searchkit-block.less index 7e3ce2ba..9fc4ca2e 100644 --- a/src/components/Views/less/springisnow-volto-searchkit-block.less +++ b/src/components/Views/less/springisnow-volto-searchkit-block.less @@ -90,9 +90,7 @@ // .ui.grid > .row > .column.facetedsearch_filter { .facetedsearch_filter { opacity: 1; - transition: - opacity 1s, - visibility 1s; + transition: opacity 1s, visibility 1s; visibility: visible; &.cards { From dfd48d1d1e4e7fc0251adaefa0c3c8d75b2ad797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 17 Mar 2024 20:44:23 +0100 Subject: [PATCH 003/226] lint now with correct image plone/frontend-dev with Node 20 --- src/components/Searchkit/CustomESRequestSerializer.jsx | 7 ++++--- src/components/Views/FacetedSearch.jsx | 9 ++------- .../Views/less/springisnow-volto-searchkit-block.less | 4 +++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index bcd350af..7de0ebcb 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -332,9 +332,10 @@ export class CustomESRequestSerializer { const filter = (fieldName) => { let myAggsFilter = terms; // Add selected filters - const terms_of_selected_options_without_self = terms_of_selected_options.filter( - (el) => !Object.keys(el.terms).includes(fieldName), - ); + const terms_of_selected_options_without_self = + terms_of_selected_options.filter( + (el) => !Object.keys(el.terms).includes(fieldName), + ); myAggsFilter = myAggsFilter.concat( terms_of_selected_options_without_self, ); diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 89e56c11..800e264a 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -562,13 +562,8 @@ const customSort = ({ }; const customPaginationElement = (props) => { - const { - currentPage, - currentSize, - totalResults, - onPageChange, - options, - } = props; + const { currentPage, currentSize, totalResults, onPageChange, options } = + props; const pages = Math.ceil(totalResults / currentSize); const boundaryRangeCount = options.boundaryRangeCount; const siblingRangeCount = options.siblingRangeCount; diff --git a/src/components/Views/less/springisnow-volto-searchkit-block.less b/src/components/Views/less/springisnow-volto-searchkit-block.less index 9fc4ca2e..7e3ce2ba 100644 --- a/src/components/Views/less/springisnow-volto-searchkit-block.less +++ b/src/components/Views/less/springisnow-volto-searchkit-block.less @@ -90,7 +90,9 @@ // .ui.grid > .row > .column.facetedsearch_filter { .facetedsearch_filter { opacity: 1; - transition: opacity 1s, visibility 1s; + transition: + opacity 1s, + visibility 1s; visibility: visible; &.cards { From 93ca69a53de48e6ca993798d129df82cabefdade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 19 Mar 2024 15:28:55 +0100 Subject: [PATCH 004/226] Remove fields: index server address and index: block requests do use backend only --- src/components/Blocks/schema.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index d0764695..34b82960 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -50,8 +50,6 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { id: 'default', title: 'API', fields: [ - 'elastic_search_api_url', - 'elastic_search_api_index', 'backend_url', 'frontend_url', ], @@ -90,12 +88,12 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { properties: { elastic_search_api_url: { title: - '(deprected) (Set in collective.elastic environment variable) Elastic Search API URL', + '(deprecated) (Set in collective.elastic environment variable) Elastic Search API URL', default: 'http://localhost:9200', }, elastic_search_api_index: { title: - '(deprected) (Set in collective.elastic environment variable) Elastic Search API Index', + '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', default: 'esploneindex', }, backend_url: { @@ -181,6 +179,5 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { default: '', }, }, - required: ['backend_url', 'frontend_url'], }; }; From 0a484189ea892e6f8cf9d2e8580cca50252bf7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 21 Mar 2024 19:33:27 +0100 Subject: [PATCH 005/226] Create Dockerfile.dev --- dockerfiles/backend/Dockerfile.dev | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 dockerfiles/backend/Dockerfile.dev diff --git a/dockerfiles/backend/Dockerfile.dev b/dockerfiles/backend/Dockerfile.dev new file mode 100644 index 00000000..bf95059a --- /dev/null +++ b/dockerfiles/backend/Dockerfile.dev @@ -0,0 +1,36 @@ +# syntax=docker/dockerfile:1 +# Dockerfile from cookiecutter-plone-starter +ARG PLONE_VERSION=6.0 +FROM plone/server-builder:${PLONE_VERSION} as builder + +WORKDIR /app + +# # Add local code +# COPY . . + + +# Install local requirements +RUN <=2.0.1 +EOT + + + + +FROM plone/server-prod-config:${PLONE_VERSION} + +# Use /app as the workdir +WORKDIR /app + +# Copy /app from builder +COPY --from=builder --chown=500:500 /app /app + +# # Enable compilation of po files into mo files (This is added here for backward compatibility) + +# ENV zope_i18n_compile_mo_files=true +# # https://github.com/pypa/pip/issues/12079 +# ENV _PIP_USE_IMPORTLIB_METADATA=0 + +# Link /data (the exposed volume) into /app/var +RUN ln -s /data /app/var \ No newline at end of file From da488beed97458109a3bcb2d68abe32d556c1b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 22 Mar 2024 15:42:39 +0100 Subject: [PATCH 006/226] pre-weekend-commit dev docker setup --- DEVELOPMENT.md | 43 +++--- Makefile | 16 ++- README.md | 11 ++ acceptance/.env | 2 - acceptance/ci.yml | 2 +- acceptance/docker-compose.yml | 17 +-- dockerfiles/backend/Dockerfile.dev | 60 ++++---- .../collective.elastic.plone-configure.zcml | 1 + dockerfiles/backend/requirements-docker.txt | 2 + dockerfiles/docker-compose.yml | 129 +++++++++++++++++- .../{ => frontend}/Dockerfile.acceptance | 0 dockerfiles/{ => frontend}/Dockerfile.ci | 0 dockerfiles/{ => frontend}/Dockerfile.dev | 0 13 files changed, 201 insertions(+), 82 deletions(-) create mode 100644 dockerfiles/backend/collective.elastic.plone-configure.zcml create mode 100644 dockerfiles/backend/requirements-docker.txt rename dockerfiles/{ => frontend}/Dockerfile.acceptance (100%) rename dockerfiles/{ => frontend}/Dockerfile.ci (100%) rename dockerfiles/{ => frontend}/Dockerfile.dev (100%) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 785b9592..d4cab141 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,47 +1,40 @@ -# Development +# Development and demo -## Backend +> TODO Update to new Makefile - make dev-start-backend -## Elasticsearch +## Index server OpenSearch -Run with Docker. + make dev-opensearch -Change directory to ./development-searchkitblock/mac/ or ./development-searchkitblock/linux/ and run: +## Backend and frontend - docker compose up + make dev -Inspect with - docker exec -it bash -## Redis -Start with - redis-server /usr/local/etc/redis.conf +> OUTDATED OLD STUFF BELOW -## Celery +## Backend -Change directory to ./development-searchkitblock/celery and install: + make dev-start-backend -Install with: - python -m venv venv - source venv/bin/activate - pip install -U pip wheel mxdev - mxdev -c mx.ini +## OUTDATED Elasticsearch - pip install -r requirements-mxdev.txt +Run with Docker. + +Change directory to ./development-searchkitblock/mac/ or ./development-searchkitblock/linux/ and run: + + docker compose up + +Inspect with -Run with: + docker exec -it bash - source .env - venv/bin/celery -A collective.elastic.ingest.celery.app worker -l info - # or with more info: - venv/bin/celery -A collective.elastic.ingest.celery.app worker -l debug ## Frontend diff --git a/Makefile b/Makefile index 32c81c45..f75ae8f4 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ all: 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 :)" +# TODO Add backend-addons .PHONY: build-backend build-backend: ## Build @echo "$(GREEN)==> Build Backend Container $(RESET)" @@ -59,13 +60,14 @@ start-dev: ## Starts Dev container @echo "$(GREEN)==> Start Addon Development container $(RESET)" ${DOCKER_COMPOSE} up addon-dev +# TODO Check if 'make dev' is OK for trying the searchkit block +# TODO Add example content: plone.exportimport https://plone.github.io/plone.exportimport/features.html#plone-importer .PHONY: dev dev: ## Develop the addon - @echo "$(GREEN)==> Start Development Environment $(RESET)" - make build-backend - make start-backend - make build-addon - make start-dev + @echo "$(GREEN)==> Build and start development environment $(RESET)" + ${DOCKER_COMPOSE} --profile dev build + ${DOCKER_COMPOSE} --profile dev up + # Dev Helpers .PHONY: i18n @@ -92,7 +94,8 @@ test: ## Run unit tests test-ci: ## Run unit tests in CI ${DOCKER_COMPOSE} run -e CI=1 addon-dev test -# Acceptance backend and frontend + +# Acceptance .PHONY: build-acceptance build-acceptance: ## Install Cypress, build containers (cd acceptance && yarn) @@ -114,7 +117,6 @@ test-acceptance-headless: ## Run cypress tests in CI .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server ${ACCEPTANCE} down - ${OPENSEARCH} down .PHONY: status-test-acceptance-server status-test-acceptance-server: ## Status of Acceptance Server diff --git a/README.md b/README.md index 17b40dea..af72a79e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,17 @@ The block is prepared for Matomo analytics. ![Search @rohberg/volto-searchkit-block](public/search.png) +# Demo + +You can try the search by checking out this repository and run + + make dev-opensearch + make dev + + +Docker should be installed and running. + + # Getting started Install Plone backend add-on [`collective.elastic.plone 2.x`](https://github.com/collective/collective.elastic.plone) to provide the Plone REST API service which accepts queries and requests OpenSearch/ElasticSearch. diff --git a/acceptance/.env b/acceptance/.env index 95cefbd0..6750bda7 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -1,5 +1,3 @@ -# This env file work out-of-the-box with the opensearch example docker-compose setup - export INDEX_SERVER=opensearch:9200 export INDEX_OPENSEARCH=1 export INDEX_USE_SSL=0 diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 03b2fc46..f14cd072 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -4,7 +4,7 @@ services: addon-acceptance: build: context: ../ - dockerfile: ./dockerfiles/Dockerfile.ci + dockerfile: ./dockerfiles/frontend/Dockerfile.ci args: ADDON_NAME: "${ADDON_NAME}" ADDON_PATH: "${ADDON_PATH}" diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index e5b963c0..1b1bde1b 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -5,7 +5,7 @@ services: addon-acceptance: build: context: ../ - dockerfile: ./dockerfiles/Dockerfile.acceptance + dockerfile: ./dockerfiles/frontend/Dockerfile.acceptance args: ADDON_NAME: "${ADDON_NAME}" ADDON_PATH: "${ADDON_PATH}" @@ -34,17 +34,6 @@ services: environment: ZSERVER_HOST: "0.0.0.0" ZSERVER_PORT: "55001" - # CORS_ALLOW_ORIGIN: "*" - # DELETE_EXISTING: "true" - # SETUP_CONTENT: "true" - # ADDONS: ${BACKEND_ADDONS} - # TYPE: volto - # # Profiles to be added to the created site - # ADDITIONAL_PROFILES: "collective.elastic.plone:default" - # # Packages to be used in configuration - # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto,plone.volto.cors" - # # CONFIGURE_PACKAGES: "plone.app.contenttypes,plone.restapi,plone.volto" - # plone.elastic 2.x CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} @@ -88,10 +77,6 @@ services: build: context: ../ dockerfile: ./docker-opensearch/Dockerfile.opensearch - # args: - # ADDON_NAME: "${ADDON_NAME}" - # ADDON_PATH: "${ADDON_PATH}" - # VOLTO_VERSION: ${VOLTO_VERSION:-17} environment: - plugins.security.disabled=true - cluster.name=opensearch-cluster diff --git a/dockerfiles/backend/Dockerfile.dev b/dockerfiles/backend/Dockerfile.dev index bf95059a..8cf68689 100644 --- a/dockerfiles/backend/Dockerfile.dev +++ b/dockerfiles/backend/Dockerfile.dev @@ -1,36 +1,44 @@ # syntax=docker/dockerfile:1 -# Dockerfile from cookiecutter-plone-starter +ARG SEED=1000 ARG PLONE_VERSION=6.0 -FROM plone/server-builder:${PLONE_VERSION} as builder +FROM plone/plone-backend:${PLONE_VERSION} -WORKDIR /app -# # Add local code -# COPY . . +LABEL maintainer="Plone Foundation " \ + org.label-schema.name="searchkit-backend" \ + org.label-schema.description="searchkit backend image." \ + org.label-schema.vendor="Rohberg" +# Add local code +COPY . . -# Install local requirements -RUN <=2.0.1 -EOT - - - - -FROM plone/server-prod-config:${PLONE_VERSION} - -# Use /app as the workdir -WORKDIR /app +# # Install local requirements and fix permissions +# RUN < \ No newline at end of file diff --git a/dockerfiles/backend/requirements-docker.txt b/dockerfiles/backend/requirements-docker.txt new file mode 100644 index 00000000..043c5ce2 --- /dev/null +++ b/dockerfiles/backend/requirements-docker.txt @@ -0,0 +1,2 @@ +collective.elastic.plone[redis,opensearch]==2.0.1 +elasticsearch \ No newline at end of file diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 532ce728..55c17f13 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -5,7 +5,7 @@ services: addon-dev: build: context: ../ - dockerfile: ./dockerfiles/Dockerfile.dev + dockerfile: ./dockerfiles/frontend/Dockerfile.dev args: ADDON_NAME: "${ADDON_NAME}" ADDON_PATH: "${ADDON_PATH}" @@ -19,19 +19,19 @@ services: # RAZZLE_INTERNAL_API_PATH: http://host.docker.internal:8080/Plone RAZZLE_API_PATH: http://127.0.0.1:8080/Plone HOST: 0.0.0.0 - depends_on: - - backend ports: - 3000:3000 - 3001:3001 tty: true + depends_on: + - backend profiles: - dev addon-live: build: context: ../ - dockerfile: ./dockerfiles/Dockerfile.ci + dockerfile: ./dockerfiles/frontend/Dockerfile.ci args: ADDON_NAME: "${ADDON_NAME}" ADDON_PATH: "${ADDON_PATH}" @@ -46,11 +46,130 @@ services: - live backend: - image: plone/plone-backend:${PLONE_VERSION:-6.0} + # image: plone/plone-backend:6.0.10.1 + build: + context: ./backend + dockerfile: ./Dockerfile.dev + args: + PLONE_VERSION: 6.0.10.1 environment: + ZSERVER_HOST: "0.0.0.0" + ZSERVER_PORT: "8080" + CELERY_BROKER: ${CELERY_BROKER?unset} + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} SITE: Plone CORS_: true + ADDONS: "collective.elastic.plone[redis,opensearch]==2.0.1" + PROFILES: "collective.elastic.plone:default" + DELETE_EXISTING: True ports: - 8080:8080 profiles: - dev + - prod + + + ingest: + image: ghcr.io/collective/collective.elastic.ingest:latest + environment: + MAPPINGS_FILE: ${MAPPINGS_FILE} + ANALYSIS_FILE: ${ANALYSIS_FILE} + PREPROCESSINGS_FILE: ${PREPROCESSINGS_FILE} + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} + CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} + CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + PLONE_SERVICE: ${PLONE_SERVICE?unset} + PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} + PLONE_USER: ${PLONE_USER?unset} + PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + SENTRY_DSN: ${SENTRY_DSN} + volumes: + - ingest-configuration:/configuration + profiles: + - dev + - prod + + redis: + image: 'redis:latest' + ports: + - 6379:6379 + profiles: + - dev + - prod + + opensearch: + build: + context: ../ + dockerfile: ./docker-opensearch/Dockerfile.opensearch + environment: + - plugins.security.disabled=true + - cluster.name=opensearch-cluster + - node.name=opensearch + - discovery.type=single-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + - "../docker-opensearch/opensearch-configuration/keywords.txt:/usr/share/opensearch/config/keywords.txt" + - "../docker-opensearch/opensearch-configuration/lexicon.txt:/usr/share/opensearch/config/lexicon.txt" + ports: + - 9200:9200 # REST API + - 9600:9600 # Performance Analyzer + tty: true + profiles: + - dev + - prod + + + opensearch-dashboards: + image: opensearchproject/opensearch-dashboards:latest + ports: + - 5601:5601 + expose: + - "5601" + environment: + OPENSEARCH_HOSTS: '["http://opensearch:9200"]' + profiles: + - dev + + # dejavu: + # image: appbaseio/dejavu:latest + # container_name: dejavu + # ports: + # - "1358:1358" + # expose: + # - "1358" + # links: + # - opensearch + +volumes: + opensearch-data: + ingest-configuration: + driver: local + driver_opts: + type: none + device: ../docker-opensearch/ingest-configuration + o: bind + # opensearch-configuration: + # driver: local + # driver_opts: + # type: none + # device: ./opensearch-configuration + # o: bind diff --git a/dockerfiles/Dockerfile.acceptance b/dockerfiles/frontend/Dockerfile.acceptance similarity index 100% rename from dockerfiles/Dockerfile.acceptance rename to dockerfiles/frontend/Dockerfile.acceptance diff --git a/dockerfiles/Dockerfile.ci b/dockerfiles/frontend/Dockerfile.ci similarity index 100% rename from dockerfiles/Dockerfile.ci rename to dockerfiles/frontend/Dockerfile.ci diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/frontend/Dockerfile.dev similarity index 100% rename from dockerfiles/Dockerfile.dev rename to dockerfiles/frontend/Dockerfile.dev From 034a5a4624dce32e26d1f38aef9f67c8300761fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 24 Mar 2024 08:31:47 +0100 Subject: [PATCH 007/226] Fix Sidebar --- src/components/Blocks/DownloadFiltersMapping.jsx | 6 +++--- src/components/Blocks/Sidebar.jsx | 6 ++++-- src/components/Blocks/schema.js | 6 ++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Blocks/DownloadFiltersMapping.jsx b/src/components/Blocks/DownloadFiltersMapping.jsx index 92e568e0..8ca25f62 100644 --- a/src/components/Blocks/DownloadFiltersMapping.jsx +++ b/src/components/Blocks/DownloadFiltersMapping.jsx @@ -5,11 +5,11 @@ import { Button } from 'semantic-ui-react'; const FooComponent = ({ data }) => { const vocabularies = useSelector((state) => state.querystring?.indexes); function exportFiltersMapping(data) { - const filternames = data.facet_fields.map((el) => { + const filternames = data.facet_fields?.map((el) => { return el.field.value; }); let ff = {}; - filternames.forEach((fname) => { + filternames?.forEach((fname) => { let foo = vocabularies[fname].values; Object.keys(foo).forEach((el) => { ff[el] = foo[el].title; @@ -17,7 +17,7 @@ const FooComponent = ({ data }) => { }); let map = { facet_fields: ff, - search_sections: Object.fromEntries( + search_sections: data.search_sections && Object.fromEntries( data.search_sections.items.map((el) => { return [el.section, el.label]; }), diff --git a/src/components/Blocks/Sidebar.jsx b/src/components/Blocks/Sidebar.jsx index 39a0e262..c6741b6b 100644 --- a/src/components/Blocks/Sidebar.jsx +++ b/src/components/Blocks/Sidebar.jsx @@ -1,13 +1,13 @@ import React from 'react'; import { useIntl } from 'react-intl'; import { SearchBlockSchema } from './schema'; -import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; +import { BlockDataForm } from '@plone/volto/components'; const Sidebar = ({ data, block, onChangeBlock }) => { const intl = useIntl(); let schema = SearchBlockSchema({ data, intl }); return ( - { @@ -16,7 +16,9 @@ const Sidebar = ({ data, block, onChangeBlock }) => { [id]: value, }); }} + onChangeBlock={onChangeBlock} formData={data} + block={block} /> ); }; diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 34b82960..34855570 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -49,10 +49,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { { id: 'default', title: 'API', - fields: [ - 'backend_url', - 'frontend_url', - ], + fields: ['backend_url', 'frontend_url'], }, { id: 'facets', @@ -179,5 +176,6 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { default: '', }, }, + required: [], }; }; From 3e97ffa90330adc153a752bf99495922e1df0f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 24 Mar 2024 08:39:49 +0100 Subject: [PATCH 008/226] format --- src/components/Blocks/DownloadFiltersMapping.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Blocks/DownloadFiltersMapping.jsx b/src/components/Blocks/DownloadFiltersMapping.jsx index 8ca25f62..a9f1f1f9 100644 --- a/src/components/Blocks/DownloadFiltersMapping.jsx +++ b/src/components/Blocks/DownloadFiltersMapping.jsx @@ -17,11 +17,13 @@ const FooComponent = ({ data }) => { }); let map = { facet_fields: ff, - search_sections: data.search_sections && Object.fromEntries( - data.search_sections.items.map((el) => { - return [el.section, el.label]; - }), - ), + search_sections: + data.search_sections && + Object.fromEntries( + data.search_sections.items.map((el) => { + return [el.section, el.label]; + }), + ), }; const fileData = JSON.stringify(map); const blob = new Blob([fileData], { type: 'text/plain' }); From ce96bf80ec5e173f28abf73a681474f62091c1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 24 Mar 2024 08:52:51 +0100 Subject: [PATCH 009/226] Fix CI code, unit --- .github/workflows/code.yml | 25 +++++++++++++++++++++++++ .github/workflows/unit.yml | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index dccebc2d..42f5b627 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -1,5 +1,30 @@ name: Code analysis checks on: [push] + +env: + ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_PATH: volto-searchkit-block + VOLTO_VERSION: 17 + PLONE_VERSION: 6.0 + + INDEX_SERVER: opensearch:9200 + INDEX_OPENSEARCH: 1 + INDEX_USE_SSL: 0 + INDEX_LOGIN: admin + INDEX_PASSWORD: "oxczBG).3xWyapLn" + + CELERY_BROKER: redis://redis:6379/0 + CELERY_LOG_LEVEL: info + + PLONE_SERVICE: http://backend-acceptance:55001 + PLONE_SITE_PREFIX_PATH: plone + PLONE_USER: admin + PLONE_PASSWORD: secret + + MAPPINGS_FILE: /configuration/mappings.json + ANALYSIS_FILE: /configuration/analysis.json + PREPROCESSINGS_FILE: /configuration/preprocessings.json + jobs: codeanalysis: runs-on: ubuntu-latest diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 090177a8..8b8f0ebc 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -1,5 +1,30 @@ name: Unit Tests on: [push] + +env: + ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_PATH: volto-searchkit-block + VOLTO_VERSION: 17 + PLONE_VERSION: 6.0 + + INDEX_SERVER: opensearch:9200 + INDEX_OPENSEARCH: 1 + INDEX_USE_SSL: 0 + INDEX_LOGIN: admin + INDEX_PASSWORD: "oxczBG).3xWyapLn" + + CELERY_BROKER: redis://redis:6379/0 + CELERY_LOG_LEVEL: info + + PLONE_SERVICE: http://backend-acceptance:55001 + PLONE_SITE_PREFIX_PATH: plone + PLONE_USER: admin + PLONE_PASSWORD: secret + + MAPPINGS_FILE: /configuration/mappings.json + ANALYSIS_FILE: /configuration/analysis.json + PREPROCESSINGS_FILE: /configuration/preprocessings.json + jobs: unit: runs-on: ubuntu-latest From fb038e8187944a1dc4644127761b5035197577f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:41:00 +0100 Subject: [PATCH 010/226] Use SSL --- .github/workflows/acceptance.yml | 2 +- .github/workflows/code.yml | 2 +- .github/workflows/unit.yml | 2 +- acceptance/.env | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index ff727a81..ebbf6fda 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 0 + INDEX_USE_SSL: 1 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 42f5b627..487ed58e 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 0 + INDEX_USE_SSL: 1 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 8b8f0ebc..c8a1055c 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 0 + INDEX_USE_SSL: 1 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" diff --git a/acceptance/.env b/acceptance/.env index 6750bda7..2bc2d1a2 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -1,8 +1,8 @@ export INDEX_SERVER=opensearch:9200 export INDEX_OPENSEARCH=1 -export INDEX_USE_SSL=0 +export INDEX_USE_SSL=1 export INDEX_LOGIN=admin -export INDEX_PASSWORD="oxczBG).3xWyapLn" +export INDEX_PASSWORD=admin export CELERY_BROKER=redis://redis:6379/0 export CELERY_LOG_LEVEL=debug From ca5fb2c29919119ec98fc7ede89802af94d98d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:41:15 +0100 Subject: [PATCH 011/226] Extend tests --- acceptance/cypress/tests/search.cy.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.cy.js index 3802c4a8..983d9818 100644 --- a/acceptance/cypress/tests/search.cy.js +++ b/acceptance/cypress/tests/search.cy.js @@ -60,7 +60,7 @@ describe('Searchkit block tests – search', () => { cy.autologin(); cy.visit('/suche'); - cy.wait(5000); + cy.wait(3000); // cy.wait('@kitsearch'); }); @@ -73,6 +73,28 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'testseite-manner' }); }); + // Blocks text + it('I can search in blocks', function () { + cy.visit('/garten-blog/februar/edit'); + cy.wait('@schema'); + + // TODO + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@content'); + + cy.log('I added a text block'); + + // Searching + cy.visit('/suche'); + cy.wait(3000); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + it('I see all if no filter selected', function () { cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); @@ -113,4 +135,5 @@ describe('Searchkit block tests – search', () => { cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); }); + }); From 70358bd49f05deecbe1e0d29391487af0ca75f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:41:39 +0100 Subject: [PATCH 012/226] Fix acceptance setup --- acceptance/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 1b1bde1b..8d87f6b8 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -78,12 +78,12 @@ services: context: ../ dockerfile: ./docker-opensearch/Dockerfile.opensearch environment: - - plugins.security.disabled=true - cluster.name=opensearch-cluster - node.name=opensearch - discovery.type=single-node - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + # - plugins.security.disabled=true - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: memlock: @@ -112,7 +112,7 @@ services: expose: - "5601" environment: - OPENSEARCH_HOSTS: '["http://opensearch:9200"]' + OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: - dev From 8bf05cfcfa249946af0856fb24f1dbc1161b3312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:42:01 +0100 Subject: [PATCH 013/226] Fix preprocessing for blocks_plaintext --- docker-opensearch/ingest-configuration/preprocessings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-opensearch/ingest-configuration/preprocessings.json b/docker-opensearch/ingest-configuration/preprocessings.json index 40be4e8a..9ecd9abc 100644 --- a/docker-opensearch/ingest-configuration/preprocessings.json +++ b/docker-opensearch/ingest-configuration/preprocessings.json @@ -85,7 +85,7 @@ "comment": "If Volto is available, this is important for full text search, do not remove unless in Classic UI only environments", "action": "rewrite", "configuration": { - "source": "@components/collectiveelastic/blocks_plaintext", + "source": "@components/blocks_plaintext", "target": "blocks_plaintext", "enforce": false } From d1652f191366738054497dcae2ba79658d599bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:42:16 +0100 Subject: [PATCH 014/226] Fix dev setup --- dockerfiles/docker-compose.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 55c17f13..14415987 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -22,6 +22,8 @@ services: ports: - 3000:3000 - 3001:3001 + networks: + - opensearch-net tty: true depends_on: - backend @@ -41,6 +43,8 @@ services: RAZZLE_API_PATH: http://localhost:8080/Plone ports: - 3000:3000 + networks: + - opensearch-net tty: true profiles: - live @@ -55,6 +59,7 @@ services: environment: ZSERVER_HOST: "0.0.0.0" ZSERVER_PORT: "8080" + LANGUAGE: "de" CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} @@ -68,6 +73,8 @@ services: DELETE_EXISTING: True ports: - 8080:8080 + networks: + - opensearch-net profiles: - dev - prod @@ -94,6 +101,8 @@ services: SENTRY_DSN: ${SENTRY_DSN} volumes: - ingest-configuration:/configuration + networks: + - opensearch-net profiles: - dev - prod @@ -102,6 +111,8 @@ services: image: 'redis:latest' ports: - 6379:6379 + networks: + - opensearch-net profiles: - dev - prod @@ -111,12 +122,12 @@ services: context: ../ dockerfile: ./docker-opensearch/Dockerfile.opensearch environment: - - plugins.security.disabled=true - cluster.name=opensearch-cluster - node.name=opensearch - discovery.type=single-node - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + # - plugins.security.disabled=true - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: memlock: @@ -129,6 +140,8 @@ services: - opensearch-data:/usr/share/opensearch/data - "../docker-opensearch/opensearch-configuration/keywords.txt:/usr/share/opensearch/config/keywords.txt" - "../docker-opensearch/opensearch-configuration/lexicon.txt:/usr/share/opensearch/config/lexicon.txt" + networks: + - opensearch-net ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer @@ -145,9 +158,11 @@ services: expose: - "5601" environment: - OPENSEARCH_HOSTS: '["http://opensearch:9200"]' + OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: - dev + networks: + - opensearch-net # dejavu: # image: appbaseio/dejavu:latest @@ -173,3 +188,6 @@ volumes: # type: none # device: ./opensearch-configuration # o: bind + +networks: + opensearch-net: From e699bea93cb00684d44e14aacee71f2c20f894a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:42:36 +0100 Subject: [PATCH 015/226] Add blocks_plaintext to default searched fields --- src/components/Blocks/schema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 34855570..deb0fda2 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -141,7 +141,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { 'Type fieldnames to search in field names. Type title^1.4 to boost the title 40%.', type: 'array', creatable: true, - default: ['title^1.4', 'description^1.2'], + default: ['title^1.4', 'description^1.2', 'blocks_plaintext'], }, facet_fields: { title: 'Facets', From a777846c3b5a20ed74cdfc1b8a348314e76f2106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 26 Mar 2024 09:58:44 +0100 Subject: [PATCH 016/226] Test Opensearch SSL False --- .github/workflows/acceptance.yml | 2 +- .github/workflows/code.yml | 2 +- .github/workflows/unit.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index ebbf6fda..ff727a81 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 1 + INDEX_USE_SSL: 0 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 487ed58e..42f5b627 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 1 + INDEX_USE_SSL: 0 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c8a1055c..8b8f0ebc 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,7 @@ env: INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 1 + INDEX_USE_SSL: 0 INDEX_LOGIN: admin INDEX_PASSWORD: "oxczBG).3xWyapLn" From e7b337fc9b3ebf03539a0c76b3f493e2d5ca8886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 2 Apr 2024 14:17:16 +0200 Subject: [PATCH 017/226] Fix field type of review_state to keyword. Fix blocks_plaintext --- .../ingest-configuration/preprocessings.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docker-opensearch/ingest-configuration/preprocessings.json b/docker-opensearch/ingest-configuration/preprocessings.json index 9ecd9abc..0a531048 100644 --- a/docker-opensearch/ingest-configuration/preprocessings.json +++ b/docker-opensearch/ingest-configuration/preprocessings.json @@ -85,7 +85,7 @@ "comment": "If Volto is available, this is important for full text search, do not remove unless in Classic UI only environments", "action": "rewrite", "configuration": { - "source": "@components/blocks_plaintext", + "source": "@components/collectiveelastic/blocks_plaintext", "target": "blocks_plaintext", "enforce": false } @@ -230,5 +230,13 @@ "name": "plone.layoutaware", "field": "sectionSiteLayout" } + }, + { + "comment": "Fix field type of review_state to keyword", + "action": "additional_schema", + "configuration": { + "name": "review_state", + "field": "zope.schema._field.ASCIILine" + } } ] \ No newline at end of file From 7005f9b8b80430107af2f520b4771be143e7bb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 2 Apr 2024 14:17:42 +0200 Subject: [PATCH 018/226] Fix mapping of "subjects" --- .../ingest-configuration/mappings.json | 48 +++---------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/docker-opensearch/ingest-configuration/mappings.json b/docker-opensearch/ingest-configuration/mappings.json index bdb43cef..7c33f0f7 100644 --- a/docker-opensearch/ingest-configuration/mappings.json +++ b/docker-opensearch/ingest-configuration/mappings.json @@ -1,5 +1,5 @@ { - "behaviors/plone.basic/title": { + "zope.schema._bootstrapfields.Text": { "type": "text", "analyzer": "german_analyzer", "term_vector": "with_positions_offsets", @@ -11,7 +11,7 @@ } } }, - "behaviors/plone.basic/description": { + "zope.schema._bootstrapfields.TextLine": { "type": "text", "analyzer": "german_analyzer", "term_vector": "with_positions_offsets", @@ -32,24 +32,13 @@ "type": "text", "analyzer": "german_exact_analyzer", "term_vector": "with_positions_offsets" + }, + "keyword": { + "type": "keyword" } } }, - "behaviors/rohberg.elasticsearchblocks.elastic_search_blocks/blocks_plaintext": { - "type": "text", - "analyzer": "german_analyzer", - "term_vector": "with_positions_offsets", - "fields": { - "exact": { - "type": "text", - "analyzer": "german_exact_analyzer", - "term_vector": "with_positions_offsets" - } - } - }, - "behaviors/plone.allowdiscussion/allow_discussion": { - "type": "boolean" - }, + "plone.app.textfield.RichText": { "pipeline": { "source": "{name}__data", @@ -341,36 +330,13 @@ } } }, + "zope.schema._bootstrapfields.Bool": { "type": "boolean" }, "zope.schema._bootstrapfields.Int": { "type": "long" }, - "zope.schema._bootstrapfields.Text": { - "type": "text", - "analyzer": "german_analyzer", - "term_vector": "with_positions_offsets", - "fields": { - "exact": { - "type": "text", - "analyzer": "german_exact_analyzer", - "term_vector": "with_positions_offsets" - } - } - }, - "zope.schema._bootstrapfields.TextLine": { - "type": "text", - "analyzer": "german_analyzer", - "term_vector": "with_positions_offsets", - "fields": { - "exact": { - "type": "text", - "analyzer": "german_exact_analyzer", - "term_vector": "with_positions_offsets" - } - } - }, "zope.schema._field.ASCIILine": { "type": "keyword" }, From 58377e92489b99fafca5816cb8c73a3b5efc80d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 2 Apr 2024 14:18:20 +0200 Subject: [PATCH 019/226] docker dev: Do not delete existing Plone site --- dockerfiles/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 14415987..2c87db07 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -70,7 +70,7 @@ services: CORS_: true ADDONS: "collective.elastic.plone[redis,opensearch]==2.0.1" PROFILES: "collective.elastic.plone:default" - DELETE_EXISTING: True + DELETE_EXISTING: False ports: - 8080:8080 networks: From 97e6a1317e4858ba19e65e8f3b86ff9eed1cca1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 2 Apr 2024 14:18:50 +0200 Subject: [PATCH 020/226] Add pure start development/demo --- Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f75ae8f4..44b5b720 100644 --- a/Makefile +++ b/Makefile @@ -55,19 +55,24 @@ build-addon: ## Build Addon dev @echo "$(GREEN)==> Build Addon development container $(RESET)" ${DOCKER_COMPOSE} build addon-dev -.PHONY: start-dev -start-dev: ## Starts Dev container +.PHONY: start-addon +start-addon: ## Starts Dev container @echo "$(GREEN)==> Start Addon Development container $(RESET)" ${DOCKER_COMPOSE} up addon-dev # TODO Check if 'make dev' is OK for trying the searchkit block # TODO Add example content: plone.exportimport https://plone.github.io/plone.exportimport/features.html#plone-importer .PHONY: dev -dev: ## Develop the addon +dev: ## Build and start development/demo environment @echo "$(GREEN)==> Build and start development environment $(RESET)" ${DOCKER_COMPOSE} --profile dev build ${DOCKER_COMPOSE} --profile dev up +.PHONY: dev-start +dev-start: ## Start development/demo environment without rebuilding images + @echo "$(GREEN)==> Start development environment without rebuilding images $(RESET)" + ${DOCKER_COMPOSE} --profile dev up + # Dev Helpers .PHONY: i18n From 0941df5089e1324e7c531af0b40806890fddceb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 3 Apr 2024 17:21:12 +0200 Subject: [PATCH 021/226] Back to current behavior: "@components/blocks_plaintext" --- docker-opensearch/ingest-configuration/preprocessings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-opensearch/ingest-configuration/preprocessings.json b/docker-opensearch/ingest-configuration/preprocessings.json index 0a531048..602f9ffa 100644 --- a/docker-opensearch/ingest-configuration/preprocessings.json +++ b/docker-opensearch/ingest-configuration/preprocessings.json @@ -85,7 +85,7 @@ "comment": "If Volto is available, this is important for full text search, do not remove unless in Classic UI only environments", "action": "rewrite", "configuration": { - "source": "@components/collectiveelastic/blocks_plaintext", + "source": "@components/blocks_plaintext", "target": "blocks_plaintext", "enforce": false } From 5c399c2c91fafb54a5acc5c3b42d9a83a04bbb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 3 Apr 2024 17:21:51 +0200 Subject: [PATCH 022/226] Update keywords and lexicon for text analysis --- .../opensearch-configuration/keywords.txt | 4 +- .../opensearch-configuration/lexicon.txt | 361 +----------------- 2 files changed, 3 insertions(+), 362 deletions(-) diff --git a/docker-opensearch/opensearch-configuration/keywords.txt b/docker-opensearch/opensearch-configuration/keywords.txt index ff5bb855..f9dec303 100644 --- a/docker-opensearch/opensearch-configuration/keywords.txt +++ b/docker-opensearch/opensearch-configuration/keywords.txt @@ -1,3 +1 @@ -börse -zusammenführen -ük \ No newline at end of file +börse \ No newline at end of file diff --git a/docker-opensearch/opensearch-configuration/lexicon.txt b/docker-opensearch/opensearch-configuration/lexicon.txt index bd6f044e..496b038c 100644 --- a/docker-opensearch/opensearch-configuration/lexicon.txt +++ b/docker-opensearch/opensearch-configuration/lexicon.txt @@ -1,360 +1,3 @@ -abacus -abbildung -abbruch -abfrage -abgleich -abgrenzung -abhängigkeit -abklärung -abkommen -abkürzung -ablage -ablauf -ableitung -abmeldung -abonnement -abonnent -abraxas -abrechnung -abruf -abschluss -abschnitt -absprache -absturz -abteilung -abweichung -abweisung -abzug -adapter -administration -administrator -adress -adressdaten -adresse -ahv -aktion -aktiv -aktualisieren -aktualisierung -aktuare -aktuell -akzent -akzeptanz -allgemein -alter -alternativ -amt -amtes -analog -analyse -anbieter -anbindung -anfang -anforderung -anfrage -angabe -angebot -anlage -anlegen -anleitung -anlieferung -anliegen -anmelde -anmeldung -anmeldungen -anpassen -anpassung -anrecht -anrede -anregungen -ansatz -anschliessend -anschluss -anschrift -ansehen -ansicht -ansprechen -anspruch -anstoss -anteil -antrag -antwort -anwender -anwendung -anwendungsfall -anzahl -anzeige -app -applikation -arbeit -arbeitgeber -arbeitnehmer -arbeitsliste -arbeitszeit -architektur -archiv -artikel -arzt -aspekt -assistent -attribut -aufbau -aufbereitung -aufbewahrung -aufenthalts -aufforderung -aufgabe -auflage -auflistung -aufnahme -aufruf -aufsplittung -auftrag -aufwand -aufwendungen -ausbildung -ausdruck -ausführen -ausgabe -auskunft -ausland -ausländer -ausnahme -ausprägung -austausch -auswahl -auswertung -auswirkung -auszahlungen -auszubildend -auszubildender -auszug -authentisierung -authorization -auto -autodat -automatisch -automatisiert -autor -bachelor -bank -barcode -basis -batch -baum -beachten -bearbeitung -beauftragt -bedarf -bedeutung -bedingungen -bedürfnis -beendigung -befreiung -begriff -begründung -behörde -beilagen -beispiel -beistand -beitrag -beitritt -beleg -bemerkung -bemessung -benachrichtigung -benutzend -benutzer -berater -beratung -berechnung -berechtigung -bereich -bereinigung -bereitstellung -bericht -berücksichtigung -beruf -beschaffung -beschäftigung -bescheid -beschlussdatum -beschreibung -beschrieb -beschulung -besitzer -bestimmung -betrag -betrieb -bewegung -bewerbung -bewertung -bewilligung -bezeichnung -beziehung -bilanz -bild -bildung -block -börse -branche -brückenangebot -buchhaltung -buchung -business -businesscase -code -company -content -contract -control -darlehen -darstellung -datei -daten -datenbank -datensatz -definition -detail -deutsch -developer -dienst -direkt -dokument -dokumentation -dossier -druck -durchführung -einführung -eingabe -eingang -element -eltern -email -entwickler -erfassung -erhebung -evaluation -exam -experte -experten -fach -fachschule -fehler -feld -filial -filter -finanz -format -formular -freigabe -funktion -gemeinde -gesamt -geschäfts -grund -gruppe -gültigkeit -handbuch -haupt -hochschul -identifikator -import -informatik -information -institution -jahr -jahres -job -kandidat -kanton -katalog -kern -klassen -kompass -kontakt -konto -kontroll -korrespondenz -kosten -kurs lehr -lehre -lehrer -lehrstelle -leistung -leit -lieferung -liste -lokation -lösch -mandant -melde -mitarbeiter -modell -modul -mutation -noten -organisation -pendenz -person -portal -praktikant -praktikum -profil -programm -prüfung -qualification -qualifikation -rechnung -referenz -regel -register -schul -schule -schüler -schulung -selektion -semester -sonder -sozial -spezial -sprach -sprache -stamm -stammdaten -standard -status -stelle -steuerung -stipendien -stipendium -studien -such -system -tarif -team -teil -terminal -test -text -transaktion -transaktions -ük -überbetrieblich -übermittlung -überschuss -übertrag -variante -verarbeitung -verarbeitungs -verfahren -vertrag -verträge -vorbereitung -vorjahr -weiterbildung -weiterbildungs -zahlung -zahlungs -zusatz \ No newline at end of file +stell +börse \ No newline at end of file From 61a90f13dbd9817117fd9abb4baf26ea0bc51962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 3 Apr 2024 17:22:18 +0200 Subject: [PATCH 023/226] cypress test 'I can search for a compounded word' --- acceptance/cypress/tests/search.cy.js | 65 ++++++++++++++++++--------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.cy.js index 983d9818..fabfa089 100644 --- a/acceptance/cypress/tests/search.cy.js +++ b/acceptance/cypress/tests/search.cy.js @@ -37,6 +37,16 @@ describe('Searchkit block tests – search', () => { contentId: 'testseite-manner', contentTitle: 'Testseite Männer', }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-lsb', + contentTitle: 'Testseite Lehrstellenbörsen', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-s', + contentTitle: 'Testseite Stelle', + }); cy.visit('/suche/edit'); cy.wait('@schema'); @@ -71,29 +81,10 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'suche' }); cy.removeContent({ path: 'testseite-mann' }); cy.removeContent({ path: 'testseite-manner' }); + cy.removeContent({ path: 'testseite-lsb' }); + cy.removeContent({ path: 'testseite-s' }); }); - // Blocks text - it('I can search in blocks', function () { - cy.visit('/garten-blog/februar/edit'); - cy.wait('@schema'); - - // TODO - cy.getSlate().click(); - cy.log('when I add a text block'); - cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); - // cy.toolbarSave(); - cy.get('#toolbar-save').click(); - cy.wait('@content'); - - cy.log('I added a text block'); - - // Searching - cy.visit('/suche'); - cy.wait(3000); - cy.get('.searchbar-wrapper input').type('Montag{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); it('I see all if no filter selected', function () { cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); @@ -136,4 +127,36 @@ describe('Searchkit block tests – search', () => { cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); }); + it('I can search for a compounded word', function () { + cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + }); + + // Blocks text + it('I can search in blocks', function () { + cy.visit('/garten-blog/februar/edit'); + cy.wait('@schema'); + + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@content'); + + cy.log('I added a text block'); + + // Searching + cy.visit('/suche'); + cy.wait(3000); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + }); From a0bae1b84ddbe66c1963d1b579744cb28a89e63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 5 Apr 2024 09:59:15 +0200 Subject: [PATCH 024/226] Workarround authentication bug in cypress tests --- .gitignore | 1 + acceptance/.env | 2 +- acceptance/cypress/tests/create_search.cy.js | 4 ++-- acceptance/cypress/tests/search.cy.js | 14 ++++++-------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index a591ad0c..afc099fd 100644 --- a/.gitignore +++ b/.gitignore @@ -117,6 +117,7 @@ api/constraints-mxdev.txt api/requirements-mxdev.txt development-searchkitblock/celery/venv/ development-searchkitblock/celery/sources/ +acceptance/cypress/screenshots/ build/ .yarn/ yarn.lock diff --git a/acceptance/.env b/acceptance/.env index 2bc2d1a2..ea21419b 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -2,7 +2,7 @@ export INDEX_SERVER=opensearch:9200 export INDEX_OPENSEARCH=1 export INDEX_USE_SSL=1 export INDEX_LOGIN=admin -export INDEX_PASSWORD=admin +export INDEX_PASSWORD=paraDiesli,17 export CELERY_BROKER=redis://redis:6379/0 export CELERY_LOG_LEVEL=debug diff --git a/acceptance/cypress/tests/create_search.cy.js b/acceptance/cypress/tests/create_search.cy.js index fdc71625..69be7cd7 100644 --- a/acceptance/cypress/tests/create_search.cy.js +++ b/acceptance/cypress/tests/create_search.cy.js @@ -50,8 +50,8 @@ describe('Searchkit block tests- create search ', () => { }); it('As manager I can add a searchkit-block and find a documunt', function () { - cy.navigate('/searching/edit'); - cy.wait('@schema'); + cy.visit('/searching'); + cy.get('a.edit').click(); cy.getSlate().click(); cy.get('.button .block-add-button').click({ force: true }); diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.cy.js index fabfa089..729af0f7 100644 --- a/acceptance/cypress/tests/search.cy.js +++ b/acceptance/cypress/tests/search.cy.js @@ -48,8 +48,10 @@ describe('Searchkit block tests – search', () => { contentTitle: 'Testseite Stelle', }); - cy.visit('/suche/edit'); - cy.wait('@schema'); + + // Add search block to /suche + cy.visit('/suche'); + cy.get('a.edit').click(); cy.getSlate().click(); cy.get('.button .block-add-button').click({ force: true }); @@ -85,7 +87,6 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'testseite-s' }); }); - it('I see all if no filter selected', function () { cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); @@ -97,9 +98,6 @@ describe('Searchkit block tests – search', () => { }); it('I can search with inflection', function () { - cy.get('.searchbar-wrapper input').type('Schnelltestkit{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); @@ -140,8 +138,8 @@ describe('Searchkit block tests – search', () => { // Blocks text it('I can search in blocks', function () { - cy.visit('/garten-blog/februar/edit'); - cy.wait('@schema'); + cy.visit('/garten-blog/februar'); + cy.get('a.edit').click(); cy.getSlate().click(); cy.log('when I add a text block'); From 1c04d03eb0e695a8306c3856bcfb568e792f90cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 5 Apr 2024 10:01:28 +0200 Subject: [PATCH 025/226] Remove dependency of acceptance containers for dev with backend add-ons --- acceptance/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 8d87f6b8..3f2409fe 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -20,8 +20,8 @@ services: - 3000:3000 - 3001:3001 tty: true - depends_on: - - backend-acceptance + # depends_on: + # - backend-acceptance profiles: - dev From 4b041b94686d75499d1953d20f38a297e805891a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 5 Apr 2024 12:41:31 +0200 Subject: [PATCH 026/226] Reorder preprocessings.json --- .../ingest-configuration/preprocessings.json | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/docker-opensearch/ingest-configuration/preprocessings.json b/docker-opensearch/ingest-configuration/preprocessings.json index 602f9ffa..bfaec5ce 100644 --- a/docker-opensearch/ingest-configuration/preprocessings.json +++ b/docker-opensearch/ingest-configuration/preprocessings.json @@ -1,23 +1,4 @@ [ - - { - "comment": "... needs this information, essential rewrite, do not remove", - "action": "rewrite", - "configuration": { - "source": "@components/collectiveelastic/section_id", - "target": "section", - "enforce": true - } - }, - { - "comment": "Schema for above section_id, do not remove", - "action": "additional_schema", - "configuration": { - "name": "section", - "field": "zope.schema._field.ASCIILine" - } - }, - { "comment": "Remove all empty fields.", "action": "remove_empty" @@ -98,6 +79,23 @@ "field": "zope.schema._bootstrapfields.Text" } }, + { + "comment": "... needs this information, essential rewrite, do not remove", + "action": "rewrite", + "configuration": { + "source": "@components/collectiveelastic/section_id", + "target": "section", + "enforce": true + } + }, + { + "comment": "Schema for above section_id, do not remove", + "action": "additional_schema", + "configuration": { + "name": "section", + "field": "zope.schema._field.ASCIILine" + } + }, { "comment": "If volto.blocks is available, remove all its fields.", "action": "full_remove", From 3bb6cec22aabb81cde52c82deb749764592a4b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 5 Apr 2024 12:43:21 +0200 Subject: [PATCH 027/226] Comment out elastic_search_api_url and index --- src/components/Blocks/schema.js | 22 +++++++++++----------- src/components/Searchkit/ESSearchApi.jsx | 8 ++++---- src/components/Views/FacetedSearch.jsx | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index deb0fda2..19a18112 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -5,7 +5,7 @@ import { import messages from '../../messages'; const FacetSchema = ({ intl }) => ({ - title: intl.formatMessage(messages.facet), + title: intl.formatMessage(messages.item), fieldsets: [ { id: 'default', @@ -83,16 +83,16 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { }, ], properties: { - elastic_search_api_url: { - title: - '(deprecated) (Set in collective.elastic environment variable) Elastic Search API URL', - default: 'http://localhost:9200', - }, - elastic_search_api_index: { - title: - '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', - default: 'esploneindex', - }, + // elastic_search_api_url: { + // title: + // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API URL', + // default: 'http://localhost:9200', + // }, + // elastic_search_api_index: { + // title: + // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', + // default: 'esploneindex', + // }, backend_url: { title: 'Backend URL', default: 'http://localhost:8080/Plone', diff --git a/src/components/Searchkit/ESSearchApi.jsx b/src/components/Searchkit/ESSearchApi.jsx index 16dd1b95..51685f19 100644 --- a/src/components/Searchkit/ESSearchApi.jsx +++ b/src/components/Searchkit/ESSearchApi.jsx @@ -9,8 +9,8 @@ export class PloneSearchApi { this.validateFetchConfig(); this.initSerializers(config); this.search = this.search.bind(this); - this.elastic_search_api_url = config.elastic_search_api_url; - this.elastic_search_api_index = config.elastic_search_api_index; + // this.elastic_search_api_url = config.elastic_search_api_url; + // this.elastic_search_api_index = config.elastic_search_api_index; } validateFetchConfig() { @@ -57,8 +57,8 @@ export class PloneSearchApi { headers: this.fetchConfig.headers, body: JSON.stringify({ elasticsearch_payload: payload, - elasticsearch_url: this.elastic_search_api_url, - elasticsearch_index: this.elastic_search_api_index, + // elasticsearch_url: this.elastic_search_api_url, + // elasticsearch_index: this.elastic_search_api_index, }), }); // let results = await this.responseSerializer.serialize(response.data); diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 800e264a..76930907 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -80,8 +80,8 @@ export const ploneSearchApi = (data) => { search_sections: data.search_sections, backend_url: data.backend_url, frontend_url: data.frontend_url, - elastic_search_api_url: data.elastic_search_api_url, - elastic_search_api_index: data.elastic_search_api_index, + // elastic_search_api_url: data.elastic_search_api_url, + // elastic_search_api_index: data.elastic_search_api_index, }); }; @@ -389,7 +389,7 @@ const CustomBucketAggregationElement = (props) => { }; const dropdowntitle = - title + (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); + title || fieldname + (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); return containerCmp ? (
From b57ca9c14b3582bd84d7b9d2a9988a1bc6dc7e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 5 Apr 2024 12:43:42 +0200 Subject: [PATCH 028/226] Label for facet and meta data item --- src/messages.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/messages.js b/src/messages.js index 25069d0e..d733e786 100644 --- a/src/messages.js +++ b/src/messages.js @@ -22,6 +22,10 @@ const messages = defineMessages({ id: 'Facet', defaultMessage: 'Facet', }, + item: { + id: 'Item', + defaultMessage: 'Item', + }, label: { id: 'Label', defaultMessage: 'Label', From 551130e0fbcf06ccefc471811bdf18ae89988d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 12:36:21 +0200 Subject: [PATCH 029/226] Prepare multilingual tests --- .github/workflows/acceptance.yml | 18 ++++++- Makefile | 33 ++++++++++-- acceptance/.env | 3 +- acceptance/ci.yml | 52 +++++++++++++++++++ ...nfig.js => cypress.multilingual.config.js} | 2 +- acceptance/cypress.singlelingual.config.js | 9 ++++ ...{basic.cy.js => basic.singlelingual.cy.js} | 0 ...y.js => create_search.singlelingual.cy.js} | 0 ...earch.cy.js => search.singlelingual.cy.js} | 0 dockerfiles/backend/Dockerfile.acceptance | 8 +-- .../Dockerfile.acceptance.multilingual | 31 +++++++++++ .../Dockerfile.acceptance.multilingual | 19 +++++++ .../frontend/Dockerfile.ci.multilingual | 30 +++++++++++ dockerfiles/frontend/config_multilingual.js | 25 +++++++++ variables.mk | 2 +- 15 files changed, 217 insertions(+), 15 deletions(-) rename acceptance/{cypress.config.js => cypress.multilingual.config.js} (71%) create mode 100644 acceptance/cypress.singlelingual.config.js rename acceptance/cypress/tests/{basic.cy.js => basic.singlelingual.cy.js} (100%) rename acceptance/cypress/tests/{create_search.cy.js => create_search.singlelingual.cy.js} (100%) rename acceptance/cypress/tests/{search.cy.js => search.singlelingual.cy.js} (100%) create mode 100644 dockerfiles/backend/Dockerfile.acceptance.multilingual create mode 100644 dockerfiles/frontend/Dockerfile.acceptance.multilingual create mode 100644 dockerfiles/frontend/Dockerfile.ci.multilingual create mode 100644 dockerfiles/frontend/config_multilingual.js diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index ff727a81..9e193496 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -52,12 +52,28 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.cy.js + spec: cypress/tests/*.singlelingual.cy.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 http://localhost:9200' + - name: "Cypress: Acceptance tests multilingual" + uses: cypress-io/github-action@v6 + env: + BABEL_ENV: production + CYPRESS_RETRIES: 2 + with: + parallel: false + browser: chrome + working-directory: acceptance + spec: cypress/tests/*.multilingual.cy.js + install: false + start: | + docker compose -f ci.yml --profile multilingual up + wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' + + # Upload Cypress screenshots - uses: actions/upload-artifact@v4 if: failure() diff --git a/Makefile b/Makefile index 44b5b720..45c1602d 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,11 @@ YELLOW=`tput setaf 3` BACKEND_ADDONS='collective.elastic.plone ${KGS} $(TESTING_ADDONS)' DEV_COMPOSE=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} BACKEND_ADDONS=${BACKEND_ADDONS} docker compose +CMD_ENVS=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} BACKEND_ADDONS=${BACKEND_ADDONS} +CMD=${CMD_ENVS} docker compose DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${DEV_COMPOSE} ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE} - +ACCEPTANCE_MULTILINGUAL=${CMD} -p ${ADDON_PATH}-acceptance-multilingual -f ${ACCEPTANCE_COMPOSE} .PHONY: all all: help @@ -104,7 +105,7 @@ test-ci: ## Run unit tests in CI .PHONY: build-acceptance build-acceptance: ## Install Cypress, build containers (cd acceptance && yarn) - ${ACCEPTANCE} --profile dev build + ${ACCEPTANCE} --profile dev build --no-cache .PHONY: start-acceptance-containers start-acceptance: ## Start acceptance server-containers @@ -112,12 +113,12 @@ start-acceptance: ## Start acceptance server-containers .PHONY: test-acceptance test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open) + (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.singlelingual.config.js) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run) + (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.singlelingual.config.js) .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server @@ -126,3 +127,25 @@ stop-test-acceptance-server: ## Stop acceptance server .PHONY: status-test-acceptance-server status-test-acceptance-server: ## Status of Acceptance Server ${ACCEPTANCE} ps + + +# Acceptance multilingual +.PHONY: build-acceptance-multilingual +build-acceptance-multilingual: ## multilingual – Install Cypress, build containers for multilingual site + (cd acceptance && yarn) + @echo ${PLONE_VERSION} + @echo ${ACCEPTANCE_MULTILINGUAL} + ${ACCEPTANCE_MULTILINGUAL} --profile multilingual build --no-cache + +.PHONY: start-acceptance-containers-multilingual +start-acceptance-multilingual: ## multilingual – Start acceptance server-containers for multilingual siet + ${ACCEPTANCE_MULTILINGUAL} --profile multilingual up -d --force-recreate + +.PHONY: test-acceptance +test-acceptance-multilingual: ## Start Cypress + (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.multilingual.config.js) + +.PHONY: test-acceptance-headless +# test-acceptance-headless: install-acceptance ## Run cypress tests in CI +test-acceptance-headless-multilingual: ## Run cypress tests in CI + (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.multilingual.config.js) diff --git a/acceptance/.env b/acceptance/.env index ea21419b..ef6ba0cf 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -1,4 +1,5 @@ export INDEX_SERVER=opensearch:9200 +export INDEX_NAME=plone export INDEX_OPENSEARCH=1 export INDEX_USE_SSL=1 export INDEX_LOGIN=admin @@ -14,4 +15,4 @@ export PLONE_PASSWORD=secret export MAPPINGS_FILE=/configuration/mappings.json export ANALYSIS_FILE=/configuration/analysis.json -export PREPROCESSINGS_FILE=/configuration/preprocessings.json \ No newline at end of file +export PREPROCESSINGS_FILE=/configuration/preprocessings.json diff --git a/acceptance/ci.yml b/acceptance/ci.yml index f14cd072..3078c35b 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -19,6 +19,25 @@ services: profiles: - prod + addon-acceptance-multilingual: + build: + context: ../ + dockerfile: ./dockerfiles/frontend/Dockerfile.ci.multilingual + 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: + - multilingual + + backend-acceptance: build: context: ../ @@ -35,6 +54,9 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} + CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex + INSTALL_PRODUCTS: "collective.elastic.plone" ports: - 55001:55001 depends_on: @@ -44,6 +66,35 @@ services: profiles: - dev - prod + + + backend-acceptance-multilingual: + build: + context: ../ + dockerfile: ./dockerfiles/backend/Dockerfile.acceptance.multilingual + args: + PLONE_VERSION: ${PLONE_VERSION:-6.0} + environment: + ZSERVER_HOST: 0.0.0.0 + ZSERVER_PORT: 55001 + # plone.elastic 2.x + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} + CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex + INSTALL_PRODUCTS: "collective.elastic.plone" + ports: + - 55001:55001 + depends_on: + - ingest + - redis + - opensearch + profiles: + - multilingual ingest: build: @@ -96,3 +147,4 @@ services: profiles: - dev - prod + - multilingual diff --git a/acceptance/cypress.config.js b/acceptance/cypress.multilingual.config.js similarity index 71% rename from acceptance/cypress.config.js rename to acceptance/cypress.multilingual.config.js index d841d7ab..81fcd793 100644 --- a/acceptance/cypress.config.js +++ b/acceptance/cypress.multilingual.config.js @@ -4,6 +4,6 @@ module.exports = defineConfig({ viewportWidth: 1280, e2e: { baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.cy.{js,jsx}', + specPattern: 'cypress/tests/*.mulltilingual.cy.{js,jsx}', }, }); diff --git a/acceptance/cypress.singlelingual.config.js b/acceptance/cypress.singlelingual.config.js new file mode 100644 index 00000000..f74ee689 --- /dev/null +++ b/acceptance/cypress.singlelingual.config.js @@ -0,0 +1,9 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + viewportWidth: 1280, + e2e: { + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', + }, +}); diff --git a/acceptance/cypress/tests/basic.cy.js b/acceptance/cypress/tests/basic.singlelingual.cy.js similarity index 100% rename from acceptance/cypress/tests/basic.cy.js rename to acceptance/cypress/tests/basic.singlelingual.cy.js diff --git a/acceptance/cypress/tests/create_search.cy.js b/acceptance/cypress/tests/create_search.singlelingual.cy.js similarity index 100% rename from acceptance/cypress/tests/create_search.cy.js rename to acceptance/cypress/tests/create_search.singlelingual.cy.js diff --git a/acceptance/cypress/tests/search.cy.js b/acceptance/cypress/tests/search.singlelingual.cy.js similarity index 100% rename from acceptance/cypress/tests/search.cy.js rename to acceptance/cypress/tests/search.singlelingual.cy.js diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 0fc9578c..44128443 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -1,5 +1,6 @@ # syntax=docker/dockerfile:1 # Dockerfile from cookiecutter-plone-starter + ARG PLONE_VERSION=6.0 FROM plone/server-builder:${PLONE_VERSION} as builder @@ -8,7 +9,6 @@ WORKDIR /app # # Add local code # COPY . . - # Install local requirements RUN <=2.0.0 + bin/pip install collective.elastic.plone[redis,opensearch]>=2.0.1 +EOT + + +FROM plone/server-acceptance:${PLONE_VERSION} +ENV APPLY_PROFILES="collective.elastic.plone:default,plone.restapi:default,plone.volto:multilingual" + +# Copy /app from builder +COPY --from=builder /app /app + +RUN < Date: Wed, 10 Apr 2024 12:57:44 +0200 Subject: [PATCH 030/226] cypress configuration --- Makefile | 4 ++-- .../{cypress.singlelingual.config.js => cypress.config.js} | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) rename acceptance/{cypress.singlelingual.config.js => cypress.config.js} (71%) diff --git a/Makefile b/Makefile index 45c1602d..c73777e6 100644 --- a/Makefile +++ b/Makefile @@ -113,12 +113,12 @@ start-acceptance: ## Start acceptance server-containers .PHONY: test-acceptance test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.singlelingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.config.js) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.singlelingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.config.js) .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server diff --git a/acceptance/cypress.singlelingual.config.js b/acceptance/cypress.config.js similarity index 71% rename from acceptance/cypress.singlelingual.config.js rename to acceptance/cypress.config.js index f74ee689..4ba661c3 100644 --- a/acceptance/cypress.singlelingual.config.js +++ b/acceptance/cypress.config.js @@ -3,6 +3,9 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ viewportWidth: 1280, e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, baseUrl: 'http://localhost:3000', specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', }, From 924875dc84cfdc3db63ebe6422185608351e52cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 17:58:14 +0200 Subject: [PATCH 031/226] Update ci.yml: depends_on --- acceptance/ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 3078c35b..42625e27 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -33,7 +33,7 @@ services: ports: - 3000:3000 depends_on: - - backend-acceptance + - backend-acceptance-multilingual profiles: - multilingual @@ -144,7 +144,3 @@ services: ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer - profiles: - - dev - - prod - - multilingual From 16418f956073fd150ef50c16c589edac8c306a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 19:40:36 +0200 Subject: [PATCH 032/226] Create search.multilingual.cy.js --- .../cypress/tests/search.multilingual.cy.js | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 acceptance/cypress/tests/search.multilingual.cy.js diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js new file mode 100644 index 00000000..44da1c49 --- /dev/null +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -0,0 +1,69 @@ +describe('Searchkit block tests – search - multilingual', () => { + before(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching and Finding', + path: '/en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: '/en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'der-garten-im-februar', + contentTitle: 'Der Garten im Februar', + path: '/de', + }); + + + // Add search block to /suche + cy.visit('/en/searching'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('.blocks-chooser .common .button.searchkitblock').click({ + force: true, + }); + + cy.get('#toolbar-save').click(); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/en/searching'); + cy.wait(1000); + }); + + after(() => { + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'en/searching' }); + cy.removeContent({ path: 'de/der-garten-im-februar' }); + }); + + + it('I can search within language', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.block.searchkitsearch').should('not.contain', 'Der Garten im Februar'); + }); + +}); From 40bfd01d21edae530255748723ef3f82d2b52945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 19:40:46 +0200 Subject: [PATCH 033/226] Fix typo --- acceptance/cypress.multilingual.config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acceptance/cypress.multilingual.config.js b/acceptance/cypress.multilingual.config.js index 81fcd793..e92aa9ac 100644 --- a/acceptance/cypress.multilingual.config.js +++ b/acceptance/cypress.multilingual.config.js @@ -3,7 +3,10 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ viewportWidth: 1280, e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.mulltilingual.cy.{js,jsx}', + specPattern: 'cypress/tests/*.multilingual.cy.{js,jsx}', }, }); From d544a52d1ca6c3cd1583cf6d345022f36015b6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 19:41:02 +0200 Subject: [PATCH 034/226] Add multilingual container --- acceptance/docker-compose.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 3f2409fe..334075ac 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -25,6 +25,29 @@ services: profiles: - dev + addon-acceptance-multilingual: + build: + context: ../ + dockerfile: ./dockerfiles/frontend/Dockerfile.acceptance.multilingual + 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-multilingual + profiles: + - multilingual + backend-acceptance: build: context: ../ @@ -36,16 +59,44 @@ services: ZSERVER_PORT: "55001" CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_NAME: ${INDEX_NAME?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex + INSTALL_PRODUCTS: "collective.elastic.plone" ports: - 55001:55001 profiles: - dev - prod + backend-acceptance-multilingual: + build: + context: ../ + dockerfile: ./dockerfiles/backend/Dockerfile.acceptance.multilingual + args: + PLONE_VERSION: ${PLONE_VERSION:-6.0} + environment: + ZSERVER_HOST: "0.0.0.0" + ZSERVER_PORT: "55001" + CELERY_BROKER: ${CELERY_BROKER?unset} + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_NAME: ${INDEX_NAME?unset} + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex + INSTALL_PRODUCTS: "collective.elastic.plone" + ports: + - 55001:55001 + profiles: + - multilingual + ingest: image: ghcr.io/collective/collective.elastic.ingest:latest environment: @@ -53,6 +104,7 @@ services: ANALYSIS_FILE: ${ANALYSIS_FILE} PREPROCESSINGS_FILE: ${PREPROCESSINGS_FILE} INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_NAME: ${INDEX_NAME?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} @@ -102,6 +154,7 @@ services: tty: true profiles: - dev + - multilingual - prod @@ -115,6 +168,7 @@ services: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: - dev + - multilingual # dejavu: # image: appbaseio/dejavu:latest From 7181ccae3464c75ab5a31137e85654bfb4fbfa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 19:41:07 +0200 Subject: [PATCH 035/226] Update Makefile --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c73777e6..0dfc8e4a 100644 --- a/Makefile +++ b/Makefile @@ -113,12 +113,12 @@ start-acceptance: ## Start acceptance server-containers .PHONY: test-acceptance test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.config.js) + (cd acceptance && ./node_modules/.bin/cypress open) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.config.js) + (cd acceptance && ./node_modules/.bin/cypress run) .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server @@ -143,9 +143,9 @@ start-acceptance-multilingual: ## multilingual – Start acceptance server-conta .PHONY: test-acceptance test-acceptance-multilingual: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file tests/cypress.multilingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.multilingual.config.js) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless-multilingual: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file tests/cypress.multilingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.multilingual.config.js) From a60c62cc1c82cc4f402cec976697fe51f01a864e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 10 Apr 2024 19:41:45 +0200 Subject: [PATCH 036/226] React components: First working step with language --- src/components/Searchkit/CustomESRequestSerializer.jsx | 6 ++++++ src/components/Searchkit/ESSearchApi.jsx | 1 + src/components/Views/FacetedSearch.jsx | 6 ++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 7de0ebcb..6b8d11d4 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -9,6 +9,7 @@ export class CustomESRequestSerializer { this.allowed_content_types = config.allowed_content_types; this.allowed_review_states = config.allowed_review_states; this.search_sections = config.search_sections; + this.language = config.language; } /** * Convert Array of filters to Object of filters @@ -258,6 +259,11 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; + terms.push({ + terms: { + language: [this.language], // TODO language + }, + }); this.allowed_content_types?.length > 0 && terms.push({ terms: { diff --git a/src/components/Searchkit/ESSearchApi.jsx b/src/components/Searchkit/ESSearchApi.jsx index 51685f19..a0a37bf3 100644 --- a/src/components/Searchkit/ESSearchApi.jsx +++ b/src/components/Searchkit/ESSearchApi.jsx @@ -37,6 +37,7 @@ export class PloneSearchApi { allowed_content_types: config.allowed_content_types, allowed_review_states: config.allowed_review_states, search_sections: config.search_sections, + language: config.language, }); this.responseSerializer = new responseSerializerCls({ backend_url: config.backend_url, diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 76930907..9aa4806f 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -57,7 +57,7 @@ import './less/springisnow-volto-searchkit-block.less'; import config from '@plone/volto/registry'; // TODO Make reviewstatemapping configurable -export const ploneSearchApi = (data) => { +export const ploneSearchApi = (data, language='en') => { const cookies = new Cookies(); const authToken = cookies.get('auth_token'); return new PloneSearchApi({ @@ -78,6 +78,7 @@ export const ploneSearchApi = (data) => { allowed_content_types: data.allowed_content_types, allowed_review_states: data.allowed_review_states, search_sections: data.search_sections, + language: language, backend_url: data.backend_url, frontend_url: data.frontend_url, // elastic_search_api_url: data.elastic_search_api_url, @@ -708,6 +709,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { }; // TODO Check if check on client could be made simpler + const language = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); @@ -716,7 +718,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { {isClient && ( Date: Wed, 10 Apr 2024 20:22:03 +0200 Subject: [PATCH 037/226] Include language in request only if site isMultilingual --- src/components/Searchkit/CustomESRequestSerializer.jsx | 4 ++-- src/components/Views/FacetedSearch.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 6b8d11d4..7db37eaf 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -259,9 +259,9 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; - terms.push({ + this.language && terms.push({ terms: { - language: [this.language], // TODO language + language: [this.language], }, }); this.allowed_content_types?.length > 0 && diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 9aa4806f..909dcbde 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -57,7 +57,7 @@ import './less/springisnow-volto-searchkit-block.less'; import config from '@plone/volto/registry'; // TODO Make reviewstatemapping configurable -export const ploneSearchApi = (data, language='en') => { +export const ploneSearchApi = (data, language) => { const cookies = new Cookies(); const authToken = cookies.get('auth_token'); return new PloneSearchApi({ @@ -709,7 +709,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { }; // TODO Check if check on client could be made simpler - const language = useSelector((state) => state.intl.locale); + const language = useSelector((state) => config.settings.isMultilingual ? state.intl.locale : null); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); From eccaed21bd05f7d0ee6650693cabbe3a6a105eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 08:45:44 +0200 Subject: [PATCH 038/226] Reduce default cypress configuration --- acceptance/cypress.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/acceptance/cypress.config.js b/acceptance/cypress.config.js index 4ba661c3..f74ee689 100644 --- a/acceptance/cypress.config.js +++ b/acceptance/cypress.config.js @@ -3,9 +3,6 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ viewportWidth: 1280, e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, baseUrl: 'http://localhost:3000', specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', }, From b65b2c09a5348cf4e4e35a83f9f0877ee87d0c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 08:46:08 +0200 Subject: [PATCH 039/226] Uncomment single language acceptance tests --- .github/workflows/acceptance.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 9e193496..ad178d87 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -43,22 +43,22 @@ jobs: 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/*.singlelingual.cy.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 http://localhost:9200' + # - name: "Cypress: Acceptance tests - single language" + # uses: cypress-io/github-action@v6 + # env: + # BABEL_ENV: production + # CYPRESS_RETRIES: 2 + # with: + # parallel: false + # browser: chrome + # working-directory: acceptance + # spec: cypress/tests/*.singlelingual.cy.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 http://localhost:9200' - - name: "Cypress: Acceptance tests multilingual" + - name: "Cypress: Acceptance tests - multilingual" uses: cypress-io/github-action@v6 env: BABEL_ENV: production From 61fd962a6bcd2903d6e31b0fd77020039eaf24de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 09:05:48 +0200 Subject: [PATCH 040/226] Loosen cypress default specPattern --- acceptance/cypress.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/cypress.config.js b/acceptance/cypress.config.js index f74ee689..d841d7ab 100644 --- a/acceptance/cypress.config.js +++ b/acceptance/cypress.config.js @@ -4,6 +4,6 @@ module.exports = defineConfig({ viewportWidth: 1280, e2e: { baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', + specPattern: 'cypress/tests/*.cy.{js,jsx}', }, }); From 4aa8772ecd8417029e7d4144d8ec087991a49050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 09:06:47 +0200 Subject: [PATCH 041/226] Explicit singlelingual cypress configuration --- Makefile | 4 ++-- acceptance/cypress.singlelingual.config.js | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 acceptance/cypress.singlelingual.config.js diff --git a/Makefile b/Makefile index 0dfc8e4a..639bed8c 100644 --- a/Makefile +++ b/Makefile @@ -113,12 +113,12 @@ start-acceptance: ## Start acceptance server-containers .PHONY: test-acceptance test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open) + (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.singlelingual.config.js) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run) + (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.singlelingual.config.js) .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server diff --git a/acceptance/cypress.singlelingual.config.js b/acceptance/cypress.singlelingual.config.js new file mode 100644 index 00000000..4ba661c3 --- /dev/null +++ b/acceptance/cypress.singlelingual.config.js @@ -0,0 +1,12 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + viewportWidth: 1280, + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', + }, +}); From 5d20deb55123cd854b84fd03d93f35af51e3c1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 13:22:39 +0200 Subject: [PATCH 042/226] Temporary change from 'de' to 'it' until plone.volto:multilingual is merged --- acceptance/cypress/tests/search.multilingual.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index 44da1c49..b707e7e8 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -23,7 +23,7 @@ describe('Searchkit block tests – search - multilingual', () => { contentType: 'Document', contentId: 'der-garten-im-februar', contentTitle: 'Der Garten im Februar', - path: '/de', + path: '/it', }); @@ -56,7 +56,7 @@ describe('Searchkit block tests – search - multilingual', () => { after(() => { cy.removeContent({ path: 'en/garden-in-february' }); cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'de/der-garten-im-februar' }); + cy.removeContent({ path: 'it/der-garten-im-februar' }); }); From 6aa7fc58bcd63fbd06c7c9c66e615ba21cf4e4fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 13:22:53 +0200 Subject: [PATCH 043/226] Update Dockerfile.acceptance.multilingual --- dockerfiles/backend/Dockerfile.acceptance.multilingual | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index 9c82e484..88f58f9c 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -1,9 +1,10 @@ # syntax=docker/dockerfile:1 # Dockerfile from cookiecutter-plone-starter # APPLY_PROFILES … plone.volto:multilingual +# Run robotserver verbose ARG PLONE_VERSION=6.0 -ARG SEED=1 +ARG SEED=4 FROM plone/server-builder:${PLONE_VERSION} as builder WORKDIR /app @@ -29,3 +30,9 @@ RUN < Date: Thu, 11 Apr 2024 13:23:08 +0200 Subject: [PATCH 044/226] Fix CI multilingual --- acceptance/ci.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 42625e27..57357093 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -28,7 +28,7 @@ services: ADDON_PATH: "${ADDON_PATH}" VOLTO_VERSION: ${VOLTO_VERSION:-17} environment: - RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone + RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-multilingual:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone ports: - 3000:3000 @@ -90,7 +90,7 @@ services: ports: - 55001:55001 depends_on: - - ingest + - ingest-multilingual - redis - opensearch profiles: @@ -117,6 +117,29 @@ services: PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + ingest-multilingual: + build: + context: ../ + dockerfile: ./docker-opensearch/Dockerfile.ingest + environment: + # Different INDEX_NAME + MAPPINGS_FILE: "/configuration/mappings.json" + ANALYSIS_FILE: "/configuration/analysis.json" + PREPROCESSINGS_FILE: "/configuration/preprocessings.json" + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_NAME: multilingual + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} + CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} + CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + PLONE_SERVICE: http://backend-acceptance-multilingual:55001 + PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} + PLONE_USER: ${PLONE_USER?unset} + PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + redis: image: 'redis:latest' ports: From 38a522497225715142f599a94c808869a122892d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 13:24:15 +0200 Subject: [PATCH 045/226] Fix dev acceptance multilingual --- acceptance/docker-compose.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 334075ac..4f589751 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -36,7 +36,7 @@ services: volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: - RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone + RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-multilingual:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone HOST: 0.0.0.0 ports: @@ -84,7 +84,7 @@ services: ZSERVER_PORT: "55001" CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} - INDEX_NAME: ${INDEX_NAME?unset} + INDEX_NAME: multilingual INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} @@ -120,6 +120,30 @@ services: volumes: - ingest-configuration:/configuration + ingest-multilingual: + image: ghcr.io/collective/collective.elastic.ingest:latest + environment: + # Different INDEX_NAME + MAPPINGS_FILE: ${MAPPINGS_FILE} + ANALYSIS_FILE: ${ANALYSIS_FILE} + PREPROCESSINGS_FILE: ${PREPROCESSINGS_FILE} + INDEX_SERVER: ${INDEX_SERVER?unset} + INDEX_NAME: multilingual + INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} + INDEX_USE_SSL: ${INDEX_USE_SSL?unset} + INDEX_LOGIN: ${INDEX_LOGIN?unset} + INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} + CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} + CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + PLONE_SERVICE: http://backend-acceptance-multilingualela:55001 + PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} + PLONE_USER: ${PLONE_USER?unset} + PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + SENTRY_DSN: ${SENTRY_DSN} + volumes: + - ingest-configuration:/configuration + redis: image: 'redis:latest' ports: From 16d2b4d2bddf398154b725977b928b36898995b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 13:25:20 +0200 Subject: [PATCH 046/226] Uncomment CI single language tests --- .github/workflows/acceptance.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index ad178d87..7d9793a7 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -43,20 +43,20 @@ jobs: cd acceptance yarn - # - name: "Cypress: Acceptance tests - single language" - # uses: cypress-io/github-action@v6 - # env: - # BABEL_ENV: production - # CYPRESS_RETRIES: 2 - # with: - # parallel: false - # browser: chrome - # working-directory: acceptance - # spec: cypress/tests/*.singlelingual.cy.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 http://localhost:9200' + - name: "Cypress: Acceptance tests - single language" + uses: cypress-io/github-action@v6 + env: + BABEL_ENV: production + CYPRESS_RETRIES: 2 + with: + parallel: false + browser: chrome + working-directory: acceptance + spec: cypress/tests/*.singlelingual.cy.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 http://localhost:9200' - name: "Cypress: Acceptance tests - multilingual" uses: cypress-io/github-action@v6 From 165c5b6647e23bad675f6ed40f664a52ed3377bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 17:01:23 +0200 Subject: [PATCH 047/226] Extend multilingual tests --- .../cypress/tests/language.multilingual.cy.js | 70 +++++++++ .../cypress/tests/search.multilingual.cy.js | 139 +++++++++++++++--- 2 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 acceptance/cypress/tests/language.multilingual.cy.js diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js new file mode 100644 index 00000000..59b774fe --- /dev/null +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -0,0 +1,70 @@ +describe('Searchkit block tests – search - multilingual', () => { + before(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching and Finding', + path: '/en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: '/en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'der-garten-im-februar', + contentTitle: 'Der Garten im Februar', + path: '/it', + }); + + + // Add search block to /suche + cy.visit('/en/searching'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('.blocks-chooser .common .button.searchkitblock').click({ + force: true, + }); + + cy.get('#toolbar-save').click(); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/en/searching'); + cy.wait(1000); + }); + + after(() => { + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'en/searching' }); + cy.removeContent({ path: 'it/der-garten-im-februar' }); + }); + + + it('I can search within language', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.block.searchkitsearch').should('not.contain', 'Der Garten im Februar'); + }); + + }); + \ No newline at end of file diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index b707e7e8..87020a76 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit block tests – search - multilingual', () => { +describe('Searchkit block tests – search', () => { before(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); @@ -7,33 +7,61 @@ describe('Searchkit block tests – search - multilingual', () => { cy.createContent({ contentType: 'Document', - contentId: 'searching', - contentTitle: 'Searching and Finding', - path: '/en', + contentId: 'suche', + contentTitle: 'Suche', + path: '/de', }); - cy.createContent({ contentType: 'Document', - contentId: 'garden-in-february', - contentTitle: 'The garden in february', - path: '/en', + contentId: 'garten-blog', + contentTitle: 'Garten-Blog', + path: '/de', }); - cy.createContent({ contentType: 'Document', - contentId: 'der-garten-im-februar', + contentId: 'februar', contentTitle: 'Der Garten im Februar', - path: '/it', + path: '/de/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'marz', + contentTitle: 'Der Garten im März', + path: '/de/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-mann', + contentTitle: 'Testseite Mann', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-manner', + contentTitle: 'Testseite Männer', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-lsb', + contentTitle: 'Testseite Lehrstellenbörsen', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-s', + contentTitle: 'Testseite Stelle', + path: '/de', }); // Add search block to /suche - cy.visit('/en/searching'); + cy.visit('/de/suche'); cy.get('a.edit').click(); cy.getSlate().click(); cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('div[aria-label="Ausklappen Common blocks"]').click(); cy.get('.blocks-chooser .common .button.searchkitblock').click({ force: true, }); @@ -49,21 +77,90 @@ describe('Searchkit block tests – search - multilingual', () => { cy.autologin(); - cy.visit('/en/searching'); - cy.wait(1000); + cy.visit('/de/suche'); + cy.wait(3000); + // cy.wait('@kitsearch'); }); after(() => { - cy.removeContent({ path: 'en/garden-in-february' }); - cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'it/der-garten-im-februar' }); + // cy.removeContent({ path: 'de/garten-blog/februar' }); + // cy.removeContent({ path: 'de/garten-blog/marz' }); + cy.removeContent({ path: 'de/garten-blog' }); + cy.removeContent({ path: 'de/suche' }); + cy.removeContent({ path: 'de/testseite-mann' }); + cy.removeContent({ path: 'de/testseite-manner' }); + cy.removeContent({ path: 'de/testseite-lsb' }); + cy.removeContent({ path: 'de/testseite-s' }); }); + it('I see all if no filter selected', function () { + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); - it('I can search within language', function () { + it('I can search fuzzy', function () { cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - cy.get('.block.searchkitsearch').should('not.contain', 'Der Garten im Februar'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').should('not.contain', 'März'); + }); + + it('I can search with inflection', function () { + cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Männer'); + }); + + it('I can search with decompounding', function () { + cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.get('.block.searchkitsearch').contains('Garten-Blog'); + + cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.get('.block.searchkitsearch').contains('Februar'); + }); + + it('I can search with wildcard', function () { + cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search for an exact match', function () { + cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); + }); + + it('I can search for a compounded word', function () { + cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + }); + + // Blocks text + it('I can search in blocks', function () { + cy.visit('/de/garten-blog/februar'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@content'); + + cy.log('I added a text block'); + + // Searching + cy.visit('de//suche'); + cy.wait(3000); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); }); From 4b821003ac6492237bc9d00fe6ba6155d8adb917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 19:36:44 +0200 Subject: [PATCH 048/226] Clean up docker compose --- acceptance/docker-compose.yml | 12 +----------- dockerfiles/docker-compose.yml | 8 -------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 4f589751..6c0cd63c 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -170,8 +170,6 @@ services: hard: 65536 volumes: - opensearch-data:/usr/share/opensearch/data - - "../docker-opensearch/opensearch-configuration/keywords.txt:/usr/share/opensearch/config/keywords.txt" - - "../docker-opensearch/opensearch-configuration/lexicon.txt:/usr/share/opensearch/config/lexicon.txt" ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer @@ -211,12 +209,4 @@ volumes: driver_opts: type: none device: ../docker-opensearch/ingest-configuration - o: bind - # opensearch-configuration: - # driver: local - # driver_opts: - # type: none - # device: ./opensearch-configuration - # o: bind - - + o: bind \ No newline at end of file diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 2c87db07..78b66ab5 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -138,8 +138,6 @@ services: hard: 65536 volumes: - opensearch-data:/usr/share/opensearch/data - - "../docker-opensearch/opensearch-configuration/keywords.txt:/usr/share/opensearch/config/keywords.txt" - - "../docker-opensearch/opensearch-configuration/lexicon.txt:/usr/share/opensearch/config/lexicon.txt" networks: - opensearch-net ports: @@ -182,12 +180,6 @@ volumes: type: none device: ../docker-opensearch/ingest-configuration o: bind - # opensearch-configuration: - # driver: local - # driver_opts: - # type: none - # device: ./opensearch-configuration - # o: bind networks: opensearch-net: From 516353c0d3980b5ed4edcd87008b1f83704882bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 19:37:09 +0200 Subject: [PATCH 049/226] Clean up --- Makefile | 1 - .../mappings_example.json | 455 ------------------ .../Blocks/SearchSectionsWidget.jsx | 2 - 3 files changed, 458 deletions(-) delete mode 100644 docker-opensearch/ingest-configuration/mappings_example.json diff --git a/Makefile b/Makefile index 639bed8c..93ba314a 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,6 @@ all: 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 :)" -# TODO Add backend-addons .PHONY: build-backend build-backend: ## Build @echo "$(GREEN)==> Build Backend Container $(RESET)" diff --git a/docker-opensearch/ingest-configuration/mappings_example.json b/docker-opensearch/ingest-configuration/mappings_example.json deleted file mode 100644 index 1ed30087..00000000 --- a/docker-opensearch/ingest-configuration/mappings_example.json +++ /dev/null @@ -1,455 +0,0 @@ -{ - "behaviors/plone.basic/title": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - }, - "behaviors/plone.basic/description": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - - } - }, - "behaviors/plone.categorization/subjects": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - - } - }, - "behaviors/plone.allowdiscussion/allow_discussion": { - "type": "boolean" - }, - - "plone.app.textfield.RichText": { - "pipeline": { - "source": "{name}__data", - "target": "{name}__extracted", - "processors": [ - { - "attachment": { - "field": "{source}", - "target_field": "{target}", - "ignore_missing": true - } - }, - { - "remove": { - "field": "{source}", - "ignore_missing": true - } - } - ], - "type": { - "type": "nested", - "dynamic": false, - "properties": { - "author": { - "type": "text" - }, - "content": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - - } - }, - "content_length": { - "type": "long" - }, - "content_type": { - "type": "keyword" - }, - "date": { - "type": "date" - }, - "keywords": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "title": { - "type": "text" - } - } - }, - "expansion": { - "method": "field", - "field": "data" - } - }, - "definition": { - "type": "nested", - "dynamic": false, - "properties": { - "data": { - "type": "text" - }, - "content-type": { - "type": "keyword" - }, - "encoding": { - "type": "keyword" - } - } - } - }, - "plone.namedfile.field.NamedBlobFile": { - "pipeline": { - "source": "{name}__data", - "target": "{name}__extracted", - "processors": [ - { - "attachment": { - "field": "{source}", - "target_field": "{target}", - "ignore_missing": true - } - }, - { - "remove": { - "field": "{source}", - "ignore_missing": true - } - } - ], - "type": { - "type": "nested", - "dynamic": false, - "properties": { - "author": { - "type": "text" - }, - "content": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - - } - }, - "content_length": { - "type": "long" - }, - "content_type": { - "type": "keyword" - }, - "date": { - "type": "date" - }, - "keywords": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "title": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - } - } - }, - "expansion": { - "method": "fetch", - "field": "download" - } - }, - "definition": { - "type": "nested", - "dynamic": false, - "properties": { - "content-type": { - "type": "keyword" - }, - "download": { - "type": "text" - }, - "filename": { - "type": "text" - }, - "size": { - "type": "long" - } - } - } - }, - "plone.namedfile.field.NamedBlobImage": { - "pipeline": { - "source": "{name}__data", - "target": "{name}__extracted", - "processors": [ - { - "attachment": { - "field": "{source}", - "target_field": "{target}", - "ignore_missing": true - } - }, - { - "remove": { - "field": "{source}", - "ignore_missing": true - } - } - ], - "type": { - "type": "nested", - "dynamic": false, - "properties": { - "author": { - "type": "text" - }, - "content": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - }, - "content_length": { - "type": "long" - }, - "content_type": { - "type": "keyword" - }, - "date": { - "type": "date" - }, - "keywords": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "title": { - "type": "text" - } - } - }, - "expansion": { - "method": "fetch", - "field": "download" - } - }, - "definition": { - "type": "nested", - "dynamic": false, - "properties": { - "content-type": { - "type": "keyword" - }, - "download": { - "type": "text" - }, - "filename": { - "type": "text" - }, - "size": { - "type": "long" - }, - "height": { - "type": "long" - }, - "width": { - "type": "long" - }, - "scales": { - "type": "nested", - "dynamic": false, - "properties": { - "download": { - "type": "text" - }, - "height": { - "type": "long" - }, - "width": { - "type": "long" - } - } - } - } - } - }, - "plone.schema.jsonfield.JSONField": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - }, - "z3c.relationfield.schema.RelationList": { - "type": "nested", - "dynamic": false, - "properties": { - "@id": { - "type": "keyword" - }, - "@type": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "review_state": { - "type": "keyword" - }, - "title": { - "type": "text" - } - } - }, - "z3c.relationfield.schema.RelationChoice": { - "type": "nested", - "dynamic": false, - "properties": { - "@id": { - "type": "keyword" - }, - "@type": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "review_state": { - "type": "keyword" - }, - "title": { - "type": "text" - } - } - }, - "zope.schema._bootstrapfields.Bool": { - "type": "boolean" - }, - "zope.schema._bootstrapfields.Int": { - "type": "long" - }, - "zope.schema._bootstrapfields.Text": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - }, - "zope.schema._bootstrapfields.TextLine": { - "type": "text", - "fields": { - "de": { - "type": "text", - "analyzer": "german" - }, - "en": { - "type": "text", - "analyzer": "english" - } - } - }, - "zope.schema._field.ASCIILine": { - "type": "keyword" - }, - "zope.schema._field.Choice": { - "type": "nested", - "dynamic": false, - "properties": { - "token": { - "type": "keyword" - }, - "title": { - "type": "text" - } - } - }, - "zope.schema._field.Datetime": { - "type": "date" - }, - "zope.schema._field.Dict": { - "type": "object" - }, - "zope.schema._field.Float": { - "type": "double" - }, - "zope.schema._field.List": { - "type": "keyword" - }, - "zope.schema._field.Tuple": { - "type": "keyword" - }, - "zope.schema._field.URI": { - "type": "text" - } - } diff --git a/src/components/Blocks/SearchSectionsWidget.jsx b/src/components/Blocks/SearchSectionsWidget.jsx index 81372829..53b99ae7 100644 --- a/src/components/Blocks/SearchSectionsWidget.jsx +++ b/src/components/Blocks/SearchSectionsWidget.jsx @@ -50,9 +50,7 @@ const ItemSchema = ({ intl }) => { }; }; -// TODO defaultData const SearchSectionsWidget = (props) => { - // TODO intl return ( Date: Thu, 11 Apr 2024 19:39:38 +0200 Subject: [PATCH 050/226] Multilingual: Add analyzer. Mappings with fields. Add lexicon, keywords --- docker-opensearch/Dockerfile.opensearch | 2 + .../ingest-configuration/analysis.json | 37 ++++++++++-- .../ingest-configuration/mappings.json | 57 +++++++++++++++++-- .../keywords_english.txt | 0 .../lexicon_english.txt | 0 5 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 docker-opensearch/opensearch-configuration/keywords_english.txt create mode 100644 docker-opensearch/opensearch-configuration/lexicon_english.txt diff --git a/docker-opensearch/Dockerfile.opensearch b/docker-opensearch/Dockerfile.opensearch index 8d685ecf..97d04fcf 100644 --- a/docker-opensearch/Dockerfile.opensearch +++ b/docker-opensearch/Dockerfile.opensearch @@ -2,5 +2,7 @@ FROM opensearchproject/opensearch:latest COPY docker-opensearch/opensearch-configuration/keywords.txt /usr/share/opensearch/config/keywords.txt COPY docker-opensearch/opensearch-configuration/lexicon.txt /usr/share/opensearch/config/lexicon.txt +COPY docker-opensearch/opensearch-configuration/keywords_english.txt /usr/share/opensearch/config/keywords_english.txt +COPY docker-opensearch/opensearch-configuration/lexicon_english.txt /usr/share/opensearch/config/lexicon_english.txt RUN /usr/share/opensearch/bin/opensearch-plugin install --batch ingest-attachment diff --git a/docker-opensearch/ingest-configuration/analysis.json b/docker-opensearch/ingest-configuration/analysis.json index 194b395d..2fdf91f3 100644 --- a/docker-opensearch/ingest-configuration/analysis.json +++ b/docker-opensearch/ingest-configuration/analysis.json @@ -6,8 +6,8 @@ "tokenizer": "standard", "filter": [ "lowercase", - "custom_dictionary_decompounder", - "no_stem", + "custom_dictionary_decompounder_german", + "no_stem_german", "light_german_stemmer" ] }, @@ -16,20 +16,49 @@ "filter": [ "lowercase" ] + }, + + "english_analyzer": { + "tokenizer": "standard", + "filter": [ + "lowercase", + "custom_dictionary_decompounder_english", + "no_stem_english", + "light_english_stemmer" + ] + }, + "english_exact_analyzer": { + "tokenizer": "standard", + "filter": [ + "lowercase" + ] } }, "filter": { - "custom_dictionary_decompounder": { + "custom_dictionary_decompounder_german": { "type": "dictionary_decompounder", "word_list_path": "lexicon.txt" }, - "no_stem": { + "no_stem_german": { "type": "keyword_marker", "keywords_path": "keywords.txt" }, "light_german_stemmer": { "type": "stemmer", "language": "light_german" + }, + + "custom_dictionary_decompounder_english": { + "type": "dictionary_decompounder", + "word_list_path": "lexicon_english.txt" + }, + "no_stem_english": { + "type": "keyword_marker", + "keywords_path": "keywords_english.txt" + }, + "light_english_stemmer": { + "type": "stemmer", + "language": "light_english" } } } diff --git a/docker-opensearch/ingest-configuration/mappings.json b/docker-opensearch/ingest-configuration/mappings.json index 7c33f0f7..47fa4f26 100644 --- a/docker-opensearch/ingest-configuration/mappings.json +++ b/docker-opensearch/ingest-configuration/mappings.json @@ -1,38 +1,83 @@ { "zope.schema._bootstrapfields.Text": { "type": "text", - "analyzer": "german_analyzer", + "analyzer": "english_analyzer", "term_vector": "with_positions_offsets", "fields": { - "exact": { + "de": { + "type": "text", + "analyzer": "german_analyzer", + "term_vector": "with_positions_offsets" + }, + "de_exact": { "type": "text", "analyzer": "german_exact_analyzer", "term_vector": "with_positions_offsets" + }, + "en": { + "type": "text", + "analyzer": "english_analyzer", + "term_vector": "with_positions_offsets" + }, + "en_exact": { + "type": "text", + "analyzer": "english_exact_analyzer", + "term_vector": "with_positions_offsets" } } }, "zope.schema._bootstrapfields.TextLine": { "type": "text", - "analyzer": "german_analyzer", + "analyzer": "english_analyzer", "term_vector": "with_positions_offsets", "fields": { - "exact": { + "de": { + "type": "text", + "analyzer": "german_analyzer", + "term_vector": "with_positions_offsets" + }, + "de_exact": { "type": "text", "analyzer": "german_exact_analyzer", "term_vector": "with_positions_offsets" + }, + "en": { + "type": "text", + "analyzer": "english_analyzer", + "term_vector": "with_positions_offsets" + }, + "en_exact": { + "type": "text", + "analyzer": "english_exact_analyzer", + "term_vector": "with_positions_offsets" } } }, "behaviors/plone.categorization/subjects": { "type": "text", - "analyzer": "german_analyzer", + "analyzer": "english_analyzer", "term_vector": "with_positions_offsets", "fields": { - "exact": { + "de": { + "type": "text", + "analyzer": "german_analyzer", + "term_vector": "with_positions_offsets" + }, + "de_exact": { "type": "text", "analyzer": "german_exact_analyzer", "term_vector": "with_positions_offsets" }, + "en": { + "type": "text", + "analyzer": "english_analyzer", + "term_vector": "with_positions_offsets" + }, + "en_exact": { + "type": "text", + "analyzer": "english_exact_analyzer", + "term_vector": "with_positions_offsets" + }, "keyword": { "type": "keyword" } diff --git a/docker-opensearch/opensearch-configuration/keywords_english.txt b/docker-opensearch/opensearch-configuration/keywords_english.txt new file mode 100644 index 00000000..e69de29b diff --git a/docker-opensearch/opensearch-configuration/lexicon_english.txt b/docker-opensearch/opensearch-configuration/lexicon_english.txt new file mode 100644 index 00000000..e69de29b From d6898462a4d57758c6222f806bc86640d5137328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 19:40:42 +0200 Subject: [PATCH 051/226] Request with fields according language (z.B. "title.de_exact^1.4") --- .../Searchkit/CustomESRequestSerializer.jsx | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 7db37eaf..a47d437a 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -149,11 +149,17 @@ export class CustomESRequestSerializer { _make_fuzzy_and_enrich_with_word_parts(word); }); + // fields with boosting let searchedFields = [...this.searchedFields]; - let searchedFields_exact = [...this.searchedFields]; - searchedFields_exact = searchedFields_exact.map((fld) => { + + let searchedFields_simple = searchedFields.map((fld) => { const fieldname = fld.split('^')[0]; - return fld.replace(fieldname, `${fieldname}.exact`); + return fld.replace(fieldname, `${fieldname}.${this.language}`); + }); + + let searchedFields_exact = searchedFields.map((fld) => { + const fieldname = fld.split('^')[0]; + return fld.replace(fieldname, `${fieldname}.${this.language}_exact`); }); // Construction of query @@ -165,7 +171,7 @@ export class CustomESRequestSerializer { shouldList.push({ query_string: { query: element, - fields: searchedFields, + fields: searchedFields_simple, }, }); }); @@ -182,7 +188,7 @@ export class CustomESRequestSerializer { mustList.push({ query_string: { query: el, - fields: searchedFields, + fields: searchedFields_simple, }, }); }); @@ -213,26 +219,14 @@ export class CustomESRequestSerializer { bodyParams['highlight'] = { number_of_fragments: 20, - fields: [ - { - title: { - matched_fields: ['title', 'title.exact'], - type: 'fvh', - }, - }, - { - description: { - matched_fields: ['description', 'description.exact'], + fields: ['title', 'description', 'blocks_plaintext'].map(fieldname => { + return { + [fieldname]: { + matched_fields: [`${fieldname}.${this.language}`, `${fieldname}.${this.language}_exact`], type: 'fvh', }, - }, - { - blocks_plaintext: { - matched_fields: ['blocks_plaintext', 'blocks_plaintext.exact'], - type: 'fvh', - }, - }, - ], + } + }) }; } @@ -259,6 +253,7 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; + // If isMultilingual, search only in language this.language && terms.push({ terms: { language: [this.language], From 9f5fa25708fe2a855a4819e13673459eafae08fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Thu, 11 Apr 2024 19:57:19 +0200 Subject: [PATCH 052/226] Test multilingual first --- .github/workflows/acceptance.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 7d9793a7..ebec40cf 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -43,7 +43,7 @@ jobs: cd acceptance yarn - - name: "Cypress: Acceptance tests - single language" + - name: "Cypress: Acceptance tests - multilingual" uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -52,13 +52,13 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.singlelingual.cy.js + spec: cypress/tests/*.multilingual.cy.js install: false start: | - docker compose -f ci.yml --profile prod up + docker compose -f ci.yml --profile multilingual up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' - - name: "Cypress: Acceptance tests - multilingual" + - name: "Cypress: Acceptance tests - single language" uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -67,10 +67,10 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.multilingual.cy.js + spec: cypress/tests/*.singlelingual.cy.js install: false start: | - docker compose -f ci.yml --profile multilingual up + docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' From 7f118d46539bc363c71d56e2e0b09893078025d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 12 Apr 2024 09:37:14 +0200 Subject: [PATCH 053/226] Fix docker compose --- acceptance/docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 6c0cd63c..0aa9aa06 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -119,6 +119,9 @@ services: SENTRY_DSN: ${SENTRY_DSN} volumes: - ingest-configuration:/configuration + profiles: + - dev + - prod ingest-multilingual: image: ghcr.io/collective/collective.elastic.ingest:latest @@ -143,6 +146,8 @@ services: SENTRY_DSN: ${SENTRY_DSN} volumes: - ingest-configuration:/configuration + profiles: + - multilingual redis: image: 'redis:latest' From 186d1af57761319344e4b089967a9899e817ba27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 12 Apr 2024 10:36:51 +0200 Subject: [PATCH 054/226] Search in language fields, but do filter on language only if isMultilingual --- src/components/Searchkit/CustomESRequestSerializer.jsx | 7 ++++++- src/components/Views/FacetedSearch.jsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index a47d437a..761245c8 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -1,6 +1,10 @@ import { extend, isEmpty, keyBy, trim } from 'lodash'; import { getObjectFromObjectList } from '../helpers.jsx'; +import config from '@plone/volto/registry'; + +const volto_config = config; + export class CustomESRequestSerializer { constructor(config) { this.reviewstatemapping = config.reviewstatemapping; @@ -254,7 +258,8 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; // If isMultilingual, search only in language - this.language && terms.push({ + + this.language && volto_config.settings.isMultilingual && terms.push({ terms: { language: [this.language], }, diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 909dcbde..c5fca971 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -709,7 +709,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { }; // TODO Check if check on client could be made simpler - const language = useSelector((state) => config.settings.isMultilingual ? state.intl.locale : null); + const language = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); From 979cd92a0287705c9f967a314ed2dff87cb92ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sat, 20 Apr 2024 17:58:00 +0200 Subject: [PATCH 055/226] Fixi --- .github/workflows/acceptance.yml | 2 +- Makefile | 6 +- ...onfig.js => cypress.monolingual.config.js} | 2 +- ...elingual.cy.js => basic.monolingual.cy.js} | 0 ....cy.js => create_search.monolingual.cy.js} | 2 +- .../cypress/tests/language.multilingual.cy.js | 4 +- ...lingual.cy.js => search.monolingual.cy.js} | 120 +++++++++--------- acceptance/docker-compose.yml | 7 +- dockerfiles/backend/Dockerfile.acceptance | 6 +- .../Dockerfile.acceptance.multilingual | 8 +- dockerfiles/backend/requirements-docker.txt | 2 +- dockerfiles/docker-compose.yml | 2 +- dockerfiles/frontend/Dockerfile.acceptance | 3 + dockerfiles/frontend/Dockerfile.ci | 3 + dockerfiles/frontend/config_monolingual.js | 24 ++++ dockerfiles/frontend/config_multilingual.js | 2 +- src/components/Views/FacetedSearch.jsx | 1 + 17 files changed, 116 insertions(+), 78 deletions(-) rename acceptance/{cypress.singlelingual.config.js => cypress.monolingual.config.js} (79%) rename acceptance/cypress/tests/{basic.singlelingual.cy.js => basic.monolingual.cy.js} (100%) rename acceptance/cypress/tests/{create_search.singlelingual.cy.js => create_search.monolingual.cy.js} (96%) rename acceptance/cypress/tests/{search.singlelingual.cy.js => search.monolingual.cy.js} (50%) create mode 100644 dockerfiles/frontend/config_monolingual.js diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index ebec40cf..45a2a6a4 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -67,7 +67,7 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.singlelingual.cy.js + spec: cypress/tests/*.monolingual.cy.js install: false start: | docker compose -f ci.yml --profile prod up diff --git a/Makefile b/Makefile index 93ba314a..4e41bca6 100644 --- a/Makefile +++ b/Makefile @@ -112,12 +112,12 @@ start-acceptance: ## Start acceptance server-containers .PHONY: test-acceptance test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.singlelingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.monolingual.config.js) .PHONY: test-acceptance-headless # test-acceptance-headless: install-acceptance ## Run cypress tests in CI test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.singlelingual.config.js) + (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.monolingual.config.js) .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server @@ -137,7 +137,7 @@ build-acceptance-multilingual: ## multilingual – Install Cypress, build contai ${ACCEPTANCE_MULTILINGUAL} --profile multilingual build --no-cache .PHONY: start-acceptance-containers-multilingual -start-acceptance-multilingual: ## multilingual – Start acceptance server-containers for multilingual siet +start-acceptance-multilingual: ## multilingual – Start acceptance server-containers for multilingual site ${ACCEPTANCE_MULTILINGUAL} --profile multilingual up -d --force-recreate .PHONY: test-acceptance diff --git a/acceptance/cypress.singlelingual.config.js b/acceptance/cypress.monolingual.config.js similarity index 79% rename from acceptance/cypress.singlelingual.config.js rename to acceptance/cypress.monolingual.config.js index 4ba661c3..9db25763 100644 --- a/acceptance/cypress.singlelingual.config.js +++ b/acceptance/cypress.monolingual.config.js @@ -7,6 +7,6 @@ module.exports = defineConfig({ // implement node event listeners here }, baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.singlelingual.cy.{js,jsx}', + specPattern: 'cypress/tests/*.monolingual.cy.{js,jsx}', }, }); diff --git a/acceptance/cypress/tests/basic.singlelingual.cy.js b/acceptance/cypress/tests/basic.monolingual.cy.js similarity index 100% rename from acceptance/cypress/tests/basic.singlelingual.cy.js rename to acceptance/cypress/tests/basic.monolingual.cy.js diff --git a/acceptance/cypress/tests/create_search.singlelingual.cy.js b/acceptance/cypress/tests/create_search.monolingual.cy.js similarity index 96% rename from acceptance/cypress/tests/create_search.singlelingual.cy.js rename to acceptance/cypress/tests/create_search.monolingual.cy.js index 69be7cd7..58d5c530 100644 --- a/acceptance/cypress/tests/create_search.singlelingual.cy.js +++ b/acceptance/cypress/tests/create_search.monolingual.cy.js @@ -55,7 +55,7 @@ describe('Searchkit block tests- create search ', () => { cy.getSlate().click(); cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('div[aria-label="Ausklappen Common blocks"]').click(); cy.get('.blocks-chooser .common .button.searchkitblock').click({ force: true, }); diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index 59b774fe..9a0579e9 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -23,7 +23,7 @@ describe('Searchkit block tests – search - multilingual', () => { contentType: 'Document', contentId: 'der-garten-im-februar', contentTitle: 'Der Garten im Februar', - path: '/it', + path: '/de', }); @@ -56,7 +56,7 @@ describe('Searchkit block tests – search - multilingual', () => { after(() => { cy.removeContent({ path: 'en/garden-in-february' }); cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'it/der-garten-im-februar' }); + cy.removeContent({ path: 'de/der-garten-im-februar' }); }); diff --git a/acceptance/cypress/tests/search.singlelingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js similarity index 50% rename from acceptance/cypress/tests/search.singlelingual.cy.js rename to acceptance/cypress/tests/search.monolingual.cy.js index 729af0f7..45c39478 100644 --- a/acceptance/cypress/tests/search.singlelingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -55,7 +55,7 @@ describe('Searchkit block tests – search', () => { cy.getSlate().click(); cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Unfold Common blocks"]').click(); + cy.get('div[aria-label="Ausklappen Common blocks"]').click(); cy.get('.blocks-chooser .common .button.searchkitblock').click({ force: true, }); @@ -87,15 +87,15 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'testseite-s' }); }); - it('I see all if no filter selected', function () { - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); + // it('I see all if no filter selected', function () { + // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + // }); - it('I can search fuzzy', function () { - cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - cy.get('.block.searchkitsearch').should('not.contain', 'März'); - }); + // it('I can search fuzzy', function () { + // cy.get('.searchbar-wrapper input').type('februax{enter}'); + // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + // cy.get('.block.searchkitsearch').should('not.contain', 'März'); + // }); it('I can search with inflection', function () { cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); @@ -105,56 +105,56 @@ describe('Searchkit block tests – search', () => { cy.get('.block.searchkitsearch').contains('Testseite Männer'); }); - it('I can search with decompounding', function () { - cy.get('.searchbar-wrapper input').type('Garten{enter}'); - cy.get('.block.searchkitsearch').contains('Garten-Blog'); - - cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - cy.get('.block.searchkitsearch').contains('Februar'); - }); - - it('I can search with wildcard', function () { - cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); - - it('I can search for an exact match', function () { - cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); - }); - - it('I can search for a compounded word', function () { - cy.get('.searchbar-wrapper input').type('stelle{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Stelle'); - }); - - // Blocks text - it('I can search in blocks', function () { - cy.visit('/garten-blog/februar'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.log('when I add a text block'); - cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); - // cy.toolbarSave(); - cy.get('#toolbar-save').click(); - cy.wait('@content'); - - cy.log('I added a text block'); - - // Searching - cy.visit('/suche'); - cy.wait(3000); - cy.get('.searchbar-wrapper input').type('Montag{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); + // it('I can search with decompounding', function () { + // cy.get('.searchbar-wrapper input').type('Garten{enter}'); + // cy.get('.block.searchkitsearch').contains('Garten-Blog'); + + // cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + // cy.get('.block.searchkitsearch').contains('Februar'); + // }); + + // it('I can search with wildcard', function () { + // cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + // }); + + // it('I can search for an exact match', function () { + // cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + // cy.get('.block.searchkitsearch').contains('Testseite Mann'); + // cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + // cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); + // }); + + // it('I can search for a compounded word', function () { + // cy.get('.searchbar-wrapper input').type('stelle{enter}'); + // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + // cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + // cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + // cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + // cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + // }); + + // // Blocks text + // it('I can search in blocks', function () { + // cy.visit('/garten-blog/februar'); + // cy.get('a.edit').click(); + + // cy.getSlate().click(); + // cy.log('when I add a text block'); + // cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); + // // cy.toolbarSave(); + // cy.get('#toolbar-save').click(); + // cy.wait('@content'); + + // cy.log('I added a text block'); + + // // Searching + // cy.visit('/suche'); + // cy.wait(3000); + // cy.get('.searchbar-wrapper input').type('Montag{enter}'); + // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + // }); }); diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 0aa9aa06..b9c32b1a 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -67,6 +67,8 @@ services: CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: "collective.elastic.plone" + volumes: + - backend-data:/data ports: - 55001:55001 profiles: @@ -92,6 +94,8 @@ services: CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: "collective.elastic.plone" + volumes: + - backend-data:/data ports: - 55001:55001 profiles: @@ -139,7 +143,7 @@ services: CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} - PLONE_SERVICE: http://backend-acceptance-multilingualela:55001 + PLONE_SERVICE: http://backend-acceptance-multilingual:55001 PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} @@ -208,6 +212,7 @@ services: # - opensearch volumes: + backend-data: opensearch-data: ingest-configuration: driver: local diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 44128443..e038025f 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -2,6 +2,7 @@ # Dockerfile from cookiecutter-plone-starter ARG PLONE_VERSION=6.0 +ARG SEED=2 FROM plone/server-builder:${PLONE_VERSION} as builder WORKDIR /app @@ -13,16 +14,17 @@ WORKDIR /app RUN <=2.0.0 - bin/pip install collective.elastic.plone[redis,opensearch]>=2.0.1 + bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="collective.elastic.plone:default,plone.restapi:default,plone.volto:default-homepage" +ENV APPLY_PROFILES="collective.elastic.plone:monolingual" # Copy /app from builder COPY --from=builder /app /app RUN <=2.0.0 - bin/pip install collective.elastic.plone[redis,opensearch]>=2.0.1 + bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="collective.elastic.plone:default,plone.restapi:default,plone.volto:multilingual" +ENV APPLY_PROFILES="collective.elastic.plone:multilingual" # Copy /app from builder COPY --from=builder /app /app diff --git a/dockerfiles/backend/requirements-docker.txt b/dockerfiles/backend/requirements-docker.txt index 043c5ce2..e8eb6699 100644 --- a/dockerfiles/backend/requirements-docker.txt +++ b/dockerfiles/backend/requirements-docker.txt @@ -1,2 +1,2 @@ -collective.elastic.plone[redis,opensearch]==2.0.1 +collective.elastic.plone[redis,opensearch]==2.1.0 elasticsearch \ No newline at end of file diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 78b66ab5..00981de1 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -68,7 +68,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} SITE: Plone CORS_: true - ADDONS: "collective.elastic.plone[redis,opensearch]==2.0.1" + ADDONS: "collective.elastic.plone[redis,opensearch]==2.1.0" PROFILES: "collective.elastic.plone:default" DELETE_EXISTING: False ports: diff --git a/dockerfiles/frontend/Dockerfile.acceptance b/dockerfiles/frontend/Dockerfile.acceptance index a6e51074..a3750f70 100644 --- a/dockerfiles/frontend/Dockerfile.acceptance +++ b/dockerfiles/frontend/Dockerfile.acceptance @@ -10,6 +10,9 @@ COPY --chown=node:node volto.config.js* /app/ COPY --chown=node:node package.json /app/src/addons/${ADDON_PATH}/ +# not isMultilingual +COPY --chown=node:node ./dockerfiles/frontend/config_monolingual.js /app/src/config.js + RUN < { // TODO Check if check on client could be made simpler const language = useSelector((state) => state.intl.locale); + console.debug('language', language); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); From 8c33e8843c82be6f87916281e953780f9962bcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 15:01:34 +0200 Subject: [PATCH 056/226] Increase version (already released 0.4.0) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b23029f..3c08af58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rohberg/volto-searchkit-block", - "version": "0.3.3", + "version": "0.4.0", "description": "Find content. Pardon typos. Allow search queries with and provide results with compound words.", "main": "src/index.js", "author": "Katja Süss, https://github.com/rohberg", From afceb49abf99d0b6402ce47a36e1ac8604fe6e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 15:02:55 +0200 Subject: [PATCH 057/226] Remove redundancy --- src/components/Blocks/FacetedSearchBlockEdit.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Blocks/FacetedSearchBlockEdit.jsx b/src/components/Blocks/FacetedSearchBlockEdit.jsx index 591ca2b8..e9238263 100644 --- a/src/components/Blocks/FacetedSearchBlockEdit.jsx +++ b/src/components/Blocks/FacetedSearchBlockEdit.jsx @@ -3,7 +3,7 @@ import { Container, Segment } from 'semantic-ui-react'; import { SidebarPortal } from '@plone/volto/components'; import Sidebar from './Sidebar'; -import FacetedSearch from '../Views/FacetedSearch'; +import FacetedSearchBlockView from './FacetedSearchBlockView'; import DownloadFiltersMapping from './DownloadFiltersMapping'; const Edit = ({ data, onChangeBlock, block, selected }) => { @@ -18,9 +18,7 @@ const Edit = ({ data, onChangeBlock, block, selected }) => { -
- -
+
); }; From e2041fe5671428d796e5f63e98350aaf797cd4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 15:55:56 +0200 Subject: [PATCH 058/226] import { createPortal } from 'react-dom'; --- src/components/Views/FacetedSearch.jsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 810ca86e..e5ebb623 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -2,10 +2,11 @@ import React from 'react'; import { compact, truncate } from 'lodash'; import cx from 'classnames'; import Cookies from 'universal-cookie'; +import { createPortal } from 'react-dom'; import { useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; import { FormattedMessage, useIntl } from 'react-intl'; -import { Portal } from 'react-portal'; + import { OverridableContext } from 'react-overridable'; import { @@ -390,7 +391,9 @@ const CustomBucketAggregationElement = (props) => { }; const dropdowntitle = - title || fieldname + (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); + title || + fieldname + + (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); return containerCmp ? (
@@ -729,15 +732,12 @@ const FacetedSearch = ({ data, overriddenComponents }) => { > {typeof document !== 'undefined' && relocation?.length > 0 ? ( - , + true && document.querySelectorAll(relocation) && - document.querySelectorAll(relocation)[0] - } - > - - + document.querySelectorAll(relocation)[0], + ) ) : ( From 22ea13996a37a640189d4c159211e553d69ec3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 15:58:35 +0200 Subject: [PATCH 059/226] Delete .project.eslintrc.js --- .project.eslintrc.js | 50 -------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 .project.eslintrc.js diff --git a/.project.eslintrc.js b/.project.eslintrc.js deleted file mode 100644 index 6e392578..00000000 --- a/.project.eslintrc.js +++ /dev/null @@ -1,50 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const projectRootPath = fs.realpathSync('./project'); // __dirname -const packageJson = require(path.join(projectRootPath, 'package.json')); - -let voltoPath = path.join(projectRootPath, '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 AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`); -const reg = new AddonConfigurationRegistry(projectRootPath); - -// 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, -]); - -module.exports = { - extends: `${voltoPath}/.eslintrc`, - settings: { - 'import/resolver': { - alias: { - map: [ - ['@plone/volto', '@plone/volto/src'], - ...addonAliases, - ['@package', `${__dirname}/src`], - ['@root', `${__dirname}/src`], - ['~', `${__dirname}/src`], - ], - extensions: ['.js', '.jsx', '.json'], - }, - 'babel-plugin-root-import': { - rootPathSuffix: 'src', - }, - }, - }, -}; From 11fb0afe1293e80968e629e2848d8867676febc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 16:26:34 +0200 Subject: [PATCH 060/226] Remove dependencies (moved to @plone/volto) --- package.json | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3c08af58..5765a918 100644 --- a/package.json +++ b/package.json @@ -44,24 +44,10 @@ "react-searchkit": "v2.2.0" }, "devDependencies": { - "@babel/eslint-parser": "7.22.15", - "@plone/scripts": "^3.3.2", - "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", + "@plone/scripts": "^3.6.2", "postcss-less": "6.0.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" + "release-it": "^17.1.1" } } From 0dd45a18b4fcb24b9bfbc30081e4ca751ed09791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Fri, 7 Jun 2024 20:33:42 +0200 Subject: [PATCH 061/226] Revert "Delete .project.eslintrc.js" This reverts commit 22ea13996a37a640189d4c159211e553d69ec3b7. --- .project.eslintrc.js | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .project.eslintrc.js diff --git a/.project.eslintrc.js b/.project.eslintrc.js new file mode 100644 index 00000000..6e392578 --- /dev/null +++ b/.project.eslintrc.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const path = require('path'); + +const projectRootPath = fs.realpathSync('./project'); // __dirname +const packageJson = require(path.join(projectRootPath, 'package.json')); + +let voltoPath = path.join(projectRootPath, '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 AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`); +const reg = new AddonConfigurationRegistry(projectRootPath); + +// 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, +]); + +module.exports = { + extends: `${voltoPath}/.eslintrc`, + settings: { + 'import/resolver': { + alias: { + map: [ + ['@plone/volto', '@plone/volto/src'], + ...addonAliases, + ['@package', `${__dirname}/src`], + ['@root', `${__dirname}/src`], + ['~', `${__dirname}/src`], + ], + extensions: ['.js', '.jsx', '.json'], + }, + 'babel-plugin-root-import': { + rootPathSuffix: 'src', + }, + }, + }, +}; From dd5eb22e4c072ae74927093e3cfcd6f944ab608b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sat, 8 Jun 2024 07:54:48 +0200 Subject: [PATCH 062/226] Update .project.eslintrc.js --- .project.eslintrc.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.project.eslintrc.js b/.project.eslintrc.js index 6e392578..51d0befe 100644 --- a/.project.eslintrc.js +++ b/.project.eslintrc.js @@ -19,7 +19,8 @@ if (configFile) { voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`; } -const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`); +// const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`); +const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); const reg = new AddonConfigurationRegistry(projectRootPath); // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons @@ -35,6 +36,7 @@ module.exports = { alias: { map: [ ['@plone/volto', '@plone/volto/src'], + ['@plone/volto-slate', '@plone/volto-slate/src'], ...addonAliases, ['@package', `${__dirname}/src`], ['@root', `${__dirname}/src`], From 86fa7f71035c5a5237c66ba6cc0c30edfa6f3875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sat, 8 Jun 2024 07:54:52 +0200 Subject: [PATCH 063/226] Update package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 5765a918..98aaa440 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "@plone/scripts": "^3.6.2", "postcss-less": "6.0.0", "postcss-scss": "4.0.8", - "prettier": "3.0.3", "release-it": "^17.1.1" } } From e996a6f752d6753c5bd3ed29cca6d407091dea2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sat, 8 Jun 2024 07:55:24 +0200 Subject: [PATCH 064/226] Delete webpack.config.js --- webpack.config.js | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 webpack.config.js diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 655b2948..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('path'); - -module.exports = { - entry: './src/index.js', - output: { - filename: 'main.js', - path: path.resolve(__dirname, 'dist'), - }, -}; From eb8c4c93631d266c2a5fcc2c3677ebfc8660d233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:04:43 +0200 Subject: [PATCH 065/226] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index afc099fd..94c324eb 100644 --- a/.gitignore +++ b/.gitignore @@ -122,5 +122,5 @@ build/ .yarn/ yarn.lock -!docker-opensearch/.env +!dockerfiles/opensearch/.env !acceptance/.env From 32d4f26f47a843f53513910ee2919283090af060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:07:39 +0200 Subject: [PATCH 066/226] Show dates (optional). Fix Test panel. --- .../Blocks/FacetedSearchBlockEdit.jsx | 2 +- .../Blocks/FacetedSearchBlockView.jsx | 2 - src/components/Blocks/schema.js | 31 +++- .../Searchkit/CustomESRequestSerializer.jsx | 37 ++-- .../Searchkit/CustomESResponseSerializer.jsx | 2 +- .../Searchkit/ElasticSearchHighlights.jsx | 4 +- src/components/Searchkit/SearchBarSection.jsx | 2 +- src/components/Searchkit/SectionsSearch.jsx | 2 +- src/components/Views/FacetedSearch.jsx | 117 +++++++----- .../Views/TestSearchkitQuerystrings.jsx | 168 +++++++++--------- src/components/helpers.jsx | 1 + src/index.js | 15 +- src/messages.js | 4 + 13 files changed, 233 insertions(+), 154 deletions(-) diff --git a/src/components/Blocks/FacetedSearchBlockEdit.jsx b/src/components/Blocks/FacetedSearchBlockEdit.jsx index e9238263..cb2b8b33 100644 --- a/src/components/Blocks/FacetedSearchBlockEdit.jsx +++ b/src/components/Blocks/FacetedSearchBlockEdit.jsx @@ -18,7 +18,7 @@ const Edit = ({ data, onChangeBlock, block, selected }) => { - +
); }; diff --git a/src/components/Blocks/FacetedSearchBlockView.jsx b/src/components/Blocks/FacetedSearchBlockView.jsx index 14b27a2e..6cc0e92d 100644 --- a/src/components/Blocks/FacetedSearchBlockView.jsx +++ b/src/components/Blocks/FacetedSearchBlockView.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - import FacetedSearch from '../Views/FacetedSearch'; const View = ({ data }) => { diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 19a18112..2f90f0b8 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -69,12 +69,18 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { 'allowed_content_types', 'allowed_review_states', 'searchedFields', + 'batchSize', ], }, { id: 'results', title: 'Results', - fields: ['extrainfo_fields', 'subjectsFieldname'], + fields: [ + 'extrainfo_fields', + 'subjectsFieldname', + 'showNewsItemPublishedDate', + 'showEventStartDate', + ], }, { id: 'divers', @@ -143,6 +149,11 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { creatable: true, default: ['title^1.4', 'description^1.2', 'blocks_plaintext'], }, + batchSize: { + title: 'Batch size', + type: 'number', + default: 10, + }, facet_fields: { title: 'Facets', description: 'Fields to filter on.', @@ -167,7 +178,23 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { title: 'Field name of tags field', description: 'Show tags to search for. Let the field empty to not show tags.', - default: '', + default: 'subjects', + }, + showNewsItemPublishedDate: { + title: 'Show published date of news items', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, + }, + showEventStartDate: { + title: 'Show start date of events', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, }, relocation: { title: 'Relocation', diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 761245c8..4fe7e7f9 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -155,7 +155,7 @@ export class CustomESRequestSerializer { // fields with boosting let searchedFields = [...this.searchedFields]; - + let searchedFields_simple = searchedFields.map((fld) => { const fieldname = fld.split('^')[0]; return fld.replace(fieldname, `${fieldname}.${this.language}`); @@ -223,14 +223,19 @@ export class CustomESRequestSerializer { bodyParams['highlight'] = { number_of_fragments: 20, - fields: ['title', 'description', 'blocks_plaintext'].map(fieldname => { - return { - [fieldname]: { - matched_fields: [`${fieldname}.${this.language}`, `${fieldname}.${this.language}_exact`], - type: 'fvh', - }, - } - }) + fields: ['title', 'description', 'blocks_plaintext'].map( + (fieldname) => { + return { + [fieldname]: { + matched_fields: [ + `${fieldname}.${this.language}`, + `${fieldname}.${this.language}_exact`, + ], + type: 'fvh', + }, + }; + }, + ), }; } @@ -258,12 +263,14 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; // If isMultilingual, search only in language - - this.language && volto_config.settings.isMultilingual && terms.push({ - terms: { - language: [this.language], - }, - }); + + this.language && + volto_config.settings.isMultilingual && + terms.push({ + terms: { + language: [this.language], + }, + }); this.allowed_content_types?.length > 0 && terms.push({ terms: { diff --git a/src/components/Searchkit/CustomESResponseSerializer.jsx b/src/components/Searchkit/CustomESResponseSerializer.jsx index 07cdfbee..8327a86d 100644 --- a/src/components/Searchkit/CustomESResponseSerializer.jsx +++ b/src/components/Searchkit/CustomESResponseSerializer.jsx @@ -41,7 +41,7 @@ export class CustomESResponseSerializer { hit._source['highlight'] = hit.highlight; return hit._source; }) || [], - total: hits?.total.value < 11 ? hits.hits.length : hits?.total.value || 0, + total: hits?.total.value || 0, }; return foo; } diff --git a/src/components/Searchkit/ElasticSearchHighlights.jsx b/src/components/Searchkit/ElasticSearchHighlights.jsx index c948b2c0..739fb2ab 100644 --- a/src/components/Searchkit/ElasticSearchHighlights.jsx +++ b/src/components/Searchkit/ElasticSearchHighlights.jsx @@ -6,7 +6,7 @@ import React from 'react'; import { useIntl } from 'react-intl'; import messages from '../../messages'; -export const ElasticSearchHighlights = ({ highlight, indexResult }) => { +export const ElasticSearchHighlights = ({ highlight }) => { const [toggleDetails, setToggleDetails] = React.useState(false); const intl = useIntl(); @@ -32,7 +32,6 @@ export const ElasticSearchHighlights = ({ highlight, indexResult }) => { onClick={showDetails} role="button" onKeyPress={showDetails} - tabIndex={indexResult} > {fragments.slice(0, 3).map((el, index) => { return
; @@ -44,7 +43,6 @@ export const ElasticSearchHighlights = ({ highlight, indexResult }) => { onClick={showDetails} role="button" onKeyPress={showDetails} - tabIndex={indexResult} > {Object.keys(highlight) .reverse() diff --git a/src/components/Searchkit/SearchBarSection.jsx b/src/components/Searchkit/SearchBarSection.jsx index 81939111..7c4917d4 100644 --- a/src/components/Searchkit/SearchBarSection.jsx +++ b/src/components/Searchkit/SearchBarSection.jsx @@ -13,7 +13,7 @@ const _SearchBarSection = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, queryString: '', }, }; diff --git a/src/components/Searchkit/SectionsSearch.jsx b/src/components/Searchkit/SectionsSearch.jsx index 133ad80c..70b7b65d 100644 --- a/src/components/Searchkit/SectionsSearch.jsx +++ b/src/components/Searchkit/SectionsSearch.jsx @@ -67,7 +67,7 @@ const _SectionsSearch = (props) => { sortOrder: 'desc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: currentQueryState.filters, }; if (currentQueryState.queryString) { diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index e5ebb623..7a157a9b 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -32,7 +32,7 @@ import { } from 'react-searchkit'; import { expandToBackendURL } from '@plone/volto/helpers'; -import { Icon } from '@plone/volto/components'; +import { FormattedDate, Icon } from '@plone/volto/components'; import leftAngle from '@plone/volto/icons/left-key.svg'; import rightAngle from '@plone/volto/icons/right-key.svg'; import firstAngle from '@plone/volto/icons/first.svg'; @@ -104,7 +104,7 @@ const _ExtraInfo = (props) => { const translate = (key, fieldname) => { let label = key; - if (querystringindexes || querystringindexes[fieldname]) { + if (querystringindexes && fieldname in querystringindexes) { label = querystringindexes[fieldname]?.values[key]?.title || key; } return label; @@ -131,7 +131,7 @@ const _ExtraInfo = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: [[`${extrainfo_key}_agg`, item]], }, }; @@ -182,7 +182,7 @@ const _ExtraInfo = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, queryString: tito, }, }; @@ -206,15 +206,22 @@ const _ExtraInfo = (props) => { const ExtraInfo = withState(_ExtraInfo); const _CustomResultsListItem = (props) => { - const { result, index } = props; + const { result } = props; const backend_url = props.currentQueryState.data?.backend_url; const is_external_content = !result['@id'].includes(backend_url); const item_url = result['@id'].includes(backend_url) ? flattenESUrlToPath(result['@id']) : result['@id']; + const locale = useSelector((state) => state.query?.locale); const querystringindexes = useSelector( (state) => state.query?.data?.querystringindexes, ); + const showNewsItemPublishedDate = useSelector( + (state) => state.query?.data.showNewsItemPublishedDate, + ); + const showEventStartDate = useSelector( + (state) => state.query?.data.showEventStartDate, + ); const translate = (key) => { let label = key; @@ -226,7 +233,7 @@ const _CustomResultsListItem = (props) => { return ( @@ -241,7 +248,7 @@ const _CustomResultsListItem = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: [['informationtype_agg', item]], }, }; @@ -264,6 +271,17 @@ const _CustomResultsListItem = (props) => { > {result.title} + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} {truncate(result.description, { length: 200 })} @@ -275,6 +293,17 @@ const _CustomResultsListItem = (props) => { {result.title} + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} {truncate(result.description, { length: 200 })} @@ -283,10 +312,7 @@ const _CustomResultsListItem = (props) => { )} - + ); @@ -345,7 +371,7 @@ const CustomBucketAggregationElement = (props) => { * @returns */ const translate = (bucks) => { - if (querystringindexes[fieldname]) { + if (querystringindexes && fieldname in querystringindexes) { bucks.forEach((element) => { element.label = querystringindexes[fieldname].values[element.key]?.title || @@ -651,35 +677,6 @@ const sortValues = [ // }, ]; -const initialState = { - sortBy: 'bestmatch', - sortOrder: 'asc', - // sortBy: 'modified', - // sortOrder: 'desc', - queryString: '', - layout: 'list', - page: 1, - size: 10, -}; - -const defaultOverriddenComponents = { - 'ResultsList.item.elasticsearch': CustomResultsListItem, - 'Count.element': MyCountElement, - 'ActiveFilters.element': myActiveFiltersElement, - 'EmptyResults.element': customEmpytResultsElement, - 'Sort.element.volto': customSort, - 'Pagination.element': customPaginationElement, - 'Error.element': ErrorComponent, -}; - -const dropdownOverriddenComponents = { - 'BucketAggregation.element': CustomBucketAggregationElement, - 'BucketAggregationContainer.element': CustomBucketAggregationContainerElement, - 'BucketAggregationValues.element': withState( - CustomBucketAggregationValuesElement, - ), -}; - /** * FacetedSearch * @param {string} filterLayout default 'dropdown' @@ -704,6 +701,35 @@ const FacetedSearch = ({ data, overriddenComponents }) => { delete facet_fields_object.Subject; } + // TODO Get config from data + const initialState = { + page: 1, + queryString: '', + sortBy: 'modified', + sortOrder: 'desc', + size: data.batchSize, + layout: 'list', + }; + + const defaultOverriddenComponents = { + 'ResultsList.item.elasticsearch': CustomResultsListItem, + 'Count.element': MyCountElement, + 'ActiveFilters.element': myActiveFiltersElement, + 'EmptyResults.element': customEmpytResultsElement, + 'Sort.element.volto': customSort, + 'Pagination.element': customPaginationElement, + 'Error.element': ErrorComponent, + }; + + const dropdownOverriddenComponents = { + 'BucketAggregation.element': CustomBucketAggregationElement, + 'BucketAggregationContainer.element': + CustomBucketAggregationContainerElement, + 'BucketAggregationValues.element': withState( + CustomBucketAggregationValuesElement, + ), + }; + overriddenComponents = { ...defaultOverriddenComponents, ...(filterLayout === 'dropdown' && dropdownOverriddenComponents), @@ -712,8 +738,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { }; // TODO Check if check on client could be made simpler - const language = useSelector((state) => state.intl.locale); - console.debug('language', language); + const locale = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); @@ -722,11 +747,13 @@ const FacetedSearch = ({ data, overriddenComponents }) => { {isClient && ( diff --git a/src/components/Views/TestSearchkitQuerystrings.jsx b/src/components/Views/TestSearchkitQuerystrings.jsx index 0d5e3148..5f456102 100644 --- a/src/components/Views/TestSearchkitQuerystrings.jsx +++ b/src/components/Views/TestSearchkitQuerystrings.jsx @@ -1,11 +1,13 @@ import React from 'react'; import { useIntl } from 'react-intl'; +import { createPortal } from 'react-dom'; import { Container, Header, Segment } from 'semantic-ui-react'; +import { useHistory } from 'react-router'; import { Link, useLocation } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { OverridableContext } from 'react-overridable'; -import { getControlpanel } from '@plone/volto/actions'; -import { Icon as IconNext } from '@plone/volto/components'; +import { Icon, Toolbar } from '@plone/volto/components'; +import { getParentUrl } from '@plone/volto/helpers'; import backSVG from '@plone/volto/icons/back.svg'; import { @@ -21,6 +23,8 @@ import { ploneSearchApi } from './FacetedSearch'; import { ElasticSearchMatches } from '../Searchkit/ElasticSearchHighlights'; import messages from '../../messages'; +import config from '@plone/volto/registry'; + const sort_caseinsensitive = (a, b) => { var nameA = a.toUpperCase(); // Groß-/Kleinschreibung ignorieren var nameB = b.toUpperCase(); // Groß-/Kleinschreibung ignorieren @@ -103,27 +107,8 @@ const overriddenComponents = { const TestSearchkitQuerystrings = (props) => { const intl = useIntl(); - const dispatch = useDispatch(); - const searchkitblock_controlpanel = useSelector( - (state) => state.controlpanels.controlpanel?.data, - ); - const searchconfig = searchkitblock_controlpanel - ? { - elastic_search_api_url: - searchkitblock_controlpanel?.testsearch_elasticsearch_url, - elastic_search_api_index: - searchkitblock_controlpanel?.testsearch_elasticsearch_index, - - searchedFields: [], - facet_fields: [], - allowed_content_types: - searchkitblock_controlpanel?.allowed_content_types, - allowed_review_states: - searchkitblock_controlpanel?.allowed_review_states, - backend_url: searchkitblock_controlpanel?.testsearch_backend, - frontend_url: searchkitblock_controlpanel?.testsearch_frontend, - } - : {}; + const history = useHistory(); + const searchconfig = config.blocks.blocksConfig.searchkitblock.searchconfig; const initialState = { sortBy: 'bestmatch', @@ -145,69 +130,88 @@ const TestSearchkitQuerystrings = (props) => { return; }; + const locale = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); - React.useEffect(() => { - dispatch(getControlpanel('volto_searchkit_block_control_panel')); - }, [dispatch]); - return ( - - -
- Matches -
-
- {isClient && searchkitblock_controlpanel && ( - - - <> - - {/* { - onchangehandler(event, data); - }} - /> */} - { - onchangehandler(event, data); - }} + + + +
+ Matches +
+
+ {isClient && ( + + + <> + + {/* { + onchangehandler(event, data); + }} + /> */} + { + onchangehandler(event, data); + }} + /> + + + + +
Documents with title and matches
+ +
+ +
+
+ )} +
+ + {isClient && + createPortal( + { + history.push(getParentUrl(location.pathname)); + }} + > + -
- - - -
Documents with title and matches
- -
- -
-
- )} - - - -
+ + } + />, + document.getElementById('toolbar'), + )} + ); }; diff --git a/src/components/helpers.jsx b/src/components/helpers.jsx index 919dffa5..71814bde 100644 --- a/src/components/helpers.jsx +++ b/src/components/helpers.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useSelector } from 'react-redux'; class NoSSR extends React.Component { state = { diff --git a/src/index.js b/src/index.js index 058e3fca..c6334a56 100644 --- a/src/index.js +++ b/src/index.js @@ -65,7 +65,6 @@ const applyConfig = (config) => { title: 'Test searchkit querystrings', }, ]; - config.addonRoutes = [ ...config.addonRoutes, { @@ -73,6 +72,20 @@ const applyConfig = (config) => { component: TestSearchkitQuerystrings, }, ]; + // Configure 'Test searchkit querystrings' controlpanel + config.blocks.blocksConfig.searchkitblock.searchconfig = { + searchedFields: [ + 'title^1.4', + 'description^1.2', + 'blocks_plaintext', + 'subjects^1.2', + ], + facet_fields: [], + allowed_content_types: ['Document', 'News Item', 'Event'], + allowed_review_states: [], + backend_url: 'http://host.docker.internal:8080/Plone', + frontend_url: 'http://localhost:3000', + }; // Fetch querystring indexes. // See /effective-volto/addons/asyncconnect diff --git a/src/messages.js b/src/messages.js index d733e786..ca12b01e 100644 --- a/src/messages.js +++ b/src/messages.js @@ -71,6 +71,10 @@ const messages = defineMessages({ id: 'Content', defaultMessage: 'Content', }, + cancel: { + id: 'Cancel', + defaultMessage: 'Cancel', + }, }); export default messages; From 39a547ca7cfa7e19b010a3ad649fdac6fa9ada50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:08:57 +0200 Subject: [PATCH 067/226] make opensearchandingest (containers) --- Makefile | 21 ++++++++-- README.md | 2 +- acceptance/ci.yml | 6 +-- acceptance/docker-compose.yml | 38 ++++++++--------- docker-opensearch/Dockerfile.ingest | 3 -- docker-opensearch/Dockerfile.opensearch | 8 ---- dockerfiles/docker-compose.yml | 41 ++++++++++--------- dockerfiles/opensearch/Dockerfile.opensearch | 8 ++++ .../opensearch}/README.md | 0 .../ingest-configuration/analysis.json | 0 .../ingest-configuration/mappings.json | 0 .../ingest-configuration/preprocessings.json | 0 .../opensearch-configuration/keywords.txt | 0 .../keywords_english.txt | 0 .../opensearch-configuration/lexicon.txt | 0 .../lexicon_english.txt | 0 variables.mk | 4 +- 17 files changed, 71 insertions(+), 60 deletions(-) delete mode 100644 docker-opensearch/Dockerfile.ingest delete mode 100644 docker-opensearch/Dockerfile.opensearch create mode 100644 dockerfiles/opensearch/Dockerfile.opensearch rename {docker-opensearch => dockerfiles/opensearch}/README.md (100%) rename {docker-opensearch => dockerfiles/opensearch}/ingest-configuration/analysis.json (100%) rename {docker-opensearch => dockerfiles/opensearch}/ingest-configuration/mappings.json (100%) rename {docker-opensearch => dockerfiles/opensearch}/ingest-configuration/preprocessings.json (100%) rename {docker-opensearch => dockerfiles/opensearch}/opensearch-configuration/keywords.txt (100%) rename {docker-opensearch => dockerfiles/opensearch}/opensearch-configuration/keywords_english.txt (100%) rename {docker-opensearch => dockerfiles/opensearch}/opensearch-configuration/lexicon.txt (100%) rename {docker-opensearch => dockerfiles/opensearch}/opensearch-configuration/lexicon_english.txt (100%) diff --git a/Makefile b/Makefile index 4e41bca6..292ce45d 100644 --- a/Makefile +++ b/Makefile @@ -24,9 +24,10 @@ DEV_COMPOSE=dockerfiles/docker-compose.yml ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml CMD_ENVS=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} BACKEND_ADDONS=${BACKEND_ADDONS} CMD=${CMD_ENVS} docker compose -DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${DEV_COMPOSE} -ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE} -ACCEPTANCE_MULTILINGUAL=${CMD} -p ${ADDON_PATH}-acceptance-multilingual -f ${ACCEPTANCE_COMPOSE} +PROJECT_NAME=${ADDON_PATH} +DOCKER_COMPOSE=${CMD} -p ${PROJECT_NAME} -f ${DEV_COMPOSE} +ACCEPTANCE=${CMD} -p ${PROJECT_NAME}-acceptance -f ${ACCEPTANCE_COMPOSE} +ACCEPTANCE_MULTILINGUAL=${CMD} -p ${PROJECT_NAME}-acceptance-multilingual -f ${ACCEPTANCE_COMPOSE} .PHONY: all all: help @@ -74,6 +75,20 @@ dev-start: ## Start development/demo environment without rebuilding images ${DOCKER_COMPOSE} --profile dev up +# Opensearch and ingest containers (everything but backend and frontend) +.PHONY: opensearchandingest-build +opensearchandingest-build: ## Build containers for opensearch and ingest `make opensearchandingest-build PROJECT_NAME=foo` + @echo "$(GREEN)==> Build containers for opensearch and ingest $(RESET)" + ${DOCKER_COMPOSE} --profile opensearchandingest build + @echo "$(GREEN)==> Successfully build containers for opensearch and ingest $(RESET)" + ${DOCKER_COMPOSE} --profile opensearchandingest config + +.PHONY: opensearchandingest-up +opensearchandingest-up: ## Start containers for opensearch and ingest. `make opensearchandingest-up PROJECT_NAME=foo` + @echo "$(GREEN)==> Start containers for opensearch and ingest $(RESET)" + ${DOCKER_COMPOSE} --profile opensearchandingest up + + # Dev Helpers .PHONY: i18n i18n: ## Sync i18n diff --git a/README.md b/README.md index af72a79e..5e1372a6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Install Plone backend add-on [`collective.elastic.plone 2.x`](https://github.com Install Plone backend add-on [`collective.elastic.ingest 2.x`](https://github.com/collective/collective.elastic.ingest) to index Plone content. Setting up OpenSearch/ElasticSearch instructions can be found on [`collective.elastic.plone 2.x`](https://github.com/collective/collective.elastic.plone). -See the [example](docker-opensearch) configuration of collective.elastic of a mapping, attachment handling and last but not least analysis. +See the [example](dockerfiles/opensearch) configuration of collective.elastic of a mapping, attachment handling and last but not least analysis. # Configuration diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 57357093..307ebc5b 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -99,7 +99,7 @@ services: ingest: build: context: ../ - dockerfile: ./docker-opensearch/Dockerfile.ingest + dockerfile: .//Dockerfile.ingest environment: MAPPINGS_FILE: "/configuration/mappings.json" ANALYSIS_FILE: "/configuration/analysis.json" @@ -120,7 +120,7 @@ services: ingest-multilingual: build: context: ../ - dockerfile: ./docker-opensearch/Dockerfile.ingest + dockerfile: .//Dockerfile.ingest environment: # Different INDEX_NAME MAPPINGS_FILE: "/configuration/mappings.json" @@ -148,7 +148,7 @@ services: opensearch: build: context: ../ - dockerfile: ./docker-opensearch/Dockerfile.opensearch + dockerfile: .//Dockerfile.opensearch environment: - plugins.security.disabled=true - cluster.name=opensearch-cluster diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index b9c32b1a..3668c828 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -1,14 +1,13 @@ -version: "3" +version: '3' services: - addon-acceptance: build: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.acceptance args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" + ADDON_NAME: '${ADDON_NAME}' + ADDON_PATH: '${ADDON_PATH}' VOLTO_VERSION: ${VOLTO_VERSION:-17} volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ @@ -30,8 +29,8 @@ services: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.acceptance.multilingual args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" + ADDON_NAME: '${ADDON_NAME}' + ADDON_PATH: '${ADDON_PATH}' VOLTO_VERSION: ${VOLTO_VERSION:-17} volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ @@ -55,8 +54,8 @@ services: args: PLONE_VERSION: ${PLONE_VERSION:-6.0} environment: - ZSERVER_HOST: "0.0.0.0" - ZSERVER_PORT: "55001" + ZSERVER_HOST: '0.0.0.0' + ZSERVER_PORT: '55001' CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_NAME: ${INDEX_NAME?unset} @@ -64,9 +63,9 @@ services: INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} - CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex - INSTALL_PRODUCTS: "collective.elastic.plone" + INSTALL_PRODUCTS: 'collective.elastic.plone' volumes: - backend-data:/data ports: @@ -82,8 +81,8 @@ services: args: PLONE_VERSION: ${PLONE_VERSION:-6.0} environment: - ZSERVER_HOST: "0.0.0.0" - ZSERVER_PORT: "55001" + ZSERVER_HOST: '0.0.0.0' + ZSERVER_PORT: '55001' CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_NAME: multilingual @@ -91,9 +90,9 @@ services: INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} - CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex - INSTALL_PRODUCTS: "collective.elastic.plone" + INSTALL_PRODUCTS: 'collective.elastic.plone' volumes: - backend-data:/data ports: @@ -161,13 +160,13 @@ services: opensearch: build: context: ../ - dockerfile: ./docker-opensearch/Dockerfile.opensearch + dockerfile: ./dockerfiles/opensearch/Dockerfile.opensearch environment: - cluster.name=opensearch-cluster - node.name=opensearch - discovery.type=single-node - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + - 'OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g' # - plugins.security.disabled=true - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: @@ -188,13 +187,12 @@ services: - multilingual - prod - opensearch-dashboards: image: opensearchproject/opensearch-dashboards:latest ports: - 5601:5601 expose: - - "5601" + - '5601' environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: @@ -218,5 +216,5 @@ volumes: driver: local driver_opts: type: none - device: ../docker-opensearch/ingest-configuration - o: bind \ No newline at end of file + device: ../dockerfiles/opensearch/ingest-configuration + o: bind diff --git a/docker-opensearch/Dockerfile.ingest b/docker-opensearch/Dockerfile.ingest deleted file mode 100644 index 739e8a84..00000000 --- a/docker-opensearch/Dockerfile.ingest +++ /dev/null @@ -1,3 +0,0 @@ -FROM ghcr.io/collective/collective.elastic.ingest:latest - -COPY docker-opensearch/ingest-configuration /configuration diff --git a/docker-opensearch/Dockerfile.opensearch b/docker-opensearch/Dockerfile.opensearch deleted file mode 100644 index 97d04fcf..00000000 --- a/docker-opensearch/Dockerfile.opensearch +++ /dev/null @@ -1,8 +0,0 @@ -FROM opensearchproject/opensearch:latest - -COPY docker-opensearch/opensearch-configuration/keywords.txt /usr/share/opensearch/config/keywords.txt -COPY docker-opensearch/opensearch-configuration/lexicon.txt /usr/share/opensearch/config/lexicon.txt -COPY docker-opensearch/opensearch-configuration/keywords_english.txt /usr/share/opensearch/config/keywords_english.txt -COPY docker-opensearch/opensearch-configuration/lexicon_english.txt /usr/share/opensearch/config/lexicon_english.txt - -RUN /usr/share/opensearch/bin/opensearch-plugin install --batch ingest-attachment diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 00981de1..7edf1ddd 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -1,15 +1,14 @@ -version: "3" +version: '3' services: - addon-dev: build: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.dev args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" - VOLTO_VERSION: "${VOLTO_VERSION:-17}" + ADDON_NAME: '${ADDON_NAME}' + ADDON_PATH: '${ADDON_PATH}' + VOLTO_VERSION: '${VOLTO_VERSION:-17}' volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: @@ -35,9 +34,9 @@ services: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.ci args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" - VOLTO_VERSION: "${VOLTO_VERSION:-17}" + ADDON_NAME: '${ADDON_NAME}' + ADDON_PATH: '${ADDON_PATH}' + VOLTO_VERSION: '${VOLTO_VERSION:-17}' environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: http://localhost:8080/Plone @@ -55,11 +54,11 @@ services: context: ./backend dockerfile: ./Dockerfile.dev args: - PLONE_VERSION: 6.0.10.1 + PLONE_VERSION: 6.0.11.1 environment: - ZSERVER_HOST: "0.0.0.0" - ZSERVER_PORT: "8080" - LANGUAGE: "de" + ZSERVER_HOST: '0.0.0.0' + ZSERVER_PORT: '8080' + LANGUAGE: 'de' CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} @@ -68,8 +67,8 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} SITE: Plone CORS_: true - ADDONS: "collective.elastic.plone[redis,opensearch]==2.1.0" - PROFILES: "collective.elastic.plone:default" + ADDONS: 'collective.elastic.plone[redis,opensearch]==2.1.0' + PROFILES: 'collective.elastic.plone:default' DELETE_EXISTING: False ports: - 8080:8080 @@ -79,7 +78,6 @@ services: - dev - prod - ingest: image: ghcr.io/collective/collective.elastic.ingest:latest environment: @@ -106,6 +104,7 @@ services: profiles: - dev - prod + - opensearchandingest redis: image: 'redis:latest' @@ -116,17 +115,18 @@ services: profiles: - dev - prod + - opensearchandingest opensearch: build: context: ../ - dockerfile: ./docker-opensearch/Dockerfile.opensearch + dockerfile: ./dockerfiles/opensearch/Dockerfile.opensearch environment: - cluster.name=opensearch-cluster - node.name=opensearch - discovery.type=single-node - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + - 'OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g' # - plugins.security.disabled=true - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: @@ -147,18 +147,19 @@ services: profiles: - dev - prod - + - opensearchandingest opensearch-dashboards: image: opensearchproject/opensearch-dashboards:latest ports: - 5601:5601 expose: - - "5601" + - '5601' environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: - dev + - opensearchandingest networks: - opensearch-net @@ -178,7 +179,7 @@ volumes: driver: local driver_opts: type: none - device: ../docker-opensearch/ingest-configuration + device: ../dockerfiles/opensearch/ingest-configuration o: bind networks: diff --git a/dockerfiles/opensearch/Dockerfile.opensearch b/dockerfiles/opensearch/Dockerfile.opensearch new file mode 100644 index 00000000..48ecd6f5 --- /dev/null +++ b/dockerfiles/opensearch/Dockerfile.opensearch @@ -0,0 +1,8 @@ +FROM opensearchproject/opensearch:latest + +COPY dockerfiles/opensearch/opensearch-configuration/keywords.txt /usr/share/opensearch/config/keywords.txt +COPY dockerfiles/opensearch/opensearch-configuration/lexicon.txt /usr/share/opensearch/config/lexicon.txt +COPY dockerfiles/opensearch/opensearch-configuration/keywords_english.txt /usr/share/opensearch/config/keywords_english.txt +COPY dockerfiles/opensearch/opensearch-configuration/lexicon_english.txt /usr/share/opensearch/config/lexicon_english.txt + +RUN /usr/share/opensearch/bin/opensearch-plugin install --batch ingest-attachment diff --git a/docker-opensearch/README.md b/dockerfiles/opensearch/README.md similarity index 100% rename from docker-opensearch/README.md rename to dockerfiles/opensearch/README.md diff --git a/docker-opensearch/ingest-configuration/analysis.json b/dockerfiles/opensearch/ingest-configuration/analysis.json similarity index 100% rename from docker-opensearch/ingest-configuration/analysis.json rename to dockerfiles/opensearch/ingest-configuration/analysis.json diff --git a/docker-opensearch/ingest-configuration/mappings.json b/dockerfiles/opensearch/ingest-configuration/mappings.json similarity index 100% rename from docker-opensearch/ingest-configuration/mappings.json rename to dockerfiles/opensearch/ingest-configuration/mappings.json diff --git a/docker-opensearch/ingest-configuration/preprocessings.json b/dockerfiles/opensearch/ingest-configuration/preprocessings.json similarity index 100% rename from docker-opensearch/ingest-configuration/preprocessings.json rename to dockerfiles/opensearch/ingest-configuration/preprocessings.json diff --git a/docker-opensearch/opensearch-configuration/keywords.txt b/dockerfiles/opensearch/opensearch-configuration/keywords.txt similarity index 100% rename from docker-opensearch/opensearch-configuration/keywords.txt rename to dockerfiles/opensearch/opensearch-configuration/keywords.txt diff --git a/docker-opensearch/opensearch-configuration/keywords_english.txt b/dockerfiles/opensearch/opensearch-configuration/keywords_english.txt similarity index 100% rename from docker-opensearch/opensearch-configuration/keywords_english.txt rename to dockerfiles/opensearch/opensearch-configuration/keywords_english.txt diff --git a/docker-opensearch/opensearch-configuration/lexicon.txt b/dockerfiles/opensearch/opensearch-configuration/lexicon.txt similarity index 100% rename from docker-opensearch/opensearch-configuration/lexicon.txt rename to dockerfiles/opensearch/opensearch-configuration/lexicon.txt diff --git a/docker-opensearch/opensearch-configuration/lexicon_english.txt b/dockerfiles/opensearch/opensearch-configuration/lexicon_english.txt similarity index 100% rename from docker-opensearch/opensearch-configuration/lexicon_english.txt rename to dockerfiles/opensearch/opensearch-configuration/lexicon_english.txt diff --git a/variables.mk b/variables.mk index dd1fed64..75e4bed0 100644 --- a/variables.mk +++ b/variables.mk @@ -1,5 +1,5 @@ -PLONE_VERSION=6.0.10.1 -VOLTO_VERSION=17.15.2 +PLONE_VERSION=6.0.11.1 +VOLTO_VERSION=17.16.0 ADDON_NAME='@rohberg/volto-searchkit-block' ADDON_PATH='volto-searchkit-block' From 95bc263c4524ab8e71536a315e6b6d9857bd78a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:10:49 +0200 Subject: [PATCH 068/226] Create dockerfiles/_env --- dockerfiles/_env | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 dockerfiles/_env diff --git a/dockerfiles/_env b/dockerfiles/_env new file mode 100644 index 00000000..ab727a96 --- /dev/null +++ b/dockerfiles/_env @@ -0,0 +1,17 @@ +export INDEX_SERVER=opensearch:9200 +export INDEX_OPENSEARCH=1 +export INDEX_USE_SSL=1 +export INDEX_LOGIN=admin +export INDEX_PASSWORD=mypswdappknowsmyindexserverpswd + +export CELERY_BROKER=redis://redis:6379/0 +export CELERY_LOG_LEVEL=debug + +export PLONE_SERVICE=http://backend:8080 +export PLONE_SITE_PREFIX_PATH=Plone +export PLONE_USER=admin +export PLONE_PASSWORD=mypswdappknowsmyplonepswd + +export MAPPINGS_FILE=./configuration/mappings.json +export ANALYSIS_FILE=./configuration/analysis.json +export PREPROCESSINGS_FILE=./configuration/preprocessings.json From 1783b22d1ee8873a55c4f9f42ad5c4e3f47203d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:11:46 +0200 Subject: [PATCH 069/226] Update README.md --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5e1372a6..f32a8302 100644 --- a/README.md +++ b/README.md @@ -18,29 +18,34 @@ The block is prepared for Matomo analytics. ![Search @rohberg/volto-searchkit-block](public/search.png) - # Demo You can try the search by checking out this repository and run - make dev-opensearch make dev - Docker should be installed and running. - -# Getting started +# TODO Getting started Install Plone backend add-on [`collective.elastic.plone 2.x`](https://github.com/collective/collective.elastic.plone) to provide the Plone REST API service which accepts queries and requests OpenSearch/ElasticSearch. Install Plone backend add-on [`collective.elastic.ingest 2.x`](https://github.com/collective/collective.elastic.ingest) to index Plone content. -Setting up OpenSearch/ElasticSearch instructions can be found on [`collective.elastic.plone 2.x`](https://github.com/collective/collective.elastic.plone). -See the [example](dockerfiles/opensearch) configuration of collective.elastic of a mapping, attachment handling and last but not least analysis. +Setting up OpenSearch/ElasticSearch: + +Create file `volto-searchkit-block/dockerfiles/.env` like the provided template in this folder. + +Build and start containers for OpenSearch/ElasticSearch and friends: + + make opensearchandingest-build + make opensearchandingest-up + +Create file `backend/.env` like the one already mentioned above if you do not run backend with Docker. -# Configuration + +# Configuration of the search parameters The block is not for editors. So please enable adding a searchkit block once by @@ -50,7 +55,7 @@ config.blocks.blocksConfig.searchkitblock.restricted = true; and disable the block after adding it to a page of your choice. -The block can be configured by +The block can be configured by - searchable fields with boosting - facets @@ -59,11 +64,10 @@ The block can be configured by ![Configuration](public/configuration.png) - Enable Matomo tracking via ```js - config.settings.searchkitblock.trackVoltoMatomo = true +config.settings.searchkitblock.trackVoltoMatomo = true; ``` # Overriding components @@ -75,9 +79,7 @@ const MySearchkitResultsListItem = ({ result, index }) => { return (
- - {result.title} - + {result.title}
); @@ -86,8 +88,7 @@ const MySearchkitResultsListItem = ({ result, index }) => { config.settings.searchkitblock.overriddenComponents = { 'ResultsList.item.elasticsearch': MySearchkitResultsListItem, }; -```` - +``` # Panel for testing matches @@ -95,10 +96,9 @@ config.settings.searchkitblock.overriddenComponents = { Please update the settings according to your deployment: `/controlpanel/volto_searchkit_block_control_panel` - # User documentation -The search is a fuzzy search, that means typos are compensated. +The search is a fuzzy search, that means typos are compensated. Approximate matches and inflections are found. To force the match of a search string, precede it with "+". @@ -118,11 +118,11 @@ Example: "LSR-Lehrbetrieb" is found by a search for "LSR". Search results do include at least one of the search strings. - # Credits This package is a Plone Volto integration of react-searchkit https://www.npmjs.com/package/react-searchkit Copyright (C) 2015-2019 CERN. +The development of this plugin has been kindly sponsored by IGIB GRIF. # Copyright and license From cdb55d01f01b928e0de0db6b3e5da5ccec6231f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:17:57 +0200 Subject: [PATCH 070/226] Update ci.yml --- acceptance/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 307ebc5b..5baadb35 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -97,9 +97,7 @@ services: - multilingual ingest: - build: - context: ../ - dockerfile: .//Dockerfile.ingest + image: ghcr.io/collective/collective.elastic.ingest:latest environment: MAPPINGS_FILE: "/configuration/mappings.json" ANALYSIS_FILE: "/configuration/analysis.json" From 461dc64d28d5a60880d2e751128f21a3bc1ddf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:22:06 +0200 Subject: [PATCH 071/226] Update ci.yml --- acceptance/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 5baadb35..c74bf53d 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -116,9 +116,7 @@ services: PLONE_PASSWORD: ${PLONE_PASSWORD?unset} ingest-multilingual: - build: - context: ../ - dockerfile: .//Dockerfile.ingest + image: ghcr.io/collective/collective.elastic.ingest:latest environment: # Different INDEX_NAME MAPPINGS_FILE: "/configuration/mappings.json" @@ -146,7 +144,7 @@ services: opensearch: build: context: ../ - dockerfile: .//Dockerfile.opensearch + dockerfile: ./dockerfiles/opensearch/Dockerfile.opensearch environment: - plugins.security.disabled=true - cluster.name=opensearch-cluster From 5f3005387ece38a63b2f67da117438238683905d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:38:47 +0200 Subject: [PATCH 072/226] naming: monolingual --- .github/workflows/acceptance.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 45a2a6a4..22dac341 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -2,7 +2,7 @@ name: Acceptance tests on: [push] env: - ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block VOLTO_VERSION: 17 PLONE_VERSION: 6.0 @@ -11,7 +11,7 @@ env: INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: "oxczBG).3xWyapLn" + INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 CELERY_LOG_LEVEL: info @@ -43,7 +43,7 @@ jobs: cd acceptance yarn - - name: "Cypress: Acceptance tests - multilingual" + - name: 'Cypress: Acceptance tests - multilingual' uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -58,7 +58,7 @@ jobs: docker compose -f ci.yml --profile multilingual up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' - - name: "Cypress: Acceptance tests - single language" + - name: 'Cypress: Acceptance tests - monolingual' uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -73,7 +73,6 @@ jobs: docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' - # Upload Cypress screenshots - uses: actions/upload-artifact@v4 if: failure() From d64c2b72bf1ddd6043bb9f1f7c31f99dc0721bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 14:39:22 +0200 Subject: [PATCH 073/226] ci tests: first monolingual, then multilingual --- .github/workflows/acceptance.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 22dac341..73bc2d08 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -43,7 +43,7 @@ jobs: cd acceptance yarn - - name: 'Cypress: Acceptance tests - multilingual' + - name: 'Cypress: Acceptance tests - monolingual' uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -52,13 +52,13 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.multilingual.cy.js + spec: cypress/tests/*.monolingual.cy.js install: false start: | - docker compose -f ci.yml --profile multilingual up + docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' - - name: 'Cypress: Acceptance tests - monolingual' + - name: 'Cypress: Acceptance tests - multilingual' uses: cypress-io/github-action@v6 env: BABEL_ENV: production @@ -67,10 +67,10 @@ jobs: parallel: false browser: chrome working-directory: acceptance - spec: cypress/tests/*.monolingual.cy.js + spec: cypress/tests/*.multilingual.cy.js install: false start: | - docker compose -f ci.yml --profile prod up + docker compose -f ci.yml --profile multilingual up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' # Upload Cypress screenshots From 903aa8e4699ab18c55a411861a438d9f1be1b577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 15:52:23 +0200 Subject: [PATCH 074/226] More TODOs --- src/components/Views/FacetedSearch.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 7a157a9b..77bafe9d 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -701,7 +701,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { delete facet_fields_object.Subject; } - // TODO Get config from data + // TODO Get config from blocks data const initialState = { page: 1, queryString: '', From 5b6e97e7405e258946aec600457f11a67ddd88fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 16:02:41 +0200 Subject: [PATCH 075/226] default for new fields --- src/components/Blocks/schema.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 2f90f0b8..60c41258 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -187,6 +187,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { items: { vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, }, + default: [], }, showEventStartDate: { title: 'Show start date of events', @@ -195,6 +196,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { items: { vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, }, + default: [], }, relocation: { title: 'Relocation', From 75e91be57f5d9f1bde740b72837f22cc454ae147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 16:03:08 +0200 Subject: [PATCH 076/226] Fix ci: either monolingual ingest or multilingual --- acceptance/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index c74bf53d..852e65ac 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -114,6 +114,8 @@ services: PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + profiles: + - prod ingest-multilingual: image: ghcr.io/collective/collective.elastic.ingest:latest @@ -135,6 +137,8 @@ services: PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + profiles: + - multilingual redis: image: 'redis:latest' From 61a95e8f354855e380944b365334cf48a080b474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 16:03:13 +0200 Subject: [PATCH 077/226] Update yarn.lock --- acceptance/yarn.lock | 4651 ++++++++++++++++++------------------------ 1 file changed, 2013 insertions(+), 2638 deletions(-) diff --git a/acceptance/yarn.lock b/acceptance/yarn.lock index e13d2606..da3d1445 100644 --- a/acceptance/yarn.lock +++ b/acceptance/yarn.lock @@ -1,2638 +1,2013 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 6 - cacheKey: 8 - -"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13": - version: 7.18.6 - resolution: "@babel/code-frame@npm:7.18.6" - dependencies: - "@babel/highlight": ^7.18.6 - checksum: 195e2be3172d7684bf95cff69ae3b7a15a9841ea9d27d3c843662d50cdd7d6470fd9c8e64be84d031117e4a4083486effba39f9aef6bbb2c89f7f21bcfba33ba - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.18.6": - version: 7.19.1 - resolution: "@babel/helper-validator-identifier@npm:7.19.1" - checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" - dependencies: - "@babel/helper-validator-identifier": ^7.18.6 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.9.2": - version: 7.20.13 - resolution: "@babel/runtime@npm:7.20.13" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 09b7a97a05c80540db6c9e4ddf8c5d2ebb06cae5caf3a87e33c33f27f8c4d49d9c67a2d72f1570e796045288fad569f98a26ceba0c4f5fad2af84b6ad855c4fb - languageName: node - linkType: hard - -"@colors/colors@npm:1.5.0": - version: 1.5.0 - resolution: "@colors/colors@npm:1.5.0" - checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 - languageName: node - linkType: hard - -"@cypress/request@npm:^3.0.0": - version: 3.0.1 - resolution: "@cypress/request@npm:3.0.1" - dependencies: - aws-sign2: ~0.7.0 - aws4: ^1.8.0 - caseless: ~0.12.0 - combined-stream: ~1.0.6 - extend: ~3.0.2 - forever-agent: ~0.6.1 - form-data: ~2.3.2 - http-signature: ~1.3.6 - is-typedarray: ~1.0.0 - isstream: ~0.1.2 - json-stringify-safe: ~5.0.1 - mime-types: ~2.1.19 - performance-now: ^2.1.0 - qs: 6.10.4 - safe-buffer: ^5.1.2 - tough-cookie: ^4.1.3 - tunnel-agent: ^0.6.0 - uuid: ^8.3.2 - checksum: 7175522ebdbe30e3c37973e204c437c23ce659e58d5939466615bddcd58d778f3a8ea40f087b965ae8b8138ea8d102b729c6eb18c6324f121f3778f4a2e8e727 - languageName: node - linkType: hard - -"@cypress/xvfb@npm:^1.2.4": - version: 1.2.4 - resolution: "@cypress/xvfb@npm:1.2.4" - dependencies: - debug: ^3.1.0 - lodash.once: ^4.1.1 - checksum: 7bdcdaeb1bb692ec9d9bf8ec52538aa0bead6764753f4a067a171a511807a43fab016f7285a56bef6a606c2467ff3f1365e1ad2d2d583b81beed849ee1573fd1 - languageName: node - linkType: hard - -"@jest/expect-utils@npm:^29.4.2": - version: 29.4.2 - resolution: "@jest/expect-utils@npm:29.4.2" - dependencies: - jest-get-type: ^29.4.2 - checksum: 5d23a09a4f85f0cb8da3bac3a6118efe4365dce6923702bc7f1a0edf36699c5c91d9c77e95349e71e305d36ae35d4e06086c923bf9635ccf3fffda05cc7cc2e8 - languageName: node - linkType: hard - -"@jest/schemas@npm:^29.4.2": - version: 29.4.2 - resolution: "@jest/schemas@npm:29.4.2" - dependencies: - "@sinclair/typebox": ^0.25.16 - checksum: 85d9416dce85604400e65ba0b2146fea5ba313612d6d1fa8f39c30bcb42fabd7120193d277327fb10228ea3112f3b83e914bc7ab42137d19a1e1c37093f32363 - languageName: node - linkType: hard - -"@jest/types@npm:^29.4.2": - version: 29.4.2 - resolution: "@jest/types@npm:29.4.2" - dependencies: - "@jest/schemas": ^29.4.2 - "@types/istanbul-lib-coverage": ^2.0.0 - "@types/istanbul-reports": ^3.0.0 - "@types/node": "*" - "@types/yargs": ^17.0.8 - chalk: ^4.0.0 - checksum: da2caa2c1d3ce7461167faddf9a4158a4be5c900e44f22db9c370b189c804b7492051c635a8c0c62ac4e41aff3bc6c324a008043e193bc5d6ce7b44aaa449258 - languageName: node - linkType: hard - -"@plone/volto-testing@npm:^4.0.0": - version: 4.0.0 - resolution: "@plone/volto-testing@npm:4.0.0" - dependencies: - "@testing-library/cypress": 9.0.0 - "@testing-library/jest-dom": 5.16.4 - "@testing-library/react": 12.1.5 - axe-core: 4.6.3 - cypress: 13.1.0 - cypress-axe: 1.5.0 - cypress-file-upload: 5.0.8 - checksum: ce4f3149177e3314cc6cff7cfad03637dde512cc5f4a689fb43e88f56b2aadc93c9b8283092a9b40139d4846e0f741f20a080405bfde7706873067219fec1a4d - languageName: node - linkType: hard - -"@sinclair/typebox@npm:^0.25.16": - version: 0.25.21 - resolution: "@sinclair/typebox@npm:0.25.21" - checksum: 763af1163fe4eabee9b914d4e4548a39fbba3287d2b3b1ff043c1da3c5a321e99d50a3ca94eb182988131e00b006a6f019799cde8da2f61e2f118b30b0276a00 - languageName: node - linkType: hard - -"@testing-library/cypress@npm:9.0.0": - version: 9.0.0 - resolution: "@testing-library/cypress@npm:9.0.0" - dependencies: - "@babel/runtime": ^7.14.6 - "@testing-library/dom": ^8.1.0 - peerDependencies: - cypress: ^12.0.0 - checksum: fbd24e8f0b8a60279b336de5f6bc0e7ad6fb31316eacab5128dacc7fccde1eb40935b90f2c3bddc7d814115fe3965c6dbf011785448cd15b5a5b0bc40ef5bb4c - languageName: node - linkType: hard - -"@testing-library/dom@npm:^8.0.0, @testing-library/dom@npm:^8.1.0": - version: 8.20.0 - resolution: "@testing-library/dom@npm:8.20.0" - dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/runtime": ^7.12.5 - "@types/aria-query": ^5.0.1 - aria-query: ^5.0.0 - chalk: ^4.1.0 - dom-accessibility-api: ^0.5.9 - lz-string: ^1.4.4 - pretty-format: ^27.0.2 - checksum: 1e599129a2fe91959ce80900a0a4897232b89e2a8e22c1f5950c36d39c97629ea86b4986b60b173b5525a05de33fde1e35836ea597b03de78cc51b122835c6f0 - languageName: node - linkType: hard - -"@testing-library/jest-dom@npm:5.16.4": - version: 5.16.4 - resolution: "@testing-library/jest-dom@npm:5.16.4" - dependencies: - "@babel/runtime": ^7.9.2 - "@types/testing-library__jest-dom": ^5.9.1 - aria-query: ^5.0.0 - chalk: ^3.0.0 - css: ^3.0.0 - css.escape: ^1.5.1 - dom-accessibility-api: ^0.5.6 - lodash: ^4.17.15 - redent: ^3.0.0 - checksum: 4240501223b72b97a44d4e3c669f39b208c49fb645d11d08d5f178d607265c5dfad07efbe027f41a0e2458178ff1fd5bf437fc05661b9109dcd013b95a37079e - languageName: node - linkType: hard - -"@testing-library/react@npm:12.1.5": - version: 12.1.5 - resolution: "@testing-library/react@npm:12.1.5" - dependencies: - "@babel/runtime": ^7.12.5 - "@testing-library/dom": ^8.0.0 - "@types/react-dom": <18.0.0 - peerDependencies: - react: <18.0.0 - react-dom: <18.0.0 - checksum: 4abd0490405e709a7df584a0db604e508a4612398bb1326e8fa32dd9393b15badc826dcf6d2f7525437886d507871f719f127b9860ed69ddd204d1fa834f576a - languageName: node - linkType: hard - -"@types/aria-query@npm:^5.0.1": - version: 5.0.1 - resolution: "@types/aria-query@npm:5.0.1" - checksum: 69fd7cceb6113ed370591aef04b3fd0742e9a1b06dd045c43531448847b85de181495e4566f98e776b37c422a12fd71866e0a1dfd904c5ec3f84d271682901de - languageName: node - linkType: hard - -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0": - version: 2.0.4 - resolution: "@types/istanbul-lib-coverage@npm:2.0.4" - checksum: a25d7589ee65c94d31464c16b72a9dc81dfa0bea9d3e105ae03882d616e2a0712a9c101a599ec482d297c3591e16336962878cb3eb1a0a62d5b76d277a890ce7 - languageName: node - linkType: hard - -"@types/istanbul-lib-report@npm:*": - version: 3.0.0 - resolution: "@types/istanbul-lib-report@npm:3.0.0" - dependencies: - "@types/istanbul-lib-coverage": "*" - checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 - languageName: node - linkType: hard - -"@types/istanbul-reports@npm:^3.0.0": - version: 3.0.1 - resolution: "@types/istanbul-reports@npm:3.0.1" - dependencies: - "@types/istanbul-lib-report": "*" - checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 - languageName: node - linkType: hard - -"@types/jest@npm:*": - version: 29.4.0 - resolution: "@types/jest@npm:29.4.0" - dependencies: - expect: ^29.0.0 - pretty-format: ^29.0.0 - checksum: 23760282362a252e6690314584d83a47512d4cd61663e957ed3398ecf98195fe931c45606ee2f9def12f8ed7d8aa102d492ec42d26facdaf8b78094a31e6568e - languageName: node - linkType: hard - -"@types/node@npm:*": - version: 18.13.0 - resolution: "@types/node@npm:18.13.0" - checksum: 4ea10f8802848b01672bce938f678b6774ca2cee0c9774f12275ab064ae07818419c3e2e41d6257ce7ba846d1ea26c63214aa1dfa4166fa3746291752b8c6416 - languageName: node - linkType: hard - -"@types/node@npm:^16.18.39": - version: 16.18.86 - resolution: "@types/node@npm:16.18.86" - checksum: 12d70d3a51c9a95c401532dabb1014ccb06f638283cb46f72886a8939baf20f62d2dcd6292743be61dca2808e837801e610b511bcbf1282e9d4033751002d870 - languageName: node - linkType: hard - -"@types/prop-types@npm:*": - version: 15.7.5 - resolution: "@types/prop-types@npm:15.7.5" - checksum: 5b43b8b15415e1f298243165f1d44390403bb2bd42e662bca3b5b5633fdd39c938e91b7fce3a9483699db0f7a715d08cef220c121f723a634972fdf596aec980 - languageName: node - linkType: hard - -"@types/react-dom@npm:<18.0.0": - version: 17.0.18 - resolution: "@types/react-dom@npm:17.0.18" - dependencies: - "@types/react": ^17 - checksum: b74525b1a13a0e27fe20859ff7a7e8f7e4581fb9d45ed1b6447ad1534d86f813818353c39d0df2e28f9d2b9be2e3af1908c244b2214a979393d19f217665e614 - languageName: node - linkType: hard - -"@types/react@npm:^17": - version: 17.0.53 - resolution: "@types/react@npm:17.0.53" - dependencies: - "@types/prop-types": "*" - "@types/scheduler": "*" - csstype: ^3.0.2 - checksum: dacfde02c260fd98bed2eb775ed0c7ce1397be4c0844f907a50763b081a4008f81f57071889a16eb1350ddcf0927f3cf1a6541702c8ad03de3c70383ef931e3f - languageName: node - linkType: hard - -"@types/scheduler@npm:*": - version: 0.16.2 - resolution: "@types/scheduler@npm:0.16.2" - checksum: b6b4dcfeae6deba2e06a70941860fb1435730576d3689225a421280b7742318d1548b3d22c1f66ab68e414f346a9542f29240bc955b6332c5b11e561077583bc - languageName: node - linkType: hard - -"@types/sinonjs__fake-timers@npm:8.1.1": - version: 8.1.1 - resolution: "@types/sinonjs__fake-timers@npm:8.1.1" - checksum: ca09d54d47091d87020824a73f026300fa06b17cd9f2f9b9387f28b549364b141ef194ee28db762f6588de71d8febcd17f753163cb7ea116b8387c18e80ebd5c - languageName: node - linkType: hard - -"@types/sizzle@npm:^2.3.2": - version: 2.3.3 - resolution: "@types/sizzle@npm:2.3.3" - checksum: 586a9fb1f6ff3e325e0f2cc1596a460615f0bc8a28f6e276ac9b509401039dd242fa8b34496d3a30c52f5b495873922d09a9e76c50c2ab2bcc70ba3fb9c4e160 - languageName: node - linkType: hard - -"@types/stack-utils@npm:^2.0.0": - version: 2.0.1 - resolution: "@types/stack-utils@npm:2.0.1" - checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 - languageName: node - linkType: hard - -"@types/testing-library__jest-dom@npm:^5.9.1": - version: 5.14.5 - resolution: "@types/testing-library__jest-dom@npm:5.14.5" - dependencies: - "@types/jest": "*" - checksum: dcb05416758fe88c1f4f3aa97b4699fcb46a5ed8f53c6b81721e66155452a48caf12ecb97dfdfd4130678e65efd66b9fca0ac434b3d63affec84842a84a6bf38 - languageName: node - linkType: hard - -"@types/yargs-parser@npm:*": - version: 21.0.0 - resolution: "@types/yargs-parser@npm:21.0.0" - checksum: b2f4c8d12ac18a567440379909127cf2cec393daffb73f246d0a25df36ea983b93b7e9e824251f959e9f928cbc7c1aab6728d0a0ff15d6145f66cec2be67d9a2 - languageName: node - linkType: hard - -"@types/yargs@npm:^17.0.8": - version: 17.0.22 - resolution: "@types/yargs@npm:17.0.22" - dependencies: - "@types/yargs-parser": "*" - checksum: 0773523fda71bafdc52f13f5970039e535a353665a60ba9261149a5c9c2b908242e6e77fbb7a8c06931ec78ce889d64d09673c68ba23eb5f5742d5385d0d1982 - languageName: node - linkType: hard - -"@types/yauzl@npm:^2.9.1": - version: 2.10.0 - resolution: "@types/yauzl@npm:2.10.0" - dependencies: - "@types/node": "*" - checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 - languageName: node - linkType: hard - -"aggregate-error@npm:^3.0.0": - version: 3.1.0 - resolution: "aggregate-error@npm:3.1.0" - dependencies: - clean-stack: ^2.0.0 - indent-string: ^4.0.0 - checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 - languageName: node - linkType: hard - -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - -"ansi-escapes@npm:^4.3.0": - version: 4.3.2 - resolution: "ansi-escapes@npm:4.3.2" - dependencies: - type-fest: ^0.21.3 - checksum: 93111c42189c0a6bed9cdb4d7f2829548e943827ee8479c74d6e0b22ee127b2a21d3f8b5ca57723b8ef78ce011fbfc2784350eb2bde3ccfccf2f575fa8489815 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b - languageName: node - linkType: hard - -"ansi-styles@npm:^3.2.1": - version: 3.2.1 - resolution: "ansi-styles@npm:3.2.1" - dependencies: - color-convert: ^1.9.0 - checksum: d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 - languageName: node - linkType: hard - -"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: ^2.0.1 - checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 - languageName: node - linkType: hard - -"ansi-styles@npm:^5.0.0": - version: 5.2.0 - resolution: "ansi-styles@npm:5.2.0" - checksum: d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 - languageName: node - linkType: hard - -"arch@npm:^2.2.0": - version: 2.2.0 - resolution: "arch@npm:2.2.0" - checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f - languageName: node - linkType: hard - -"aria-query@npm:^5.0.0": - version: 5.1.3 - resolution: "aria-query@npm:5.1.3" - dependencies: - deep-equal: ^2.0.5 - checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b - languageName: node - linkType: hard - -"asn1@npm:~0.2.3": - version: 0.2.6 - resolution: "asn1@npm:0.2.6" - dependencies: - safer-buffer: ~2.1.0 - checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d - languageName: node - linkType: hard - -"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": - version: 1.0.0 - resolution: "assert-plus@npm:1.0.0" - checksum: 19b4340cb8f0e6a981c07225eacac0e9d52c2644c080198765d63398f0075f83bbc0c8e95474d54224e297555ad0d631c1dcd058adb1ddc2437b41a6b424ac64 - languageName: node - linkType: hard - -"astral-regex@npm:^2.0.0": - version: 2.0.0 - resolution: "astral-regex@npm:2.0.0" - checksum: 876231688c66400473ba505731df37ea436e574dd524520294cc3bbc54ea40334865e01fa0d074d74d036ee874ee7e62f486ea38bc421ee8e6a871c06f011766 - languageName: node - linkType: hard - -"async@npm:^3.2.0": - version: 3.2.4 - resolution: "async@npm:3.2.4" - checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 - languageName: node - linkType: hard - -"asynckit@npm:^0.4.0": - version: 0.4.0 - resolution: "asynckit@npm:0.4.0" - checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be - languageName: node - linkType: hard - -"at-least-node@npm:^1.0.0": - version: 1.0.0 - resolution: "at-least-node@npm:1.0.0" - checksum: 463e2f8e43384f1afb54bc68485c436d7622acec08b6fad269b421cb1d29cebb5af751426793d0961ed243146fe4dc983402f6d5a51b720b277818dbf6f2e49e - languageName: node - linkType: hard - -"atob@npm:^2.1.2": - version: 2.1.2 - resolution: "atob@npm:2.1.2" - bin: - atob: bin/atob.js - checksum: dfeeeb70090c5ebea7be4b9f787f866686c645d9f39a0d184c817252d0cf08455ed25267d79c03254d3be1f03ac399992a792edcd5ffb9c91e097ab5ef42833a - languageName: node - linkType: hard - -"available-typed-arrays@npm:^1.0.5": - version: 1.0.5 - resolution: "available-typed-arrays@npm:1.0.5" - checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a - languageName: node - linkType: hard - -"aws-sign2@npm:~0.7.0": - version: 0.7.0 - resolution: "aws-sign2@npm:0.7.0" - checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525 - languageName: node - linkType: hard - -"aws4@npm:^1.8.0": - version: 1.12.0 - resolution: "aws4@npm:1.12.0" - checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc - languageName: node - linkType: hard - -"axe-core@npm:4.6.3": - version: 4.6.3 - resolution: "axe-core@npm:4.6.3" - checksum: d0c46be92b9707c48b88a53cd5f471b155a2bfc8bf6beffb514ecd14e30b4863e340b5fc4f496d82a3c562048088c1f3ff5b93b9b3b026cb9c3bfacfd535da10 - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 - languageName: node - linkType: hard - -"base64-js@npm:^1.3.1": - version: 1.5.1 - resolution: "base64-js@npm:1.5.1" - checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 - languageName: node - linkType: hard - -"bcrypt-pbkdf@npm:^1.0.0": - version: 1.0.2 - resolution: "bcrypt-pbkdf@npm:1.0.2" - dependencies: - tweetnacl: ^0.14.3 - checksum: 4edfc9fe7d07019609ccf797a2af28351736e9d012c8402a07120c4453a3b789a15f2ee1530dc49eee8f7eb9379331a8dd4b3766042b9e502f74a68e7f662291 - languageName: node - linkType: hard - -"blob-util@npm:^2.0.2": - version: 2.0.2 - resolution: "blob-util@npm:2.0.2" - checksum: d543e6b92e4ca715ca33c78e89a07a2290d43e5b2bc897d7ec588c5c7bbf59df93e45225ac0c9258aa6ce4320358990f99c9288f1c48280f8ec5d7a2e088d19b - languageName: node - linkType: hard - -"bluebird@npm:^3.7.2": - version: 3.7.2 - resolution: "bluebird@npm:3.7.2" - checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef - languageName: node - linkType: hard - -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: ^1.0.0 - concat-map: 0.0.1 - checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 - languageName: node - linkType: hard - -"braces@npm:^3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" - dependencies: - fill-range: ^7.0.1 - checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459 - languageName: node - linkType: hard - -"buffer-crc32@npm:~0.2.3": - version: 0.2.13 - resolution: "buffer-crc32@npm:0.2.13" - checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c - languageName: node - linkType: hard - -"buffer@npm:^5.6.0": - version: 5.7.1 - resolution: "buffer@npm:5.7.1" - dependencies: - base64-js: ^1.3.1 - ieee754: ^1.1.13 - checksum: e2cf8429e1c4c7b8cbd30834ac09bd61da46ce35f5c22a78e6c2f04497d6d25541b16881e30a019c6fd3154150650ccee27a308eff3e26229d788bbdeb08ab84 - languageName: node - linkType: hard - -"cachedir@npm:^2.3.0": - version: 2.3.0 - resolution: "cachedir@npm:2.3.0" - checksum: ec90cb0f2e6336e266aa748dbadf3da9e0b20e843e43f1591acab7a3f1451337dc2f26cb9dd833ae8cfefeffeeb43ef5b5ff62782a685f4e3c2305dd98482fcb - languageName: node - linkType: hard - -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" - dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 - languageName: node - linkType: hard - -"caseless@npm:~0.12.0": - version: 0.12.0 - resolution: "caseless@npm:0.12.0" - checksum: b43bd4c440aa1e8ee6baefee8063b4850fd0d7b378f6aabc796c9ec8cb26d27fb30b46885350777d9bd079c5256c0e1329ad0dc7c2817e0bb466810ebb353751 - languageName: node - linkType: hard - -"chalk@npm:^2.0.0": - version: 2.4.2 - resolution: "chalk@npm:2.4.2" - dependencies: - ansi-styles: ^3.2.1 - escape-string-regexp: ^1.0.5 - supports-color: ^5.3.0 - checksum: ec3661d38fe77f681200f878edbd9448821924e0f93a9cefc0e26a33b145f1027a2084bf19967160d11e1f03bfe4eaffcabf5493b89098b2782c3fe0b03d80c2 - languageName: node - linkType: hard - -"chalk@npm:^3.0.0": - version: 3.0.0 - resolution: "chalk@npm:3.0.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: 8e3ddf3981c4da405ddbd7d9c8d91944ddf6e33d6837756979f7840a29272a69a5189ecae0ff84006750d6d1e92368d413335eab4db5476db6e6703a1d1e0505 - languageName: node - linkType: hard - -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc - languageName: node - linkType: hard - -"check-more-types@npm:^2.24.0": - version: 2.24.0 - resolution: "check-more-types@npm:2.24.0" - checksum: b09080ec3404d20a4b0ead828994b2e5913236ef44ed3033a27062af0004cf7d2091fbde4b396bf13b7ce02fb018bc9960b48305e6ab2304cd82d73ed7a51ef4 - languageName: node - linkType: hard - -"ci-info@npm:^3.2.0": - version: 3.7.1 - resolution: "ci-info@npm:3.7.1" - checksum: 72d93d5101ea1c186511277fbd8d06ae8a6e028cc2fb94361e92bf735b39c5ebd192e8d15a66ff8c4e3ed569f87c2f844e96f90e141b2de5c649f77ec34ff601 - languageName: node - linkType: hard - -"clean-stack@npm:^2.0.0": - version: 2.2.0 - resolution: "clean-stack@npm:2.2.0" - checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 - languageName: node - linkType: hard - -"cli-cursor@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-cursor@npm:3.1.0" - dependencies: - restore-cursor: ^3.1.0 - checksum: 2692784c6cd2fd85cfdbd11f53aea73a463a6d64a77c3e098b2b4697a20443f430c220629e1ca3b195ea5ac4a97a74c2ee411f3807abf6df2b66211fec0c0a29 - languageName: node - linkType: hard - -"cli-table3@npm:~0.6.1": - version: 0.6.3 - resolution: "cli-table3@npm:0.6.3" - dependencies: - "@colors/colors": 1.5.0 - string-width: ^4.2.0 - dependenciesMeta: - "@colors/colors": - optional: true - checksum: 09897f68467973f827c04e7eaadf13b55f8aec49ecd6647cc276386ea660059322e2dd8020a8b6b84d422dbdd619597046fa89cbbbdc95b2cea149a2df7c096c - languageName: node - linkType: hard - -"cli-truncate@npm:^2.1.0": - version: 2.1.0 - resolution: "cli-truncate@npm:2.1.0" - dependencies: - slice-ansi: ^3.0.0 - string-width: ^4.2.0 - checksum: bf1e4e6195392dc718bf9cd71f317b6300dc4a9191d052f31046b8773230ece4fa09458813bf0e3455a5e68c0690d2ea2c197d14a8b85a7b5e01c97f4b5feb5d - languageName: node - linkType: hard - -"color-convert@npm:^1.9.0": - version: 1.9.3 - resolution: "color-convert@npm:1.9.3" - dependencies: - color-name: 1.1.3 - checksum: fd7a64a17cde98fb923b1dd05c5f2e6f7aefda1b60d67e8d449f9328b4e53b228a428fd38bfeaeb2db2ff6b6503a776a996150b80cdf224062af08a5c8a3a203 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: ~1.1.4 - checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 - languageName: node - linkType: hard - -"color-name@npm:1.1.3": - version: 1.1.3 - resolution: "color-name@npm:1.1.3" - checksum: 09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 - languageName: node - linkType: hard - -"colorette@npm:^2.0.16": - version: 2.0.19 - resolution: "colorette@npm:2.0.19" - checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 - languageName: node - linkType: hard - -"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": - version: 1.0.8 - resolution: "combined-stream@npm:1.0.8" - dependencies: - delayed-stream: ~1.0.0 - checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c - languageName: node - linkType: hard - -"commander@npm:^6.2.1": - version: 6.2.1 - resolution: "commander@npm:6.2.1" - checksum: d7090410c0de6bc5c67d3ca41c41760d6d268f3c799e530aafb73b7437d1826bbf0d2a3edac33f8b57cc9887b4a986dce307fa5557e109be40eadb7c43b21742 - languageName: node - linkType: hard - -"common-tags@npm:^1.8.0": - version: 1.8.2 - resolution: "common-tags@npm:1.8.2" - checksum: 767a6255a84bbc47df49a60ab583053bb29a7d9687066a18500a516188a062c4e4cd52de341f22de0b07062e699b1b8fe3cfa1cb55b241cb9301aeb4f45b4dff - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af - languageName: node - linkType: hard - -"core-util-is@npm:1.0.2": - version: 1.0.2 - resolution: "core-util-is@npm:1.0.2" - checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.0": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" - dependencies: - path-key: ^3.1.0 - shebang-command: ^2.0.0 - which: ^2.0.1 - checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52 - languageName: node - linkType: hard - -"css.escape@npm:^1.5.1": - version: 1.5.1 - resolution: "css.escape@npm:1.5.1" - checksum: f6d38088d870a961794a2580b2b2af1027731bb43261cfdce14f19238a88664b351cc8978abc20f06cc6bbde725699dec8deb6fe9816b139fc3f2af28719e774 - languageName: node - linkType: hard - -"css@npm:^3.0.0": - version: 3.0.0 - resolution: "css@npm:3.0.0" - dependencies: - inherits: ^2.0.4 - source-map: ^0.6.1 - source-map-resolve: ^0.6.0 - checksum: 4273ac816ddf99b99acb9c1d1a27d86d266a533cc01118369d941d8e8a78277a83cad3315e267a398c509d930fbb86504e193ea1ebc620a4a4212e06fe76e8be - languageName: node - linkType: hard - -"csstype@npm:^3.0.2": - version: 3.1.1 - resolution: "csstype@npm:3.1.1" - checksum: 1f7b4f5fdd955b7444b18ebdddf3f5c699159f13e9cf8ac9027ae4a60ae226aef9bbb14a6e12ca7dba3358b007cee6354b116e720262867c398de6c955ea451d - languageName: node - linkType: hard - -"cypress-axe@npm:1.5.0": - version: 1.5.0 - resolution: "cypress-axe@npm:1.5.0" - peerDependencies: - axe-core: ^3 || ^4 - cypress: ^10 || ^11 || ^12 || ^13 - checksum: 7b5574da4ea2c1a1141f79cb4465a4f30879e30b9ac08da4aca935dfc7662d3fa170254be8c4eff26841bc065e6115bf6e6b8a8eee5ff7cb9812bbcbeb863ff2 - languageName: node - linkType: hard - -"cypress-file-upload@npm:5.0.8": - version: 5.0.8 - resolution: "cypress-file-upload@npm:5.0.8" - peerDependencies: - cypress: ">3.0.0" - checksum: 9c70ca7e0bb137d0ec0b8d38987219ce15b26ac3a40e3ed4e78e6ad4690392eab905586848eec6ad8edd42ee480e68ccc63007b2ebd0a02f4b3eca116ff017e3 - languageName: node - linkType: hard - -"cypress@npm:13.1.0": - version: 13.1.0 - resolution: "cypress@npm:13.1.0" - dependencies: - "@cypress/request": ^3.0.0 - "@cypress/xvfb": ^1.2.4 - "@types/node": ^16.18.39 - "@types/sinonjs__fake-timers": 8.1.1 - "@types/sizzle": ^2.3.2 - arch: ^2.2.0 - blob-util: ^2.0.2 - bluebird: ^3.7.2 - buffer: ^5.6.0 - cachedir: ^2.3.0 - chalk: ^4.1.0 - check-more-types: ^2.24.0 - cli-cursor: ^3.1.0 - cli-table3: ~0.6.1 - commander: ^6.2.1 - common-tags: ^1.8.0 - dayjs: ^1.10.4 - debug: ^4.3.4 - enquirer: ^2.3.6 - eventemitter2: 6.4.7 - execa: 4.1.0 - executable: ^4.1.1 - extract-zip: 2.0.1 - figures: ^3.2.0 - fs-extra: ^9.1.0 - getos: ^3.2.1 - is-ci: ^3.0.0 - is-installed-globally: ~0.4.0 - lazy-ass: ^1.6.0 - listr2: ^3.8.3 - lodash: ^4.17.21 - log-symbols: ^4.0.0 - minimist: ^1.2.8 - ospath: ^1.2.2 - pretty-bytes: ^5.6.0 - process: ^0.11.10 - proxy-from-env: 1.0.0 - request-progress: ^3.0.0 - semver: ^7.5.3 - supports-color: ^8.1.1 - tmp: ~0.2.1 - untildify: ^4.0.0 - yauzl: ^2.10.0 - bin: - cypress: bin/cypress - checksum: 238057b288f54401ad3fa8693c31ec08eef2d98a7fc93ce7f60bc009fcae18539e84be4c1cd41526816603f642f63444a7adce9d914b1338a53c0bf756bdb762 - languageName: node - linkType: hard - -"dashdash@npm:^1.12.0": - version: 1.14.1 - resolution: "dashdash@npm:1.14.1" - dependencies: - assert-plus: ^1.0.0 - checksum: 3634c249570f7f34e3d34f866c93f866c5b417f0dd616275decae08147dcdf8fccfaa5947380ccfb0473998ea3a8057c0b4cd90c875740ee685d0624b2983598 - languageName: node - linkType: hard - -"dayjs@npm:^1.10.4": - version: 1.11.7 - resolution: "dayjs@npm:1.11.7" - checksum: 5003a7c1dd9ed51385beb658231c3548700b82d3548c0cfbe549d85f2d08e90e972510282b7506941452c58d32136d6362f009c77ca55381a09c704e9f177ebb - languageName: node - linkType: hard - -"debug@npm:^3.1.0": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: ^2.1.1 - checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c - languageName: node - linkType: hard - -"debug@npm:^4.1.1, debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - -"decode-uri-component@npm:^0.2.0": - version: 0.2.2 - resolution: "decode-uri-component@npm:0.2.2" - checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 - languageName: node - linkType: hard - -"deep-equal@npm:^2.0.5": - version: 2.2.0 - resolution: "deep-equal@npm:2.2.0" - dependencies: - call-bind: ^1.0.2 - es-get-iterator: ^1.1.2 - get-intrinsic: ^1.1.3 - is-arguments: ^1.1.1 - is-array-buffer: ^3.0.1 - is-date-object: ^1.0.5 - is-regex: ^1.1.4 - is-shared-array-buffer: ^1.0.2 - isarray: ^2.0.5 - object-is: ^1.1.5 - object-keys: ^1.1.1 - object.assign: ^4.1.4 - regexp.prototype.flags: ^1.4.3 - side-channel: ^1.0.4 - which-boxed-primitive: ^1.0.2 - which-collection: ^1.0.1 - which-typed-array: ^1.1.9 - checksum: 46a34509d2766d6c6dc5aec4756089cf0cc137e46787e91f08f1ee0bb570d874f19f0493146907df0cf18aed4a7b4b50f6f62c899240a76c323f057528b122e3 - languageName: node - linkType: hard - -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": - version: 1.1.4 - resolution: "define-properties@npm:1.1.4" - dependencies: - has-property-descriptors: ^1.0.0 - object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b - languageName: node - linkType: hard - -"delayed-stream@npm:~1.0.0": - version: 1.0.0 - resolution: "delayed-stream@npm:1.0.0" - checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 - languageName: node - linkType: hard - -"diff-sequences@npm:^29.4.2": - version: 29.4.2 - resolution: "diff-sequences@npm:29.4.2" - checksum: 70a9f7c5516fb62f7e2fb5aea8d9580cc319880b364779093669fa8e7bc6c47b7251e0e9f0d3289a4db0263708fbf0baa81f4305c2b839dd06b4771159835d31 - languageName: node - linkType: hard - -"dom-accessibility-api@npm:^0.5.6, dom-accessibility-api@npm:^0.5.9": - version: 0.5.16 - resolution: "dom-accessibility-api@npm:0.5.16" - checksum: 005eb283caef57fc1adec4d5df4dd49189b628f2f575af45decb210e04d634459e3f1ee64f18b41e2dcf200c844bc1d9279d80807e686a30d69a4756151ad248 - languageName: node - linkType: hard - -"ecc-jsbn@npm:~0.1.1": - version: 0.1.2 - resolution: "ecc-jsbn@npm:0.1.2" - dependencies: - jsbn: ~0.1.0 - safer-buffer: ^2.1.0 - checksum: 22fef4b6203e5f31d425f5b711eb389e4c6c2723402e389af394f8411b76a488fa414d309d866e2b577ce3e8462d344205545c88a8143cc21752a5172818888a - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 - languageName: node - linkType: hard - -"end-of-stream@npm:^1.1.0": - version: 1.4.4 - resolution: "end-of-stream@npm:1.4.4" - dependencies: - once: ^1.4.0 - checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b - languageName: node - linkType: hard - -"enquirer@npm:^2.3.6": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" - dependencies: - ansi-colors: ^4.1.1 - checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 - languageName: node - linkType: hard - -"es-get-iterator@npm:^1.1.2": - version: 1.1.3 - resolution: "es-get-iterator@npm:1.1.3" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - has-symbols: ^1.0.3 - is-arguments: ^1.1.1 - is-map: ^2.0.2 - is-set: ^2.0.2 - is-string: ^1.0.7 - isarray: ^2.0.5 - stop-iteration-iterator: ^1.0.0 - checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d - languageName: node - linkType: hard - -"escape-string-regexp@npm:^1.0.5": - version: 1.0.5 - resolution: "escape-string-regexp@npm:1.0.5" - checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 - languageName: node - linkType: hard - -"escape-string-regexp@npm:^2.0.0": - version: 2.0.0 - resolution: "escape-string-regexp@npm:2.0.0" - checksum: 9f8a2d5743677c16e85c810e3024d54f0c8dea6424fad3c79ef6666e81dd0846f7437f5e729dfcdac8981bc9e5294c39b4580814d114076b8d36318f46ae4395 - languageName: node - linkType: hard - -"eventemitter2@npm:6.4.7": - version: 6.4.7 - resolution: "eventemitter2@npm:6.4.7" - checksum: 1b36a77e139d6965ebf3a36c01fa00c089ae6b80faa1911e52888f40b3a7057b36a2cc45dcd1ad87cda3798fe7b97a0aabcbb8175a8b96092a23bb7d0f039e66 - languageName: node - linkType: hard - -"execa@npm:4.1.0": - version: 4.1.0 - resolution: "execa@npm:4.1.0" - dependencies: - cross-spawn: ^7.0.0 - get-stream: ^5.0.0 - human-signals: ^1.1.1 - is-stream: ^2.0.0 - merge-stream: ^2.0.0 - npm-run-path: ^4.0.0 - onetime: ^5.1.0 - signal-exit: ^3.0.2 - strip-final-newline: ^2.0.0 - checksum: e30d298934d9c52f90f3847704fd8224e849a081ab2b517bbc02f5f7732c24e56a21f14cb96a08256deffeb2d12b2b7cb7e2b014a12fb36f8d3357e06417ed55 - languageName: node - linkType: hard - -"executable@npm:^4.1.1": - version: 4.1.1 - resolution: "executable@npm:4.1.1" - dependencies: - pify: ^2.2.0 - checksum: f01927ce59bccec804e171bf859a26e362c1f50aa9ebc69f7cafdcce3859d29d4b6267fd47237c18b0a1830614bd3f0ee14b7380d9bad18a4e7af9b5f0b6984f - languageName: node - linkType: hard - -"expect@npm:^29.0.0": - version: 29.4.2 - resolution: "expect@npm:29.4.2" - dependencies: - "@jest/expect-utils": ^29.4.2 - jest-get-type: ^29.4.2 - jest-matcher-utils: ^29.4.2 - jest-message-util: ^29.4.2 - jest-util: ^29.4.2 - checksum: 32315804ec40011b4550b03b5549579b57af4d5d9b109727ecc611ee9fc911de9c40a174333bee7972ddc5732260592e3a9f37c82bf4f5851fb36e6f0eae7ff1 - languageName: node - linkType: hard - -"extend@npm:~3.0.2": - version: 3.0.2 - resolution: "extend@npm:3.0.2" - checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 - languageName: node - linkType: hard - -"extract-zip@npm:2.0.1": - version: 2.0.1 - resolution: "extract-zip@npm:2.0.1" - dependencies: - "@types/yauzl": ^2.9.1 - debug: ^4.1.1 - get-stream: ^5.1.0 - yauzl: ^2.10.0 - dependenciesMeta: - "@types/yauzl": - optional: true - bin: - extract-zip: cli.js - checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 - languageName: node - linkType: hard - -"extsprintf@npm:1.3.0": - version: 1.3.0 - resolution: "extsprintf@npm:1.3.0" - checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 - languageName: node - linkType: hard - -"extsprintf@npm:^1.2.0": - version: 1.4.1 - resolution: "extsprintf@npm:1.4.1" - checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 - languageName: node - linkType: hard - -"fd-slicer@npm:~1.1.0": - version: 1.1.0 - resolution: "fd-slicer@npm:1.1.0" - dependencies: - pend: ~1.2.0 - checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 - languageName: node - linkType: hard - -"figures@npm:^3.2.0": - version: 3.2.0 - resolution: "figures@npm:3.2.0" - dependencies: - escape-string-regexp: ^1.0.5 - checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b - languageName: node - linkType: hard - -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" - dependencies: - to-regex-range: ^5.0.1 - checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917 - languageName: node - linkType: hard - -"for-each@npm:^0.3.3": - version: 0.3.3 - resolution: "for-each@npm:0.3.3" - dependencies: - is-callable: ^1.1.3 - checksum: 6c48ff2bc63362319c65e2edca4a8e1e3483a2fabc72fbe7feaf8c73db94fc7861bd53bc02c8a66a0c1dd709da6b04eec42e0abdd6b40ce47305ae92a25e5d28 - languageName: node - linkType: hard - -"forever-agent@npm:~0.6.1": - version: 0.6.1 - resolution: "forever-agent@npm:0.6.1" - checksum: 766ae6e220f5fe23676bb4c6a99387cec5b7b62ceb99e10923376e27bfea72f3c3aeec2ba5f45f3f7ba65d6616965aa7c20b15002b6860833bb6e394dea546a8 - languageName: node - linkType: hard - -"form-data@npm:~2.3.2": - version: 2.3.3 - resolution: "form-data@npm:2.3.3" - dependencies: - asynckit: ^0.4.0 - combined-stream: ^1.0.6 - mime-types: ^2.1.12 - checksum: 10c1780fa13dbe1ff3100114c2ce1f9307f8be10b14bf16e103815356ff567b6be39d70fc4a40f8990b9660012dc24b0f5e1dde1b6426166eb23a445ba068ca3 - languageName: node - linkType: hard - -"fs-extra@npm:^9.1.0": - version: 9.1.0 - resolution: "fs-extra@npm:9.1.0" - dependencies: - at-least-node: ^1.0.0 - graceful-fs: ^4.2.0 - jsonfile: ^6.0.1 - universalify: ^2.0.0 - checksum: ba71ba32e0faa74ab931b7a0031d1523c66a73e225de7426e275e238e312d07313d2da2d33e34a52aa406c8763ade5712eb3ec9ba4d9edce652bcacdc29e6b20 - languageName: node - linkType: hard - -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 - languageName: node - linkType: hard - -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a - languageName: node - linkType: hard - -"functions-have-names@npm:^1.2.2": - version: 1.2.3 - resolution: "functions-have-names@npm:1.2.3" - checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": - version: 1.2.0 - resolution: "get-intrinsic@npm:1.2.0" - dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 - has-symbols: ^1.0.3 - checksum: 78fc0487b783f5c58cf2dccafc3ae656ee8d2d8062a8831ce4a95e7057af4587a1d4882246c033aca0a7b4965276f4802b45cc300338d1b77a73d3e3e3f4877d - languageName: node - linkType: hard - -"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": - version: 5.2.0 - resolution: "get-stream@npm:5.2.0" - dependencies: - pump: ^3.0.0 - checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12 - languageName: node - linkType: hard - -"getos@npm:^3.2.1": - version: 3.2.1 - resolution: "getos@npm:3.2.1" - dependencies: - async: ^3.2.0 - checksum: 42fd78a66d47cebd3e09de5566cc0044e034b08f4a000a310dbd89a77b02c65d8f4002554bfa495ea5bdc4fa9d515f5ac785a7cc474ba45383cc697f865eeaf1 - languageName: node - linkType: hard - -"getpass@npm:^0.1.1": - version: 0.1.7 - resolution: "getpass@npm:0.1.7" - dependencies: - assert-plus: ^1.0.0 - checksum: ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 - languageName: node - linkType: hard - -"glob@npm:^7.1.3": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.1.1 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 - languageName: node - linkType: hard - -"global-dirs@npm:^3.0.0": - version: 3.0.1 - resolution: "global-dirs@npm:3.0.1" - dependencies: - ini: 2.0.0 - checksum: 70147b80261601fd40ac02a104581432325c1c47329706acd773f3a6ce99bb36d1d996038c85ccacd482ad22258ec233c586b6a91535b1a116b89663d49d6438 - languageName: node - linkType: hard - -"gopd@npm:^1.0.1": - version: 1.0.1 - resolution: "gopd@npm:1.0.1" - dependencies: - get-intrinsic: ^1.1.3 - checksum: a5ccfb8806e0917a94e0b3de2af2ea4979c1da920bc381667c260e00e7cafdbe844e2cb9c5bcfef4e5412e8bf73bab837285bc35c7ba73aaaf0134d4583393a6 - languageName: node - linkType: hard - -"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.9": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da - languageName: node - linkType: hard - -"has-bigints@npm:^1.0.1": - version: 1.0.2 - resolution: "has-bigints@npm:1.0.2" - checksum: 390e31e7be7e5c6fe68b81babb73dfc35d413604d7ee5f56da101417027a4b4ce6a27e46eff97ad040c835b5d228676eae99a9b5c3bc0e23c8e81a49241ff45b - languageName: node - linkType: hard - -"has-flag@npm:^3.0.0": - version: 3.0.0 - resolution: "has-flag@npm:3.0.0" - checksum: 4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b - languageName: node - linkType: hard - -"has-flag@npm:^4.0.0": - version: 4.0.0 - resolution: "has-flag@npm:4.0.0" - checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad - languageName: node - linkType: hard - -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" - dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": - version: 1.0.3 - resolution: "has-symbols@npm:1.0.3" - checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" - dependencies: - has-symbols: ^1.0.2 - checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 - languageName: node - linkType: hard - -"http-signature@npm:~1.3.6": - version: 1.3.6 - resolution: "http-signature@npm:1.3.6" - dependencies: - assert-plus: ^1.0.0 - jsprim: ^2.0.2 - sshpk: ^1.14.1 - checksum: 10be2af4764e71fee0281392937050201ee576ac755c543f570d6d87134ce5e858663fe999a7adb3e4e368e1e356d0d7fec6b9542295b875726ff615188e7a0c - languageName: node - linkType: hard - -"human-signals@npm:^1.1.1": - version: 1.1.1 - resolution: "human-signals@npm:1.1.1" - checksum: d587647c9e8ec24e02821b6be7de5a0fc37f591f6c4e319b3054b43fd4c35a70a94c46fc74d8c1a43c47fde157d23acd7421f375e1c1365b09a16835b8300205 - languageName: node - linkType: hard - -"ieee754@npm:^1.1.13": - version: 1.2.1 - resolution: "ieee754@npm:1.2.1" - checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e - languageName: node - linkType: hard - -"indent-string@npm:^4.0.0": - version: 4.0.0 - resolution: "indent-string@npm:4.0.0" - checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 - languageName: node - linkType: hard - -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: ^1.3.0 - wrappy: 1 - checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd - languageName: node - linkType: hard - -"inherits@npm:2, inherits@npm:^2.0.4": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 - languageName: node - linkType: hard - -"ini@npm:2.0.0": - version: 2.0.0 - resolution: "ini@npm:2.0.0" - checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e - languageName: node - linkType: hard - -"internal-slot@npm:^1.0.4": - version: 1.0.4 - resolution: "internal-slot@npm:1.0.4" - dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 - side-channel: ^1.0.4 - checksum: 8974588d06bab4f675573a3b52975370facf6486df51bc0567a982c7024fa29495f10b76c0d4dc742dd951d1b72024fdc1e31bb0bedf1678dc7aacacaf5a4f73 - languageName: node - linkType: hard - -"is-arguments@npm:^1.1.1": - version: 1.1.1 - resolution: "is-arguments@npm:1.1.1" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27 - languageName: node - linkType: hard - -"is-array-buffer@npm:^3.0.1": - version: 3.0.1 - resolution: "is-array-buffer@npm:3.0.1" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - is-typed-array: ^1.1.10 - checksum: f26ab87448e698285daf707e52a533920449f7abf63714140ffab9d5571aa5a71ac2fa2677e8b793ad0d5d3e40078d4d2c8a0ab39c957e3cfc6513bb6c9dfdc9 - languageName: node - linkType: hard - -"is-bigint@npm:^1.0.1": - version: 1.0.4 - resolution: "is-bigint@npm:1.0.4" - dependencies: - has-bigints: ^1.0.1 - checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 - languageName: node - linkType: hard - -"is-boolean-object@npm:^1.1.0": - version: 1.1.2 - resolution: "is-boolean-object@npm:1.1.2" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: c03b23dbaacadc18940defb12c1c0e3aaece7553ef58b162a0f6bba0c2a7e1551b59f365b91e00d2dbac0522392d576ef322628cb1d036a0fe51eb466db67222 - languageName: node - linkType: hard - -"is-callable@npm:^1.1.3": - version: 1.2.7 - resolution: "is-callable@npm:1.2.7" - checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac - languageName: node - linkType: hard - -"is-ci@npm:^3.0.0": - version: 3.0.1 - resolution: "is-ci@npm:3.0.1" - dependencies: - ci-info: ^3.2.0 - bin: - is-ci: bin.js - checksum: 192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e - languageName: node - linkType: hard - -"is-date-object@npm:^1.0.5": - version: 1.0.5 - resolution: "is-date-object@npm:1.0.5" - dependencies: - has-tostringtag: ^1.0.0 - checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 - languageName: node - linkType: hard - -"is-installed-globally@npm:~0.4.0": - version: 0.4.0 - resolution: "is-installed-globally@npm:0.4.0" - dependencies: - global-dirs: ^3.0.0 - is-path-inside: ^3.0.2 - checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 - languageName: node - linkType: hard - -"is-map@npm:^2.0.1, is-map@npm:^2.0.2": - version: 2.0.2 - resolution: "is-map@npm:2.0.2" - checksum: ace3d0ecd667bbdefdb1852de601268f67f2db725624b1958f279316e13fecb8fa7df91fd60f690d7417b4ec180712f5a7ee967008e27c65cfd475cc84337728 - languageName: node - linkType: hard - -"is-number-object@npm:^1.0.4": - version: 1.0.7 - resolution: "is-number-object@npm:1.0.7" - dependencies: - has-tostringtag: ^1.0.0 - checksum: d1e8d01bb0a7134c74649c4e62da0c6118a0bfc6771ea3c560914d52a627873e6920dd0fd0ebc0e12ad2ff4687eac4c308f7e80320b973b2c8a2c8f97a7524f7 - languageName: node - linkType: hard - -"is-number@npm:^7.0.0": - version: 7.0.0 - resolution: "is-number@npm:7.0.0" - checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a - languageName: node - linkType: hard - -"is-path-inside@npm:^3.0.2": - version: 3.0.3 - resolution: "is-path-inside@npm:3.0.3" - checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 - languageName: node - linkType: hard - -"is-regex@npm:^1.1.4": - version: 1.1.4 - resolution: "is-regex@npm:1.1.4" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 - languageName: node - linkType: hard - -"is-set@npm:^2.0.1, is-set@npm:^2.0.2": - version: 2.0.2 - resolution: "is-set@npm:2.0.2" - checksum: b64343faf45e9387b97a6fd32be632ee7b269bd8183701f3b3f5b71a7cf00d04450ed8669d0bd08753e08b968beda96fca73a10fd0ff56a32603f64deba55a57 - languageName: node - linkType: hard - -"is-shared-array-buffer@npm:^1.0.2": - version: 1.0.2 - resolution: "is-shared-array-buffer@npm:1.0.2" - dependencies: - call-bind: ^1.0.2 - checksum: 9508929cf14fdc1afc9d61d723c6e8d34f5e117f0bffda4d97e7a5d88c3a8681f633a74f8e3ad1fe92d5113f9b921dc5ca44356492079612f9a247efbce7032a - languageName: node - linkType: hard - -"is-stream@npm:^2.0.0": - version: 2.0.1 - resolution: "is-stream@npm:2.0.1" - checksum: b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 - languageName: node - linkType: hard - -"is-string@npm:^1.0.5, is-string@npm:^1.0.7": - version: 1.0.7 - resolution: "is-string@npm:1.0.7" - dependencies: - has-tostringtag: ^1.0.0 - checksum: 323b3d04622f78d45077cf89aab783b2f49d24dc641aa89b5ad1a72114cfeff2585efc8c12ef42466dff32bde93d839ad321b26884cf75e5a7892a938b089989 - languageName: node - linkType: hard - -"is-symbol@npm:^1.0.3": - version: 1.0.4 - resolution: "is-symbol@npm:1.0.4" - dependencies: - has-symbols: ^1.0.2 - checksum: 92805812ef590738d9de49d677cd17dfd486794773fb6fa0032d16452af46e9b91bb43ffe82c983570f015b37136f4b53b28b8523bfb10b0ece7a66c31a54510 - languageName: node - linkType: hard - -"is-typed-array@npm:^1.1.10": - version: 1.1.10 - resolution: "is-typed-array@npm:1.1.10" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - checksum: aac6ecb59d4c56a1cdeb69b1f129154ef462bbffe434cb8a8235ca89b42f258b7ae94073c41b3cb7bce37f6a1733ad4499f07882d5d5093a7ba84dfc4ebb8017 - languageName: node - linkType: hard - -"is-typedarray@npm:~1.0.0": - version: 1.0.0 - resolution: "is-typedarray@npm:1.0.0" - checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 - languageName: node - linkType: hard - -"is-unicode-supported@npm:^0.1.0": - version: 0.1.0 - resolution: "is-unicode-supported@npm:0.1.0" - checksum: a2aab86ee7712f5c2f999180daaba5f361bdad1efadc9610ff5b8ab5495b86e4f627839d085c6530363c6d6d4ecbde340fb8e54bdb83da4ba8e0865ed5513c52 - languageName: node - linkType: hard - -"is-weakmap@npm:^2.0.1": - version: 2.0.1 - resolution: "is-weakmap@npm:2.0.1" - checksum: 1222bb7e90c32bdb949226e66d26cb7bce12e1e28e3e1b40bfa6b390ba3e08192a8664a703dff2a00a84825f4e022f9cd58c4599ff9981ab72b1d69479f4f7f6 - languageName: node - linkType: hard - -"is-weakset@npm:^2.0.1": - version: 2.0.2 - resolution: "is-weakset@npm:2.0.2" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.1 - checksum: 5d8698d1fa599a0635d7ca85be9c26d547b317ed8fd83fc75f03efbe75d50001b5eececb1e9971de85fcde84f69ae6f8346bc92d20d55d46201d328e4c74a367 - languageName: node - linkType: hard - -"isarray@npm:^2.0.5": - version: 2.0.5 - resolution: "isarray@npm:2.0.5" - checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 - languageName: node - linkType: hard - -"isstream@npm:~0.1.2": - version: 0.1.2 - resolution: "isstream@npm:0.1.2" - checksum: 1eb2fe63a729f7bdd8a559ab552c69055f4f48eb5c2f03724430587c6f450783c8f1cd936c1c952d0a927925180fcc892ebd5b174236cf1065d4bd5bdb37e963 - languageName: node - linkType: hard - -"jest-diff@npm:^29.4.2": - version: 29.4.2 - resolution: "jest-diff@npm:29.4.2" - dependencies: - chalk: ^4.0.0 - diff-sequences: ^29.4.2 - jest-get-type: ^29.4.2 - pretty-format: ^29.4.2 - checksum: 5f8ee70ed2cbfa8a76b7614e9d0736fc218a786df500aae6c5876ad7c58f658901fec7777112dc404e7146582c1537564d570eb7b989922f0dfcb3d6c8844952 - languageName: node - linkType: hard - -"jest-get-type@npm:^29.4.2": - version: 29.4.2 - resolution: "jest-get-type@npm:29.4.2" - checksum: 52b69cfdc8817a106ed58b44ac0ee77df36073d0deb7357ea9eb208fd8fb9be2abcc2cc6d72019460b7ca262687da482c47bd9c357eb2fbe52279397739e8c11 - languageName: node - linkType: hard - -"jest-matcher-utils@npm:^29.4.2": - version: 29.4.2 - resolution: "jest-matcher-utils@npm:29.4.2" - dependencies: - chalk: ^4.0.0 - jest-diff: ^29.4.2 - jest-get-type: ^29.4.2 - pretty-format: ^29.4.2 - checksum: e8549f8534f31ae60c81b6c5f690b5dd6d42190318165bba943b3d2c278730c59b4933d5941c70e577f08c0c633b7d92edec43696b79a5cce8e2b4080cccae3c - languageName: node - linkType: hard - -"jest-message-util@npm:^29.4.2": - version: 29.4.2 - resolution: "jest-message-util@npm:29.4.2" - dependencies: - "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.4.2 - "@types/stack-utils": ^2.0.0 - chalk: ^4.0.0 - graceful-fs: ^4.2.9 - micromatch: ^4.0.4 - pretty-format: ^29.4.2 - slash: ^3.0.0 - stack-utils: ^2.0.3 - checksum: d3b32fbf5c16100817bdf6d3eaae0cf618d39df62df0c9e8dcfa2ffc9fe2afb0c71312b9b86d4afb33b87795dc1dc3b7f7f024ae1fe21e818d2caf90c3ba6fdc - languageName: node - linkType: hard - -"jest-util@npm:^29.4.2": - version: 29.4.2 - resolution: "jest-util@npm:29.4.2" - dependencies: - "@jest/types": ^29.4.2 - "@types/node": "*" - chalk: ^4.0.0 - ci-info: ^3.2.0 - graceful-fs: ^4.2.9 - picomatch: ^2.2.3 - checksum: c570de97ccae9f6eca736a4559c77205db1b115d1d3e63f3855b0f016708306de610615f9502291f9382b8e5c9be0443841c392d6ce3197a2915997ced88bc84 - languageName: node - linkType: hard - -"js-tokens@npm:^4.0.0": - version: 4.0.0 - resolution: "js-tokens@npm:4.0.0" - checksum: 8a95213a5a77deb6cbe94d86340e8d9ace2b93bc367790b260101d2f36a2eaf4e4e22d9fa9cf459b38af3a32fb4190e638024cf82ec95ef708680e405ea7cc78 - languageName: node - linkType: hard - -"jsbn@npm:~0.1.0": - version: 0.1.1 - resolution: "jsbn@npm:0.1.1" - checksum: e5ff29c1b8d965017ef3f9c219dacd6e40ad355c664e277d31246c90545a02e6047018c16c60a00f36d561b3647215c41894f5d869ada6908a2e0ce4200c88f2 - languageName: node - linkType: hard - -"json-schema@npm:0.4.0": - version: 0.4.0 - resolution: "json-schema@npm:0.4.0" - checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 - languageName: node - linkType: hard - -"json-stringify-safe@npm:~5.0.1": - version: 5.0.1 - resolution: "json-stringify-safe@npm:5.0.1" - checksum: 48ec0adad5280b8a96bb93f4563aa1667fd7a36334f79149abd42446d0989f2ddc58274b479f4819f1f00617957e6344c886c55d05a4e15ebb4ab931e4a6a8ee - languageName: node - linkType: hard - -"jsonfile@npm:^6.0.1": - version: 6.1.0 - resolution: "jsonfile@npm:6.1.0" - dependencies: - graceful-fs: ^4.1.6 - universalify: ^2.0.0 - dependenciesMeta: - graceful-fs: - optional: true - checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 - languageName: node - linkType: hard - -"jsprim@npm:^2.0.2": - version: 2.0.2 - resolution: "jsprim@npm:2.0.2" - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - checksum: d175f6b1991e160cb0aa39bc857da780e035611986b5492f32395411879fdaf4e513d98677f08f7352dac93a16b66b8361c674b86a3fa406e2e7af6b26321838 - languageName: node - linkType: hard - -"lazy-ass@npm:^1.6.0": - version: 1.6.0 - resolution: "lazy-ass@npm:1.6.0" - checksum: 5a3ebb17915b03452320804466345382a6c25ac782ec4874fecdb2385793896cd459be2f187dc7def8899180c32ee0ab9a1aa7fe52193ac3ff3fe29bb0591729 - languageName: node - linkType: hard - -"listr2@npm:^3.8.3": - version: 3.14.0 - resolution: "listr2@npm:3.14.0" - dependencies: - cli-truncate: ^2.1.0 - colorette: ^2.0.16 - log-update: ^4.0.0 - p-map: ^4.0.0 - rfdc: ^1.3.0 - rxjs: ^7.5.1 - through: ^2.3.8 - wrap-ansi: ^7.0.0 - peerDependencies: - enquirer: ">= 2.3.0 < 3" - peerDependenciesMeta: - enquirer: - optional: true - checksum: fdb8b2d6bdf5df9371ebd5082bee46c6d0ca3d1e5f2b11fbb5a127839855d5f3da9d4968fce94f0a5ec67cac2459766abbb1faeef621065ebb1829b11ef9476d - languageName: node - linkType: hard - -"lodash.once@npm:^4.1.1": - version: 4.1.1 - resolution: "lodash.once@npm:4.1.1" - checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 - languageName: node - linkType: hard - -"lodash@npm:^4.17.15, lodash@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 - languageName: node - linkType: hard - -"log-symbols@npm:^4.0.0": - version: 4.1.0 - resolution: "log-symbols@npm:4.1.0" - dependencies: - chalk: ^4.1.0 - is-unicode-supported: ^0.1.0 - checksum: fce1497b3135a0198803f9f07464165e9eb83ed02ceb2273930a6f8a508951178d8cf4f0378e9d28300a2ed2bc49050995d2bd5f53ab716bb15ac84d58c6ef74 - languageName: node - linkType: hard - -"log-update@npm:^4.0.0": - version: 4.0.0 - resolution: "log-update@npm:4.0.0" - dependencies: - ansi-escapes: ^4.3.0 - cli-cursor: ^3.1.0 - slice-ansi: ^4.0.0 - wrap-ansi: ^6.2.0 - checksum: ae2f85bbabc1906034154fb7d4c4477c79b3e703d22d78adee8b3862fa913942772e7fa11713e3d96fb46de4e3cabefbf5d0a544344f03b58d3c4bff52aa9eb2 - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: ^4.0.0 - checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 - languageName: node - linkType: hard - -"lz-string@npm:^1.4.4": - version: 1.4.4 - resolution: "lz-string@npm:1.4.4" - bin: - lz-string: bin/bin.js - checksum: 54e31238a61a84d8f664d9860a9fba7310c5b97a52c444f80543069bc084815eff40b8d4474ae1d93992fdf6c252dca37cf27f6adbeb4dbc3df2f3ac773d0e61 - languageName: node - linkType: hard - -"merge-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "merge-stream@npm:2.0.0" - checksum: 6fa4dcc8d86629705cea944a4b88ef4cb0e07656ebf223fa287443256414283dd25d91c1cd84c77987f2aec5927af1a9db6085757cb43d90eb170ebf4b47f4f4 - languageName: node - linkType: hard - -"micromatch@npm:^4.0.4": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" - dependencies: - braces: ^3.0.2 - picomatch: ^2.3.1 - checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc - languageName: node - linkType: hard - -"mime-db@npm:1.52.0": - version: 1.52.0 - resolution: "mime-db@npm:1.52.0" - checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f - languageName: node - linkType: hard - -"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": - version: 2.1.35 - resolution: "mime-types@npm:2.1.35" - dependencies: - mime-db: 1.52.0 - checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 - languageName: node - linkType: hard - -"mimic-fn@npm:^2.1.0": - version: 2.1.0 - resolution: "mimic-fn@npm:2.1.0" - checksum: d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a - languageName: node - linkType: hard - -"min-indent@npm:^1.0.0": - version: 1.0.1 - resolution: "min-indent@npm:1.0.1" - checksum: bfc6dd03c5eaf623a4963ebd94d087f6f4bbbfd8c41329a7f09706b0cb66969c4ddd336abeb587bc44bc6f08e13bf90f0b374f9d71f9f01e04adc2cd6f083ef1 - languageName: node - linkType: hard - -"minimatch@npm:^3.1.1": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: ^1.1.7 - checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a - languageName: node - linkType: hard - -"minimist@npm:^1.2.8": - version: 1.2.8 - resolution: "minimist@npm:1.2.8" - checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 - languageName: node - linkType: hard - -"ms@npm:2.1.2": - version: 2.1.2 - resolution: "ms@npm:2.1.2" - checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f - languageName: node - linkType: hard - -"ms@npm:^2.1.1": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d - languageName: node - linkType: hard - -"npm-run-path@npm:^4.0.0": - version: 4.0.1 - resolution: "npm-run-path@npm:4.0.1" - dependencies: - path-key: ^3.0.0 - checksum: 5374c0cea4b0bbfdfae62da7bbdf1e1558d338335f4cacf2515c282ff358ff27b2ecb91ffa5330a8b14390ac66a1e146e10700440c1ab868208430f56b5f4d23 - languageName: node - linkType: hard - -"object-inspect@npm:^1.9.0": - version: 1.12.3 - resolution: "object-inspect@npm:1.12.3" - checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db - languageName: node - linkType: hard - -"object-is@npm:^1.1.5": - version: 1.1.5 - resolution: "object-is@npm:1.1.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe - languageName: node - linkType: hard - -"object-keys@npm:^1.1.1": - version: 1.1.1 - resolution: "object-keys@npm:1.1.1" - checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a - languageName: node - linkType: hard - -"object.assign@npm:^4.1.4": - version: 4.1.4 - resolution: "object.assign@npm:4.1.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - has-symbols: ^1.0.3 - object-keys: ^1.1.1 - checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 - languageName: node - linkType: hard - -"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: 1 - checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 - languageName: node - linkType: hard - -"onetime@npm:^5.1.0": - version: 5.1.2 - resolution: "onetime@npm:5.1.2" - dependencies: - mimic-fn: ^2.1.0 - checksum: 2478859ef817fc5d4e9c2f9e5728512ddd1dbc9fb7829ad263765bb6d3b91ce699d6e2332eef6b7dff183c2f490bd3349f1666427eaba4469fba0ac38dfd0d34 - languageName: node - linkType: hard - -"ospath@npm:^1.2.2": - version: 1.2.2 - resolution: "ospath@npm:1.2.2" - checksum: 505f48a4f4f1c557d6c656ec985707726e3714721680139be037613e903aa8c8fa4ddd8d1342006f9b2dc0065e6e20f8b7bea2ee05354f31257044790367b347 - languageName: node - linkType: hard - -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: ^3.0.0 - checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c - languageName: node - linkType: hard - -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 - languageName: node - linkType: hard - -"path-key@npm:^3.0.0, path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 - languageName: node - linkType: hard - -"pend@npm:~1.2.0": - version: 1.2.0 - resolution: "pend@npm:1.2.0" - checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d - languageName: node - linkType: hard - -"performance-now@npm:^2.1.0": - version: 2.1.0 - resolution: "performance-now@npm:2.1.0" - checksum: 534e641aa8f7cba160f0afec0599b6cecefbb516a2e837b512be0adbe6c1da5550e89c78059c7fabc5c9ffdf6627edabe23eb7c518c4500067a898fa65c2b550 - languageName: node - linkType: hard - -"picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 050c865ce81119c4822c45d3c84f1ced46f93a0126febae20737bd05ca20589c564d6e9226977df859ed5e03dc73f02584a2b0faad36e896936238238b0446cf - languageName: node - linkType: hard - -"pify@npm:^2.2.0": - version: 2.3.0 - resolution: "pify@npm:2.3.0" - checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba - languageName: node - linkType: hard - -"pretty-bytes@npm:^5.6.0": - version: 5.6.0 - resolution: "pretty-bytes@npm:5.6.0" - checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd - languageName: node - linkType: hard - -"pretty-format@npm:^27.0.2": - version: 27.5.1 - resolution: "pretty-format@npm:27.5.1" - dependencies: - ansi-regex: ^5.0.1 - ansi-styles: ^5.0.0 - react-is: ^17.0.1 - checksum: cf610cffcb793885d16f184a62162f2dd0df31642d9a18edf4ca298e909a8fe80bdbf556d5c9573992c102ce8bf948691da91bf9739bee0ffb6e79c8a8a6e088 - languageName: node - linkType: hard - -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.4.2": - version: 29.4.2 - resolution: "pretty-format@npm:29.4.2" - dependencies: - "@jest/schemas": ^29.4.2 - ansi-styles: ^5.0.0 - react-is: ^18.0.0 - checksum: ef322c76b617494efda4a7fe277fe10ac4b34ffc4dc2149adbd2533f3b03a67a58ab0c32ee6a9a9ac143a4822c971a071502f6c9ecd87b07ba5d43c58619c2e1 - languageName: node - linkType: hard - -"process@npm:^0.11.10": - version: 0.11.10 - resolution: "process@npm:0.11.10" - checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3 - languageName: node - linkType: hard - -"proxy-from-env@npm:1.0.0": - version: 1.0.0 - resolution: "proxy-from-env@npm:1.0.0" - checksum: 292e28d1de0c315958d71d8315eb546dd3cd8c8cbc2dab7c54eeb9f5c17f421771964ad0b5e1f77011bab2305bdae42e1757ce33bdb1ccc3e87732322a8efcf1 - languageName: node - linkType: hard - -"psl@npm:^1.1.33": - version: 1.9.0 - resolution: "psl@npm:1.9.0" - checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d - languageName: node - linkType: hard - -"pump@npm:^3.0.0": - version: 3.0.0 - resolution: "pump@npm:3.0.0" - dependencies: - end-of-stream: ^1.1.0 - once: ^1.3.1 - checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 - languageName: node - linkType: hard - -"punycode@npm:^2.1.1": - version: 2.3.0 - resolution: "punycode@npm:2.3.0" - checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 - languageName: node - linkType: hard - -"qs@npm:6.10.4": - version: 6.10.4 - resolution: "qs@npm:6.10.4" - dependencies: - side-channel: ^1.0.4 - checksum: 31e4fedd759d01eae52dde6692abab175f9af3e639993c5caaa513a2a3607b34d8058d3ae52ceeccf37c3025f22ed5e90e9ddd6c2537e19c0562ddd10dc5b1eb - languageName: node - linkType: hard - -"querystringify@npm:^2.1.1": - version: 2.2.0 - resolution: "querystringify@npm:2.2.0" - checksum: 5641ea231bad7ef6d64d9998faca95611ed4b11c2591a8cae741e178a974f6a8e0ebde008475259abe1621cb15e692404e6b6626e927f7b849d5c09392604b15 - languageName: node - linkType: hard - -"react-is@npm:^17.0.1": - version: 17.0.2 - resolution: "react-is@npm:17.0.2" - checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 - languageName: node - linkType: hard - -"react-is@npm:^18.0.0": - version: 18.2.0 - resolution: "react-is@npm:18.2.0" - checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e - languageName: node - linkType: hard - -"redent@npm:^3.0.0": - version: 3.0.0 - resolution: "redent@npm:3.0.0" - dependencies: - indent-string: ^4.0.0 - strip-indent: ^3.0.0 - checksum: fa1ef20404a2d399235e83cc80bd55a956642e37dd197b4b612ba7327bf87fa32745aeb4a1634b2bab25467164ab4ed9c15be2c307923dd08b0fe7c52431ae6b - languageName: node - linkType: hard - -"regenerator-runtime@npm:^0.13.11": - version: 0.13.11 - resolution: "regenerator-runtime@npm:0.13.11" - checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 - languageName: node - linkType: hard - -"regexp.prototype.flags@npm:^1.4.3": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 - languageName: node - linkType: hard - -"request-progress@npm:^3.0.0": - version: 3.0.0 - resolution: "request-progress@npm:3.0.0" - dependencies: - throttleit: ^1.0.0 - checksum: 6ea1761dcc8a8b7b5894afd478c0286aa31bd69438d7050294bd4fd0d0b3e09b5cde417d38deef9c49809039c337d8744e4bb49d8632b0c3e4ffa5e8a687e0fd - languageName: node - linkType: hard - -"requires-port@npm:^1.0.0": - version: 1.0.0 - resolution: "requires-port@npm:1.0.0" - checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff - languageName: node - linkType: hard - -"restore-cursor@npm:^3.1.0": - version: 3.1.0 - resolution: "restore-cursor@npm:3.1.0" - dependencies: - onetime: ^5.1.0 - signal-exit: ^3.0.2 - checksum: f877dd8741796b909f2a82454ec111afb84eb45890eb49ac947d87991379406b3b83ff9673a46012fca0d7844bb989f45cc5b788254cf1a39b6b5a9659de0630 - languageName: node - linkType: hard - -"rfdc@npm:^1.3.0": - version: 1.3.0 - resolution: "rfdc@npm:1.3.0" - checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 - languageName: node - linkType: hard - -"rimraf@npm:^3.0.0": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: ^7.1.3 - bin: - rimraf: bin.js - checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 - languageName: node - linkType: hard - -"root-workspace-0b6124@workspace:.": - version: 0.0.0-use.local - resolution: "root-workspace-0b6124@workspace:." - dependencies: - "@plone/volto-testing": ^4.0.0 - languageName: unknown - linkType: soft - -"rxjs@npm:^7.5.1": - version: 7.8.0 - resolution: "rxjs@npm:7.8.0" - dependencies: - tslib: ^2.1.0 - checksum: 61b4d4fd323c1043d8d6ceb91f24183b28bcf5def4f01ca111511d5c6b66755bc5578587fe714ef5d67cf4c9f2e26f4490d4e1d8cabf9bd5967687835e9866a2 - languageName: node - linkType: hard - -"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.2": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 - languageName: node - linkType: hard - -"safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 - languageName: node - linkType: hard - -"semver@npm:^7.5.3": - version: 7.6.0 - resolution: "semver@npm:7.6.0" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 7427f05b70786c696640edc29fdd4bc33b2acf3bbe1740b955029044f80575fc664e1a512e4113c3af21e767154a94b4aa214bf6cd6e42a1f6dba5914e0b208c - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: ^3.0.0 - checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 - languageName: node - linkType: hard - -"side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" - dependencies: - call-bind: ^1.0.0 - get-intrinsic: ^1.0.2 - object-inspect: ^1.9.0 - checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.2": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 - languageName: node - linkType: hard - -"slash@npm:^3.0.0": - version: 3.0.0 - resolution: "slash@npm:3.0.0" - checksum: 94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c - languageName: node - linkType: hard - -"slice-ansi@npm:^3.0.0": - version: 3.0.0 - resolution: "slice-ansi@npm:3.0.0" - dependencies: - ansi-styles: ^4.0.0 - astral-regex: ^2.0.0 - is-fullwidth-code-point: ^3.0.0 - checksum: 5ec6d022d12e016347e9e3e98a7eb2a592213a43a65f1b61b74d2c78288da0aded781f665807a9f3876b9daa9ad94f64f77d7633a0458876c3a4fdc4eb223f24 - languageName: node - linkType: hard - -"slice-ansi@npm:^4.0.0": - version: 4.0.0 - resolution: "slice-ansi@npm:4.0.0" - dependencies: - ansi-styles: ^4.0.0 - astral-regex: ^2.0.0 - is-fullwidth-code-point: ^3.0.0 - checksum: 4a82d7f085b0e1b070e004941ada3c40d3818563ac44766cca4ceadd2080427d337554f9f99a13aaeb3b4a94d9964d9466c807b3d7b7541d1ec37ee32d308756 - languageName: node - linkType: hard - -"source-map-resolve@npm:^0.6.0": - version: 0.6.0 - resolution: "source-map-resolve@npm:0.6.0" - dependencies: - atob: ^2.1.2 - decode-uri-component: ^0.2.0 - checksum: fe503b9e5dac1c54be835282fcfec10879434e7b3ee08a9774f230299c724a8d403484d9531276d1670c87390e0e4d1d3f92b14cca6e4a2445ea3016b786ecd4 - languageName: node - linkType: hard - -"source-map@npm:^0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 - languageName: node - linkType: hard - -"sshpk@npm:^1.14.1": - version: 1.17.0 - resolution: "sshpk@npm:1.17.0" - dependencies: - asn1: ~0.2.3 - assert-plus: ^1.0.0 - bcrypt-pbkdf: ^1.0.0 - dashdash: ^1.12.0 - ecc-jsbn: ~0.1.1 - getpass: ^0.1.1 - jsbn: ~0.1.0 - safer-buffer: ^2.0.2 - tweetnacl: ~0.14.0 - bin: - sshpk-conv: bin/sshpk-conv - sshpk-sign: bin/sshpk-sign - sshpk-verify: bin/sshpk-verify - checksum: ba109f65c8e6c35133b8e6ed5576abeff8aa8d614824b7275ec3ca308f081fef483607c28d97780c1e235818b0f93ed8c8b56d0a5968d5a23fd6af57718c7597 - languageName: node - linkType: hard - -"stack-utils@npm:^2.0.3": - version: 2.0.6 - resolution: "stack-utils@npm:2.0.6" - dependencies: - escape-string-regexp: ^2.0.0 - checksum: 052bf4d25bbf5f78e06c1d5e67de2e088b06871fa04107ca8d3f0e9d9263326e2942c8bedee3545795fc77d787d443a538345eef74db2f8e35db3558c6f91ff7 - languageName: node - linkType: hard - -"stop-iteration-iterator@npm:^1.0.0": - version: 1.0.0 - resolution: "stop-iteration-iterator@npm:1.0.0" - dependencies: - internal-slot: ^1.0.4 - checksum: d04173690b2efa40e24ab70e5e51a3ff31d56d699550cfad084104ab3381390daccb36652b25755e420245f3b0737de66c1879eaa2a8d4fc0a78f9bf892fcb42 - languageName: node - linkType: hard - -"string-width@npm:^4.1.0, string-width@npm:^4.2.0": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.1 - checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c - languageName: node - linkType: hard - -"strip-final-newline@npm:^2.0.0": - version: 2.0.0 - resolution: "strip-final-newline@npm:2.0.0" - checksum: 69412b5e25731e1938184b5d489c32e340605bb611d6140344abc3421b7f3c6f9984b21dff296dfcf056681b82caa3bb4cc996a965ce37bcfad663e92eae9c64 - languageName: node - linkType: hard - -"strip-indent@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-indent@npm:3.0.0" - dependencies: - min-indent: ^1.0.0 - checksum: 18f045d57d9d0d90cd16f72b2313d6364fd2cb4bf85b9f593523ad431c8720011a4d5f08b6591c9d580f446e78855c5334a30fb91aa1560f5d9f95ed1b4a0530 - languageName: node - linkType: hard - -"supports-color@npm:^5.3.0": - version: 5.5.0 - resolution: "supports-color@npm:5.5.0" - dependencies: - has-flag: ^3.0.0 - checksum: 95f6f4ba5afdf92f495b5a912d4abee8dcba766ae719b975c56c084f5004845f6f5a5f7769f52d53f40e21952a6d87411bafe34af4a01e65f9926002e38e1dac - languageName: node - linkType: hard - -"supports-color@npm:^7.1.0": - version: 7.2.0 - resolution: "supports-color@npm:7.2.0" - dependencies: - has-flag: ^4.0.0 - checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a - languageName: node - linkType: hard - -"supports-color@npm:^8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: ^4.0.0 - checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 - languageName: node - linkType: hard - -"throttleit@npm:^1.0.0": - version: 1.0.0 - resolution: "throttleit@npm:1.0.0" - checksum: 1b2db4d2454202d589e8236c07a69d2fab838876d370030ebea237c34c0a7d1d9cf11c29f994531ebb00efd31e9728291042b7754f2798a8352ec4463455b659 - languageName: node - linkType: hard - -"through@npm:^2.3.8": - version: 2.3.8 - resolution: "through@npm:2.3.8" - checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd - languageName: node - linkType: hard - -"tmp@npm:~0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: ^3.0.0 - checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e - languageName: node - linkType: hard - -"to-regex-range@npm:^5.0.1": - version: 5.0.1 - resolution: "to-regex-range@npm:5.0.1" - dependencies: - is-number: ^7.0.0 - checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed - languageName: node - linkType: hard - -"tough-cookie@npm:^4.1.3": - version: 4.1.3 - resolution: "tough-cookie@npm:4.1.3" - dependencies: - psl: ^1.1.33 - punycode: ^2.1.1 - universalify: ^0.2.0 - url-parse: ^1.5.3 - checksum: c9226afff36492a52118432611af083d1d8493a53ff41ec4ea48e5b583aec744b989e4280bcf476c910ec1525a89a4a0f1cae81c08b18fb2ec3a9b3a72b91dcc - languageName: node - linkType: hard - -"tslib@npm:^2.1.0": - version: 2.5.0 - resolution: "tslib@npm:2.5.0" - checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 - languageName: node - linkType: hard - -"tunnel-agent@npm:^0.6.0": - version: 0.6.0 - resolution: "tunnel-agent@npm:0.6.0" - dependencies: - safe-buffer: ^5.0.1 - checksum: 05f6510358f8afc62a057b8b692f05d70c1782b70db86d6a1e0d5e28a32389e52fa6e7707b6c5ecccacc031462e4bc35af85ecfe4bbc341767917b7cf6965711 - languageName: node - linkType: hard - -"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": - version: 0.14.5 - resolution: "tweetnacl@npm:0.14.5" - checksum: 6061daba1724f59473d99a7bb82e13f211cdf6e31315510ae9656fefd4779851cb927adad90f3b488c8ed77c106adc0421ea8055f6f976ff21b27c5c4e918487 - languageName: node - linkType: hard - -"type-fest@npm:^0.21.3": - version: 0.21.3 - resolution: "type-fest@npm:0.21.3" - checksum: e6b32a3b3877f04339bae01c193b273c62ba7bfc9e325b8703c4ee1b32dc8fe4ef5dfa54bf78265e069f7667d058e360ae0f37be5af9f153b22382cd55a9afe0 - languageName: node - linkType: hard - -"universalify@npm:^0.2.0": - version: 0.2.0 - resolution: "universalify@npm:0.2.0" - checksum: e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5 - languageName: node - linkType: hard - -"universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 - languageName: node - linkType: hard - -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 - languageName: node - linkType: hard - -"url-parse@npm:^1.5.3": - version: 1.5.10 - resolution: "url-parse@npm:1.5.10" - dependencies: - querystringify: ^2.1.1 - requires-port: ^1.0.0 - checksum: fbdba6b1d83336aca2216bbdc38ba658d9cfb8fc7f665eb8b17852de638ff7d1a162c198a8e4ed66001ddbf6c9888d41e4798912c62b4fd777a31657989f7bdf - languageName: node - linkType: hard - -"uuid@npm:^8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" - bin: - uuid: dist/bin/uuid - checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df - languageName: node - linkType: hard - -"verror@npm:1.10.0": - version: 1.10.0 - resolution: "verror@npm:1.10.0" - dependencies: - assert-plus: ^1.0.0 - core-util-is: 1.0.2 - extsprintf: ^1.2.0 - checksum: c431df0bedf2088b227a4e051e0ff4ca54df2c114096b0c01e1cbaadb021c30a04d7dd5b41ab277bcd51246ca135bf931d4c4c796ecae7a4fef6d744ecef36ea - languageName: node - linkType: hard - -"which-boxed-primitive@npm:^1.0.2": - version: 1.0.2 - resolution: "which-boxed-primitive@npm:1.0.2" - dependencies: - is-bigint: ^1.0.1 - is-boolean-object: ^1.1.0 - is-number-object: ^1.0.4 - is-string: ^1.0.5 - is-symbol: ^1.0.3 - checksum: 53ce774c7379071729533922adcca47220228405e1895f26673bbd71bdf7fb09bee38c1d6399395927c6289476b5ae0629863427fd151491b71c4b6cb04f3a5e - languageName: node - linkType: hard - -"which-collection@npm:^1.0.1": - version: 1.0.1 - resolution: "which-collection@npm:1.0.1" - dependencies: - is-map: ^2.0.1 - is-set: ^2.0.1 - is-weakmap: ^2.0.1 - is-weakset: ^2.0.1 - checksum: c815bbd163107ef9cb84f135e6f34453eaf4cca994e7ba85ddb0d27cea724c623fae2a473ceccfd5549c53cc65a5d82692de418166df3f858e1e5dc60818581c - languageName: node - linkType: hard - -"which-typed-array@npm:^1.1.9": - version: 1.1.9 - resolution: "which-typed-array@npm:1.1.9" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - is-typed-array: ^1.1.10 - checksum: fe0178ca44c57699ca2c0e657b64eaa8d2db2372a4e2851184f568f98c478ae3dc3fdb5f7e46c384487046b0cf9e23241423242b277e03e8ba3dabc7c84c98ef - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: ^2.0.0 - bin: - node-which: ./bin/node-which - checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 - languageName: node - linkType: hard - -"wrap-ansi@npm:^6.2.0": - version: 6.2.0 - resolution: "wrap-ansi@npm:6.2.0" - dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a - languageName: node - linkType: hard - -"wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 - languageName: node - linkType: hard - -"yauzl@npm:^2.10.0": - version: 2.10.0 - resolution: "yauzl@npm:2.10.0" - dependencies: - buffer-crc32: ~0.2.3 - fd-slicer: ~1.1.0 - checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b - languageName: node - linkType: hard +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.9.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" + integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== + dependencies: + regenerator-runtime "^0.14.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@cypress/request@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" + integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + http-signature "~1.3.6" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "6.10.4" + safe-buffer "^5.1.2" + tough-cookie "^4.1.3" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@plone/volto-testing@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@plone/volto-testing/-/volto-testing-4.0.0.tgz#172d1cb0c174c722b8b4f230ad204528ee4da8b9" + integrity sha512-0fnrxZEDMg04Ml5NGGkDai0DX3zfOZz1RQ7XjZg/+rhRMemiu53CwU1QOsnOoXhhoPx9/h+UHgLFEQhlR+3MvQ== + dependencies: + "@testing-library/cypress" "9.0.0" + "@testing-library/jest-dom" "5.16.4" + "@testing-library/react" "12.1.5" + axe-core "4.6.3" + cypress "13.1.0" + cypress-axe "1.5.0" + cypress-file-upload "5.0.8" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@testing-library/cypress@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/cypress/-/cypress-9.0.0.tgz#3facad49c4654a99bbd138f83f33b415d2d6f097" + integrity sha512-c1XiCGeHGGTWn0LAU12sFUfoX3qfId5gcSE2yHode+vsyHDWraxDPALjVnHd4/Fa3j4KBcc5k++Ccy6A9qnkMA== + dependencies: + "@babel/runtime" "^7.14.6" + "@testing-library/dom" "^8.1.0" + +"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.1.0": + version "8.20.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" + integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@5.16.4": + version "5.16.4" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd" + integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA== + dependencies: + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + +"@testing-library/react@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" + integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.0.0" + "@types/react-dom" "<18.0.0" + +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@*": + version "29.5.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/node@*": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== + dependencies: + undici-types "~5.26.4" + +"@types/node@^16.18.39": + version "16.18.98" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.98.tgz#3554bb7911ea2bbc3a528be0776d6ab16b7674d2" + integrity sha512-fpiC20NvLpTLAzo3oVBKIqBGR6Fx/8oAK/SSf7G+fydnXMY1x4x9RZ6sBXhqKlCU21g2QapUsbLlhv3+a7wS+Q== + +"@types/prop-types@*": + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + +"@types/react-dom@<18.0.0": + version "17.0.25" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" + integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== + dependencies: + "@types/react" "^17" + +"@types/react@^17": + version "17.0.80" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.80.tgz#a5dfc351d6a41257eb592d73d3a85d3b7dbcbb41" + integrity sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "^0.16" + csstype "^3.0.2" + +"@types/scheduler@^0.16": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== + +"@types/sizzle@^2.3.2": + version "2.3.8" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627" + integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg== + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.9" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" + integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== + dependencies: + "@types/jest" "*" + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +aria-query@^5.0.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@^3.2.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.0.tgz#d9b802e9bb9c248d7be5f7f5ef178dc3684e9dcc" + integrity sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g== + +axe-core@4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" + integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +cachedir@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" + integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-table3@~0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.16: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +cypress-axe@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.5.0.tgz#95082734583da77b51ce9b7784e14a442016c7a1" + integrity sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ== + +cypress-file-upload@5.0.8: + version "5.0.8" + resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" + integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== + +cypress@13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.1.0.tgz#18f268e66662cd91b1766db18bd1f63a66592205" + integrity sha512-LUKxCYlB973QBFls1Up4FAE9QIYobT+2I8NvvAwMfQS2YwsWbr6yx7y9hmsk97iqbHkKwZW3MRjoK1RToBFVdQ== + dependencies: + "@cypress/request" "^3.0.0" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^16.18.39" + "@types/sinonjs__fake-timers" "8.1.1" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + buffer "^5.6.0" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.1" + commander "^6.2.1" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.4" + enquirer "^2.3.6" + eventemitter2 "6.4.7" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.8" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + process "^0.11.10" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + semver "^7.5.3" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + yauzl "^2.10.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +dayjs@^1.10.4: + version "1.11.11" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" + integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.1, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enquirer@^2.3.6: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-bigints@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +internal-slot@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-ci@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.10.4: + version "6.10.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" + integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== + dependencies: + side-channel "^1.0.4" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== + dependencies: + throttleit "^1.0.0" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rxjs@^7.5.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.5.3: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.14.1: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +throttleit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5" + integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ== + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@~0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tslib@^2.1.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.13: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" From 7d98efff564167fa6d75bddb0624f247453456c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 16:04:18 +0200 Subject: [PATCH 078/226] TO BE REVERTED! Temporary testing with unreleased collective.elastic.plone --- dockerfiles/backend/Dockerfile.acceptance | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index e038025f..cdb85742 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -15,6 +15,8 @@ RUN <=2.0.0 bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 + # TODO Switch back to released + bin/pip install git+https://github.com/collective/collective.elastic.plone.git@testing-profiles EOT From e4f06bb2969b7fec322a09e8c87f7964eae91a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:05:51 +0200 Subject: [PATCH 079/226] Update Dockerfile.acceptance --- dockerfiles/backend/Dockerfile.acceptance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index cdb85742..28c21883 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -14,8 +14,8 @@ WORKDIR /app RUN <=2.0.0 - bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 # TODO Switch back to released + # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 bin/pip install git+https://github.com/collective/collective.elastic.plone.git@testing-profiles EOT From 2ee99620c657f7f41d691bd317967af4e2b55f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:06:06 +0200 Subject: [PATCH 080/226] Add TODOs --- src/components/Blocks/schema.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 60c41258..e5216c9b 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -1,3 +1,4 @@ +// TODO translations title and descriptions of fields import { hasNonValueOperation, hasDateOperation, From 9e7c66be20f18f1450e5f578bfacd25dfc9a7c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:07:07 +0200 Subject: [PATCH 081/226] clean up docker-compose acceptance --- acceptance/docker-compose.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 3668c828..1043ec85 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -15,9 +15,6 @@ services: 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 @@ -56,13 +53,13 @@ services: environment: ZSERVER_HOST: '0.0.0.0' ZSERVER_PORT: '55001' - CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_NAME: ${INDEX_NAME?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' @@ -83,13 +80,13 @@ services: environment: ZSERVER_HOST: '0.0.0.0' ZSERVER_PORT: '55001' - CELERY_BROKER: ${CELERY_BROKER?unset} INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_NAME: multilingual INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} + CELERY_BROKER: ${CELERY_BROKER?unset} CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' @@ -194,7 +191,7 @@ services: expose: - '5601' environment: - OPENSEARCH_HOSTS: '["https://opensearch:9200"]' + OPENSEARCH_HOSTS: "['https://opensearch:9200']" profiles: - dev - multilingual From 25d70888cc6b68baa7d922e203f6ba12bb5d7b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:07:22 +0200 Subject: [PATCH 082/226] clean up docker-compose CI --- acceptance/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 852e65ac..78937784 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -12,8 +12,6 @@ services: 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: @@ -141,7 +139,7 @@ services: - multilingual redis: - image: 'redis:latest' + image: "redis:latest" ports: - 6379:6379 From 531ae9b391db4a7acdfe9aa9465db869471b267c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:09:16 +0200 Subject: [PATCH 083/226] Update create_search.monolingual.cy.js --- acceptance/cypress/tests/create_search.monolingual.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/create_search.monolingual.cy.js b/acceptance/cypress/tests/create_search.monolingual.cy.js index 58d5c530..703d7496 100644 --- a/acceptance/cypress/tests/create_search.monolingual.cy.js +++ b/acceptance/cypress/tests/create_search.monolingual.cy.js @@ -49,7 +49,7 @@ describe('Searchkit block tests- create search ', () => { cy.removeContent({ path: 'searching' }); }); - it('As manager I can add a searchkit-block and find a documunt', function () { + it('As manager I can add a searchkit-block and find a document', function () { cy.visit('/searching'); cy.get('a.edit').click(); From 3c19ef413606f4411030e57c64507c9125e6c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:22:11 +0200 Subject: [PATCH 084/226] Update ci.yml --- acceptance/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 78937784..f10ee093 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -112,6 +112,8 @@ services: PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + volumes: + - ../dockerfiles/opensearch/ingest-configuration:/configuration profiles: - prod @@ -135,6 +137,8 @@ services: PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} PLONE_PASSWORD: ${PLONE_PASSWORD?unset} + volumes: + - ../dockerfiles/opensearch/ingest-configuration:/configuration profiles: - multilingual From df36c3de6493a5a95dd2ec8a08a7fff31d759a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:46:54 +0200 Subject: [PATCH 085/226] Update Dockerfile.acceptance --- dockerfiles/backend/Dockerfile.acceptance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 28c21883..8460d7f0 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -16,7 +16,7 @@ RUN <=2.0.0 # TODO Switch back to released # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install git+https://github.com/collective/collective.elastic.plone.git@testing-profiles + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles EOT From 61d93dc97f36a37b727d1e660c43b1d5484d8a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 16 Jun 2024 20:52:05 +0200 Subject: [PATCH 086/226] Update Dockerfile.acceptance --- dockerfiles/backend/Dockerfile.acceptance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 8460d7f0..68064d92 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -16,7 +16,7 @@ RUN <=2.0.0 # TODO Switch back to released # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles' EOT From f1b114f7ea0b66e07089284aed42b12e2f0392e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 17 Jun 2024 08:28:23 +0200 Subject: [PATCH 087/226] Update ci.yml Explicitly export port of frontend --- acceptance/ci.yml | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index f10ee093..8682ba55 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -1,4 +1,4 @@ -version: "3" +version: '3' services: addon-acceptance: @@ -6,12 +6,14 @@ services: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.ci args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" + 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: @@ -22,8 +24,8 @@ services: context: ../ dockerfile: ./dockerfiles/frontend/Dockerfile.ci.multilingual args: - ADDON_NAME: "${ADDON_NAME}" - ADDON_PATH: "${ADDON_PATH}" + ADDON_NAME: '${ADDON_NAME}' + ADDON_PATH: '${ADDON_PATH}' VOLTO_VERSION: ${VOLTO_VERSION:-17} environment: RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-multilingual:55001/plone @@ -34,7 +36,6 @@ services: - backend-acceptance-multilingual profiles: - multilingual - backend-acceptance: build: @@ -43,18 +44,17 @@ services: args: PLONE_VERSION: ${PLONE_VERSION:-6.0} environment: - ZSERVER_HOST: 0.0.0.0 - ZSERVER_PORT: 55001 - # plone.elastic 2.x + ZSERVER_HOST: '0.0.0.0' + ZSERVER_PORT: '55001' INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex - INSTALL_PRODUCTS: "collective.elastic.plone" + INSTALL_PRODUCTS: 'collective.elastic.plone' ports: - 55001:55001 depends_on: @@ -64,7 +64,6 @@ services: profiles: - dev - prod - backend-acceptance-multilingual: build: @@ -82,9 +81,9 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: "plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone" + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex - INSTALL_PRODUCTS: "collective.elastic.plone" + INSTALL_PRODUCTS: 'collective.elastic.plone' ports: - 55001:55001 depends_on: @@ -97,9 +96,9 @@ services: ingest: image: ghcr.io/collective/collective.elastic.ingest:latest environment: - MAPPINGS_FILE: "/configuration/mappings.json" - ANALYSIS_FILE: "/configuration/analysis.json" - PREPROCESSINGS_FILE: "/configuration/preprocessings.json" + MAPPINGS_FILE: '/configuration/mappings.json' + ANALYSIS_FILE: '/configuration/analysis.json' + PREPROCESSINGS_FILE: '/configuration/preprocessings.json' INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} INDEX_USE_SSL: ${INDEX_USE_SSL?unset} @@ -121,9 +120,9 @@ services: image: ghcr.io/collective/collective.elastic.ingest:latest environment: # Different INDEX_NAME - MAPPINGS_FILE: "/configuration/mappings.json" - ANALYSIS_FILE: "/configuration/analysis.json" - PREPROCESSINGS_FILE: "/configuration/preprocessings.json" + MAPPINGS_FILE: '/configuration/mappings.json' + ANALYSIS_FILE: '/configuration/analysis.json' + PREPROCESSINGS_FILE: '/configuration/preprocessings.json' INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_NAME: multilingual INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} @@ -143,7 +142,7 @@ services: - multilingual redis: - image: "redis:latest" + image: 'redis:latest' ports: - 6379:6379 @@ -157,7 +156,7 @@ services: - node.name=opensearch - discovery.type=single-node - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g" + - 'OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx2g' - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${INDEX_PASSWORD?unset} ulimits: memlock: From 1b87e16c1a9f103f4aba9c103d3992e5a04e55c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 17 Jun 2024 09:05:00 +0200 Subject: [PATCH 088/226] Update Dockerfile.acceptance.multilingual --- dockerfiles/backend/Dockerfile.acceptance.multilingual | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index f5d78692..fcddfb6b 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -16,7 +16,9 @@ WORKDIR /app RUN <=2.0.0 - bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 + # TODO Switch back to released + # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles' EOT From 3dd350e4d28ea4adc60e46830f73adf8e17d7c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 17 Jun 2024 10:48:49 +0200 Subject: [PATCH 089/226] Fix cypress multilingual --- .../cypress/tests/language.multilingual.cy.js | 127 +++++++++--------- 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index 9a0579e9..15028f72 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -1,70 +1,65 @@ describe('Searchkit block tests – search - multilingual', () => { - before(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'searching', - contentTitle: 'Searching and Finding', - path: '/en', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'garden-in-february', - contentTitle: 'The garden in february', - path: '/en', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'der-garten-im-februar', - contentTitle: 'Der Garten im Februar', - path: '/de', - }); - - - // Add search block to /suche - cy.visit('/en/searching'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Unfold Common blocks"]').click(); - cy.get('.blocks-chooser .common .button.searchkitblock').click({ - force: true, - }); - - cy.get('#toolbar-save').click(); - cy.wait('@content'); + before(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: 'en', }); - - beforeEach(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); - cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); - - cy.autologin(); - - cy.visit('/en/searching'); - cy.wait(1000); + + cy.createContent({ + contentType: 'Document', + contentId: 'der-garten-im-februar', + contentTitle: 'Der Garten im Februar', + path: 'de', }); - - after(() => { - cy.removeContent({ path: 'en/garden-in-february' }); - cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'de/der-garten-im-februar' }); - }); - - - it('I can search within language', function () { - cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - cy.get('.block.searchkitsearch').should('not.contain', 'Der Garten im Februar'); - }); - + + // Add search block + cy.visit('/en/edit'); + + cy.getSlate().clear().type('{enter}'); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common') + .contains('Searchkit') + .click({ force: true }); + + cy.get('#toolbar-save').click(); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/en'); + cy.wait('@content'); + }); + + after(() => { + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'de/der-garten-im-februar' }); + }); + + it('I can search within language', function () { + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + }); + + it('I can search within language', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.block.searchkitsearch').should( + 'not.contain', + 'Der Garten im Februar', + ); }); - \ No newline at end of file +}); From 5505df15d48894504caef34dfbf8494a2c871419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 17 Jun 2024 11:11:53 +0200 Subject: [PATCH 090/226] Update language.multilingual.cy.js --- acceptance/cypress/tests/language.multilingual.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index 15028f72..2dbaac5a 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -49,7 +49,7 @@ describe('Searchkit block tests – search - multilingual', () => { cy.removeContent({ path: 'de/der-garten-im-februar' }); }); - it('I can search within language', function () { + it('I can search', function () { cy.get('.searchbar-wrapper input').type('february{enter}'); cy.get('.block.searchkitsearch').contains('The garden in february'); }); From 61834ad9c9b0b733380b379810ff8922e8e36e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 17 Jun 2024 11:12:14 +0200 Subject: [PATCH 091/226] Update acceptance.yml: Split into two jobs --- .github/workflows/acceptance.yml | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 73bc2d08..05f2c5b2 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -26,7 +26,7 @@ env: PREPROCESSINGS_FILE: /configuration/preprocessings.json jobs: - acceptance: + acceptance-monolingual: runs-on: ubuntu-latest timeout-minutes: 45 strategy: @@ -58,6 +58,37 @@ jobs: docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' + # Upload Cypress screenshots + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots-acceptance + path: acceptance/cypress/screenshots + + # Upload Cypress videos + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-videos-acceptance + path: acceptance/cypress/videos + + acceptance-multilingual: + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install Cypress + run: | + cd acceptance + yarn + - name: 'Cypress: Acceptance tests - multilingual' uses: cypress-io/github-action@v6 env: From f6aa4c4c8b0d9c6c2614aabfbda2f073c7c452fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 11:11:56 +0200 Subject: [PATCH 092/226] Update ci.yml --- acceptance/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 8682ba55..e617d22a 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -32,8 +32,8 @@ services: RAZZLE_API_PATH: http://localhost:55001/plone ports: - 3000:3000 - depends_on: - - backend-acceptance-multilingual + # depends_on: + # - backend-acceptance-multilingual profiles: - multilingual @@ -72,8 +72,8 @@ services: args: PLONE_VERSION: ${PLONE_VERSION:-6.0} environment: - ZSERVER_HOST: 0.0.0.0 - ZSERVER_PORT: 55001 + ZSERVER_HOST: '0.0.0.0' + ZSERVER_PORT: '55001' # plone.elastic 2.x INDEX_SERVER: ${INDEX_SERVER?unset} INDEX_OPENSEARCH: ${INDEX_OPENSEARCH?unset} From 4ac9f6762f7d25ef63411fdd0a5eed44716770c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 11:12:19 +0200 Subject: [PATCH 093/226] Fix tests --- acceptance/cypress/tests/search.monolingual.cy.js | 4 +--- acceptance/cypress/tests/search.multilingual.cy.js | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 45c39478..74a8a0ad 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -48,7 +48,6 @@ describe('Searchkit block tests – search', () => { contentTitle: 'Testseite Stelle', }); - // Add search block to /suche cy.visit('/suche'); cy.get('a.edit').click(); @@ -67,7 +66,7 @@ describe('Searchkit block tests – search', () => { beforeEach(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); - cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); @@ -156,5 +155,4 @@ describe('Searchkit block tests – search', () => { // cy.get('.searchbar-wrapper input').type('Montag{enter}'); // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); // }); - }); diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index 87020a76..b3e7c385 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -2,6 +2,7 @@ describe('Searchkit block tests – search', () => { before(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); @@ -54,7 +55,6 @@ describe('Searchkit block tests – search', () => { path: '/de', }); - // Add search block to /suche cy.visit('/de/suche'); cy.get('a.edit').click(); @@ -68,12 +68,13 @@ describe('Searchkit block tests – search', () => { cy.get('#toolbar-save').click(); cy.wait('@content'); + cy.wait('@kitsearch'); }); beforeEach(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); - cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); @@ -86,11 +87,11 @@ describe('Searchkit block tests – search', () => { // cy.removeContent({ path: 'de/garten-blog/februar' }); // cy.removeContent({ path: 'de/garten-blog/marz' }); cy.removeContent({ path: 'de/garten-blog' }); - cy.removeContent({ path: 'de/suche' }); cy.removeContent({ path: 'de/testseite-mann' }); cy.removeContent({ path: 'de/testseite-manner' }); cy.removeContent({ path: 'de/testseite-lsb' }); cy.removeContent({ path: 'de/testseite-s' }); + cy.removeContent({ path: 'de/suche' }); }); it('I see all if no filter selected', function () { @@ -149,7 +150,9 @@ describe('Searchkit block tests – search', () => { cy.getSlate().click(); cy.log('when I add a text block'); - cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( + 'Montags gehen wir in den Zoo.', + ); // cy.toolbarSave(); cy.get('#toolbar-save').click(); cy.wait('@content'); @@ -162,5 +165,4 @@ describe('Searchkit block tests – search', () => { cy.get('.searchbar-wrapper input').type('Montag{enter}'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); - }); From c5f3cabc3d34b4461fca46d794b1d36a07f40710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 11:13:09 +0200 Subject: [PATCH 094/226] Get testing profiles from rohberg.volto --- dockerfiles/backend/Dockerfile.acceptance | 5 +++-- dockerfiles/backend/Dockerfile.acceptance.multilingual | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 68064d92..c5c7a3f7 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -14,9 +14,10 @@ WORKDIR /app RUN <=2.0.0 - # TODO Switch back to released + # TODO Switch back to released collective.elastic.plone # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles' + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@restapi-search-anonymous-allowedRolesAndUsers' + bin/pip install 'rohberg.volto[test] @ git+https://github.com/rohberg/rohberg.volto.git' EOT diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index fcddfb6b..d1a0b52f 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -16,9 +16,10 @@ WORKDIR /app RUN <=2.0.0 - # TODO Switch back to released + # TODO Switch back to released collective.elastic.plone # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@testing-profiles' + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@restapi-search-anonymous-allowedRolesAndUsers' + bin/pip install 'rohberg.volto[test] @ git+https://github.com/rohberg/rohberg.volto.git' EOT From 7bc69b5482fcf3a0f36f3adcd03e4a1c2422a301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 11:16:36 +0200 Subject: [PATCH 095/226] Update language.multilingual.cy.js --- acceptance/cypress/tests/language.multilingual.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index 2dbaac5a..8855b784 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -36,7 +36,7 @@ describe('Searchkit block tests – search - multilingual', () => { beforeEach(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); - cy.intercept('GET', '/**/@kitsearch').as('kitsearch'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); From c363bbba5e6815597ee6c8a3c49667b01103055f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 12:19:34 +0200 Subject: [PATCH 096/226] Get testing profiles from rohberg.volto --- dockerfiles/backend/Dockerfile.acceptance | 2 +- dockerfiles/backend/Dockerfile.acceptance.multilingual | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index c5c7a3f7..9c8b19e7 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -22,7 +22,7 @@ EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="collective.elastic.plone:monolingual" +ENV APPLY_PROFILES="rohberg.volto:monolingual" # Copy /app from builder COPY --from=builder /app /app diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index d1a0b52f..64251df5 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -24,7 +24,7 @@ EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="collective.elastic.plone:multilingual" +ENV APPLY_PROFILES="rohberg.volto:multilingual" # Copy /app from builder COPY --from=builder /app /app From bc4e78e719286e6e64f572643d217b7ed45ebab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 12:19:43 +0200 Subject: [PATCH 097/226] Fix tests --- .../cypress/tests/language.multilingual.cy.js | 22 +-- .../tests/search.anonymous.multilingual.cy.js | 94 +++++++++++++ .../cypress/tests/search.monolingual.cy.js | 133 +++++++++--------- .../cypress/tests/search.multilingual.cy.js | 14 +- 4 files changed, 176 insertions(+), 87 deletions(-) create mode 100644 acceptance/cypress/tests/search.anonymous.multilingual.cy.js diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index 8855b784..e44bf344 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -1,10 +1,16 @@ describe('Searchkit block tests – search - multilingual', () => { before(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching', + path: 'en', + }); + cy.createContent({ contentType: 'Document', contentId: 'garden-in-february', @@ -20,7 +26,7 @@ describe('Searchkit block tests – search - multilingual', () => { }); // Add search block - cy.visit('/en/edit'); + cy.visit('/en/searching/edit'); cy.getSlate().clear().type('{enter}'); cy.get('.button .block-add-button').click({ force: true }); @@ -30,21 +36,19 @@ describe('Searchkit block tests – search - multilingual', () => { .click({ force: true }); cy.get('#toolbar-save').click(); - cy.wait('@content'); + cy.wait('@kitsearch'); }); beforeEach(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.autologin(); - cy.visit('/en'); - cy.wait('@content'); + cy.visit('/en/searching/edit'); + cy.wait('@kitsearch'); }); after(() => { + cy.removeContent({ path: 'en/searching' }); cy.removeContent({ path: 'en/garden-in-february' }); cy.removeContent({ path: 'de/der-garten-im-februar' }); }); diff --git a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js new file mode 100644 index 00000000..504f0356 --- /dev/null +++ b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js @@ -0,0 +1,94 @@ +describe('Searchkit block tests – search - anonymous', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching', + path: 'en', + }); + + cy.setWorkflow({ + path: 'en/searching', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: 'en', + }); + + cy.setWorkflow({ + path: 'en/garden-in-february', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-march', + contentTitle: 'The garden in march', + path: 'en', + }); + + // Add search block + cy.visit('/en/searching/edit'); + + cy.getSlate().clear().type('{enter}'); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common') + .contains('Searchkit') + .click({ force: true }); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/en/searching'); + cy.wait('@kitsearch'); + }); + + after(() => { + cy.removeContent({ path: 'en/searching' }); + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'en/garden-in-march' }); + }); + + it('I can search', function () { + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.searchbar-wrapper input').clear().type('march{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in march'); + }); + + it('As anonymous I see only published content', function () { + cy.intercept('/**/@logout').as('logout'); + + cy.visit('/logout'); + cy.wait('@logout'); + + cy.visit('/en/searching'); + cy.wait('@kitsearch'); + + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + + cy.get('.searchbar-wrapper input').clear().type('march{enter}'); + cy.get('.block.searchkitsearch').should( + 'not.contain', + 'The garden in march', + ); + }); +}); diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 74a8a0ad..cd24ce1a 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -1,7 +1,6 @@ describe('Searchkit block tests – search', () => { before(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); @@ -60,19 +59,14 @@ describe('Searchkit block tests – search', () => { }); cy.get('#toolbar-save').click(); - cy.wait('@content'); + cy.wait('@kitsearch'); }); beforeEach(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.autologin(); cy.visit('/suche'); - cy.wait(3000); - // cy.wait('@kitsearch'); + cy.wait('@kitsearch'); }); after(() => { @@ -86,15 +80,15 @@ describe('Searchkit block tests – search', () => { cy.removeContent({ path: 'testseite-s' }); }); - // it('I see all if no filter selected', function () { - // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - // }); + it('I see all if no filter selected', function () { + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); - // it('I can search fuzzy', function () { - // cy.get('.searchbar-wrapper input').type('februax{enter}'); - // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - // cy.get('.block.searchkitsearch').should('not.contain', 'März'); - // }); + it('I can search fuzzy', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').should('not.contain', 'März'); + }); it('I can search with inflection', function () { cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); @@ -104,55 +98,58 @@ describe('Searchkit block tests – search', () => { cy.get('.block.searchkitsearch').contains('Testseite Männer'); }); - // it('I can search with decompounding', function () { - // cy.get('.searchbar-wrapper input').type('Garten{enter}'); - // cy.get('.block.searchkitsearch').contains('Garten-Blog'); - - // cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - // cy.get('.block.searchkitsearch').contains('Februar'); - // }); - - // it('I can search with wildcard', function () { - // cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - // }); - - // it('I can search for an exact match', function () { - // cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - // cy.get('.block.searchkitsearch').contains('Testseite Mann'); - // cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - // cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); - // }); - - // it('I can search for a compounded word', function () { - // cy.get('.searchbar-wrapper input').type('stelle{enter}'); - // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - // cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); - // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - // cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); - // cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - // cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); - // cy.get('.block.searchkitsearch').contains('Testseite Stelle'); - // }); - - // // Blocks text - // it('I can search in blocks', function () { - // cy.visit('/garten-blog/februar'); - // cy.get('a.edit').click(); - - // cy.getSlate().click(); - // cy.log('when I add a text block'); - // cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains('Montags gehen wir in den Zoo.'); - // // cy.toolbarSave(); - // cy.get('#toolbar-save').click(); - // cy.wait('@content'); - - // cy.log('I added a text block'); - - // // Searching - // cy.visit('/suche'); - // cy.wait(3000); - // cy.get('.searchbar-wrapper input').type('Montag{enter}'); - // cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - // }); + it('I can search with decompounding', function () { + cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.get('.block.searchkitsearch').contains('Garten-Blog'); + + cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.get('.block.searchkitsearch').contains('Februar'); + }); + + it('I can search with wildcard', function () { + cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search for an exact match', function () { + cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); + }); + + it('I can search for a compounded word', function () { + cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + }); + + // Blocks text + it('I can search in blocks', function () { + cy.visit('/garten-blog/februar'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( + 'Montags gehen wir in den Zoo.', + ); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@content'); + + cy.log('I added a text block'); + + // Searching + cy.visit('/suche'); + cy.wait('@kitsearch'); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.wait('@kitsearch'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); }); diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index b3e7c385..f95396c3 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -1,7 +1,5 @@ describe('Searchkit block tests – search', () => { before(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); @@ -67,20 +65,16 @@ describe('Searchkit block tests – search', () => { }); cy.get('#toolbar-save').click(); - cy.wait('@content'); cy.wait('@kitsearch'); }); beforeEach(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); cy.visit('/de/suche'); - cy.wait(3000); - // cy.wait('@kitsearch'); + cy.wait('@kitsearch'); }); after(() => { @@ -155,13 +149,13 @@ describe('Searchkit block tests – search', () => { ); // cy.toolbarSave(); cy.get('#toolbar-save').click(); - cy.wait('@content'); + cy.wait('@kitsearch'); cy.log('I added a text block'); // Searching - cy.visit('de//suche'); - cy.wait(3000); + cy.visit('de/suche'); + cy.wait('@kitsearch'); cy.get('.searchbar-wrapper input').type('Montag{enter}'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); From 8b77baab08fd62c29fbe157caf9c7b0ed4ad44bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 13:26:50 +0200 Subject: [PATCH 098/226] Volto 18 --- .github/workflows/acceptance.yml | 2 +- .github/workflows/code.yml | 6 +++--- .github/workflows/unit.yml | 6 +++--- acceptance/ci.yml | 6 ++---- dockerfiles/docker-compose.yml | 6 ++---- variables.mk | 2 +- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 05f2c5b2..7dd4c139 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -4,7 +4,7 @@ on: [push] env: ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 + VOLTO_VERSION: 18.0.0-alpha.35 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 42f5b627..8e8a0707 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -2,16 +2,16 @@ name: Code analysis checks on: [push] env: - ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 + VOLTO_VERSION: 18.0.0-alpha.35 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: "oxczBG).3xWyapLn" + INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 CELERY_LOG_LEVEL: info diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 8b8f0ebc..038ae902 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -2,16 +2,16 @@ name: Unit Tests on: [push] env: - ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 + VOLTO_VERSION: 18.0.0-alpha.35 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: "oxczBG).3xWyapLn" + INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 CELERY_LOG_LEVEL: info diff --git a/acceptance/ci.yml b/acceptance/ci.yml index e617d22a..a22d3d08 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -1,5 +1,3 @@ -version: '3' - services: addon-acceptance: build: @@ -8,7 +6,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-17} + VOLTO_VERSION: ${VOLTO_VERSION:-18} environment: RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone @@ -26,7 +24,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-17} + VOLTO_VERSION: ${VOLTO_VERSION:-18} environment: RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-multilingual:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 7edf1ddd..cc313a2c 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: addon-dev: build: @@ -8,7 +6,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: '${VOLTO_VERSION:-17}' + VOLTO_VERSION: '${VOLTO_VERSION:-18}' volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: @@ -36,7 +34,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: '${VOLTO_VERSION:-17}' + VOLTO_VERSION: '${VOLTO_VERSION:-18}' environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: http://localhost:8080/Plone diff --git a/variables.mk b/variables.mk index 75e4bed0..1adb194a 100644 --- a/variables.mk +++ b/variables.mk @@ -1,5 +1,5 @@ PLONE_VERSION=6.0.11.1 -VOLTO_VERSION=17.16.0 +VOLTO_VERSION=18.0.0-alpha.35 ADDON_NAME='@rohberg/volto-searchkit-block' ADDON_PATH='volto-searchkit-block' From ffeb2241de613e2fadda6d067a6092dac6376a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 13:27:06 +0200 Subject: [PATCH 099/226] Add missing arg rohberg.volto --- acceptance/docker-compose.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index 1043ec85..d4825bf6 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: addon-acceptance: build: @@ -8,13 +6,16 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-17} + VOLTO_VERSION: ${VOLTO_VERSION:-18} 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 @@ -28,7 +29,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-17} + VOLTO_VERSION: ${VOLTO_VERSION:-18} volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: @@ -60,7 +61,7 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone,rohberg.volto' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' volumes: @@ -87,7 +88,7 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone,rohberg.volto' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' volumes: From 264815570394c11996da46d5b5581898d75bff93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 13:27:26 +0200 Subject: [PATCH 100/226] set build seed backend --- dockerfiles/backend/Dockerfile.acceptance | 3 ++- dockerfiles/backend/Dockerfile.acceptance.multilingual | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 9c8b19e7..09aea68b 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -2,7 +2,7 @@ # Dockerfile from cookiecutter-plone-starter ARG PLONE_VERSION=6.0 -ARG SEED=2 +ARG SEED=6 FROM plone/server-builder:${PLONE_VERSION} as builder WORKDIR /app @@ -12,6 +12,7 @@ WORKDIR /app # Install local requirements RUN <=2.0.0 # TODO Switch back to released collective.elastic.plone diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index 64251df5..2330c0f5 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -4,7 +4,7 @@ # Run robotserver verbose ARG PLONE_VERSION=6.0 -ARG SEED=5 +ARG SEED=6 FROM plone/server-builder:${PLONE_VERSION} as builder WORKDIR /app @@ -14,6 +14,7 @@ WORKDIR /app # Install local requirements RUN <=2.0.0 # TODO Switch back to released collective.elastic.plone From d56ac87f4d2510c887a7980c5c10a451a33a757b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 13:44:49 +0200 Subject: [PATCH 101/226] yarn -> pnpm. We are testing with Volt 18 --- dockerfiles/frontend/Dockerfile.acceptance | 2 +- .../frontend/Dockerfile.acceptance.multilingual | 2 +- dockerfiles/frontend/Dockerfile.ci | 10 +++++----- dockerfiles/frontend/Dockerfile.ci.multilingual | 10 +++++----- dockerfiles/frontend/Dockerfile.dev | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dockerfiles/frontend/Dockerfile.acceptance b/dockerfiles/frontend/Dockerfile.acceptance index a3750f70..6f509121 100644 --- a/dockerfiles/frontend/Dockerfile.acceptance +++ b/dockerfiles/frontend/Dockerfile.acceptance @@ -15,5 +15,5 @@ COPY --chown=node:node ./dockerfiles/frontend/config_monolingual.js /app/src/con RUN < Date: Tue, 18 Jun 2024 14:03:39 +0200 Subject: [PATCH 102/226] Revert "yarn -> pnpm. We are testing with Volt 18" This reverts commit d56ac87f4d2510c887a7980c5c10a451a33a757b. --- dockerfiles/frontend/Dockerfile.acceptance | 2 +- .../frontend/Dockerfile.acceptance.multilingual | 2 +- dockerfiles/frontend/Dockerfile.ci | 10 +++++----- dockerfiles/frontend/Dockerfile.ci.multilingual | 10 +++++----- dockerfiles/frontend/Dockerfile.dev | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dockerfiles/frontend/Dockerfile.acceptance b/dockerfiles/frontend/Dockerfile.acceptance index 6f509121..a3750f70 100644 --- a/dockerfiles/frontend/Dockerfile.acceptance +++ b/dockerfiles/frontend/Dockerfile.acceptance @@ -15,5 +15,5 @@ COPY --chown=node:node ./dockerfiles/frontend/config_monolingual.js /app/src/con RUN < Date: Tue, 18 Jun 2024 14:04:10 +0200 Subject: [PATCH 103/226] Revert "Volto 18" This reverts commit 8b77baab08fd62c29fbe157caf9c7b0ed4ad44bc. --- .github/workflows/acceptance.yml | 2 +- .github/workflows/code.yml | 6 +++--- .github/workflows/unit.yml | 6 +++--- acceptance/ci.yml | 6 ++++-- dockerfiles/docker-compose.yml | 6 ++++-- variables.mk | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 7dd4c139..05f2c5b2 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -4,7 +4,7 @@ on: [push] env: ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 18.0.0-alpha.35 + VOLTO_VERSION: 17 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 8e8a0707..42f5b627 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -2,16 +2,16 @@ name: Code analysis checks on: [push] env: - ADDON_NAME: '@rohberg/volto-searchkit-block' + ADDON_NAME: "@rohberg/volto-searchkit-block" ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 18.0.0-alpha.35 + VOLTO_VERSION: 17 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: 'oxczBG).3xWyapLn' + INDEX_PASSWORD: "oxczBG).3xWyapLn" CELERY_BROKER: redis://redis:6379/0 CELERY_LOG_LEVEL: info diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 038ae902..8b8f0ebc 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -2,16 +2,16 @@ name: Unit Tests on: [push] env: - ADDON_NAME: '@rohberg/volto-searchkit-block' + ADDON_NAME: "@rohberg/volto-searchkit-block" ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 18.0.0-alpha.35 + VOLTO_VERSION: 17 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: 'oxczBG).3xWyapLn' + INDEX_PASSWORD: "oxczBG).3xWyapLn" CELERY_BROKER: redis://redis:6379/0 CELERY_LOG_LEVEL: info diff --git a/acceptance/ci.yml b/acceptance/ci.yml index a22d3d08..e617d22a 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -1,3 +1,5 @@ +version: '3' + services: addon-acceptance: build: @@ -6,7 +8,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-18} + VOLTO_VERSION: ${VOLTO_VERSION:-17} environment: RAZZLE_INTERNAL_API_PATH: http://backend-acceptance:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone @@ -24,7 +26,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-18} + VOLTO_VERSION: ${VOLTO_VERSION:-17} environment: RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-multilingual:55001/plone RAZZLE_API_PATH: http://localhost:55001/plone diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index cc313a2c..7edf1ddd 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -1,3 +1,5 @@ +version: '3' + services: addon-dev: build: @@ -6,7 +8,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: '${VOLTO_VERSION:-18}' + VOLTO_VERSION: '${VOLTO_VERSION:-17}' volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: @@ -34,7 +36,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: '${VOLTO_VERSION:-18}' + VOLTO_VERSION: '${VOLTO_VERSION:-17}' environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: http://localhost:8080/Plone diff --git a/variables.mk b/variables.mk index 1adb194a..75e4bed0 100644 --- a/variables.mk +++ b/variables.mk @@ -1,5 +1,5 @@ PLONE_VERSION=6.0.11.1 -VOLTO_VERSION=18.0.0-alpha.35 +VOLTO_VERSION=17.16.0 ADDON_NAME='@rohberg/volto-searchkit-block' ADDON_PATH='volto-searchkit-block' From 626293bcc8e63581c296343c746f73b96f9a4db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 14:05:14 +0200 Subject: [PATCH 104/226] Back to Volto 17 --- acceptance/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index d4825bf6..b06f508c 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -6,7 +6,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-18} + VOLTO_VERSION: ${VOLTO_VERSION:-17} volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: @@ -29,7 +29,7 @@ services: args: ADDON_NAME: '${ADDON_NAME}' ADDON_PATH: '${ADDON_PATH}' - VOLTO_VERSION: ${VOLTO_VERSION:-18} + VOLTO_VERSION: ${VOLTO_VERSION:-17} volumes: - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/ environment: From 994a058e2a9c1e060276e8e13e80f314e9c3c6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 14:18:06 +0200 Subject: [PATCH 105/226] Add missing arg rohberg.volto --- acceptance/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index e617d22a..35955ac5 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -52,7 +52,7 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone,rohberg.volto' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' ports: @@ -81,7 +81,7 @@ services: INDEX_LOGIN: ${INDEX_LOGIN?unset} INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} - CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone' + CONFIGURE_PACKAGES: 'plone.restapi,plone.volto,plone.volto.cors,collective.elastic.plone,rohberg.volto' # Run initialize of collective.elastic.plone to register ElasticSearchProxyIndex INSTALL_PRODUCTS: 'collective.elastic.plone' ports: From 8ce0d09a33ac0c07ad2b8d3100039ac1013309dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 15:00:57 +0200 Subject: [PATCH 106/226] Update docker-compose.yml --- acceptance/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index b06f508c..f20cbd30 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -192,7 +192,7 @@ services: expose: - '5601' environment: - OPENSEARCH_HOSTS: "['https://opensearch:9200']" + OPENSEARCH_HOSTS: '["https://opensearch:9200"]' profiles: - dev - multilingual From 263abb7ff777e14255d74330c8aae410e97e8f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 15:01:22 +0200 Subject: [PATCH 107/226] apply collective.elastic.plone:default --- dockerfiles/backend/Dockerfile.acceptance | 2 +- dockerfiles/backend/Dockerfile.acceptance.multilingual | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index 09aea68b..b0001b8a 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -23,7 +23,7 @@ EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="rohberg.volto:monolingual" +ENV APPLY_PROFILES="collective.elastic.plone:default,rohberg.volto:monolingual" # Copy /app from builder COPY --from=builder /app /app diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index 2330c0f5..f12dd274 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -25,7 +25,7 @@ EOT FROM plone/server-acceptance:${PLONE_VERSION} -ENV APPLY_PROFILES="rohberg.volto:multilingual" +ENV APPLY_PROFILES="collective.elastic.plone:default,rohberg.volto:multilingual" # Copy /app from builder COPY --from=builder /app /app From 00d36f1b5b2022f56854260becc1e5cbcfb1c2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 15:08:35 +0200 Subject: [PATCH 108/226] Add cypress aliases --- acceptance/cypress/tests/search.anonymous.multilingual.cy.js | 1 + acceptance/cypress/tests/search.monolingual.cy.js | 2 ++ acceptance/cypress/tests/search.multilingual.cy.js | 1 + 3 files changed, 4 insertions(+) diff --git a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js index 504f0356..733b2b4b 100644 --- a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js +++ b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js @@ -74,6 +74,7 @@ describe('Searchkit block tests – search - anonymous', () => { }); it('As anonymous I see only published content', function () { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.intercept('/**/@logout').as('logout'); cy.visit('/logout'); diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index cd24ce1a..ba21b70a 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -63,6 +63,7 @@ describe('Searchkit block tests – search', () => { }); beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.autologin(); cy.visit('/suche'); @@ -131,6 +132,7 @@ describe('Searchkit block tests – search', () => { // Blocks text it('I can search in blocks', function () { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.visit('/garten-blog/februar'); cy.get('a.edit').click(); diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index f95396c3..f96cc5bb 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -139,6 +139,7 @@ describe('Searchkit block tests – search', () => { // Blocks text it('I can search in blocks', function () { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.visit('/de/garten-blog/februar'); cy.get('a.edit').click(); From 85aef6655255ff73a668cf48075c221c2f5629a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 15:23:56 +0200 Subject: [PATCH 109/226] Add missing cypress alias --- acceptance/cypress/tests/search.monolingual.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index ba21b70a..0465143e 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -133,6 +133,7 @@ describe('Searchkit block tests – search', () => { // Blocks text it('I can search in blocks', function () { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.visit('/garten-blog/februar'); cy.get('a.edit').click(); From 5029ec65068585c2275085cbe97e306a92b76523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 18 Jun 2024 15:30:49 +0200 Subject: [PATCH 110/226] Add wait after creating content. Give ingest time to ingest --- acceptance/cypress/tests/search.monolingual.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 0465143e..01931e2c 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -60,6 +60,7 @@ describe('Searchkit block tests – search', () => { cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); + cy.wait(3000); }); beforeEach(() => { From 0164d9b9672fb962d2a0f117b87294dd96b66ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:29:32 +0200 Subject: [PATCH 111/226] Update .env celery log level debug --- acceptance/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/.env b/acceptance/.env index ef6ba0cf..2aea87af 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -6,7 +6,7 @@ export INDEX_LOGIN=admin export INDEX_PASSWORD=paraDiesli,17 export CELERY_BROKER=redis://redis:6379/0 -export CELERY_LOG_LEVEL=debug +export CELERY_LOG_LEVEL=info export PLONE_SERVICE=http://backend-acceptance:55001 export PLONE_SITE_PREFIX_PATH=plone From d17a125ba7cac3868fe9a8268fbe3f0229d32511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:30:22 +0200 Subject: [PATCH 112/226] Fix typo CELERY_LOG_LEVEL --- acceptance/ci.yml | 4 ++-- acceptance/docker-compose.yml | 4 ++-- dockerfiles/docker-compose.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 35955ac5..7945924a 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -106,7 +106,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} @@ -131,7 +131,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} PLONE_SERVICE: http://backend-acceptance-multilingual:55001 PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index f20cbd30..b24ea006 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -112,7 +112,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} @@ -139,7 +139,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} PLONE_SERVICE: http://backend-acceptance-multilingual:55001 PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 7edf1ddd..4dda180a 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -91,7 +91,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELEREY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} From 2933406c93f7c85800d50b82c3b47c94139125a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:31:00 +0200 Subject: [PATCH 113/226] Switch to collective.elastic.plone[redis,opensearch] from repo main branch --- dockerfiles/backend/Dockerfile.acceptance | 2 +- dockerfiles/backend/Dockerfile.acceptance.multilingual | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerfiles/backend/Dockerfile.acceptance b/dockerfiles/backend/Dockerfile.acceptance index b0001b8a..09801c2f 100644 --- a/dockerfiles/backend/Dockerfile.acceptance +++ b/dockerfiles/backend/Dockerfile.acceptance @@ -17,7 +17,7 @@ RUN <=2.0.0 # TODO Switch back to released collective.elastic.plone # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@restapi-search-anonymous-allowedRolesAndUsers' + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git' bin/pip install 'rohberg.volto[test] @ git+https://github.com/rohberg/rohberg.volto.git' EOT diff --git a/dockerfiles/backend/Dockerfile.acceptance.multilingual b/dockerfiles/backend/Dockerfile.acceptance.multilingual index f12dd274..61f412cc 100644 --- a/dockerfiles/backend/Dockerfile.acceptance.multilingual +++ b/dockerfiles/backend/Dockerfile.acceptance.multilingual @@ -19,7 +19,7 @@ RUN <=2.0.0 # TODO Switch back to released collective.elastic.plone # bin/pip install collective.elastic.plone[redis,opensearch]>=2.1.0 - bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git@restapi-search-anonymous-allowedRolesAndUsers' + bin/pip install 'collective.elastic.plone[redis,opensearch] @ git+https://github.com/collective/collective.elastic.plone.git' bin/pip install 'rohberg.volto[test] @ git+https://github.com/rohberg/rohberg.volto.git' EOT From a84e37869d6ca703f85580b52ba32b2cd31425d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:31:26 +0200 Subject: [PATCH 114/226] warning about missing licence --- acceptance/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance/package.json b/acceptance/package.json index c11c8e33..b54f8659 100644 --- a/acceptance/package.json +++ b/acceptance/package.json @@ -1,5 +1,6 @@ { "dependencies": { "@plone/volto-testing": "^4.0.0" - } + }, + "license": "MIT" } From a4fbdb9dab6e6efbd54437b79dc8becdc31b1582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:31:52 +0200 Subject: [PATCH 115/226] Fix locked search page --- acceptance/cypress/tests/language.multilingual.cy.js | 2 ++ acceptance/cypress/tests/search.anonymous.multilingual.cy.js | 2 ++ acceptance/cypress/tests/search.monolingual.cy.js | 3 ++- acceptance/cypress/tests/search.multilingual.cy.js | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index e44bf344..c7e1c0fe 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -1,6 +1,7 @@ describe('Searchkit block tests – search - multilingual', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.autologin(); @@ -37,6 +38,7 @@ describe('Searchkit block tests – search - multilingual', () => { cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); + cy.wait('@content'); }); beforeEach(() => { diff --git a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js index 733b2b4b..6e8df1ec 100644 --- a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js +++ b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js @@ -1,6 +1,7 @@ describe('Searchkit block tests – search - anonymous', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.autologin(); @@ -49,6 +50,7 @@ describe('Searchkit block tests – search - anonymous', () => { cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); + cy.wait('@content'); }); beforeEach(() => { diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 01931e2c..594b7be2 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -1,6 +1,7 @@ describe('Searchkit block tests – search', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.autologin(); @@ -60,7 +61,7 @@ describe('Searchkit block tests – search', () => { cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); - cy.wait(3000); + cy.wait('@content'); }); beforeEach(() => { diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index f96cc5bb..ee8d62c1 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -1,6 +1,7 @@ describe('Searchkit block tests – search', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.autologin(); @@ -66,6 +67,7 @@ describe('Searchkit block tests – search', () => { cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); + cy.wait('@content'); }); beforeEach(() => { From ab4606d63c61cc9138b9d920c17b44e228a61844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:39:36 +0200 Subject: [PATCH 116/226] Fix typo CELERY_LOGLEVEL --- .github/workflows/acceptance.yml | 2 +- .github/workflows/code.yml | 6 +++--- .github/workflows/unit.yml | 6 +++--- acceptance/.env | 2 +- acceptance/ci.yml | 4 ++-- acceptance/docker-compose.yml | 4 ++-- dockerfiles/_env | 2 +- dockerfiles/docker-compose.yml | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 05f2c5b2..37973f56 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -14,7 +14,7 @@ env: INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 - CELERY_LOG_LEVEL: info + CELERY_LOGLEVEL: info PLONE_SERVICE: http://backend-acceptance:55001 PLONE_SITE_PREFIX_PATH: plone diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 42f5b627..9bc221c1 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -2,7 +2,7 @@ name: Code analysis checks on: [push] env: - ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block VOLTO_VERSION: 17 PLONE_VERSION: 6.0 @@ -11,10 +11,10 @@ env: INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: "oxczBG).3xWyapLn" + INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 - CELERY_LOG_LEVEL: info + CELERY_LOGLEVEL: info PLONE_SERVICE: http://backend-acceptance:55001 PLONE_SITE_PREFIX_PATH: plone diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 8b8f0ebc..754aba4c 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -2,7 +2,7 @@ name: Unit Tests on: [push] env: - ADDON_NAME: "@rohberg/volto-searchkit-block" + ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block VOLTO_VERSION: 17 PLONE_VERSION: 6.0 @@ -11,10 +11,10 @@ env: INDEX_OPENSEARCH: 1 INDEX_USE_SSL: 0 INDEX_LOGIN: admin - INDEX_PASSWORD: "oxczBG).3xWyapLn" + INDEX_PASSWORD: 'oxczBG).3xWyapLn' CELERY_BROKER: redis://redis:6379/0 - CELERY_LOG_LEVEL: info + CELERY_LOGLEVEL: info PLONE_SERVICE: http://backend-acceptance:55001 PLONE_SITE_PREFIX_PATH: plone diff --git a/acceptance/.env b/acceptance/.env index 2aea87af..a52b3cea 100644 --- a/acceptance/.env +++ b/acceptance/.env @@ -6,7 +6,7 @@ export INDEX_LOGIN=admin export INDEX_PASSWORD=paraDiesli,17 export CELERY_BROKER=redis://redis:6379/0 -export CELERY_LOG_LEVEL=info +export CELERY_LOGLEVEL=info export PLONE_SERVICE=http://backend-acceptance:55001 export PLONE_SITE_PREFIX_PATH=plone diff --git a/acceptance/ci.yml b/acceptance/ci.yml index 7945924a..13ac842f 100644 --- a/acceptance/ci.yml +++ b/acceptance/ci.yml @@ -106,7 +106,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOGLEVEL: ${CELERY_LOGLEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} @@ -131,7 +131,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOGLEVEL: ${CELERY_LOGLEVEL:-info} PLONE_SERVICE: http://backend-acceptance-multilingual:55001 PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} diff --git a/acceptance/docker-compose.yml b/acceptance/docker-compose.yml index b24ea006..80357a38 100644 --- a/acceptance/docker-compose.yml +++ b/acceptance/docker-compose.yml @@ -112,7 +112,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOGLEVEL: ${CELERY_LOGLEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} @@ -139,7 +139,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOGLEVEL: ${CELERY_LOGLEVEL:-info} PLONE_SERVICE: http://backend-acceptance-multilingual:55001 PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} diff --git a/dockerfiles/_env b/dockerfiles/_env index ab727a96..ed384403 100644 --- a/dockerfiles/_env +++ b/dockerfiles/_env @@ -5,7 +5,7 @@ export INDEX_LOGIN=admin export INDEX_PASSWORD=mypswdappknowsmyindexserverpswd export CELERY_BROKER=redis://redis:6379/0 -export CELERY_LOG_LEVEL=debug +export CELERY_LOGLEVEL=debug export PLONE_SERVICE=http://backend:8080 export PLONE_SITE_PREFIX_PATH=Plone diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml index 4dda180a..dc7f2f38 100644 --- a/dockerfiles/docker-compose.yml +++ b/dockerfiles/docker-compose.yml @@ -91,7 +91,7 @@ services: INDEX_PASSWORD: ${INDEX_PASSWORD?unset} CELERY_BROKER: ${CELERY_BROKER?unset} CELERY_CONCURRENCY: ${CELERY_CONCURRENCY:-1} - CELERY_LOG_LEVEL: ${CELERY_LOG_LEVEL:-info} + CELERY_LOGLEVEL: ${CELERY_LOGLEVEL:-info} PLONE_SERVICE: ${PLONE_SERVICE?unset} PLONE_SITE_PREFIX_PATH: ${PLONE_SITE_PREFIX_PATH?unset} PLONE_USER: ${PLONE_USER?unset} From a7add6cf1066342ccb0f39c58cd477435fcf575e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 09:55:47 +0200 Subject: [PATCH 117/226] wait for search results --- acceptance/cypress/tests/search.monolingual.cy.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 594b7be2..9b50cd9b 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -66,10 +66,12 @@ describe('Searchkit block tests – search', () => { beforeEach(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); cy.autologin(); cy.visit('/suche'); cy.wait('@kitsearch'); + cy.wait('@content'); }); after(() => { From e8272cc8b6d19ab1cbc3cd560b1b8c6f0406d860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 10:30:27 +0200 Subject: [PATCH 118/226] Cypress testing: More waits --- .../cypress/tests/search.monolingual.cy.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 9b50cd9b..c1c4ee30 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -86,58 +86,73 @@ describe('Searchkit block tests – search', () => { }); it('I see all if no filter selected', function () { + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); it('I can search fuzzy', function () { cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); cy.get('.block.searchkitsearch').should('not.contain', 'März'); }); it('I can search with inflection', function () { cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Männer'); }); it('I can search with decompounding', function () { cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Garten-Blog'); cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Februar'); }); it('I can search with wildcard', function () { cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); it('I can search for an exact match', function () { cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); }); it('I can search for a compounded word', function () { cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Stelle'); }); // Blocks text it('I can search in blocks', function () { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); cy.visit('/garten-blog/februar'); cy.get('a.edit').click(); From 747ded52e42658e7fa39571a799e207d3ca98c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 10:52:06 +0200 Subject: [PATCH 119/226] Renaming tests --- acceptance/cypress/tests/language.multilingual.cy.js | 2 +- acceptance/cypress/tests/search.anonymous.multilingual.cy.js | 2 +- acceptance/cypress/tests/search.multilingual.cy.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js index c7e1c0fe..a604af69 100644 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ b/acceptance/cypress/tests/language.multilingual.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit block tests – search - multilingual', () => { +describe('Searchkit block tests – search - multilingual - language', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.intercept('GET', `/**/*?expand*`).as('content'); diff --git a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js index 6e8df1ec..7d06c31e 100644 --- a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js +++ b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit block tests – search - anonymous', () => { +describe('Searchkit block tests – search - multilingual - anonymous', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.intercept('GET', `/**/*?expand*`).as('content'); diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js index ee8d62c1..32bb80dc 100644 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ b/acceptance/cypress/tests/search.multilingual.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit block tests – search', () => { +describe('Searchkit block tests – search -multilingual - fuzzy etc', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.intercept('GET', `/**/*?expand*`).as('content'); From ac6edb21ecf13035110f29556fc05d4108f5e5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 10:52:40 +0200 Subject: [PATCH 120/226] cy.addNewBlock. Remove waits. cy.navigate --- .../cypress/tests/search.monolingual.cy.js | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index c1c4ee30..9ed55b36 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -1,4 +1,4 @@ -describe('Searchkit block tests – search', () => { +describe('Searchkit block tests – search - monolingual', () => { before(() => { cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); cy.intercept('GET', `/**/*?expand*`).as('content'); @@ -52,12 +52,7 @@ describe('Searchkit block tests – search', () => { cy.visit('/suche'); cy.get('a.edit').click(); - cy.getSlate().click(); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Ausklappen Common blocks"]').click(); - cy.get('.blocks-chooser .common .button.searchkitblock').click({ - force: true, - }); + cy.addNewBlock('searchkit'); cy.get('#toolbar-save').click(); cy.wait('@kitsearch'); @@ -86,68 +81,55 @@ describe('Searchkit block tests – search', () => { }); it('I see all if no filter selected', function () { - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); it('I can search fuzzy', function () { cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); cy.get('.block.searchkitsearch').should('not.contain', 'März'); }); it('I can search with inflection', function () { cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Männer'); }); it('I can search with decompounding', function () { cy.get('.searchbar-wrapper input').type('Garten{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Garten-Blog'); cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Februar'); }); it('I can search with wildcard', function () { cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); it('I can search for an exact match', function () { cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); }); it('I can search for a compounded word', function () { cy.get('.searchbar-wrapper input').type('stelle{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Testseite Stelle'); }); @@ -168,10 +150,9 @@ describe('Searchkit block tests – search', () => { cy.log('I added a text block'); // Searching - cy.visit('/suche'); + cy.navigate('/suche'); cy.wait('@kitsearch'); cy.get('.searchbar-wrapper input').type('Montag{enter}'); - cy.wait('@kitsearch'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); }); From 9bdd5af3e6dae81a3baca7619bd5fc7b2d838444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 11:06:24 +0200 Subject: [PATCH 121/226] CYPRESS_RETRIES: 3 --- .github/workflows/acceptance.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 37973f56..4597788d 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -47,7 +47,7 @@ jobs: uses: cypress-io/github-action@v6 env: BABEL_ENV: production - CYPRESS_RETRIES: 2 + CYPRESS_RETRIES: 3 with: parallel: false browser: chrome @@ -93,7 +93,7 @@ jobs: uses: cypress-io/github-action@v6 env: BABEL_ENV: production - CYPRESS_RETRIES: 2 + CYPRESS_RETRIES: 3 with: parallel: false browser: chrome From 79e05de5c62584457f6d55792437e25913b5cdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 11:06:46 +0200 Subject: [PATCH 122/226] Remove clear before type --- acceptance/cypress/tests/search.monolingual.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index 9ed55b36..ce97a686 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -91,7 +91,7 @@ describe('Searchkit block tests – search - monolingual', () => { }); it('I can search with inflection', function () { - cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); + cy.get('.searchbar-wrapper input').type('Männer{enter}'); cy.get('.block.searchkitsearch').contains('Testseite Mann'); cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); From daca3f22a94a82898ae64d46878281a22820da67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 11:21:16 +0200 Subject: [PATCH 123/226] secrets.GITHUB_TOKEN --- .github/workflows/acceptance.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 4597788d..2cda40d0 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -48,6 +48,9 @@ jobs: env: BABEL_ENV: production CYPRESS_RETRIES: 3 + # Recommended: pass the GitHub token lets this action correctly + # determine the unique run id necessary to re-run the checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: parallel: false browser: chrome @@ -94,6 +97,9 @@ jobs: env: BABEL_ENV: production CYPRESS_RETRIES: 3 + # Recommended: pass the GitHub token lets this action correctly + # determine the unique run id necessary to re-run the checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: parallel: false browser: chrome From bcb16ba47b7a9f35face1eda9647dfc21959d441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 11:43:04 +0200 Subject: [PATCH 124/226] defaultCommandTimeout: 8000 --- acceptance/cypress.monolingual.config.js | 1 + acceptance/cypress.multilingual.config.js | 1 + 2 files changed, 2 insertions(+) diff --git a/acceptance/cypress.monolingual.config.js b/acceptance/cypress.monolingual.config.js index 9db25763..12fdddb2 100644 --- a/acceptance/cypress.monolingual.config.js +++ b/acceptance/cypress.monolingual.config.js @@ -1,6 +1,7 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ + defaultCommandTimeout: 8000, viewportWidth: 1280, e2e: { setupNodeEvents(on, config) { diff --git a/acceptance/cypress.multilingual.config.js b/acceptance/cypress.multilingual.config.js index e92aa9ac..488260f2 100644 --- a/acceptance/cypress.multilingual.config.js +++ b/acceptance/cypress.multilingual.config.js @@ -1,6 +1,7 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ + defaultCommandTimeout: 8000, viewportWidth: 1280, e2e: { setupNodeEvents(on, config) { From 9588fd9af081ce4899f83be7816709e395106a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 12:00:43 +0200 Subject: [PATCH 125/226] increase timout --- .github/workflows/acceptance.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 2cda40d0..046139dd 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -52,6 +52,7 @@ jobs: # determine the unique run id necessary to re-run the checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: + config: pageLoadTimeout=100000,defaultCommandTimeout=8000 parallel: false browser: chrome working-directory: acceptance @@ -101,6 +102,7 @@ jobs: # determine the unique run id necessary to re-run the checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: + config: pageLoadTimeout=100000,defaultCommandTimeout=8000 parallel: false browser: chrome working-directory: acceptance From 979e67c9f8245bd14d758481f9d7e60e6ab3e259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 12:15:53 +0200 Subject: [PATCH 126/226] Update search.monolingual.cy.js --- acceptance/cypress/tests/search.monolingual.cy.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index ce97a686..eafe5d60 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -70,8 +70,6 @@ describe('Searchkit block tests – search - monolingual', () => { }); after(() => { - cy.removeContent({ path: 'garten-blog/februar' }); - cy.removeContent({ path: 'garten-blog/marz' }); cy.removeContent({ path: 'garten-blog' }); cy.removeContent({ path: 'suche' }); cy.removeContent({ path: 'testseite-mann' }); From bfc1fbe424703f44bfd80a0d55aa4f721ea4ffb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 13:33:52 +0200 Subject: [PATCH 127/226] cy.navigate -> cy.visit --- acceptance/cypress/tests/search.monolingual.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js index eafe5d60..c48544a2 100644 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ b/acceptance/cypress/tests/search.monolingual.cy.js @@ -148,8 +148,8 @@ describe('Searchkit block tests – search - monolingual', () => { cy.log('I added a text block'); // Searching - cy.navigate('/suche'); - cy.wait('@kitsearch'); + // WARNING Do not use cy.navigate TODO understand difference between cy.visit and cy.navigate + cy.visit('/suche'); cy.get('.searchbar-wrapper input').type('Montag{enter}'); cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); }); From cf74119bc2712f4201b2313b3d7c5e247a675bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Wed, 19 Jun 2024 20:05:23 +0200 Subject: [PATCH 128/226] Check publishing date. Check start date of event --- .../cypress/tests/results.monolingual.cy.js | 70 +++++++++++++++++++ src/components/Blocks/schema.js | 4 +- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 acceptance/cypress/tests/results.monolingual.cy.js diff --git a/acceptance/cypress/tests/results.monolingual.cy.js b/acceptance/cypress/tests/results.monolingual.cy.js new file mode 100644 index 00000000..c287f9f3 --- /dev/null +++ b/acceptance/cypress/tests/results.monolingual.cy.js @@ -0,0 +1,70 @@ +describe('Searchkit block tests – search - monolingual', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'suche', + contentTitle: 'Suche', + }); + + cy.createContent({ + contentType: 'News Item', + contentId: 'brunch', + contentTitle: 'Brunch - Viel gelacht und lecker gegessen', + }); + cy.createContent({ + contentType: 'Event', + contentId: 'ausflug', + contentTitle: 'Ausflug Matterhorn', + }); + + // Publish News Item + cy.setWorkflow({ + path: 'brunch', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + // Add search block to /suche + cy.visit('/suche'); + cy.get('a.edit').click(); + + cy.addNewBlock('searchkit'); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + // TODO Replace this desperate wait per seconds + cy.wait(3000); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.autologin(); + + cy.visit('/suche'); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + after(() => { + cy.removeContent({ path: 'brunch' }); + cy.removeContent({ path: 'ausflug' }); + cy.removeContent({ path: 'suche' }); + }); + + it('I see the publishing date', function () { + cy.get('.searchbar-wrapper input').type('brunch{enter}'); + cy.get('.block.searchkitsearch').contains('2018'); + }); + + it('I see the start date', function () { + cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); + cy.get('.block.searchkitsearch').contains('.202'); + }); +}); diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index e5216c9b..e26bfc7e 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -188,7 +188,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { items: { vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, }, - default: [], + default: ['News Item'], }, showEventStartDate: { title: 'Show start date of events', @@ -197,7 +197,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { items: { vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, }, - default: [], + default: ['Event'], }, relocation: { title: 'Relocation', From 08c257a92eec633a89fada35bc6909d625c33110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 10:58:25 +0200 Subject: [PATCH 129/226] Fix eslint --- .eslintrc.js | 31 ++++++++++++++++++++++++++ .project.eslintrc.js | 52 -------------------------------------------- 2 files changed, 31 insertions(+), 52 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .project.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..ddf0f598 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const projectRootPath = __dirname; + +let coreLocation; +if (fs.existsSync(`${projectRootPath}/core`)) + coreLocation = `${projectRootPath}/core`; +else if (fs.existsSync(`${projectRootPath}/../../core`)) + coreLocation = `${projectRootPath}/../../core`; + +module.exports = { + extends: `${coreLocation}/packages/volto/.eslintrc`, + rules: { + 'import/no-unresolved': 1, + }, + settings: { + 'import/resolver': { + alias: { + map: [ + ['@plone/volto', `${coreLocation}/packages/volto/src`], + [ + '@plone/volto-slate', + `${coreLocation}/core/packages/volto-slate/src`, + ], + ['@plone/registry', `${coreLocation}/packages/registry/src`], + ['@rohberg/volto-searchkit-block', './src'], + ], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], + }, + }, + }, +}; diff --git a/.project.eslintrc.js b/.project.eslintrc.js deleted file mode 100644 index 51d0befe..00000000 --- a/.project.eslintrc.js +++ /dev/null @@ -1,52 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const projectRootPath = fs.realpathSync('./project'); // __dirname -const packageJson = require(path.join(projectRootPath, 'package.json')); - -let voltoPath = path.join(projectRootPath, '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 AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`); -const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); -const reg = new AddonConfigurationRegistry(projectRootPath); - -// 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, -]); - -module.exports = { - extends: `${voltoPath}/.eslintrc`, - settings: { - 'import/resolver': { - alias: { - map: [ - ['@plone/volto', '@plone/volto/src'], - ['@plone/volto-slate', '@plone/volto-slate/src'], - ...addonAliases, - ['@package', `${__dirname}/src`], - ['@root', `${__dirname}/src`], - ['~', `${__dirname}/src`], - ], - extensions: ['.js', '.jsx', '.json'], - }, - 'babel-plugin-root-import': { - rootPathSuffix: 'src', - }, - }, - }, -}; From 8479c8be9f72c9cf52fbe198d43d888160e50dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:00:01 +0200 Subject: [PATCH 130/226] locales --- locales/de/LC_MESSAGES/volto.po | 33 ++++++++++++++++++++++++--------- locales/en/LC_MESSAGES/volto.po | 29 ++++++++++++++++++++++------- locales/volto.pot | 31 +++++++++++++++++++++++-------- src/messages.js | 32 ++++++++++++++++++++++++++++---- 4 files changed, 97 insertions(+), 28 deletions(-) diff --git a/locales/de/LC_MESSAGES/volto.po b/locales/de/LC_MESSAGES/volto.po index 8eb20265..07eef637 100644 --- a/locales/de/LC_MESSAGES/volto.po +++ b/locales/de/LC_MESSAGES/volto.po @@ -11,10 +11,15 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. Default: "Add search section" -#: components/Blocks/SearchSectionsWidget -msgid "Add search section" -msgstr "Füge Suchsektion hinzu" +#. Default: "Add {type}" +#: messages +msgid "Add {type}" +msgstr "" + +#. Default: "Cancel" +#: messages +msgid "Cancel" +msgstr "" #. Default: "Check the configuration of your searchkit block!" #: components/Searchkit/Error @@ -91,6 +96,16 @@ msgstr "Relevanz" msgid "Search block" msgstr "Such-Block" +#. Default: "Search can be restricted by sections / paths" +#: messages +msgid "Search can be restricted by sections / paths" +msgstr "Die Suche kann auf Sektionen / Pfade eingeschränkt werden" + +#. Default: "Search in sections" +#: messages +msgid "Search in sections" +msgstr "Suche in Sektionen" + #. Default: "Search result" #: messages msgid "Search result" @@ -101,13 +116,13 @@ msgstr "Suchergebnis" msgid "Search results" msgstr "Suchergebnisse" -#. Default: "Search section" -#: components/Blocks/SearchSectionsWidget +#. Default: "section" +#: messages msgid "Search section" -msgstr "Suche in Sektion" +msgstr "Sektion" #. Default: "Search section label" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Search section label" msgstr "Label Such-Sektion" @@ -122,7 +137,7 @@ msgid "Select all" msgstr "Wähle alle" #. Default: "Show filter" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Show filters" msgstr "Zeige Filter" diff --git a/locales/en/LC_MESSAGES/volto.po b/locales/en/LC_MESSAGES/volto.po index 7161fde5..6b178ea1 100644 --- a/locales/en/LC_MESSAGES/volto.po +++ b/locales/en/LC_MESSAGES/volto.po @@ -11,9 +11,14 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. Default: "Add search section" -#: components/Blocks/SearchSectionsWidget -msgid "Add search section" +#. Default: "Add {type}" +#: messages +msgid "Add {type}" +msgstr "" + +#. Default: "Cancel" +#: messages +msgid "Cancel" msgstr "" #. Default: "Check the configuration of your searchkit block!" @@ -91,6 +96,16 @@ msgstr "" msgid "Search block" msgstr "" +#. Default: "Search can be restricted by sections / paths" +#: messages +msgid "Search can be restricted by sections / paths" +msgstr "" + +#. Default: "Search in sections" +#: messages +msgid "Search in sections" +msgstr "" + #. Default: "Search result" #: messages msgid "Search result" @@ -101,13 +116,13 @@ msgstr "" msgid "Search results" msgstr "" -#. Default: "Search section" -#: components/Blocks/SearchSectionsWidget +#. Default: "section" +#: messages msgid "Search section" msgstr "" #. Default: "Search section label" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Search section label" msgstr "" @@ -122,7 +137,7 @@ msgid "Select all" msgstr "" #. Default: "Show filter" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Show filters" msgstr "" diff --git a/locales/volto.pot b/locales/volto.pot index eac6d645..caa1758c 100644 --- a/locales/volto.pot +++ b/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-03-08T12:35:25.144Z\n" +"POT-Creation-Date: 2024-06-20T18:55:48.220Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -13,9 +13,14 @@ msgstr "" "Preferred-Encodings: utf-8\n" "Domain: volto\n" -#. Default: "Add search section" -#: components/Blocks/SearchSectionsWidget -msgid "Add search section" +#. Default: "Add {type}" +#: messages +msgid "Add {type}" +msgstr "" + +#. Default: "Cancel" +#: messages +msgid "Cancel" msgstr "" #. Default: "Check the configuration of your searchkit block!" @@ -93,6 +98,16 @@ msgstr "" msgid "Search block" msgstr "" +#. Default: "Search can be restricted by sections / paths" +#: messages +msgid "Search can be restricted by sections / paths" +msgstr "" + +#. Default: "Search in sections" +#: messages +msgid "Search in sections" +msgstr "" + #. Default: "Search result" #: messages msgid "Search result" @@ -103,13 +118,13 @@ msgstr "" msgid "Search results" msgstr "" -#. Default: "Search section" -#: components/Blocks/SearchSectionsWidget +#. Default: "section" +#: messages msgid "Search section" msgstr "" #. Default: "Search section label" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Search section label" msgstr "" @@ -124,7 +139,7 @@ msgid "Select all" msgstr "" #. Default: "Show filter" -#: components/Blocks/SearchSectionsWidget +#: messages msgid "Show filters" msgstr "" diff --git a/src/messages.js b/src/messages.js index ca12b01e..30a595c9 100644 --- a/src/messages.js +++ b/src/messages.js @@ -22,10 +22,10 @@ const messages = defineMessages({ id: 'Facet', defaultMessage: 'Facet', }, - item: { - id: 'Item', - defaultMessage: 'Item', - }, + // item: { + // id: 'Item', + // defaultMessage: 'Item', + // }, label: { id: 'Label', defaultMessage: 'Label', @@ -54,6 +54,26 @@ const messages = defineMessages({ id: 'Search results', defaultMessage: 'Search results', }, + searchInSections: { + id: 'Search in sections', + defaultMessage: 'Search in sections', + }, + searchInSectionsDescription: { + id: 'Search can be restricted by sections / paths', + defaultMessage: 'Search can be restricted by sections / paths', + }, + searchSection: { + id: 'Search section', + defaultMessage: 'section', + }, + searchSectionLabel: { + id: 'Search section label', + defaultMessage: 'Search section label', + }, + showFilter: { + id: 'Show filters', + defaultMessage: 'Show filter', + }, // highlights title: { id: 'Title', @@ -75,6 +95,10 @@ const messages = defineMessages({ id: 'Cancel', defaultMessage: 'Cancel', }, + add: { + id: 'Add {type}', + defaultMessage: 'Add {type}', + }, }); export default messages; From db0b61fafa84c90820c2d428384cfc994e2f4aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:01:11 +0200 Subject: [PATCH 131/226] Fix flattenESUrlToPath without backend_url from block data --- src/components/{helpers.jsx => helpers.js} | 33 +++++++++++++--------- src/components/helpers.test.js | 18 ++++++++++++ 2 files changed, 38 insertions(+), 13 deletions(-) rename src/components/{helpers.jsx => helpers.js} (62%) create mode 100644 src/components/helpers.test.js diff --git a/src/components/helpers.jsx b/src/components/helpers.js similarity index 62% rename from src/components/helpers.jsx rename to src/components/helpers.js index 71814bde..b1a7fbe4 100644 --- a/src/components/helpers.jsx +++ b/src/components/helpers.js @@ -1,5 +1,5 @@ import React from 'react'; -import { useSelector } from 'react-redux'; +import config from '@plone/volto/registry'; class NoSSR extends React.Component { state = { @@ -15,24 +15,31 @@ class NoSSR extends React.Component { } } -// TODO replace ugly flattenESUrlToPath hack. Problem Elastic responds with backend Plone Zeo client url. +// TODO replace ugly flattenESUrlToPath hack. Problem Elastic responds with backend Plone url which is host.docker…. +/** + * flatten url to path if internal, else leave it as it is + * @param {String} url + * @returns path + * + * "http://host.docker.internal:17091/Plone/news/sprint-on-accessibility" + * -> + * "/news/sprint-on-accessibility" + */ function flattenESUrlToPath(url) { - var pathArray = url.split('/'); - var newPathname = ''; - for (let i = 4; i < pathArray.length; i++) { - newPathname += '/'; - newPathname += pathArray[i]; + if (url.startsWith('https')) { + // external url + return url; } + const urlArray = url.split(':'); + const newPathname = `http://localhost:${urlArray.pop()}`.replace( + config.settings.internalApiPath, + '', + ); return newPathname; } const scrollToTarget = (target, offsetHeight = 0) => { - const bodyRect = document.body.getBoundingClientRect().top; - const targetRect = target.getBoundingClientRect().top; - const targetPosition = targetRect - bodyRect - offsetHeight; - - return window.scrollTo({ - top: targetPosition, + target.scrollIntoView({ behavior: 'smooth', }); }; diff --git a/src/components/helpers.test.js b/src/components/helpers.test.js new file mode 100644 index 00000000..7b5b8292 --- /dev/null +++ b/src/components/helpers.test.js @@ -0,0 +1,18 @@ +import config from '@plone/volto/registry'; + +import { flattenESUrlToPath } from './helpers'; + +beforeEach(() => { + config.settings.legacyTraverse = false; +}); + +const { settings } = config; + +describe('helpers', () => { + describe('flattenESUrlToPath', () => { + it('flattens a given URL to the app URL', () => { + // TODO + // expect(flattenESUrlToPath(`${settings.apiPath}/edit`)).toBe('/edit'); + }); + }); +}); From de323606cb3c758e79bf4104212d6df2810be18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:01:30 +0200 Subject: [PATCH 132/226] Move messages --- .../Blocks/SearchSectionsWidget.jsx | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/components/Blocks/SearchSectionsWidget.jsx b/src/components/Blocks/SearchSectionsWidget.jsx index 53b99ae7..d3e1ec41 100644 --- a/src/components/Blocks/SearchSectionsWidget.jsx +++ b/src/components/Blocks/SearchSectionsWidget.jsx @@ -3,29 +3,14 @@ import { defineMessages } from 'react-intl'; import ObjectListWidget from '@plone/volto/components/manage/Widgets/ObjectListWidget'; -const messages = defineMessages({ - searchSection: { - id: 'Search section', - defaultMessage: 'Search section', - }, - searchSectionLabel: { - id: 'Search section label', - defaultMessage: 'Search section label', - }, - addSearchSection: { - id: 'Add search section', - defaultMessage: 'Add search section', - }, - showFilter: { - id: 'Show filters', - defaultMessage: 'Show filter', - }, -}); +import messages from '../../messages'; const ItemSchema = ({ intl }) => { return { title: intl.formatMessage(messages.searchSection), - addMessage: intl.formatMessage(messages.addSearchSection), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.searchSection), + }), properties: { section: { title: intl.formatMessage(messages.searchSection), From 21597dcc591ce5be8f61d4e27716245aa1934135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:02:35 +0200 Subject: [PATCH 133/226] Easier configuration of block: no backend_url anymore --- src/components/Blocks/schema.js | 57 ++++++++++--------- .../Searchkit/CustomESRequestSerializer.jsx | 2 +- .../Searchkit/CustomESResponseSerializer.jsx | 2 - src/components/Searchkit/ESSearchApi.jsx | 5 +- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index e26bfc7e..8f9a6cc5 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -6,7 +6,10 @@ import { import messages from '../../messages'; const FacetSchema = ({ intl }) => ({ - title: intl.formatMessage(messages.item), + title: intl.formatMessage(messages.facet), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.facet), + }), fieldsets: [ { id: 'default', @@ -42,27 +45,22 @@ const FacetSchema = ({ intl }) => ({ }, required: ['field'], }); +const ExtrainfoSchema = ({ intl }) => { + const facetschema = FacetSchema({ intl }); + const extrainfoschema = { + ...facetschema, + title: intl.formatMessage(messages.metadata), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.metadata), + }), + }; + return extrainfoschema; +}; export const SearchBlockSchema = ({ data = {}, intl }) => { return { title: intl.formatMessage(messages.searchBlock), fieldsets: [ - { - id: 'default', - title: 'API', - fields: ['backend_url', 'frontend_url'], - }, - { - id: 'facets', - title: 'Facets', - fields: [ - 'search_sections', - 'allow_search_excluded_sections', - 'show_filter_for_excluded_sections', - 'facet_fields', - 'filterLayout', - ], - }, { id: 'search', title: 'Search', @@ -73,6 +71,17 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { 'batchSize', ], }, + { + id: 'facets', + title: intl.formatMessage(messages.facets), + fields: [ + 'facet_fields', + 'filterLayout', + 'search_sections', + 'allow_search_excluded_sections', + 'show_filter_for_excluded_sections', + ], + }, { id: 'results', title: 'Results', @@ -100,17 +109,9 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', // default: 'esploneindex', // }, - backend_url: { - title: 'Backend URL', - default: 'http://localhost:8080/Plone', - }, - frontend_url: { - title: 'Frontend URL', - default: 'http://localhost:3000', - }, search_sections: { - title: 'Search in sections', - description: 'Search can be restricted by sections / paths', + title: intl.formatMessage(messages.searchInSections), + description: intl.formatMessage(messages.searchInSectionsDescription), type: 'dict', factory: 'JSONField', widget: 'searchsectionswidget', @@ -173,7 +174,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { extrainfo_fields: { title: intl.formatMessage(messages.metadata), widget: 'object_list', - schema: FacetSchema({ intl }), + schema: ExtrainfoSchema({ intl }), }, subjectsFieldname: { title: 'Field name of tags field', diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 4fe7e7f9..faad29d3 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -1,5 +1,5 @@ import { extend, isEmpty, keyBy, trim } from 'lodash'; -import { getObjectFromObjectList } from '../helpers.jsx'; +import { getObjectFromObjectList } from '../helpers'; import config from '@plone/volto/registry'; diff --git a/src/components/Searchkit/CustomESResponseSerializer.jsx b/src/components/Searchkit/CustomESResponseSerializer.jsx index 8327a86d..cf770b3d 100644 --- a/src/components/Searchkit/CustomESResponseSerializer.jsx +++ b/src/components/Searchkit/CustomESResponseSerializer.jsx @@ -22,8 +22,6 @@ function _pimpedAggregations(aggregations) { export class CustomESResponseSerializer { constructor(config) { this.serialize = this.serialize.bind(this); - this.backend_url = config.backend_url; - this.frontend_url = config.frontend_url; } /** diff --git a/src/components/Searchkit/ESSearchApi.jsx b/src/components/Searchkit/ESSearchApi.jsx index a0a37bf3..a9b20c1d 100644 --- a/src/components/Searchkit/ESSearchApi.jsx +++ b/src/components/Searchkit/ESSearchApi.jsx @@ -39,10 +39,7 @@ export class PloneSearchApi { search_sections: config.search_sections, language: config.language, }); - this.responseSerializer = new responseSerializerCls({ - backend_url: config.backend_url, - frontend_url: config.frontend_url, - }); + this.responseSerializer = new responseSerializerCls({}); } /** From a49444749c02c51cd7c059906988cf3100e1e7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:02:41 +0200 Subject: [PATCH 134/226] Update Results.js --- src/components/Searchkit/Results.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Searchkit/Results.js b/src/components/Searchkit/Results.js index 71b535bd..812c067e 100644 --- a/src/components/Searchkit/Results.js +++ b/src/components/Searchkit/Results.js @@ -75,7 +75,7 @@ Results.propTypes = {}; Results.defaultProps = {}; const MyResults = (props) => { - // Add scroll to input field search + // Add scroll to search input field React.useEffect(() => { const el = document.querySelector('.searchkitsearch'); if (el) { From 50b2575efc9a207f5f453050040e86af5ee50e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:03:11 +0200 Subject: [PATCH 135/226] Fix jumping of page when navigating on search page --- src/components/Searchkit/ResultsLoader.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/components/Searchkit/ResultsLoader.jsx diff --git a/src/components/Searchkit/ResultsLoader.jsx b/src/components/Searchkit/ResultsLoader.jsx new file mode 100644 index 00000000..64663564 --- /dev/null +++ b/src/components/Searchkit/ResultsLoader.jsx @@ -0,0 +1,12 @@ +import { Loader } from 'semantic-ui-react'; + +function ResultsLoaderComponent() { + return ( + <> + +
+ + ); +} + +export default ResultsLoaderComponent; From a01311a648a9a59bfa76d44503dd2338dcf7a211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:04:11 +0200 Subject: [PATCH 136/226] No more backend_url from block data. Refactoring translate function / querystringindexes --- src/components/Views/FacetedSearch.jsx | 60 +++++++++++++------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 77bafe9d..8a32e917 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -33,6 +33,7 @@ import { import { expandToBackendURL } from '@plone/volto/helpers'; import { FormattedDate, Icon } from '@plone/volto/components'; +import { addAppURL, toPublicURL } from '@plone/volto/helpers'; import leftAngle from '@plone/volto/icons/left-key.svg'; import rightAngle from '@plone/volto/icons/right-key.svg'; import firstAngle from '@plone/volto/icons/first.svg'; @@ -48,6 +49,7 @@ import { CustomESResponseSerializer } from '../Searchkit/CustomESResponseSeriali import { OnResults } from '../Searchkit/Results'; import SectionsSearch from '../Searchkit/SectionsSearch'; import SearchBarSection from '../Searchkit/SearchBarSection'; +import MyResultsLoaderElement from '../Searchkit/ResultsLoader'; import { ElasticSearchHighlights } from '../Searchkit/ElasticSearchHighlights'; import ErrorComponent from '../Searchkit/Error'; @@ -57,10 +59,26 @@ import './less/springisnow-volto-searchkit-block.less'; import config from '@plone/volto/registry'; +/** + * + * @param {Object} querystringindexes + * @param {String} fieldname One of the indexes + * @param {String} key to be translated + * @returns {String} + */ +const translate = (querystringindexes, fieldname, key) => { + let label = key; + if (querystringindexes && fieldname in querystringindexes) { + label = querystringindexes[fieldname].values[key]?.title || key; + } + return label; +}; + // TODO Make reviewstatemapping configurable export const ploneSearchApi = (data, language) => { const cookies = new Cookies(); const authToken = cookies.get('auth_token'); + return new PloneSearchApi({ fetchPayload: { url: expandToBackendURL('/@kitsearch'), @@ -80,8 +98,6 @@ export const ploneSearchApi = (data, language) => { allowed_review_states: data.allowed_review_states, search_sections: data.search_sections, language: language, - backend_url: data.backend_url, - frontend_url: data.frontend_url, // elastic_search_api_url: data.elastic_search_api_url, // elastic_search_api_index: data.elastic_search_api_index, }); @@ -99,17 +115,9 @@ const _ExtraInfo = (props) => { let subjectsFieldname = props.currentQueryState.data?.subjectsFieldname; // "subjects"; const querystringindexes = useSelector( - (state) => state.query?.data?.querystringindexes, + (state) => state.query?.querystringindexes, ); - const translate = (key, fieldname) => { - let label = key; - if (querystringindexes && fieldname in querystringindexes) { - label = querystringindexes[fieldname]?.values[key]?.title || key; - } - return label; - }; - return ( {Object.keys(extrainfo_fields).map((extrainfo_key, idx) => { @@ -124,7 +132,7 @@ const _ExtraInfo = (props) => { {extrainfo_fields[extrainfo_key]}: {extrainfo_value?.map((item, index) => { - let tito = translate(item, extrainfo_key); + let tito = translate(querystringindexes, extrainfo_key, item); let payloadOfFilter = { searchQuery: { sortBy: 'bestmatch', @@ -207,14 +215,11 @@ const ExtraInfo = withState(_ExtraInfo); const _CustomResultsListItem = (props) => { const { result } = props; - const backend_url = props.currentQueryState.data?.backend_url; - const is_external_content = !result['@id'].includes(backend_url); - const item_url = result['@id'].includes(backend_url) - ? flattenESUrlToPath(result['@id']) - : result['@id']; + const item_url = flattenESUrlToPath(result['@id']); + const is_external_content = item_url.startsWith('https'); const locale = useSelector((state) => state.query?.locale); const querystringindexes = useSelector( - (state) => state.query?.data?.querystringindexes, + (state) => state.query?.querystringindexes, ); const showNewsItemPublishedDate = useSelector( (state) => state.query?.data.showNewsItemPublishedDate, @@ -223,14 +228,6 @@ const _CustomResultsListItem = (props) => { (state) => state.query?.data.showEventStartDate, ); - const translate = (key) => { - let label = key; - if (querystringindexes?.informationtype) { - label = querystringindexes.informationtype.values[key]?.title || key; - } - return label; - }; - return ( { result.informationtype?.length > 0 ? ( {result.informationtype?.map((item, index) => { - let tito = translate(item); + let tito = translate(querystringindexes, 'informationtype', item); const payload = { searchQuery: { sortBy: 'bestmatch', @@ -362,7 +359,7 @@ const CustomBucketAggregationElement = (props) => { const { title, containerCmp, updateQueryFilters } = props; const fieldname = props.agg.field; const querystringindexes = useSelector( - (state) => state.query?.data?.querystringindexes, + (state) => state.query?.querystringindexes, ); /** @@ -370,7 +367,7 @@ const CustomBucketAggregationElement = (props) => { * @param {*} bucks * @returns */ - const translate = (bucks) => { + const translateBuckets = (bucks) => { if (querystringindexes && fieldname in querystringindexes) { bucks.forEach((element) => { element.label = @@ -383,7 +380,7 @@ const CustomBucketAggregationElement = (props) => { // Get label from token let buckets = containerCmp.props.buckets; - buckets = translate(buckets); + buckets = translateBuckets(buckets); let filter_labels_dict = Object.fromEntries( Array.from(buckets, (x) => [x.key, x.label]), // TODO Translate label ); @@ -540,7 +537,7 @@ const CustomBucketAggregationValuesElement = (props) => { const customEmpytResultsElement = (props) => { const { resetQuery } = props; return ( - +
@@ -719,6 +716,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { 'Sort.element.volto': customSort, 'Pagination.element': customPaginationElement, 'Error.element': ErrorComponent, + 'ResultsLoader.element': MyResultsLoaderElement, }; const dropdownOverriddenComponents = { From c042122db5fbd40cfbd5c564aab9eedc19fcd9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:04:18 +0200 Subject: [PATCH 137/226] Update springisnow-volto-searchkit-block.less --- .../Views/less/springisnow-volto-searchkit-block.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/Views/less/springisnow-volto-searchkit-block.less b/src/components/Views/less/springisnow-volto-searchkit-block.less index 7e3ce2ba..d06a1ae1 100644 --- a/src/components/Views/less/springisnow-volto-searchkit-block.less +++ b/src/components/Views/less/springisnow-volto-searchkit-block.less @@ -270,6 +270,11 @@ body.section_without_filter .facetedsearch_filter { display: none; } +// Loading results +.resultsPlaceholder { + height: 10rem; +} + .highlight { margin-top: 0.5rem; cursor: zoom-in; From 5367415c3fb48c9cc058474204ea8b2e3ca2c025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:46:07 +0200 Subject: [PATCH 138/226] Update index.js --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index c6334a56..cb15d57f 100644 --- a/src/index.js +++ b/src/index.js @@ -83,8 +83,8 @@ const applyConfig = (config) => { facet_fields: [], allowed_content_types: ['Document', 'News Item', 'Event'], allowed_review_states: [], - backend_url: 'http://host.docker.internal:8080/Plone', - frontend_url: 'http://localhost:3000', + // backend_url: 'http://host.docker.internal:8080/Plone', + // frontend_url: 'http://localhost:3000', }; // Fetch querystring indexes. From abe5ed80370033df0fb595b43cb3d89b8cfed5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:46:26 +0200 Subject: [PATCH 139/226] UI test panel --- .../Views/TestSearchkitQuerystrings.jsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/Views/TestSearchkitQuerystrings.jsx b/src/components/Views/TestSearchkitQuerystrings.jsx index 5f456102..7bd5841a 100644 --- a/src/components/Views/TestSearchkitQuerystrings.jsx +++ b/src/components/Views/TestSearchkitQuerystrings.jsx @@ -64,7 +64,7 @@ const _OnHighlights = (props) => { matches_sorted.sort(sort_caseinsensitive); return (
-
Found {matches_sorted.length} matches.
+
{matches_sorted.length} matches found:
{matches_sorted.map((match) => (
{ return ( @@ -97,7 +101,7 @@ const CustomResultsListItem = ({ result, index }) => { }; const DocumentsCount = ({ totalResults }) => { - return
Found {totalResults} documents.
; + return
{totalResults} documents found:
; }; const overriddenComponents = { @@ -176,9 +180,8 @@ const TestSearchkitQuerystrings = (props) => { /> - -
Documents with title and matches
+
From 049ce1ae1142b479886b7a87b31c8e884ae38a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 11:55:10 +0200 Subject: [PATCH 140/226] UI test panel #2 --- src/components/Searchkit/ElasticSearchHighlights.jsx | 2 +- src/components/Views/TestSearchkitQuerystrings.jsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Searchkit/ElasticSearchHighlights.jsx b/src/components/Searchkit/ElasticSearchHighlights.jsx index 739fb2ab..f21270b3 100644 --- a/src/components/Searchkit/ElasticSearchHighlights.jsx +++ b/src/components/Searchkit/ElasticSearchHighlights.jsx @@ -111,8 +111,8 @@ export const ElasticSearchMatches = ({ highlight, indexResult }) => { const matches = getMatches(highlight); return (
+ {matches.length > 0 && matches: } {matches.join(' | ')} -
); }; diff --git a/src/components/Views/TestSearchkitQuerystrings.jsx b/src/components/Views/TestSearchkitQuerystrings.jsx index 7bd5841a..fb6dcba4 100644 --- a/src/components/Views/TestSearchkitQuerystrings.jsx +++ b/src/components/Views/TestSearchkitQuerystrings.jsx @@ -64,7 +64,9 @@ const _OnHighlights = (props) => { matches_sorted.sort(sort_caseinsensitive); return (
-
{matches_sorted.length} matches found:
+ {matches_sorted.length > 0 && ( +
{matches_sorted.length} matches found:
+ )} {matches_sorted.map((match) => (
+
); }; From 64e08fef0f5e7c59886609f47364c6888acb667e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 13:41:27 +0200 Subject: [PATCH 141/226] fieldset with name 'default' is required --- src/components/Blocks/schema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 8f9a6cc5..1fa7e576 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -62,7 +62,7 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { title: intl.formatMessage(messages.searchBlock), fieldsets: [ { - id: 'search', + id: 'default', title: 'Search', fields: [ 'allowed_content_types', From f958729eb6857d1ce4734b5d5c15dd89374c4ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 14:33:20 +0200 Subject: [PATCH 142/226] Add test 'I can open a result' --- acceptance/cypress/tests/results.monolingual.cy.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acceptance/cypress/tests/results.monolingual.cy.js b/acceptance/cypress/tests/results.monolingual.cy.js index c287f9f3..07c848e5 100644 --- a/acceptance/cypress/tests/results.monolingual.cy.js +++ b/acceptance/cypress/tests/results.monolingual.cy.js @@ -67,4 +67,10 @@ describe('Searchkit block tests – search - monolingual', () => { cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); cy.get('.block.searchkitsearch').contains('.202'); }); + + it('I can open a result', function () { + cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); + cy.get('.searchkitresultitem a').first().click(); + cy.get('.block').contains('Matterhorn'); + }); }); From cc5580be48f4ffeb490e0eaa048d5bc2c6d3b254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 14:34:08 +0200 Subject: [PATCH 143/226] Fix flattenESUrlToPath for multiple scenarios where everthing is running in containers or less --- src/components/helpers.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/helpers.js b/src/components/helpers.js index b1a7fbe4..5b82632d 100644 --- a/src/components/helpers.js +++ b/src/components/helpers.js @@ -30,11 +30,12 @@ function flattenESUrlToPath(url) { // external url return url; } - const urlArray = url.split(':'); - const newPathname = `http://localhost:${urlArray.pop()}`.replace( - config.settings.internalApiPath, - '', - ); + + const urlObj = new URL(url); + const urlArray = urlObj.pathname.split('/').reverse(); + urlArray.pop(); + urlArray.pop(); + const newPathname = `/${urlArray.reverse().join('/')}`; return newPathname; } From 818625bccfeccb254a94410fc2af8ffbbf966b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 18:43:32 +0200 Subject: [PATCH 144/226] Show head_title in results if available --- src/components/Views/FacetedSearch.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index 8a32e917..05fa8263 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -258,6 +258,7 @@ const _CustomResultsListItem = (props) => { })} ) : null} + {result.head_title ? {result.head_title} : null} {is_external_content ? ( Date: Sun, 23 Jun 2024 18:43:54 +0200 Subject: [PATCH 145/226] Test with Volto 17 and 18alpha --- .github/workflows/acceptance.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 046139dd..2d4940fb 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -4,7 +4,6 @@ on: [push] env: ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 @@ -31,6 +30,8 @@ jobs: timeout-minutes: 45 strategy: fail-fast: false + matrix: + volto-version: [17, 18] steps: - name: Checkout uses: actions/checkout@v4 @@ -59,7 +60,7 @@ jobs: spec: cypress/tests/*.monolingual.cy.js install: false start: | - docker compose -f ci.yml --profile prod up + VOLTO_VERSION=${{ matrix.volto-version }} docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' # Upload Cypress screenshots From 6315a511b720e3fb573050b543b40f3d78490b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 18:57:41 +0200 Subject: [PATCH 146/226] Update acceptance.yml --- .github/workflows/acceptance.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 2d4940fb..c6609a3f 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -47,6 +47,7 @@ jobs: - name: 'Cypress: Acceptance tests - monolingual' uses: cypress-io/github-action@v6 env: + VOLTO_VERSION: ${{ matrix.volto-version }} BABEL_ENV: production CYPRESS_RETRIES: 3 # Recommended: pass the GitHub token lets this action correctly @@ -60,7 +61,7 @@ jobs: spec: cypress/tests/*.monolingual.cy.js install: false start: | - VOLTO_VERSION=${{ matrix.volto-version }} docker compose -f ci.yml --profile prod up + docker compose -f ci.yml --profile prod up wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 http://localhost:9200' # Upload Cypress screenshots @@ -97,6 +98,7 @@ jobs: - name: 'Cypress: Acceptance tests - multilingual' uses: cypress-io/github-action@v6 env: + VOLTO_VERSION: ${{ matrix.volto-version }} BABEL_ENV: production CYPRESS_RETRIES: 3 # Recommended: pass the GitHub token lets this action correctly From 8dff275740df65d362a4ecd58fff76832044d0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 19:06:01 +0200 Subject: [PATCH 147/226] Update helpers.test.js --- src/components/helpers.test.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/helpers.test.js b/src/components/helpers.test.js index 7b5b8292..de1b3e9a 100644 --- a/src/components/helpers.test.js +++ b/src/components/helpers.test.js @@ -11,8 +11,14 @@ const { settings } = config; describe('helpers', () => { describe('flattenESUrlToPath', () => { it('flattens a given URL to the app URL', () => { - // TODO - // expect(flattenESUrlToPath(`${settings.apiPath}/edit`)).toBe('/edit'); + expect( + flattenESUrlToPath( + 'http://host.docker.internal:17091/Plone/news/sprint-on-accessibility', + ), + ).toBe('/news/sprint-on-accessibility'); + expect(flattenESUrlToPath('https://nzz.ch/arosa/piste')).toBe( + '/arosa/piste', + ); }); }); }); From 4ee1fa686b513b20967bb3bf863d02c53f475f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 19:26:03 +0200 Subject: [PATCH 148/226] Update acceptance.yml --- .github/workflows/acceptance.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index c6609a3f..848f18dd 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -83,6 +83,8 @@ jobs: timeout-minutes: 45 strategy: fail-fast: false + matrix: + volto-version: [17, 18] steps: - name: Checkout uses: actions/checkout@v4 From 194b65bd7da96df58d8790d15281c83f0396f9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 19:26:27 +0200 Subject: [PATCH 149/226] Update helpers.test.js --- src/components/helpers.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/helpers.test.js b/src/components/helpers.test.js index de1b3e9a..3887b2ce 100644 --- a/src/components/helpers.test.js +++ b/src/components/helpers.test.js @@ -17,7 +17,7 @@ describe('helpers', () => { ), ).toBe('/news/sprint-on-accessibility'); expect(flattenESUrlToPath('https://nzz.ch/arosa/piste')).toBe( - '/arosa/piste', + 'https://nzz.ch/arosa/piste', ); }); }); From e9f72f709e6b8c13ed734135e151a07c92811f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 19:26:54 +0200 Subject: [PATCH 150/226] Update unit.yml matrix Volto --- .github/workflows/unit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 754aba4c..fea0be17 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -31,10 +31,13 @@ jobs: strategy: matrix: node-version: [20.x] + volto-version: [17, 18] steps: - name: Main checkout uses: actions/checkout@v4 - name: Unit tests + env: + VOLTO_VERSION: ${{ matrix.volto-version }} run: make test-ci From d35490db3522b8c5c169d90f3f1dba1537ae2fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 20:58:49 +0200 Subject: [PATCH 151/226] Update unit.yml --- .github/workflows/unit.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index fea0be17..fe7f3a26 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -4,7 +4,7 @@ on: [push] env: ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 + VOLTO_VERSION: 18 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 @@ -38,6 +38,4 @@ jobs: uses: actions/checkout@v4 - name: Unit tests - env: - VOLTO_VERSION: ${{ matrix.volto-version }} run: make test-ci From 7ab6ac73ec8e18ab3df0ec625d60da196f1fd563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 20:59:58 +0200 Subject: [PATCH 152/226] Update unit.yml --- .github/workflows/unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index fe7f3a26..6109a00b 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -4,7 +4,7 @@ on: [push] env: ADDON_NAME: '@rohberg/volto-searchkit-block' ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 18 + VOLTO_VERSION: 17 PLONE_VERSION: 6.0 INDEX_SERVER: opensearch:9200 From 1643b29e596bcec874861c02610b6f7bf4c43198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 21:24:07 +0200 Subject: [PATCH 153/226] Update unit.yml --- .github/workflows/unit.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 6109a00b..c5363526 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -31,11 +31,13 @@ jobs: strategy: matrix: node-version: [20.x] - volto-version: [17, 18] + volto-version: [17] steps: - name: Main checkout uses: actions/checkout@v4 - name: Unit tests + # TODO do 'docker compose with ' env: VOLTO_VERSION: ${{ matrix.volto-version }} + # TODO Remove matrix option node-version run: make test-ci From 9e07c4c9be3d0a81f103d5e77f58ced3c3b469e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 21:30:09 +0200 Subject: [PATCH 154/226] Update acceptance.yml (test with Volto 18 postponed: pnpm, new project structure) --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 848f18dd..5c392260 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - volto-version: [17, 18] + volto-version: [17] steps: - name: Checkout uses: actions/checkout@v4 From 9b35e1e932a90413ed1b56321fb2979fb7b10b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 21:41:34 +0200 Subject: [PATCH 155/226] Update acceptance.yml --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 5c392260..354dd4a1 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -27,7 +27,7 @@ env: jobs: acceptance-monolingual: runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: 46 strategy: fail-fast: false matrix: From abd7f4895c0d6363fcea024f9061af5518e02702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Sun, 23 Jun 2024 22:07:12 +0200 Subject: [PATCH 156/226] Update acceptance.yml --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 354dd4a1..6b34509e 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -84,7 +84,7 @@ jobs: strategy: fail-fast: false matrix: - volto-version: [17, 18] + volto-version: [17] steps: - name: Checkout uses: actions/checkout@v4 From b7347a0f84327ba997fadc2c6ba3362a7d04da34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:25:18 +0200 Subject: [PATCH 157/226] Update .eslintrc.js --- .eslintrc.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index ddf0f598..72392967 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,6 @@ const fs = require('fs'); const projectRootPath = __dirname; +const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); let coreLocation; if (fs.existsSync(`${projectRootPath}/core`)) @@ -7,6 +8,16 @@ if (fs.existsSync(`${projectRootPath}/core`)) else if (fs.existsSync(`${projectRootPath}/../../core`)) coreLocation = `${projectRootPath}/../../core`; +const registry = new AddonConfigurationRegistry( + `${coreLocation}/packages/volto`, +); + +// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons +const addonAliases = Object.keys(registry.packages).map((o) => [ + o, + registry.packages[o].modulePath, +]); + module.exports = { extends: `${coreLocation}/packages/volto/.eslintrc`, rules: { @@ -22,7 +33,11 @@ module.exports = { `${coreLocation}/core/packages/volto-slate/src`, ], ['@plone/registry', `${coreLocation}/packages/registry/src`], - ['@rohberg/volto-searchkit-block', './src'], + [ + '@rohberg/volto-searchkit-block', + './packages/volto-searchkit-block/src', + ], + ...addonAliases, ], extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], }, From a02548b3e68394c9dceeb2baa351175f13d60dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:26:07 +0200 Subject: [PATCH 158/226] Update .gitignore --- .gitignore | 136 +++++------------------------------------------------ 1 file changed, 11 insertions(+), 125 deletions(-) diff --git a/.gitignore b/.gitignore index 94c324eb..0e623cec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,126 +1,12 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port -testing/volumes/src/ -testing/volumes/var/ -volumes/src/ -volumes/var/ - -addon-testing-project -addon-testing-project_cypress - -api/venv/ -api/instance/ -api/sources/ -api/constraints-mxdev.txt -api/requirements-mxdev.txt -development-searchkitblock/celery/venv/ -development-searchkitblock/celery/sources/ -acceptance/cypress/screenshots/ -build/ -.yarn/ +.*project +.settings/ +.vscode +*~ +acceptance/cypress/videos/ +acceptance/node_modules +.storybook-build +build +core +node_modules +results yarn.lock - -!dockerfiles/opensearch/.env -!acceptance/.env From fcddad9d60023c131027df41c03ae45da191f863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:28:05 +0200 Subject: [PATCH 159/226] configuration project --- .npmignore | 16 ++++++++++++++++ .npmrc | 6 ++++++ .prettierignore | 1 + .prettierrc | 12 ++++++++++++ .release-it.json | 16 ---------------- .stylelintrc | 32 ++++++++++++++++++++++++++++++++ babel.config.js | 17 ----------------- jest-addon.config.js | 20 ++------------------ pm2.config.js | 14 -------------- pnpm-workspace.yaml | 4 ++++ 10 files changed, 73 insertions(+), 65 deletions(-) create mode 100644 .npmignore create mode 100644 .npmrc create mode 100644 .prettierrc delete mode 100644 .release-it.json create mode 100644 .stylelintrc delete mode 100644 babel.config.js delete mode 100644 pm2.config.js create mode 100644 pnpm-workspace.yaml diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..eb6fa944 --- /dev/null +++ b/.npmignore @@ -0,0 +1,16 @@ +.vscode/ +.history +logs +*.log +npm-debug.log* +.DS_Store +*.swp +yarn-error.log + +node_modules +dockerfiles +acceptance +build +dist +yarn.lock +.storybook diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..71c68438 --- /dev/null +++ b/.npmrc @@ -0,0 +1,6 @@ +public-hoist-pattern[]=*eslint* +public-hoist-pattern[]=*prettier* +public-hoist-pattern[]=*stylelint* +public-hoist-pattern[]=*cypress* +public-hoist-pattern[]=*process* +public-hoist-pattern[]=*parcel* diff --git a/.prettierignore b/.prettierignore index 192ea8b4..16f05c73 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ +.storybook CHANGELOG.md README.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..c56f6269 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "all", + "singleQuote": true, + "overrides": [ + { + "files": "*.overrides", + "options": { + "parser": "less" + } + } + ] + } diff --git a/.release-it.json b/.release-it.json deleted file mode 100644 index c3aa1317..00000000 --- a/.release-it.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "npm": { - "publish": true - }, - "git": { - "changelog": "npx auto-changelog --stdout --commit-limit false -u --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", - "tagName": "${version}" - }, - "github": { - "release": true - }, - "hooks": { - "before:init": "", - "after:bump": "npx auto-changelog --commit-limit false -p" - } -} diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 00000000..09d4a276 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,32 @@ +{ + "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, + "order/properties-alphabetical-order": null + } +} diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 51bd52b5..00000000 --- a/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/jest-addon.config.js b/jest-addon.config.js index f66acd49..984c267e 100644 --- a/jest-addon.config.js +++ b/jest-addon.config.js @@ -1,27 +1,11 @@ module.exports = { - testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ['../../../packages'], + testMatch: ['/../../../../**/?(*.)+(spec|test).[jt]s?(x)'], collectCoverageFrom: [ 'src/addons/**/src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts', ], transformIgnorePatterns: ['node_modules/(?!(volto-slate|@plone/volto)/)'], - moduleNameMapper: { - '@plone/volto/cypress': '/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, diff --git a/pm2.config.js b/pm2.config.js deleted file mode 100644 index 13bd2e88..00000000 --- a/pm2.config.js +++ /dev/null @@ -1,14 +0,0 @@ -let apps = [ - { - name: 'searchkit_backend', - script: 'venv/bin/runwsgi instance/etc/zope.ini', - cwd: './backend', - }, - { - name: 'searchkit_frontend', - script: 'yarn build && yarn start:prod', - cwd: './frontend', - }, -]; - -module.exports = { apps: apps }; diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..f9c04858 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + # all packages in direct subdirs of packages/ + - 'core/packages/*' + - 'packages/*' From 5ef3fb09b8ddc4590c34d8df04b4fc2b662c5ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:28:20 +0200 Subject: [PATCH 160/226] Create volto.config.js --- volto.config.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 volto.config.js diff --git a/volto.config.js b/volto.config.js new file mode 100644 index 00000000..b493706e --- /dev/null +++ b/volto.config.js @@ -0,0 +1,7 @@ +const addons = ['@rohberg/volto-searchkit-block']; +const theme = ''; + +module.exports = { + addons, + theme, +}; From efb1a028acd01cbadb3e4e4d6d205e1b324b0522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:28:35 +0200 Subject: [PATCH 161/226] Delete variables.mk --- variables.mk | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 variables.mk diff --git a/variables.mk b/variables.mk deleted file mode 100644 index 75e4bed0..00000000 --- a/variables.mk +++ /dev/null @@ -1,7 +0,0 @@ -PLONE_VERSION=6.0.11.1 -VOLTO_VERSION=17.16.0 - -ADDON_NAME='@rohberg/volto-searchkit-block' -ADDON_PATH='volto-searchkit-block' -KGS=plone.restapi==9.4.2 plone.volto==4.3.0 plone.rest==4.1.3 -TESTING_ADDONS=plone.app.robotframework==2.1.2 plone.app.testing==7.0.2 \ No newline at end of file From 2512c038553ed95d807c5d4341cfba6907f4df8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:28:49 +0200 Subject: [PATCH 162/226] Delete TODO.md --- TODO.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 TODO.md diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 6a54d48b..00000000 --- a/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ - -# TODOs - -- Restrict block creation to Site Admins. - In future, blocks creation can be restricted by permission / role. Then change attribute "restricted" to false. Until then: download add-on, change restricted to false, add block, switch back to restricted true. - `config.blocks.blocksConfig.searchkitblock.restricted` From 88ebbf1bd50e8d7850ef1749512a38b30710c5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:36:37 +0200 Subject: [PATCH 163/226] Move src to packages/ and respect new files of new structure --- packages/volto-searchkit-block/.gitignore | 3 + .../volto-searchkit-block/.release-it.json | 25 + packages/volto-searchkit-block/CHANGELOG.md | 9 + .../volto-searchkit-block/babel.config.js | 17 + .../locales/de/LC_MESSAGES/volto.po | 12 + .../locales/en/LC_MESSAGES/volto.po | 12 + .../locales/es/LC_MESSAGES/volto.po | 19 + .../locales/pt_BR/LC_MESSAGES/volto.po | 17 + .../volto-searchkit-block/locales/volto.pot | 14 + packages/volto-searchkit-block/news/.gitkeep | 0 packages/volto-searchkit-block/package.json | 39 + .../volto-searchkit-block/public/.gitkeep | 0 .../Blocks/DownloadFiltersMapping.jsx | 48 + .../Blocks/FacetedSearchBlockEdit.jsx | 26 + .../Blocks/FacetedSearchBlockView.jsx | 11 + .../Reference/ReferenceSearchBlockEdit.jsx | 20 + .../Reference/ReferenceSearchBlockView.jsx | 17 + .../src/components/Blocks/Reference/index.js | 4 + .../Blocks/SearchSectionsWidget.jsx | 49 ++ .../src/components/Blocks/Sidebar.jsx | 26 + .../src/components/Blocks/schema.js | 212 +++++ .../Searchkit/CustomESRequestSerializer.jsx | 438 ++++++++++ .../Searchkit/CustomESResponseSerializer.jsx | 46 + .../src/components/Searchkit/ESSearchApi.jsx | 74 ++ .../Searchkit/ElasticSearchHighlights.jsx | 118 +++ .../src/components/Searchkit/Error.jsx | 20 + .../src/components/Searchkit/Error.test.js | 36 + .../Searchkit/LICENSE_Cern_react-searchkit.md | 21 + .../src/components/Searchkit/Results.js | 89 ++ .../components/Searchkit/ResultsLoader.jsx | 12 + .../components/Searchkit/SearchBarSection.jsx | 51 ++ .../components/Searchkit/SectionsSearch.jsx | 156 ++++ .../__snapshots__/Error.test.js.snap | 23 + .../src/components/StateLogger.jsx | 23 + .../src/components/Views/FacetedSearch.jsx | 826 ++++++++++++++++++ .../Views/TestSearchkitQuerystrings.jsx | 224 +++++ .../springisnow-volto-searchkit-block.less | 296 +++++++ .../src/components/helpers.js | 65 ++ .../src/components/helpers.test.js | 24 + .../src/components/index.js | 9 + packages/volto-searchkit-block/src/index.js | 119 +++ .../volto-searchkit-block/src/messages.js | 104 +++ packages/volto-searchkit-block/towncrier.toml | 33 + packages/volto-searchkit-block/tsconfig.json | 29 + 44 files changed, 3416 insertions(+) create mode 100644 packages/volto-searchkit-block/.gitignore create mode 100644 packages/volto-searchkit-block/.release-it.json create mode 100644 packages/volto-searchkit-block/CHANGELOG.md create mode 100644 packages/volto-searchkit-block/babel.config.js create mode 100644 packages/volto-searchkit-block/locales/de/LC_MESSAGES/volto.po create mode 100644 packages/volto-searchkit-block/locales/en/LC_MESSAGES/volto.po create mode 100644 packages/volto-searchkit-block/locales/es/LC_MESSAGES/volto.po create mode 100644 packages/volto-searchkit-block/locales/pt_BR/LC_MESSAGES/volto.po create mode 100644 packages/volto-searchkit-block/locales/volto.pot create mode 100644 packages/volto-searchkit-block/news/.gitkeep create mode 100644 packages/volto-searchkit-block/package.json create mode 100644 packages/volto-searchkit-block/public/.gitkeep create mode 100644 packages/volto-searchkit-block/src/components/Blocks/DownloadFiltersMapping.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockEdit.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockView.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/Reference/index.js create mode 100644 packages/volto-searchkit-block/src/components/Blocks/SearchSectionsWidget.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/Sidebar.jsx create mode 100644 packages/volto-searchkit-block/src/components/Blocks/schema.js create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/CustomESRequestSerializer.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/CustomESResponseSerializer.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/ESSearchApi.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/ElasticSearchHighlights.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/Error.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/Error.test.js create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/LICENSE_Cern_react-searchkit.md create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/Results.js create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/ResultsLoader.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/SearchBarSection.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/SectionsSearch.jsx create mode 100644 packages/volto-searchkit-block/src/components/Searchkit/__snapshots__/Error.test.js.snap create mode 100644 packages/volto-searchkit-block/src/components/StateLogger.jsx create mode 100644 packages/volto-searchkit-block/src/components/Views/FacetedSearch.jsx create mode 100644 packages/volto-searchkit-block/src/components/Views/TestSearchkitQuerystrings.jsx create mode 100644 packages/volto-searchkit-block/src/components/Views/less/springisnow-volto-searchkit-block.less create mode 100644 packages/volto-searchkit-block/src/components/helpers.js create mode 100644 packages/volto-searchkit-block/src/components/helpers.test.js create mode 100644 packages/volto-searchkit-block/src/components/index.js create mode 100644 packages/volto-searchkit-block/src/index.js create mode 100644 packages/volto-searchkit-block/src/messages.js create mode 100644 packages/volto-searchkit-block/towncrier.toml create mode 100644 packages/volto-searchkit-block/tsconfig.json diff --git a/packages/volto-searchkit-block/.gitignore b/packages/volto-searchkit-block/.gitignore new file mode 100644 index 00000000..b462bc98 --- /dev/null +++ b/packages/volto-searchkit-block/.gitignore @@ -0,0 +1,3 @@ +node_modules +build +README.md diff --git a/packages/volto-searchkit-block/.release-it.json b/packages/volto-searchkit-block/.release-it.json new file mode 100644 index 00000000..9ef4f97b --- /dev/null +++ b/packages/volto-searchkit-block/.release-it.json @@ -0,0 +1,25 @@ +{ + "hooks": { + "after:bump": [ + "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", + "pipx run towncrier build --yes --version ${version}", + "cp ../../README.md ./ && cp CHANGELOG.md ../../CHANGELOG.md", + "python3 -c 'import json; data = json.load(open(\"../../package.json\")); data[\"version\"] = \"${version}\"; json.dump(data, open(\"../../package.json\", \"w\"), indent=2)'", + "git add ../../CHANGELOG.md ../../package.json" + ], + "after:release": "rm .changelog.draft README.md" + }, + "git": { + "changelog": "pipx run towncrier build --draft --yes --version 0.0.0", + "requireUpstream": false, + "requireCleanWorkingDir": false, + "commitMessage": "Release ${version}", + "tagName": "${version}", + "tagAnnotation": "Release ${version}" + }, + "github": { + "release": true, + "releaseName": "${version}", + "releaseNotes": "cat .changelog.draft" + } +} diff --git a/packages/volto-searchkit-block/CHANGELOG.md b/packages/volto-searchkit-block/CHANGELOG.md new file mode 100644 index 00000000..e24f3a8e --- /dev/null +++ b/packages/volto-searchkit-block/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + + + + diff --git a/packages/volto-searchkit-block/babel.config.js b/packages/volto-searchkit-block/babel.config.js new file mode 100644 index 00000000..51bd52b5 --- /dev/null +++ b/packages/volto-searchkit-block/babel.config.js @@ -0,0 +1,17 @@ +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/packages/volto-searchkit-block/locales/de/LC_MESSAGES/volto.po b/packages/volto-searchkit-block/locales/de/LC_MESSAGES/volto.po new file mode 100644 index 00000000..796f3cf1 --- /dev/null +++ b/packages/volto-searchkit-block/locales/de/LC_MESSAGES/volto.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language: de\n" +"Language-Team: \n" +"Content-Type: \n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/packages/volto-searchkit-block/locales/en/LC_MESSAGES/volto.po b/packages/volto-searchkit-block/locales/en/LC_MESSAGES/volto.po new file mode 100644 index 00000000..38c369ef --- /dev/null +++ b/packages/volto-searchkit-block/locales/en/LC_MESSAGES/volto.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language: en\n" +"Language-Team: \n" +"Content-Type: \n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/packages/volto-searchkit-block/locales/es/LC_MESSAGES/volto.po b/packages/volto-searchkit-block/locales/es/LC_MESSAGES/volto.po new file mode 100644 index 00000000..7288b050 --- /dev/null +++ b/packages/volto-searchkit-block/locales/es/LC_MESSAGES/volto.po @@ -0,0 +1,19 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-05-09 11:45-0400\n" +"PO-Revision-Date: 2023-05-10 11:34-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" +"Preferred-Encodings: utf-8\n" +"MIME-Version: 1.0\n" +"Language-Code: es\n" +"Language-Name: Español\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" +"X-Generator: Poedit 2.2.1\n" diff --git a/packages/volto-searchkit-block/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto-searchkit-block/locales/pt_BR/LC_MESSAGES/volto.po new file mode 100644 index 00000000..e01604d6 --- /dev/null +++ b/packages/volto-searchkit-block/locales/pt_BR/LC_MESSAGES/volto.po @@ -0,0 +1,17 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-04-06T16:01:32.969Z\n" +"PO-Revision-Date: \n" +"Last-Translator: Plone i18n \n" +"Language: \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: pt_BR\n" +"Language-Name: Português do Brasil\n" +"Preferred-Encodings: utf-8\n" +"Domain: volto\n" diff --git a/packages/volto-searchkit-block/locales/volto.pot b/packages/volto-searchkit-block/locales/volto.pot new file mode 100644 index 00000000..ea538b12 --- /dev/null +++ b/packages/volto-searchkit-block/locales/volto.pot @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"POT-Creation-Date: 2024-03-22T12:43:34.158Z\n" +"Last-Translator: Plone i18n \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: en\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8\n" +"Domain: volto\n" diff --git a/packages/volto-searchkit-block/news/.gitkeep b/packages/volto-searchkit-block/news/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/volto-searchkit-block/package.json b/packages/volto-searchkit-block/package.json new file mode 100644 index 00000000..3dd3daec --- /dev/null +++ b/packages/volto-searchkit-block/package.json @@ -0,0 +1,39 @@ +{ + "name": "@rohberg/volto-searchkit-block", + "version": "1.0.0-alpha.0", + "description": "Search with OpenSearch", + "main": "src/index.js", + "license": "MIT", + "keywords": [ + "volto-addon", + "volto", + "plone", + "react" + ], + "author": "Katja Süss", + "homepage": "https://github.com/rohberg/volto-searchkit-block#readme", + "repository": { + "type": "git", + "url": "git@github.com:rohberg/volto-searchkit-block.git" + }, + "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" + }, + "dependencies": {}, + "peerDependencies": { + "react": "18.2.0", + "react-dom": "18.2.0", + "react-intl": "3.8.0" + }, + "devDependencies": { + "@plone/scripts": "^3.6.1", + "release-it": "^17.1.1" + } +} diff --git a/packages/volto-searchkit-block/public/.gitkeep b/packages/volto-searchkit-block/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/volto-searchkit-block/src/components/Blocks/DownloadFiltersMapping.jsx b/packages/volto-searchkit-block/src/components/Blocks/DownloadFiltersMapping.jsx new file mode 100644 index 00000000..a9f1f1f9 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/DownloadFiltersMapping.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { Button } from 'semantic-ui-react'; + +const FooComponent = ({ data }) => { + const vocabularies = useSelector((state) => state.querystring?.indexes); + function exportFiltersMapping(data) { + const filternames = data.facet_fields?.map((el) => { + return el.field.value; + }); + let ff = {}; + filternames?.forEach((fname) => { + let foo = vocabularies[fname].values; + Object.keys(foo).forEach((el) => { + ff[el] = foo[el].title; + }); + }); + let map = { + facet_fields: ff, + search_sections: + data.search_sections && + Object.fromEntries( + data.search_sections.items.map((el) => { + return [el.section, el.label]; + }), + ), + }; + const fileData = JSON.stringify(map); + const blob = new Blob([fileData], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.download = 'filter-mapping.json'; + link.href = url; + link.click(); + } + + return ( + + ); +}; + +export default FooComponent; diff --git a/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockEdit.jsx b/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockEdit.jsx new file mode 100644 index 00000000..cb2b8b33 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockEdit.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Container, Segment } from 'semantic-ui-react'; +import { SidebarPortal } from '@plone/volto/components'; + +import Sidebar from './Sidebar'; +import FacetedSearchBlockView from './FacetedSearchBlockView'; +import DownloadFiltersMapping from './DownloadFiltersMapping'; + +const Edit = ({ data, onChangeBlock, block, selected }) => { + return ( +
+ + + + + + + + + + +
+ ); +}; + +export default Edit; diff --git a/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockView.jsx b/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockView.jsx new file mode 100644 index 00000000..6cc0e92d --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/FacetedSearchBlockView.jsx @@ -0,0 +1,11 @@ +import FacetedSearch from '../Views/FacetedSearch'; + +const View = ({ data }) => { + return ( +
+ +
+ ); +}; + +export default View; diff --git a/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx b/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx new file mode 100644 index 00000000..111774b9 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { SidebarPortal } from '@plone/volto/components'; + +import Sidebar from '../Sidebar'; +// import FacetedSearch from '../Views/FacetedSearch'; + +const Edit = ({ data, onChangeBlock, block, selected }) => { + return ( +
+ + + + +

Search with bucket aggregation of ElasticSearch

+

Reference with react-searchkit defaults

+
+ ); +}; + +export default Edit; diff --git a/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx b/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx new file mode 100644 index 00000000..38d12acf --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import FacetedSearch from '../../Views/FacetedSearch'; + +const View = ({ data }) => { + return ( +
+ +
+ ); +}; + +export default View; diff --git a/packages/volto-searchkit-block/src/components/Blocks/Reference/index.js b/packages/volto-searchkit-block/src/components/Blocks/Reference/index.js new file mode 100644 index 00000000..b1c77d4b --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/Reference/index.js @@ -0,0 +1,4 @@ +import ReferenceSearchBlockEdit from './ReferenceSearchBlockEdit'; +import ReferenceSearchBlockView from './ReferenceSearchBlockView'; + +export { ReferenceSearchBlockEdit, ReferenceSearchBlockView }; diff --git a/packages/volto-searchkit-block/src/components/Blocks/SearchSectionsWidget.jsx b/packages/volto-searchkit-block/src/components/Blocks/SearchSectionsWidget.jsx new file mode 100644 index 00000000..d3e1ec41 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/SearchSectionsWidget.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { defineMessages } from 'react-intl'; + +import ObjectListWidget from '@plone/volto/components/manage/Widgets/ObjectListWidget'; + +import messages from '../../messages'; + +const ItemSchema = ({ intl }) => { + return { + title: intl.formatMessage(messages.searchSection), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.searchSection), + }), + properties: { + section: { + title: intl.formatMessage(messages.searchSection), + }, + label: { + title: intl.formatMessage(messages.searchSectionLabel), + }, + show_filter: { + title: intl.formatMessage(messages.showFilter), + type: 'boolean', + default: true, + }, + }, + fieldsets: [ + { + id: 'default', + title: 'History-Eintrag', + fields: ['section', 'label', 'show_filter'], + }, + ], + required: [], + }; +}; + +const SearchSectionsWidget = (props) => { + return ( + props.onChange(id, { items: value })} + /> + ); +}; + +export default SearchSectionsWidget; diff --git a/packages/volto-searchkit-block/src/components/Blocks/Sidebar.jsx b/packages/volto-searchkit-block/src/components/Blocks/Sidebar.jsx new file mode 100644 index 00000000..c6741b6b --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/Sidebar.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { useIntl } from 'react-intl'; +import { SearchBlockSchema } from './schema'; +import { BlockDataForm } from '@plone/volto/components'; + +const Sidebar = ({ data, block, onChangeBlock }) => { + const intl = useIntl(); + let schema = SearchBlockSchema({ data, intl }); + return ( + { + onChangeBlock(block, { + ...data, + [id]: value, + }); + }} + onChangeBlock={onChangeBlock} + formData={data} + block={block} + /> + ); +}; + +export default Sidebar; diff --git a/packages/volto-searchkit-block/src/components/Blocks/schema.js b/packages/volto-searchkit-block/src/components/Blocks/schema.js new file mode 100644 index 00000000..1fa7e576 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Blocks/schema.js @@ -0,0 +1,212 @@ +// TODO translations title and descriptions of fields +import { + hasNonValueOperation, + hasDateOperation, +} from '@plone/volto/components/manage/Blocks/Search/utils'; +import messages from '../../messages'; + +const FacetSchema = ({ intl }) => ({ + title: intl.formatMessage(messages.facet), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.facet), + }), + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['title', 'field'], + }, + ], + properties: { + title: { + title: intl.formatMessage(messages.label), + }, + field: { + title: intl.formatMessage(messages.field), + widget: 'select_querystring_field', + vocabulary: { '@id': 'plone.app.vocabularies.MetadataFields' }, + filterOptions: (options) => { + // Only allow indexes that provide simple, fixed vocabularies. + // This should be improved, together with the facets. The querystring + // widget implementation should serve as inspiration for those dynamic + // types of facets. + return Object.assign( + {}, + ...Object.keys(options).map((k) => + Object.keys(options[k].values || {}).length || + hasNonValueOperation(options[k].operations) || + hasDateOperation(options[k].operations) + ? { [k]: options[k] } + : {}, + ), + ); + }, + }, + }, + required: ['field'], +}); +const ExtrainfoSchema = ({ intl }) => { + const facetschema = FacetSchema({ intl }); + const extrainfoschema = { + ...facetschema, + title: intl.formatMessage(messages.metadata), + addMessage: intl.formatMessage(messages.add, { + type: intl.formatMessage(messages.metadata), + }), + }; + return extrainfoschema; +}; + +export const SearchBlockSchema = ({ data = {}, intl }) => { + return { + title: intl.formatMessage(messages.searchBlock), + fieldsets: [ + { + id: 'default', + title: 'Search', + fields: [ + 'allowed_content_types', + 'allowed_review_states', + 'searchedFields', + 'batchSize', + ], + }, + { + id: 'facets', + title: intl.formatMessage(messages.facets), + fields: [ + 'facet_fields', + 'filterLayout', + 'search_sections', + 'allow_search_excluded_sections', + 'show_filter_for_excluded_sections', + ], + }, + { + id: 'results', + title: 'Results', + fields: [ + 'extrainfo_fields', + 'subjectsFieldname', + 'showNewsItemPublishedDate', + 'showEventStartDate', + ], + }, + { + id: 'divers', + title: 'Divers', + fields: ['relocation'], + }, + ], + properties: { + // elastic_search_api_url: { + // title: + // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API URL', + // default: 'http://localhost:9200', + // }, + // elastic_search_api_index: { + // title: + // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', + // default: 'esploneindex', + // }, + search_sections: { + title: intl.formatMessage(messages.searchInSections), + description: intl.formatMessage(messages.searchInSectionsDescription), + type: 'dict', + factory: 'JSONField', + widget: 'searchsectionswidget', + }, + allow_search_excluded_sections: { + title: 'Allow search everywhere except in sections', + type: 'boolean', + }, + show_filter_for_excluded_sections: { + title: 'Show filter for excluded sections', + type: 'boolean', + default: true, + }, + allowed_content_types: { + title: 'Types', + description: 'Restrict types to display.', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, + }, + allowed_review_states: { + title: 'States', + description: 'Restrict review states.', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.WorkflowStates' }, + }, + }, + searchedFields: { + title: 'Searchable fields with boosting', + description: + 'Type fieldnames to search in field names. Type title^1.4 to boost the title 40%.', + type: 'array', + creatable: true, + default: ['title^1.4', 'description^1.2', 'blocks_plaintext'], + }, + batchSize: { + title: 'Batch size', + type: 'number', + default: 10, + }, + facet_fields: { + title: 'Facets', + description: 'Fields to filter on.', + widget: 'object_list', + schema: FacetSchema({ intl }), + }, + filterLayout: { + title: intl.formatMessage(messages.facetWidget), + // widget: SelectWidget, + choices: [ + ['dropdown', 'Dropdown'], + ['checkboxes', 'Checkboxes'], + ], + default: 'dropdown', + }, + extrainfo_fields: { + title: intl.formatMessage(messages.metadata), + widget: 'object_list', + schema: ExtrainfoSchema({ intl }), + }, + subjectsFieldname: { + title: 'Field name of tags field', + description: + 'Show tags to search for. Let the field empty to not show tags.', + default: 'subjects', + }, + showNewsItemPublishedDate: { + title: 'Show published date of news items', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, + default: ['News Item'], + }, + showEventStartDate: { + title: 'Show start date of events', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, + default: ['Event'], + }, + relocation: { + title: 'Relocation', + description: + 'CSS selector for relocation of search bar. Leave empty to keep search bar in block.', + default: '', + }, + }, + required: [], + }; +}; diff --git a/packages/volto-searchkit-block/src/components/Searchkit/CustomESRequestSerializer.jsx b/packages/volto-searchkit-block/src/components/Searchkit/CustomESRequestSerializer.jsx new file mode 100644 index 00000000..faad29d3 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -0,0 +1,438 @@ +import { extend, isEmpty, keyBy, trim } from 'lodash'; +import { getObjectFromObjectList } from '../helpers'; + +import config from '@plone/volto/registry'; + +const volto_config = config; + +export class CustomESRequestSerializer { + constructor(config) { + this.reviewstatemapping = config.reviewstatemapping; + this.searchedFields = config.searchedFields; + this.facet_fields = getObjectFromObjectList(config.facet_fields); + this.allowed_content_types = config.allowed_content_types; + this.allowed_review_states = config.allowed_review_states; + this.search_sections = config.search_sections; + this.language = config.language; + } + /** + * Convert Array of filters to Object of filters + * @param {Array} filters Array of filters + * @return {Object} Object of filters + * input: [ + * [ 'type_agg', 'value1' ] + * [ 'type_agg', 'value2', [ 'subtype_agg', 'a value' ] ] + * ] + * output: { + * type_agg: ['value1', 'value2'] + * subtype_agg: [ 'a value' ] + * } + */ + getFilters = (filters) => { + const aggValueObj = {}; + + const getChildFilter = (filter) => { + const aggName = filter[0]; + const fieldValue = filter[1]; + if (aggName in aggValueObj) { + aggValueObj[aggName].push(fieldValue); + } else { + aggValueObj[aggName] = [fieldValue]; + } + const hasChild = filter.length === 3; + if (hasChild) { + getChildFilter(filter[2]); + } + }; + + filters.forEach((filterObj) => { + getChildFilter(filterObj); + }); + return aggValueObj; + }; + + /** + * Return a serialized version of the app state `query` for the API backend. + * @param {object} stateQuery the `query` state to serialize + */ + serialize = (stateQuery) => { + const { queryString, sortBy, sortOrder, page, size, filters } = stateQuery; + const bodyParams = {}; + const force_fuzzy = true; // search for `${word}` and `${word}~` + + let qs_tailored_should_notexact = []; + let qs_tailored_should_exact = []; + let qs_tailored_must_notexact = []; + let qs_tailored_must_exact = []; + let qs_tailored_mustNot_exact = []; + + const _remove_orphan_leading_or_trailing_quotmarks = (word) => { + let word_without_plus_or_minus = trim(word, '+'); + word_without_plus_or_minus = trim(word_without_plus_or_minus, '-'); + if ( + !( + word_without_plus_or_minus.startsWith('"') && + word_without_plus_or_minus.endsWith('"') + ) && + !( + !word_without_plus_or_minus.startsWith('"') && + !word_without_plus_or_minus.endsWith('"') + ) + ) { + return word.replace('"', ''); + } + return word; + }; + + const _removeQuotationMarks = (word) => { + word.replace('"', ''); + word.replace("'", ''); + return word; + }; + + const _make_fuzzy_and_enrich_with_word_parts = (word) => { + // EXCLUDE + if (word.startsWith('-')) { + qs_tailored_mustNot_exact.push(_removeQuotationMarks(word.slice(1))); + return; + } + // MUST + if (word.startsWith('+')) { + if (word.includes('"') || word.includes('*') || word.includes('?')) { + qs_tailored_must_exact.push(word.slice(1)); + } else { + qs_tailored_must_notexact.push(word.slice(1)); + } + return; + } + + // WILDCARD + if (word.includes('*') || word.includes('?')) { + qs_tailored_should_exact.push(_removeQuotationMarks(word)); + return; + } + // EXACT + if (word.includes('"')) { + qs_tailored_should_exact.push(word); + return; + } + + // Words with hyphen + let word_new; + let wordpartlist = word.split('-'); // common hyphens + if (wordpartlist.length > 1) { + // word with hyphen + let resultlist = []; + wordpartlist.push(word); + wordpartlist.forEach((el) => { + if (force_fuzzy) { + resultlist.push(`${el} ${el}~`); + } else { + resultlist.push(el); + } + }); + word_new = resultlist.join(' '); + } else { + // word without hyphen + word_new = force_fuzzy ? `${word} ${word}~` : `${word}`; + } + qs_tailored_should_notexact.push(word_new); + return; + }; + + if (!isEmpty(queryString)) { + // - search fuzzy + // - search also for word parts (LSR-Lehrbetrieb: search also for LSR and Lehrbetrieb) + let words = queryString.trim().split(' '); + words = words + // filter out spaces, orphan ", "AND", and "OR" + .filter((word) => !['', '"', 'AND', 'OR', 'NOT'].includes(word)); + + words.forEach((word) => { + word = _remove_orphan_leading_or_trailing_quotmarks(word); + _make_fuzzy_and_enrich_with_word_parts(word); + }); + + // fields with boosting + let searchedFields = [...this.searchedFields]; + + let searchedFields_simple = searchedFields.map((fld) => { + const fieldname = fld.split('^')[0]; + return fld.replace(fieldname, `${fieldname}.${this.language}`); + }); + + let searchedFields_exact = searchedFields.map((fld) => { + const fieldname = fld.split('^')[0]; + return fld.replace(fieldname, `${fieldname}.${this.language}_exact`); + }); + + // Construction of query + let shouldList = []; + let mustList = []; + let must_notList = []; + + qs_tailored_should_notexact.forEach((element) => { + shouldList.push({ + query_string: { + query: element, + fields: searchedFields_simple, + }, + }); + }); + qs_tailored_should_exact.forEach((element) => { + shouldList.push({ + query_string: { + query: element, + fields: searchedFields_exact, + }, + }); + }); + + qs_tailored_must_notexact.forEach((el) => { + mustList.push({ + query_string: { + query: el, + fields: searchedFields_simple, + }, + }); + }); + qs_tailored_must_exact.forEach((element) => { + mustList.push({ + query_string: { + query: element, + fields: searchedFields_exact, + }, + }); + }); + qs_tailored_mustNot_exact.forEach((element) => { + must_notList.push({ + query_string: { + query: element, + fields: searchedFields_exact, + }, + }); + }); + + bodyParams['query'] = { + bool: { + should: shouldList, + must: mustList, + must_not: must_notList, + }, + }; + + bodyParams['highlight'] = { + number_of_fragments: 20, + fields: ['title', 'description', 'blocks_plaintext'].map( + (fieldname) => { + return { + [fieldname]: { + matched_fields: [ + `${fieldname}.${this.language}`, + `${fieldname}.${this.language}_exact`, + ], + type: 'fvh', + }, + }; + }, + ), + }; + } + + if (sortBy !== 'bestmatch') { + bodyParams['sort'] = bodyParams['sort'] || []; + const sortObj = {}; + sortObj[sortBy] = sortOrder && sortOrder === 'desc' ? 'desc' : 'asc'; + bodyParams['sort'].push(sortObj); + } + + if (size > 0) { + bodyParams['size'] = size; // batch size + } + + if (page > 0) { + const s = size > 0 ? size : 0; + const from = (page - 1) * s; + bodyParams['from'] = from; + } + + const getFieldnameFromAgg = (agg) => { + return agg.replace('_agg', ''); + }; + + // Generate terms of global filters + let terms = []; + // If isMultilingual, search only in language + + this.language && + volto_config.settings.isMultilingual && + terms.push({ + terms: { + language: [this.language], + }, + }); + this.allowed_content_types?.length > 0 && + terms.push({ + terms: { + portal_type: this.allowed_content_types, + }, + }); + this.allowed_review_states?.length > 0 && + terms.push({ + terms: { + review_state: this.allowed_review_states, + }, + }); + + const filters_dict = keyBy(filters, (e) => { + return e[0]; + }); + const section = filters_dict['section']; + + // Generate terms of selected options + let terms_of_selected_options = []; + if (filters.length) { + // Convert to object. + const aggValueObj = this.getFilters(filters); + + terms_of_selected_options = Object.keys(aggValueObj).reduce( + (accumulator, aggName) => { + const obj = {}; + const fieldName = getFieldnameFromAgg(aggName); + if (fieldName === 'subjects') { + obj['subjects.keyword'] = aggValueObj[aggName]; + } else { + obj[fieldName] = aggValueObj[aggName]; + } + if ( + aggName !== 'section' || + JSON.stringify(aggValueObj[aggName]) !== '["others"]' + ) { + accumulator.push({ terms: obj }); + } + return accumulator; + }, + [], + ); + } + + /** + * ES post_filter + */ + + const post_filter = { + bool: { must: terms.concat(terms_of_selected_options) }, + }; + + // Exclude sections + if (section && section[1] === 'others') { + post_filter['bool']['must_not'] = [ + { + terms: { + section: this.search_sections.items.map((el) => { + return el.section; + }), + }, + }, + ]; + } + + bodyParams['post_filter'] = post_filter; + + /** + * Aggregations + */ + const filter = (fieldName) => { + let myAggsFilter = terms; + // Add selected filters + const terms_of_selected_options_without_self = + terms_of_selected_options.filter( + (el) => !Object.keys(el.terms).includes(fieldName), + ); + myAggsFilter = myAggsFilter.concat( + terms_of_selected_options_without_self, + ); + + // So far + let res = myAggsFilter + ? { + bool: { + must: myAggsFilter, + }, + } + : null; + + if (fieldName !== 'section') { + if (section) { + if (section[1] === 'others') { + res = res || { + bool: {}, + }; + res.bool.must_not = [ + { + terms: { + section: this.search_sections.items.map((el) => { + return el.section; + }), + }, + }, + ]; + } else { + // // Must section + // res = res || { + // bool: { + // must: [], + // }, + // }; + // res.bool.must.push([section[1]]); + } + } + } + + return res; + }; + + bodyParams['aggs'] = {}; + let aggregations = Object.keys(this.facet_fields); + aggregations.push('section'); + aggregations.forEach((fieldName) => { + let aggName = `${fieldName}_agg`; + let field = fieldName; + if (fieldName === 'Subject') { + field = 'subjects.keyword'; + aggName = 'subjects_agg'; + } + if (fieldName === 'section') { + field = 'section'; + } + let aggBucketTermsComponent = { + [aggName]: { + aggs: { + [aggName]: { + terms: { + field: `${field}`, + order: { + _key: 'asc', + }, + size: 500, // number of buckets + }, + }, + somemoredatafromelasticsearch: { + top_hits: { + size: 1, + _source: { includes: [field] }, + }, + }, + }, + }, + }; + const filter_fieldname = filter(fieldName); + if (filter_fieldname) { + aggBucketTermsComponent[aggName].filter = filter_fieldname; + } + extend(bodyParams['aggs'], aggBucketTermsComponent); + }); + + return bodyParams; + }; +} diff --git a/packages/volto-searchkit-block/src/components/Searchkit/CustomESResponseSerializer.jsx b/packages/volto-searchkit-block/src/components/Searchkit/CustomESResponseSerializer.jsx new file mode 100644 index 00000000..cf770b3d --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/CustomESResponseSerializer.jsx @@ -0,0 +1,46 @@ +function _pimpedAggregations(aggregations) { + let result = Object.assign({}, aggregations); + let buckets = []; + Object.keys(result).forEach((element) => { + if (result[element] && result[element][element].buckets) { + result[element].buckets = result[element][element].buckets; + buckets = result[element].buckets; + } else { + buckets = []; + } + buckets && + buckets.forEach((bucket) => { + bucket.label = + bucket.somemoredatafromelasticsearch?.hits.hits[0]._source.title ?? + bucket.key; + }); + }); + + return result; +} + +export class CustomESResponseSerializer { + constructor(config) { + this.serialize = this.serialize.bind(this); + } + + /** + * Return a serialized version of the API backend response for the app state `results`. + * @param {object} payload the backend response payload + */ + + serialize(payload) { + const { aggregations, hits } = payload; + const foo = { + aggregations: _pimpedAggregations(aggregations) || {}, + hits: + hits?.hits.map((hit) => { + // TODO Replace hack: Add highlights to _source data + hit._source['highlight'] = hit.highlight; + return hit._source; + }) || [], + total: hits?.total.value || 0, + }; + return foo; + } +} diff --git a/packages/volto-searchkit-block/src/components/Searchkit/ESSearchApi.jsx b/packages/volto-searchkit-block/src/components/Searchkit/ESSearchApi.jsx new file mode 100644 index 00000000..a9b20c1d --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/ESSearchApi.jsx @@ -0,0 +1,74 @@ +import _get from 'lodash/get'; +import _hasIn from 'lodash/hasIn'; +import { CustomESRequestSerializer } from './CustomESRequestSerializer'; +import { CustomESResponseSerializer } from './CustomESResponseSerializer'; + +export class PloneSearchApi { + constructor(config) { + this.fetchConfig = _get(config, 'fetchPayload', {}); + this.validateFetchConfig(); + this.initSerializers(config); + this.search = this.search.bind(this); + // this.elastic_search_api_url = config.elastic_search_api_url; + // this.elastic_search_api_index = config.elastic_search_api_index; + } + + validateFetchConfig() { + if (!_hasIn(this.fetchConfig, 'url')) { + throw new Error('PloneSearchApi config: `url` is required.'); + } + } + + initSerializers(config) { + const requestSerializerCls = _get( + config, + 'es.requestSerializer', + CustomESRequestSerializer, + ); + const responseSerializerCls = _get( + config, + 'es.responseSerializer', + CustomESResponseSerializer, + ); + + this.requestSerializer = new requestSerializerCls({ + searchedFields: config.searchedFields, + facet_fields: config.facet_fields, + allowed_content_types: config.allowed_content_types, + allowed_review_states: config.allowed_review_states, + search_sections: config.search_sections, + language: config.language, + }); + this.responseSerializer = new responseSerializerCls({}); + } + + /** + * Perform the backend request to search and return the serialized list of results for the app state `results`. + * @param {string} stateQuery the `query` state with the user input + */ + async search(stateQuery) { + const payload = this.requestSerializer.serialize(stateQuery); + // Extend paylod with url and index to address elasticsearch server + try { + const response = await fetch(this.fetchConfig.url, { + method: 'POST', + headers: this.fetchConfig.headers, + body: JSON.stringify({ + elasticsearch_payload: payload, + // elasticsearch_url: this.elastic_search_api_url, + // elasticsearch_index: this.elastic_search_api_index, + }), + }); + // let results = await this.responseSerializer.serialize(response.data); + let results = await response.json(); + if (results.message) { + throw results; + // throw new Error(`${results.type} ${results.message}`); + } + results = this.responseSerializer.serialize(results); + return results; + } catch (error) { + throw error; + } + } +} diff --git a/packages/volto-searchkit-block/src/components/Searchkit/ElasticSearchHighlights.jsx b/packages/volto-searchkit-block/src/components/Searchkit/ElasticSearchHighlights.jsx new file mode 100644 index 00000000..f21270b3 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/ElasticSearchHighlights.jsx @@ -0,0 +1,118 @@ +/** + * Component to show matches per document + * fragment_size is set in CustomESRequestSerializer + */ +import React from 'react'; +import { useIntl } from 'react-intl'; +import messages from '../../messages'; + +export const ElasticSearchHighlights = ({ highlight }) => { + const [toggleDetails, setToggleDetails] = React.useState(false); + + const intl = useIntl(); + + let fieldmapping = { + title: intl.formatMessage(messages.title), + description: intl.formatMessage(messages.description), + subjects: intl.formatMessage(messages.tags), + freemanualtags_searchable: intl.formatMessage(messages.tags), + blocks_plaintext: intl.formatMessage(messages.content), + manualfilecontent: intl.formatMessage(messages.content), + }; + + const showDetails = () => { + setToggleDetails(!toggleDetails); + }; + + const fragments = getFragments(highlight); + if (highlight) { + return !toggleDetails ? ( +
+ {fragments.slice(0, 3).map((el, index) => { + return
; + })} +
+ ) : ( +
+ {Object.keys(highlight) + .reverse() + .map((fld) => { + return ( +
+
+ Matches in {fieldmapping[fld] || fld}: +
+
    + {highlight[fld].map((el, index) => { + return ( +
  • + ); + })} +
+
+ ); + })} +
+ ); + } else { + return null; + } +}; + +/** + * Get fragments of matches in a document + * @param {Object} highlight. part of response of Elasticsearch query + * @returns {Array} Array of strings + */ +export const getFragments = (highlight) => { + let fragments = []; + highlight && + Object.keys(highlight) + .reverse() + .forEach((fld) => { + highlight[fld].forEach((mtch) => { + fragments.push(mtch); + }); + }); + return fragments; +}; + +/** + * Get matches in a document + * @param {Object} highlight. part of response of Elasticsearch query + * @returns {Array} Array of strings + */ +export const getMatches = (highlight) => { + const regex = /(.*?)<\/em>/gm; + let fragments = getFragments(highlight); + let matches = []; + fragments.forEach((fragment) => { + const fragmentmatches = [...fragment.matchAll(regex)]; + matches = matches.concat(fragmentmatches.map((match) => match[1])); + }); + matches = [...new Set(matches)]; + return matches; +}; + +export const ElasticSearchMatches = ({ highlight, indexResult }) => { + const matches = getMatches(highlight); + return ( +
+ {matches.length > 0 && matches: } + {matches.join(' | ')} +
+ ); +}; diff --git a/packages/volto-searchkit-block/src/components/Searchkit/Error.jsx b/packages/volto-searchkit-block/src/components/Searchkit/Error.jsx new file mode 100644 index 00000000..cf1ba620 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/Error.jsx @@ -0,0 +1,20 @@ +import { Header, Segment } from 'semantic-ui-react'; +import { FormattedMessage } from 'react-intl'; + +const Error = ({ error }) => { + return error?.type ? ( + +
+

+ +

+
+ {error?.type}: {error?.message} +
+ ) : null; +}; + +export default Error; diff --git a/packages/volto-searchkit-block/src/components/Searchkit/Error.test.js b/packages/volto-searchkit-block/src/components/Searchkit/Error.test.js new file mode 100644 index 00000000..2608ece0 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/Error.test.js @@ -0,0 +1,36 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; +import { MemoryRouter } from 'react-router-dom'; + +import Error from './Error'; + +beforeAll(() => {}); + +const mockStore = configureStore(); + +describe('Generic Error', () => { + it('renders a simple error component', () => { + const store = mockStore({ + intl: { + locale: 'en', + messages: {}, + }, + }); + const component = renderer.create( + + + + + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); +}); diff --git a/packages/volto-searchkit-block/src/components/Searchkit/LICENSE_Cern_react-searchkit.md b/packages/volto-searchkit-block/src/components/Searchkit/LICENSE_Cern_react-searchkit.md new file mode 100644 index 00000000..19aa587f --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/LICENSE_Cern_react-searchkit.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2015-2019 CERN. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/volto-searchkit-block/src/components/Searchkit/Results.js b/packages/volto-searchkit-block/src/components/Searchkit/Results.js new file mode 100644 index 00000000..812c067e --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/Results.js @@ -0,0 +1,89 @@ +import React, { Component } from 'react'; +import { Grid } from 'semantic-ui-react'; +import { + Count, + Pagination, + ResultsMultiLayout, + Sort, + withState, +} from 'react-searchkit'; + +import config from '@plone/volto/registry'; + +// TODO conditional Matomo tracking: catch case if app has not volto-matomo installed +import { trackSiteSearch } from '@eeacms/volto-matomo/utils'; + +import { scrollToTarget } from '../helpers'; + +class Results extends Component { + componentDidMount() { + // Dispatch event (on query change), other add-ons can subscribe to. + var evt = new CustomEvent('searchkitQueryChanged', {}); + window && window.dispatchEvent(evt); + if ( + config.settings.searchkitblock.trackVoltoMatomo && + this.props.currentQueryState.queryString + ) { + let options = { + keyword: this.props.currentQueryState.queryString, + category: 'Suche in Dokumentation', // optional + // count: 4, // optional + documentTitle: 'Suche in Dokumentation', // optional + href: '/search', // optional + count: this.props.currentResultsState.data.total, + // customDimensions: [ + // { + // id: 1, + // value: 'loggedIn', + // }, + // ], // optional + }; + trackSiteSearch(options); + } + } + + render() { + const { total } = this.props.currentResultsState.data; + return total ? ( +
+ + + + + + <> {cmp}} + overridableId="volto" + /> + + + + + + + + +
+ ) : null; + } +} + +Results.propTypes = {}; + +Results.defaultProps = {}; + +const MyResults = (props) => { + // Add scroll to search input field + React.useEffect(() => { + const el = document.querySelector('.searchkitsearch'); + if (el) { + scrollToTarget(el); + } + }, []); + + return ; +}; + +export const OnResults = withState(MyResults); diff --git a/packages/volto-searchkit-block/src/components/Searchkit/ResultsLoader.jsx b/packages/volto-searchkit-block/src/components/Searchkit/ResultsLoader.jsx new file mode 100644 index 00000000..64663564 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/ResultsLoader.jsx @@ -0,0 +1,12 @@ +import { Loader } from 'semantic-ui-react'; + +function ResultsLoaderComponent() { + return ( + <> + +
+ + ); +} + +export default ResultsLoaderComponent; diff --git a/packages/volto-searchkit-block/src/components/Searchkit/SearchBarSection.jsx b/packages/volto-searchkit-block/src/components/Searchkit/SearchBarSection.jsx new file mode 100644 index 00000000..7c4917d4 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/SearchBarSection.jsx @@ -0,0 +1,51 @@ +import { useIntl } from 'react-intl'; +import { Icon as IconSemantic } from 'semantic-ui-react'; +import { onQueryChanged, SearchBar, withState } from 'react-searchkit'; + +import messages from '../../messages'; + +const _SearchBarSection = (props) => { + const intl = useIntl(); + + const payloadOfReset = { + searchQuery: { + sortBy: 'bestmatch', + sortOrder: 'asc', + layout: 'list', + page: 1, + size: props.currentQueryState.data.batchSize, + queryString: '', + }, + }; + + const onResetHandler = (event) => { + onQueryChanged(payloadOfReset); + }; + + return ( +
+ + onResetHandler(event)} + /> +
+ ); +}; + +export default withState(_SearchBarSection); diff --git a/packages/volto-searchkit-block/src/components/Searchkit/SectionsSearch.jsx b/packages/volto-searchkit-block/src/components/Searchkit/SectionsSearch.jsx new file mode 100644 index 00000000..70b7b65d --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/SectionsSearch.jsx @@ -0,0 +1,156 @@ +import React from 'react'; +import { isEmpty, keyBy } from 'lodash'; +import { withState } from 'react-searchkit'; +import { BodyClass } from '@plone/volto/helpers'; +// import StateLogger from '../StateLogger'; + +const _SectionsSearch = (props) => { + const { + allow_search_excluded_sections, + show_filter_for_excluded_sections, + search_sections, + currentQueryState, + updateQueryState, + } = props; + + // State + const [activeSection, setActiveSection] = React.useState('all'); + + // Helpers + const search_sections_dict = keyBy(search_sections?.items || [], (el) => { + return el.section; + }); + + let doc_count_others = 0; + let doc_count_all = 0; + + if ( + props.currentResultsState.data.aggregations.section_agg?.section_agg + ?.buckets + ) { + const buckets = + props.currentResultsState.data.aggregations.section_agg?.section_agg + ?.buckets; + let bucket_dict = {}; + buckets.forEach((el) => { + bucket_dict[el.key] = el.doc_count; + }); + + // calculate doc_counts of others and all + let count_others = 0; + let count_all = 0; + Object.keys(bucket_dict).forEach((el) => { + if (!Object.keys(search_sections_dict).includes(el)) { + count_others = count_others + bucket_dict[el]; + } + }); + Object.keys(bucket_dict).forEach((el) => { + count_all = count_all + bucket_dict[el]; + }); + doc_count_others = count_others; + doc_count_all = count_all; + } + + React.useEffect(() => { + const filters_dictionary = keyBy(currentQueryState.filters, (el) => { + return el[0]; + }); + setActiveSection( + filters_dictionary.section ? filters_dictionary.section[1] : 'all', + ); + }, [currentQueryState]); + + const restrictSearchToSection = (section) => { + setActiveSection(section); + let kitquerystate = { + sortBy: 'modified', + sortOrder: 'desc', + layout: 'list', + page: 1, + size: props.currentQueryState.data.batchSize, + filters: currentQueryState.filters, + }; + if (currentQueryState.queryString) { + kitquerystate.queryString = currentQueryState.queryString; + } + + // Empty filters for sections without filter + if ( + (search_sections_dict[section] && + !search_sections_dict[section].show_filter) || + (section === 'others' && !show_filter_for_excluded_sections) + ) { + kitquerystate.filters = []; + } + // Replace filter 'section' + kitquerystate.filters = kitquerystate.filters.filter((el) => { + return el[0] !== 'section'; + }); + if (section === 'all') { + // pass + } else if (section === 'others') { + kitquerystate.filters.push(['section', section]); + } else { + kitquerystate.filters.push(['section', section]); + } + // Do search! + updateQueryState(kitquerystate); + }; + + return isEmpty(props.currentResultsState.error) ? ( + <> + +
+ {search_sections?.items?.length > 0 ? ( + + ) : null} + {search_sections?.items?.length > 0 && + allow_search_excluded_sections ? ( + + ) : null} + {search_sections + ? search_sections.items.map((el) => { + return ( + + ); + }) + : null} +
+ {/* */} + + ) : null; +}; + +export default withState(_SectionsSearch); diff --git a/packages/volto-searchkit-block/src/components/Searchkit/__snapshots__/Error.test.js.snap b/packages/volto-searchkit-block/src/components/Searchkit/__snapshots__/Error.test.js.snap new file mode 100644 index 00000000..a78b8382 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Searchkit/__snapshots__/Error.test.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Generic Error renders a simple error component 1`] = ` +
+
+

+ Check the configuration of your searchkit block! +

+
+ + ConnectionError + : + + + + Service pipapo not found. + +
+`; diff --git a/packages/volto-searchkit-block/src/components/StateLogger.jsx b/packages/volto-searchkit-block/src/components/StateLogger.jsx new file mode 100644 index 00000000..c8590d83 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/StateLogger.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { withState } from 'react-searchkit'; + +class _StateLogger extends React.Component { + render() { + return ( +
+
+

Current results state

+
{JSON.stringify(this.props.currentResultsState, null, 2)}
+
+
+

Current query state

+
{JSON.stringify(this.props.currentQueryState, null, 2)}
+
+
+ ); + } +} + +const StateLogger = withState(_StateLogger); + +export default StateLogger; diff --git a/packages/volto-searchkit-block/src/components/Views/FacetedSearch.jsx b/packages/volto-searchkit-block/src/components/Views/FacetedSearch.jsx new file mode 100644 index 00000000..05fa8263 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Views/FacetedSearch.jsx @@ -0,0 +1,826 @@ +import React from 'react'; +import { compact, truncate } from 'lodash'; +import cx from 'classnames'; +import Cookies from 'universal-cookie'; +import { createPortal } from 'react-dom'; +import { useSelector } from 'react-redux'; +import { Link } from 'react-router-dom'; +import { FormattedMessage, useIntl } from 'react-intl'; + +import { OverridableContext } from 'react-overridable'; + +import { + Button, + Container, + Dropdown, + Grid, + Header, + Icon as IconSemantic, + Item, + Label, + Pagination as Paginator, + Segment, +} from 'semantic-ui-react'; +import { + BucketAggregation, + EmptyResults, + onQueryChanged, + ReactSearchKit, + ResultsLoader, + withState, + Error as ErrorCp, +} from 'react-searchkit'; + +import { expandToBackendURL } from '@plone/volto/helpers'; +import { FormattedDate, Icon } from '@plone/volto/components'; +import { addAppURL, toPublicURL } from '@plone/volto/helpers'; +import leftAngle from '@plone/volto/icons/left-key.svg'; +import rightAngle from '@plone/volto/icons/right-key.svg'; +import firstAngle from '@plone/volto/icons/first.svg'; +import lastAngle from '@plone/volto/icons/last.svg'; +import clearSVG from '@plone/volto/icons/clear.svg'; + +import messages from '../../messages'; +import { flattenESUrlToPath, getObjectFromObjectList } from '../helpers'; + +import { PloneSearchApi } from '../Searchkit/ESSearchApi'; +import { CustomESRequestSerializer } from '../Searchkit/CustomESRequestSerializer'; +import { CustomESResponseSerializer } from '../Searchkit/CustomESResponseSerializer'; +import { OnResults } from '../Searchkit/Results'; +import SectionsSearch from '../Searchkit/SectionsSearch'; +import SearchBarSection from '../Searchkit/SearchBarSection'; +import MyResultsLoaderElement from '../Searchkit/ResultsLoader'; + +import { ElasticSearchHighlights } from '../Searchkit/ElasticSearchHighlights'; +import ErrorComponent from '../Searchkit/Error'; +// import StateLogger from '../StateLogger'; + +import './less/springisnow-volto-searchkit-block.less'; + +import config from '@plone/volto/registry'; + +/** + * + * @param {Object} querystringindexes + * @param {String} fieldname One of the indexes + * @param {String} key to be translated + * @returns {String} + */ +const translate = (querystringindexes, fieldname, key) => { + let label = key; + if (querystringindexes && fieldname in querystringindexes) { + label = querystringindexes[fieldname].values[key]?.title || key; + } + return label; +}; + +// TODO Make reviewstatemapping configurable +export const ploneSearchApi = (data, language) => { + const cookies = new Cookies(); + const authToken = cookies.get('auth_token'); + + return new PloneSearchApi({ + fetchPayload: { + url: expandToBackendURL('/@kitsearch'), + timeout: 5000, + headers: { + Accept: 'application/json', + Authorization: `Bearer ${authToken}`, + }, + }, + es: { + requestSerializer: CustomESRequestSerializer, + responseSerializer: CustomESResponseSerializer, + }, + searchedFields: data.searchedFields || ['title'], + facet_fields: data.facet_fields, + allowed_content_types: data.allowed_content_types, + allowed_review_states: data.allowed_review_states, + search_sections: data.search_sections, + language: language, + // elastic_search_api_url: data.elastic_search_api_url, + // elastic_search_api_index: data.elastic_search_api_index, + }); +}; + +const _ExtraInfo = (props) => { + const { result } = props; + + const extrainfo_fields = getObjectFromObjectList( + props.currentQueryState.data.extrainfo_fields, + ); + const facet_fields = getObjectFromObjectList( + props.currentQueryState.data.facet_fields, + ); + let subjectsFieldname = props.currentQueryState.data?.subjectsFieldname; // "subjects"; + + const querystringindexes = useSelector( + (state) => state.query?.querystringindexes, + ); + + return ( + + {Object.keys(extrainfo_fields).map((extrainfo_key, idx) => { + if (!result[extrainfo_key]) { + return null; + } + const extrainfo_value = Array.isArray(result[extrainfo_key]) + ? result[extrainfo_key] + : [result[extrainfo_key]]; + + return Object.keys(facet_fields).includes(extrainfo_key) ? ( + + {extrainfo_fields[extrainfo_key]}: + {extrainfo_value?.map((item, index) => { + let tito = translate(querystringindexes, extrainfo_key, item); + let payloadOfFilter = { + searchQuery: { + sortBy: 'bestmatch', + sortOrder: 'asc', + layout: 'list', + page: 1, + size: props.currentQueryState.data.batchSize, + filters: [[`${extrainfo_key}_agg`, item]], + }, + }; + return ( + + ); + })} + {idx < Object.keys(extrainfo_fields).length - 1 && ( + | + )} + + ) : ( + + {extrainfo_fields[extrainfo_key]}: + {extrainfo_value?.map((item, index) => { + let tito = item.title || item.token || item; + return ( + + {tito} + {index < extrainfo_value.length - 1 ? ',' : null} + + ); + })} + {idx < Object.keys(extrainfo_fields).length - 1 && ( + | + )} + + ); + })} + + {Array.isArray(result[subjectsFieldname]) && + result[subjectsFieldname]?.length > 0 ? ( +
+ + : + + {result[subjectsFieldname]?.map((item, index) => { + let tito = item; + let payloadOfTag = { + searchQuery: { + sortBy: 'bestmatch', + sortOrder: 'asc', + layout: 'list', + page: 1, + size: props.currentQueryState.data.batchSize, + queryString: tito, + }, + }; + return ( + + ); + })} +
+ ) : null} +
+ ); +}; + +const ExtraInfo = withState(_ExtraInfo); + +const _CustomResultsListItem = (props) => { + const { result } = props; + const item_url = flattenESUrlToPath(result['@id']); + const is_external_content = item_url.startsWith('https'); + const locale = useSelector((state) => state.query?.locale); + const querystringindexes = useSelector( + (state) => state.query?.querystringindexes, + ); + const showNewsItemPublishedDate = useSelector( + (state) => state.query?.data.showNewsItemPublishedDate, + ); + const showEventStartDate = useSelector( + (state) => state.query?.data.showEventStartDate, + ); + + return ( + + + {Array.isArray(result.informationtype) && + result.informationtype?.length > 0 ? ( + + {result.informationtype?.map((item, index) => { + let tito = translate(querystringindexes, 'informationtype', item); + const payload = { + searchQuery: { + sortBy: 'bestmatch', + sortOrder: 'asc', + layout: 'list', + page: 1, + size: props.currentQueryState.data.batchSize, + filters: [['informationtype_agg', item]], + }, + }; + return ( + + ); + })} + + ) : null} + {result.head_title ? {result.head_title} : null} + {is_external_content ? ( + +
+ {result.title} + + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} + + + {truncate(result.description, { length: 200 })} + + + + ) : ( + + + {result.title} + + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} + + + {truncate(result.description, { length: 200 })} + + + + )} + + + + + ); +}; + +const CustomResultsListItem = withState(_CustomResultsListItem); + +const MyCountElement = ({ totalResults }) => { + const intl = useIntl(); + let labelSearchResults = intl.formatMessage(messages.searchresult); + let labelSearchResultsPlural = intl.formatMessage(messages.searchresults); + return ( +
+ {totalResults}{' '} + {totalResults === 1 ? labelSearchResults : labelSearchResultsPlural} +
+ ); +}; + +const myActiveFiltersElement = (props) => { + const { filters, removeActiveFilter, getLabel } = props; + return ( + <> + {filters.map((filter, index) => { + const { label, activeFilter } = getLabel(filter); + return ( + + ); + })} + + ); +}; + +/** + * CustomBucketAggregationElement + * One single Filter of Faceted Navigation + * props.agg.field: field name + */ +const CustomBucketAggregationElement = (props) => { + const { title, containerCmp, updateQueryFilters } = props; + const fieldname = props.agg.field; + const querystringindexes = useSelector( + (state) => state.query?.querystringindexes, + ); + + /** + * Translate labels according vocabulary + * @param {*} bucks + * @returns + */ + const translateBuckets = (bucks) => { + if (querystringindexes && fieldname in querystringindexes) { + bucks.forEach((element) => { + element.label = + querystringindexes[fieldname].values[element.key]?.title || + element.key; + }); + } + return bucks; + }; + + // Get label from token + let buckets = containerCmp.props.buckets; + buckets = translateBuckets(buckets); + let filter_labels_dict = Object.fromEntries( + Array.from(buckets, (x) => [x.key, x.label]), // TODO Translate label + ); + // List of labels of selected options + let selectedFilters = containerCmp.props.selectedFilters + .map((el) => el[1]) + .map((token) => filter_labels_dict[token]); + selectedFilters = compact(selectedFilters); + // List of all available options + let all_filters = containerCmp.props.buckets.map((el) => { + return [containerCmp.props.aggName, el.key]; + }); + + const removeAggFilters = (event) => { + if (containerCmp.props.selectedFilters.length) { + updateQueryFilters(containerCmp.props.selectedFilters); + } + event.preventDefault(); + event.stopPropagation(); + }; + + const selectAllAggFilters = (event) => { + // toggle! updateQueryFilters toggles filter selection + if (containerCmp.props.selectedFilters.length) { + updateQueryFilters(containerCmp.props.selectedFilters); + } + updateQueryFilters(all_filters); + + event.preventDefault(); + event.stopPropagation(); + }; + + const dropdowntitle = + title || + fieldname + + (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); + + return containerCmp ? ( +
+ 9, + })} + > + + + selectAllAggFilters(e)} + onKeyDown={(e) => selectAllAggFilters(e)} + role="option" + aria-selected="false" + tabIndex="0" + className="select_all" + > + + {' '} + /{' '} + removeAggFilters(e)} + onKeyDown={(e) => removeAggFilters(e)} + role="option" + aria-selected="false" + tabIndex="0" + className="deselect_all" + > + {' '} + + + + {containerCmp} + + + {/* removeAggFilters(e)} + /> */} +
+ ) : null; +}; + +function choicesSorter(a, b) { + const titleA = a.props.bucket.label; + const titleB = b.props.bucket.label; + if (titleA < titleB) { + return -1; + } else if (titleA > titleB) { + return 1; + } + return 0; +} +const CustomBucketAggregationContainerElement = ({ valuesCmp }) => { + let foo = valuesCmp; + foo.sort(choicesSorter); + return <>{foo}; +}; + +const CustomBucketAggregationValuesElement = (props) => { + const { + bucket, + keyField, + isSelected, + onFilterClicked, + childAggCmps, + // updateQueryState, + // currentQueryState, + } = props; + const label = bucket.label + ? `${bucket.label} (${bucket.doc_count})` + : `${keyField} (${bucket.doc_count})`; + + const onFilterClickedCustom = (filter, event) => { + onFilterClicked(filter); + + event.preventDefault(); + event.stopPropagation(); + }; + + return ( + + {isSelected ? ( + onFilterClickedCustom(bucket.key, event)} + className={isSelected ? 'isSelected right floated' : 'right floated'} + key={`${bucket.key}-description`} + > + + + ) : null} + onFilterClickedCustom(bucket.key, event)} + className={isSelected ? 'isSelected' : ''} + key={bucket.key} + > + {label} + + {childAggCmps} + + ); +}; + +const customEmpytResultsElement = (props) => { + const { resetQuery } = props; + return ( + +
+ +
+ +
+ ); +}; + +const customSort = ({ + currentSortBy, + currentSortOrder, + options, + onValueChange, +}) => { + const selected = currentSortBy.concat('-', currentSortOrder); + return ( +
+ + + {' '} + + +
+ ); +}; + +const customPaginationElement = (props) => { + const { currentPage, currentSize, totalResults, onPageChange, options } = + props; + const pages = Math.ceil(totalResults / currentSize); + const boundaryRangeCount = options.boundaryRangeCount; + const siblingRangeCount = options.siblingRangeCount; + const showEllipsis = options.showEllipsis; + const showFirst = options.showFirst; + const showLast = options.showLast; + const showPrev = options.showPrev; + const showNext = options.showNext; + const size = options.size || 'massive'; + const _onPageChange = (event, { activePage }) => { + onPageChange(activePage); + }; + + return pages > 1 ? ( + , + icon: true, + } + : null + } + firstItem={ + showFirst + ? { + content: , + icon: true, + } + : null + } + lastItem={ + showLast + ? { + content: , + icon: true, + } + : null + } + prevItem={ + showPrev + ? { + content: , + icon: true, + } + : null + } + nextItem={ + showNext + ? { + content: , + icon: true, + } + : null + } + size={size} + /> + ) : null; +}; + +const sortValues = [ + { + text: 'Relevanz', + sortBy: 'bestmatch', + sortOrder: 'asc', + }, + { + text: 'Neueste', + sortBy: 'modified', + sortOrder: 'desc', + }, + // { + // text: 'Alphabetisch', + // sortBy: 'sortable_title.keyword', + // sortOrder: 'asc', + // }, +]; + +/** + * FacetedSearch + * @param {string} filterLayout default 'dropdown' + * @param {object} overriddenComponents Override with custom components, ignore to stay with default 'dropdown' or step back to react-searchkit default components with value {} + * @returns + */ +const FacetedSearch = ({ data, overriddenComponents }) => { + const { + facet_fields, + allow_search_excluded_sections, + show_filter_for_excluded_sections, + relocation, + filterLayout, + search_sections, + } = data; + + const querystringindexes = useSelector((state) => state.querystring?.indexes); + + let facet_fields_object = getObjectFromObjectList(facet_fields); + if ('Subject' in facet_fields_object) { + facet_fields_object.subjects = facet_fields_object.Subject; + delete facet_fields_object.Subject; + } + + // TODO Get config from blocks data + const initialState = { + page: 1, + queryString: '', + sortBy: 'modified', + sortOrder: 'desc', + size: data.batchSize, + layout: 'list', + }; + + const defaultOverriddenComponents = { + 'ResultsList.item.elasticsearch': CustomResultsListItem, + 'Count.element': MyCountElement, + 'ActiveFilters.element': myActiveFiltersElement, + 'EmptyResults.element': customEmpytResultsElement, + 'Sort.element.volto': customSort, + 'Pagination.element': customPaginationElement, + 'Error.element': ErrorComponent, + 'ResultsLoader.element': MyResultsLoaderElement, + }; + + const dropdownOverriddenComponents = { + 'BucketAggregation.element': CustomBucketAggregationElement, + 'BucketAggregationContainer.element': + CustomBucketAggregationContainerElement, + 'BucketAggregationValues.element': withState( + CustomBucketAggregationValuesElement, + ), + }; + + overriddenComponents = { + ...defaultOverriddenComponents, + ...(filterLayout === 'dropdown' && dropdownOverriddenComponents), + ...(config.settings.searchkitblock.overriddenComponents && + config.settings.searchkitblock.overriddenComponents), + }; + + // TODO Check if check on client could be made simpler + const locale = useSelector((state) => state.intl.locale); + const [isClient, setIsClient] = React.useState(null); + React.useEffect(() => setIsClient(true), []); + + return ( + + {isClient && ( + + + + {typeof document !== 'undefined' && relocation?.length > 0 ? ( + createPortal( + , + true && + document.querySelectorAll(relocation) && + document.querySelectorAll(relocation)[0], + ) + ) : ( + + + + + + + + )} + + + + + + + + + +
+ {Object.keys(facet_fields_object)?.map((facet) => ( + + ))} +
+
+
+ + + + + + + + {/* */} + + +
+
+
+
+ )} +
+ ); +}; + +export default FacetedSearch; diff --git a/packages/volto-searchkit-block/src/components/Views/TestSearchkitQuerystrings.jsx b/packages/volto-searchkit-block/src/components/Views/TestSearchkitQuerystrings.jsx new file mode 100644 index 00000000..fb6dcba4 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Views/TestSearchkitQuerystrings.jsx @@ -0,0 +1,224 @@ +import React from 'react'; +import { useIntl } from 'react-intl'; +import { createPortal } from 'react-dom'; +import { Container, Header, Segment } from 'semantic-ui-react'; +import { useHistory } from 'react-router'; +import { Link, useLocation } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { OverridableContext } from 'react-overridable'; +import { Icon, Toolbar } from '@plone/volto/components'; +import { getParentUrl } from '@plone/volto/helpers'; +import backSVG from '@plone/volto/icons/back.svg'; + +import { + onQueryChanged, + ReactSearchKit, + SearchBar, + withState, + ResultsMultiLayout, + Count, +} from 'react-searchkit'; +import { flattenESUrlToPath } from '../helpers'; +import { ploneSearchApi } from './FacetedSearch'; +import { ElasticSearchMatches } from '../Searchkit/ElasticSearchHighlights'; +import messages from '../../messages'; + +import config from '@plone/volto/registry'; + +const sort_caseinsensitive = (a, b) => { + var nameA = a.toUpperCase(); // Groß-/Kleinschreibung ignorieren + var nameB = b.toUpperCase(); // Groß-/Kleinschreibung ignorieren + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; +}; + +const _OnHighlights = (props) => { + let location = useLocation(); + let highlights = props.currentResultsState; + let hits = highlights.data.hits; + + const regex = /(.*?)<\/em>/gm; + let fragments = []; + hits.map((hit) => { + hit.highlight = hit.highlight || []; + Object.keys(hit.highlight).forEach((fld) => { + hit.highlight[fld].forEach((highlightfragment) => { + fragments.push(highlightfragment); + }); + }); + return null; + }); + let matches = new Set(); + fragments.forEach((txt) => { + let result = [...txt.matchAll(regex)]; + result.forEach((match) => { + matches.add(match[1]); + }); + }); + let matches_sorted = Array.from(matches); + matches_sorted.sort(sort_caseinsensitive); + return ( +
+ {matches_sorted.length > 0 && ( +
{matches_sorted.length} matches found:
+ )} + {matches_sorted.map((match) => ( + + ))} +
+ ); +}; +const OnHighlights = withState(_OnHighlights); + +const OnResults = withState(ResultsMultiLayout); + +const CustomResultsListItem = ({ result, index }) => { + return ( + + ); +}; + +const DocumentsCount = ({ totalResults }) => { + return
{totalResults} documents found:
; +}; + +const overriddenComponents = { + 'ResultsList.item': CustomResultsListItem, + 'Count.element': DocumentsCount, +}; + +const TestSearchkitQuerystrings = (props) => { + const intl = useIntl(); + const history = useHistory(); + const searchconfig = config.blocks.blocksConfig.searchkitblock.searchconfig; + + const initialState = { + sortBy: 'bestmatch', + sortOrder: 'asc', + // sortBy: 'modified', + // sortOrder: 'desc', + queryString: '', + layout: 'list', + page: 1, + size: 50, + }; + + const onchangehandler = (event, data) => { + let searchQuery = { + ...initialState, + queryString: data.value, + }; + onQueryChanged({ searchQuery: searchQuery }); + return; + }; + + const locale = useSelector((state) => state.intl.locale); + const [isClient, setIsClient] = React.useState(null); + React.useEffect(() => setIsClient(true), []); + + return ( + + + +
+ Matches +
+
+ {isClient && ( + + + <> + + {/* { + onchangehandler(event, data); + }} + /> */} + { + onchangehandler(event, data); + }} + /> + + + + + + + + + + )} +
+ + {isClient && + createPortal( + { + history.push(getParentUrl(location.pathname)); + }} + > + + + } + />, + document.getElementById('toolbar'), + )} +
+ ); +}; + +export default TestSearchkitQuerystrings; diff --git a/packages/volto-searchkit-block/src/components/Views/less/springisnow-volto-searchkit-block.less b/packages/volto-searchkit-block/src/components/Views/less/springisnow-volto-searchkit-block.less new file mode 100644 index 00000000..d06a1ae1 --- /dev/null +++ b/packages/volto-searchkit-block/src/components/Views/less/springisnow-volto-searchkit-block.less @@ -0,0 +1,296 @@ +/* + * @rohberg/volto-searchkit-block + * springisnow-volto-searchkit-block.less + */ + +@type: 'extra'; +@element: 'custom'; + +@import (multiple) '../../theme.config'; + +.fnresults .ui.items > .item { + &.private > .content > a.header { + color: red; + } + + button, + .ui.button { + padding: 0; + border: none; + margin: 0.25rem 0.1rem 0.25rem 0em; + background-color: transparent; + color: rgba(0, 0, 0, 0.6); + cursor: pointer; + font-weight: bold; + + &:hover { + background-color: transparent; + } + } + + .extra { + color: @textColor; + + .label { + margin-right: 0.5em; + } + } +} + +.searchbar-wrapper { + display: flex; + align-items: stretch; + + .ui.fluid.action.input { + width: 50%; + @media only screen and (max-width: 767px) { + width: 100%; + } + + & > input { + width: unset !important; + flex: 1; + border-color: rgba(0, 0, 0, 0.13) !important; + border-radius: 0; + + &:focus { + border-color: #85b7d9 !important; + } + } + } + + .button { + border-radius: 0; + } + // Search button + .ui.action.input > button.ui.button { + display: none; + } + + .icon.delete { + display: flex; + width: 3rem; + min-width: 3rem; + height: 3rem; + align-items: center; + justify-content: center; + margin: 0; + background-color: #878686; + color: #fff; + cursor: pointer; + font-size: 1.2rem; + font-weight: 700; + + &.unselected { + opacity: 0; + } + } +} + +// .ui.grid > .row > .column.facetedsearch_filter { +.facetedsearch_filter { + opacity: 1; + transition: + opacity 1s, + visibility 1s; + visibility: visible; + + &.cards { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: baseline; + justify-content: space-evenly; + + .ui.card { + margin: 1em 0.5em 1em 0em; + } + } + + &.dropdown { + .bucketaggregations { + display: grid; + width: 100%; + gap: 10px; + grid-template-columns: 1fr 1fr; + @media only screen and (max-width: 767px) { + grid-template-columns: 1fr; + } + + .bucketAE { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; + // width: 50%; + .ui.dropdown.fnfilter { + display: flex; + min-height: 3rem; + align-items: center; + justify-content: space-between; + // padding: 0 0 0 .3rem; + padding: 0.7rem 1rem 0.4rem 1rem; + margin: 0; + background-color: #edf1f2; + font-family: @fontName; + font-size: 1rem; + + & > .dropdown.icon { + font-size: 1rem; + } + + &.selected { + background-color: #c4d5da; + color: @black; + + .text { + max-width: 95%; + } + } + + .menu { + width: 100%; + border: none; + background-color: #edf1f2; + box-shadow: none; + + & > .item { + padding: 0.4rem 1.4rem 0.4rem 1rem !important; + line-height: 1.4em; + // color: @white; + & .item.isSelected { + font-weight: bold; + + svg { + stroke: currentColor; + stroke-width: 3px; + } + } + } + } + + div.visible .menu { + display: block !important; + visibility: visible !important; + } + + // Hack for dropdown with scrollbar + &.scrolloptions { + .menu > .item { + padding-right: 0.9rem !important; + } + } + } + + .deleteFilter { + display: flex; + width: 3rem; + min-width: 3rem; + height: 3rem; + align-items: center; + justify-content: center; + margin: 0; + background-color: #878686; + color: #fff; + cursor: pointer; + font-size: 1.2rem; + font-weight: 700; + + &.unselected { + opacity: 0; + } + } + } + } + } +} +// No filter according to block configuration +body.section_without_filter .facetedsearch_filter { + opacity: 0; + visibility: hidden; +} + +.searchsections { + display: flex; + gap: 20px; + + button { + padding: 0.7rem 1rem 0.7rem 1rem; + border: none; + background-color: #f3f1f1; + color: #6e6a6a; + cursor: pointer; + + &.active { + background-color: #dfd8d8; + } + } +} +// Mobile +@media only screen and (max-width: 767px) { + .searchsections { + flex-direction: column; + gap: 0.5rem; + + button { + display: inline-block; + } + } +} + +.countlabel { + font-weight: bold; +} + +.sortby { + display: flex; + align-items: baseline; + gap: 5px; + + .ui.button { + padding: 0; + background-color: transparent; + color: rgba(70, 92, 98, 0.77); + font-weight: normal; + + &.button-active { + color: @textColor; + font-weight: bold; + } + + &:hover { + background-color: transparent; + color: @textColor; + } + } +} + +// No results +.ui.placeholder.segment .search.icon { + display: none; +} + +// Loading results +.resultsPlaceholder { + height: 10rem; +} + +.highlight { + margin-top: 0.5rem; + cursor: zoom-in; + + div { + margin-bottom: 0.5rem; + } + + em { + background-color: #e6f2ce; + } +} + +// debug +body.section-test-searchkit-querystrings { + .ui.segment.top-wrapper { + display: none; + } +} diff --git a/packages/volto-searchkit-block/src/components/helpers.js b/packages/volto-searchkit-block/src/components/helpers.js new file mode 100644 index 00000000..5b82632d --- /dev/null +++ b/packages/volto-searchkit-block/src/components/helpers.js @@ -0,0 +1,65 @@ +import React from 'react'; +import config from '@plone/volto/registry'; + +class NoSSR extends React.Component { + state = { + isClient: false, + }; + componentDidMount() { + this.setState({ isClient: true }); + } + render() { + const { isClient } = this.state; + const { children } = this.props; + return isClient ? children : null; + } +} + +// TODO replace ugly flattenESUrlToPath hack. Problem Elastic responds with backend Plone url which is host.docker…. +/** + * flatten url to path if internal, else leave it as it is + * @param {String} url + * @returns path + * + * "http://host.docker.internal:17091/Plone/news/sprint-on-accessibility" + * -> + * "/news/sprint-on-accessibility" + */ +function flattenESUrlToPath(url) { + if (url.startsWith('https')) { + // external url + return url; + } + + const urlObj = new URL(url); + const urlArray = urlObj.pathname.split('/').reverse(); + urlArray.pop(); + urlArray.pop(); + const newPathname = `/${urlArray.reverse().join('/')}`; + return newPathname; +} + +const scrollToTarget = (target, offsetHeight = 0) => { + target.scrollIntoView({ + behavior: 'smooth', + }); +}; + +/** + * @param {Array} objlst array of Objects with ... + * @returns Object with fieldname as key and title as value + */ +function getObjectFromObjectList(objlst) { + let obj = {}; + if (!objlst) { + return {}; + } + objlst.forEach((listitem) => { + if (listitem.field) { + obj[listitem.field.value] = listitem.title; + } + }); + return obj; +} + +export { NoSSR, flattenESUrlToPath, scrollToTarget, getObjectFromObjectList }; diff --git a/packages/volto-searchkit-block/src/components/helpers.test.js b/packages/volto-searchkit-block/src/components/helpers.test.js new file mode 100644 index 00000000..3887b2ce --- /dev/null +++ b/packages/volto-searchkit-block/src/components/helpers.test.js @@ -0,0 +1,24 @@ +import config from '@plone/volto/registry'; + +import { flattenESUrlToPath } from './helpers'; + +beforeEach(() => { + config.settings.legacyTraverse = false; +}); + +const { settings } = config; + +describe('helpers', () => { + describe('flattenESUrlToPath', () => { + it('flattens a given URL to the app URL', () => { + expect( + flattenESUrlToPath( + 'http://host.docker.internal:17091/Plone/news/sprint-on-accessibility', + ), + ).toBe('/news/sprint-on-accessibility'); + expect(flattenESUrlToPath('https://nzz.ch/arosa/piste')).toBe( + 'https://nzz.ch/arosa/piste', + ); + }); + }); +}); diff --git a/packages/volto-searchkit-block/src/components/index.js b/packages/volto-searchkit-block/src/components/index.js new file mode 100644 index 00000000..73016cda --- /dev/null +++ b/packages/volto-searchkit-block/src/components/index.js @@ -0,0 +1,9 @@ +import FacetedSearchBlockEdit from './Blocks/FacetedSearchBlockEdit'; +import FacetedSearchBlockView from './Blocks/FacetedSearchBlockView'; +import TestSearchkitQuerystrings from './Views/TestSearchkitQuerystrings'; + +export { + FacetedSearchBlockEdit, + FacetedSearchBlockView, + TestSearchkitQuerystrings, +}; diff --git a/packages/volto-searchkit-block/src/index.js b/packages/volto-searchkit-block/src/index.js new file mode 100644 index 00000000..cb15d57f --- /dev/null +++ b/packages/volto-searchkit-block/src/index.js @@ -0,0 +1,119 @@ +import zoomSVG from '@plone/volto/icons/zoom.svg'; +import { getQuerystring } from '@plone/volto/actions'; + +import { + FacetedSearchBlockEdit, + FacetedSearchBlockView, + TestSearchkitQuerystrings, +} from './components'; +import { + ReferenceSearchBlockEdit, + ReferenceSearchBlockView, +} from './components/Blocks/Reference'; + +import SearchSectionsWidget from './components/Blocks/SearchSectionsWidget'; + +const applyConfig = (config) => { + config.settings.searchkitblock = { + trackVoltoMatomo: false, + }; + + config.blocks.blocksConfig.searchkitblock = { + id: 'searchkitblock', + title: 'Searchkit', + edit: FacetedSearchBlockEdit, + view: FacetedSearchBlockView, + icon: zoomSVG, + group: 'common', + restricted: false, + mostUsed: false, + sidebarTab: 1, + security: { + addPermission: [], + view: [], + }, + }; + + config.widgets.widget.searchsectionswidget = SearchSectionsWidget; + + /** + * A reference block with default components from react-searchkit + * TODO set permission to restrict to admin as soon as addPermission is implemented in Volto. For now: set restricted to true. + */ + config.blocks.blocksConfig.referencesearchkitblock = { + id: 'referencesearchkitblock', + title: 'Search Reference', + edit: ReferenceSearchBlockEdit, + view: ReferenceSearchBlockView, + icon: zoomSVG, + group: 'text', + restricted: true, + mostUsed: true, + sidebarTab: 1, + security: { + addPermission: [], + view: [], + }, + }; + + // Test some querystrings + config.settings.controlpanels = [ + ...(config.settings.controlpanels || []), + { + '@id': '/test-searchkit-querystrings', + group: 'Add-on Configuration', + title: 'Test searchkit querystrings', + }, + ]; + config.addonRoutes = [ + ...config.addonRoutes, + { + path: '/controlpanel/test-searchkit-querystrings', + component: TestSearchkitQuerystrings, + }, + ]; + // Configure 'Test searchkit querystrings' controlpanel + config.blocks.blocksConfig.searchkitblock.searchconfig = { + searchedFields: [ + 'title^1.4', + 'description^1.2', + 'blocks_plaintext', + 'subjects^1.2', + ], + facet_fields: [], + allowed_content_types: ['Document', 'News Item', 'Event'], + allowed_review_states: [], + // backend_url: 'http://host.docker.internal:8080/Plone', + // frontend_url: 'http://localhost:3000', + }; + + // Fetch querystring indexes. + // See /effective-volto/addons/asyncconnect + config.settings.asyncPropsExtenders = [ + ...(config.settings.asyncPropsExtenders || []), + { + path: '/', + extend: (dispatchActions) => { + const action = { + key: 'querystringindexes', + promise: ({ store }) => { + const state = store.getState(); + if (state.querystring?.indexes?.Title) { + return; + } + const myaction = getQuerystring(); + return store.dispatch(myaction).catch((e) => { + // eslint-disable-next-line no-console + console.error('Fetch of getQuerystring failed'); + }); + }, + }; + return [...dispatchActions, action]; + }, + }, + ]; + + return config; +}; + +export default applyConfig; diff --git a/packages/volto-searchkit-block/src/messages.js b/packages/volto-searchkit-block/src/messages.js new file mode 100644 index 00000000..30a595c9 --- /dev/null +++ b/packages/volto-searchkit-block/src/messages.js @@ -0,0 +1,104 @@ +import { defineMessages } from 'react-intl'; + +const messages = defineMessages({ + // block title translation for slash menu + blocktitle: { + id: 'Searchkit', + defaultMessage: 'Searchkit', + }, + searchBlock: { + id: 'Search block', + defaultMessage: 'Search block', + }, + search: { + id: 'search', + defaultMessage: 'search', + }, + facets: { + id: 'Facets', + defaultMessage: 'Facets', + }, + facet: { + id: 'Facet', + defaultMessage: 'Facet', + }, + // item: { + // id: 'Item', + // defaultMessage: 'Item', + // }, + label: { + id: 'Label', + defaultMessage: 'Label', + }, + field: { + id: 'Field', + defaultMessage: 'Field', + }, + multipleChoices: { + id: 'Multiple choices?', + defaultMessage: 'Multiple choices?', + }, + facetWidget: { + id: 'Facet widget', + defaultMessage: 'Facet widget', + }, + metadata: { + id: 'Meta data', + defaultMessage: 'Meta data', + }, + searchresult: { + id: 'Search result', + defaultMessage: 'Search result', + }, + searchresults: { + id: 'Search results', + defaultMessage: 'Search results', + }, + searchInSections: { + id: 'Search in sections', + defaultMessage: 'Search in sections', + }, + searchInSectionsDescription: { + id: 'Search can be restricted by sections / paths', + defaultMessage: 'Search can be restricted by sections / paths', + }, + searchSection: { + id: 'Search section', + defaultMessage: 'section', + }, + searchSectionLabel: { + id: 'Search section label', + defaultMessage: 'Search section label', + }, + showFilter: { + id: 'Show filters', + defaultMessage: 'Show filter', + }, + // highlights + title: { + id: 'Title', + defaultMessage: 'Title', + }, + description: { + id: 'Description', + defaultMessage: 'Description', + }, + tags: { + id: 'Tags', + defaultMessage: 'Tags', + }, + content: { + id: 'Content', + defaultMessage: 'Content', + }, + cancel: { + id: 'Cancel', + defaultMessage: 'Cancel', + }, + add: { + id: 'Add {type}', + defaultMessage: 'Add {type}', + }, +}); + +export default messages; diff --git a/packages/volto-searchkit-block/towncrier.toml b/packages/volto-searchkit-block/towncrier.toml new file mode 100644 index 00000000..f7c7a5f3 --- /dev/null +++ b/packages/volto-searchkit-block/towncrier.toml @@ -0,0 +1,33 @@ +[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/rohberg/volto-searchkit-block/issue/{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/packages/volto-searchkit-block/tsconfig.json b/packages/volto-searchkit-block/tsconfig.json new file mode 100644 index 00000000..ddb0f542 --- /dev/null +++ b/packages/volto-searchkit-block/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "module": "preserve", + "noEmit": true, + "lib": ["es2022", "dom", "dom.iterable"], + "jsx": "react-jsx", + "paths": { + "@plone/volto/*": ["../../core/packages/volto/src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": [ + "node_modules", + "build", + "public", + "coverage", + "**/*.test.{js,jsx,ts,tsx}", + "**/*.spec.{js,jsx,ts,tsx}", + "**/*.stories.{js,jsx,ts,tsx}" + ] +} From 5a5276ab361229f889479eb192aaee5b9e143fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:37:08 +0200 Subject: [PATCH 164/226] Remove src/ --- .../Blocks/DownloadFiltersMapping.jsx | 48 - .../Blocks/FacetedSearchBlockEdit.jsx | 26 - .../Blocks/FacetedSearchBlockView.jsx | 11 - .../Reference/ReferenceSearchBlockEdit.jsx | 20 - .../Reference/ReferenceSearchBlockView.jsx | 17 - src/components/Blocks/Reference/index.js | 4 - .../Blocks/SearchSectionsWidget.jsx | 49 -- src/components/Blocks/Sidebar.jsx | 26 - src/components/Blocks/schema.js | 212 ----- .../Searchkit/CustomESRequestSerializer.jsx | 438 ---------- .../Searchkit/CustomESResponseSerializer.jsx | 46 - src/components/Searchkit/ESSearchApi.jsx | 74 -- .../Searchkit/ElasticSearchHighlights.jsx | 118 --- src/components/Searchkit/Error.jsx | 20 - src/components/Searchkit/Error.test.js | 36 - .../Searchkit/LICENSE_Cern_react-searchkit.md | 21 - src/components/Searchkit/README.md | 3 - src/components/Searchkit/Results.js | 89 -- src/components/Searchkit/ResultsLoader.jsx | 12 - src/components/Searchkit/SearchBarSection.jsx | 51 -- src/components/Searchkit/SectionsSearch.jsx | 156 ---- .../__snapshots__/Error.test.js.snap | 23 - src/components/StateLogger.jsx | 23 - src/components/Views/FacetedSearch.jsx | 826 ------------------ .../Views/TestSearchkitQuerystrings.jsx | 224 ----- .../springisnow-volto-searchkit-block.less | 296 ------- src/components/helpers.js | 65 -- src/components/helpers.test.js | 24 - src/components/index.js | 9 - src/index.js | 119 --- src/messages.js | 104 --- 31 files changed, 3190 deletions(-) delete mode 100644 src/components/Blocks/DownloadFiltersMapping.jsx delete mode 100644 src/components/Blocks/FacetedSearchBlockEdit.jsx delete mode 100644 src/components/Blocks/FacetedSearchBlockView.jsx delete mode 100644 src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx delete mode 100644 src/components/Blocks/Reference/ReferenceSearchBlockView.jsx delete mode 100644 src/components/Blocks/Reference/index.js delete mode 100644 src/components/Blocks/SearchSectionsWidget.jsx delete mode 100644 src/components/Blocks/Sidebar.jsx delete mode 100644 src/components/Blocks/schema.js delete mode 100644 src/components/Searchkit/CustomESRequestSerializer.jsx delete mode 100644 src/components/Searchkit/CustomESResponseSerializer.jsx delete mode 100644 src/components/Searchkit/ESSearchApi.jsx delete mode 100644 src/components/Searchkit/ElasticSearchHighlights.jsx delete mode 100644 src/components/Searchkit/Error.jsx delete mode 100644 src/components/Searchkit/Error.test.js delete mode 100644 src/components/Searchkit/LICENSE_Cern_react-searchkit.md delete mode 100644 src/components/Searchkit/README.md delete mode 100644 src/components/Searchkit/Results.js delete mode 100644 src/components/Searchkit/ResultsLoader.jsx delete mode 100644 src/components/Searchkit/SearchBarSection.jsx delete mode 100644 src/components/Searchkit/SectionsSearch.jsx delete mode 100644 src/components/Searchkit/__snapshots__/Error.test.js.snap delete mode 100644 src/components/StateLogger.jsx delete mode 100644 src/components/Views/FacetedSearch.jsx delete mode 100644 src/components/Views/TestSearchkitQuerystrings.jsx delete mode 100644 src/components/Views/less/springisnow-volto-searchkit-block.less delete mode 100644 src/components/helpers.js delete mode 100644 src/components/helpers.test.js delete mode 100644 src/components/index.js delete mode 100644 src/index.js delete mode 100644 src/messages.js diff --git a/src/components/Blocks/DownloadFiltersMapping.jsx b/src/components/Blocks/DownloadFiltersMapping.jsx deleted file mode 100644 index a9f1f1f9..00000000 --- a/src/components/Blocks/DownloadFiltersMapping.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { Button } from 'semantic-ui-react'; - -const FooComponent = ({ data }) => { - const vocabularies = useSelector((state) => state.querystring?.indexes); - function exportFiltersMapping(data) { - const filternames = data.facet_fields?.map((el) => { - return el.field.value; - }); - let ff = {}; - filternames?.forEach((fname) => { - let foo = vocabularies[fname].values; - Object.keys(foo).forEach((el) => { - ff[el] = foo[el].title; - }); - }); - let map = { - facet_fields: ff, - search_sections: - data.search_sections && - Object.fromEntries( - data.search_sections.items.map((el) => { - return [el.section, el.label]; - }), - ), - }; - const fileData = JSON.stringify(map); - const blob = new Blob([fileData], { type: 'text/plain' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.download = 'filter-mapping.json'; - link.href = url; - link.click(); - } - - return ( - - ); -}; - -export default FooComponent; diff --git a/src/components/Blocks/FacetedSearchBlockEdit.jsx b/src/components/Blocks/FacetedSearchBlockEdit.jsx deleted file mode 100644 index cb2b8b33..00000000 --- a/src/components/Blocks/FacetedSearchBlockEdit.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { Container, Segment } from 'semantic-ui-react'; -import { SidebarPortal } from '@plone/volto/components'; - -import Sidebar from './Sidebar'; -import FacetedSearchBlockView from './FacetedSearchBlockView'; -import DownloadFiltersMapping from './DownloadFiltersMapping'; - -const Edit = ({ data, onChangeBlock, block, selected }) => { - return ( -
- - - - - - - - - - -
- ); -}; - -export default Edit; diff --git a/src/components/Blocks/FacetedSearchBlockView.jsx b/src/components/Blocks/FacetedSearchBlockView.jsx deleted file mode 100644 index 6cc0e92d..00000000 --- a/src/components/Blocks/FacetedSearchBlockView.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import FacetedSearch from '../Views/FacetedSearch'; - -const View = ({ data }) => { - return ( -
- -
- ); -}; - -export default View; diff --git a/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx b/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx deleted file mode 100644 index 111774b9..00000000 --- a/src/components/Blocks/Reference/ReferenceSearchBlockEdit.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { SidebarPortal } from '@plone/volto/components'; - -import Sidebar from '../Sidebar'; -// import FacetedSearch from '../Views/FacetedSearch'; - -const Edit = ({ data, onChangeBlock, block, selected }) => { - return ( -
- - - - -

Search with bucket aggregation of ElasticSearch

-

Reference with react-searchkit defaults

-
- ); -}; - -export default Edit; diff --git a/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx b/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx deleted file mode 100644 index 38d12acf..00000000 --- a/src/components/Blocks/Reference/ReferenceSearchBlockView.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import FacetedSearch from '../../Views/FacetedSearch'; - -const View = ({ data }) => { - return ( -
- -
- ); -}; - -export default View; diff --git a/src/components/Blocks/Reference/index.js b/src/components/Blocks/Reference/index.js deleted file mode 100644 index b1c77d4b..00000000 --- a/src/components/Blocks/Reference/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import ReferenceSearchBlockEdit from './ReferenceSearchBlockEdit'; -import ReferenceSearchBlockView from './ReferenceSearchBlockView'; - -export { ReferenceSearchBlockEdit, ReferenceSearchBlockView }; diff --git a/src/components/Blocks/SearchSectionsWidget.jsx b/src/components/Blocks/SearchSectionsWidget.jsx deleted file mode 100644 index d3e1ec41..00000000 --- a/src/components/Blocks/SearchSectionsWidget.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { defineMessages } from 'react-intl'; - -import ObjectListWidget from '@plone/volto/components/manage/Widgets/ObjectListWidget'; - -import messages from '../../messages'; - -const ItemSchema = ({ intl }) => { - return { - title: intl.formatMessage(messages.searchSection), - addMessage: intl.formatMessage(messages.add, { - type: intl.formatMessage(messages.searchSection), - }), - properties: { - section: { - title: intl.formatMessage(messages.searchSection), - }, - label: { - title: intl.formatMessage(messages.searchSectionLabel), - }, - show_filter: { - title: intl.formatMessage(messages.showFilter), - type: 'boolean', - default: true, - }, - }, - fieldsets: [ - { - id: 'default', - title: 'History-Eintrag', - fields: ['section', 'label', 'show_filter'], - }, - ], - required: [], - }; -}; - -const SearchSectionsWidget = (props) => { - return ( - props.onChange(id, { items: value })} - /> - ); -}; - -export default SearchSectionsWidget; diff --git a/src/components/Blocks/Sidebar.jsx b/src/components/Blocks/Sidebar.jsx deleted file mode 100644 index c6741b6b..00000000 --- a/src/components/Blocks/Sidebar.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { useIntl } from 'react-intl'; -import { SearchBlockSchema } from './schema'; -import { BlockDataForm } from '@plone/volto/components'; - -const Sidebar = ({ data, block, onChangeBlock }) => { - const intl = useIntl(); - let schema = SearchBlockSchema({ data, intl }); - return ( - { - onChangeBlock(block, { - ...data, - [id]: value, - }); - }} - onChangeBlock={onChangeBlock} - formData={data} - block={block} - /> - ); -}; - -export default Sidebar; diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js deleted file mode 100644 index 1fa7e576..00000000 --- a/src/components/Blocks/schema.js +++ /dev/null @@ -1,212 +0,0 @@ -// TODO translations title and descriptions of fields -import { - hasNonValueOperation, - hasDateOperation, -} from '@plone/volto/components/manage/Blocks/Search/utils'; -import messages from '../../messages'; - -const FacetSchema = ({ intl }) => ({ - title: intl.formatMessage(messages.facet), - addMessage: intl.formatMessage(messages.add, { - type: intl.formatMessage(messages.facet), - }), - fieldsets: [ - { - id: 'default', - title: 'Default', - fields: ['title', 'field'], - }, - ], - properties: { - title: { - title: intl.formatMessage(messages.label), - }, - field: { - title: intl.formatMessage(messages.field), - widget: 'select_querystring_field', - vocabulary: { '@id': 'plone.app.vocabularies.MetadataFields' }, - filterOptions: (options) => { - // Only allow indexes that provide simple, fixed vocabularies. - // This should be improved, together with the facets. The querystring - // widget implementation should serve as inspiration for those dynamic - // types of facets. - return Object.assign( - {}, - ...Object.keys(options).map((k) => - Object.keys(options[k].values || {}).length || - hasNonValueOperation(options[k].operations) || - hasDateOperation(options[k].operations) - ? { [k]: options[k] } - : {}, - ), - ); - }, - }, - }, - required: ['field'], -}); -const ExtrainfoSchema = ({ intl }) => { - const facetschema = FacetSchema({ intl }); - const extrainfoschema = { - ...facetschema, - title: intl.formatMessage(messages.metadata), - addMessage: intl.formatMessage(messages.add, { - type: intl.formatMessage(messages.metadata), - }), - }; - return extrainfoschema; -}; - -export const SearchBlockSchema = ({ data = {}, intl }) => { - return { - title: intl.formatMessage(messages.searchBlock), - fieldsets: [ - { - id: 'default', - title: 'Search', - fields: [ - 'allowed_content_types', - 'allowed_review_states', - 'searchedFields', - 'batchSize', - ], - }, - { - id: 'facets', - title: intl.formatMessage(messages.facets), - fields: [ - 'facet_fields', - 'filterLayout', - 'search_sections', - 'allow_search_excluded_sections', - 'show_filter_for_excluded_sections', - ], - }, - { - id: 'results', - title: 'Results', - fields: [ - 'extrainfo_fields', - 'subjectsFieldname', - 'showNewsItemPublishedDate', - 'showEventStartDate', - ], - }, - { - id: 'divers', - title: 'Divers', - fields: ['relocation'], - }, - ], - properties: { - // elastic_search_api_url: { - // title: - // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API URL', - // default: 'http://localhost:9200', - // }, - // elastic_search_api_index: { - // title: - // '(deprecated) (Set in collective.elastic environment variable) Elastic Search API Index', - // default: 'esploneindex', - // }, - search_sections: { - title: intl.formatMessage(messages.searchInSections), - description: intl.formatMessage(messages.searchInSectionsDescription), - type: 'dict', - factory: 'JSONField', - widget: 'searchsectionswidget', - }, - allow_search_excluded_sections: { - title: 'Allow search everywhere except in sections', - type: 'boolean', - }, - show_filter_for_excluded_sections: { - title: 'Show filter for excluded sections', - type: 'boolean', - default: true, - }, - allowed_content_types: { - title: 'Types', - description: 'Restrict types to display.', - type: 'array', - widget: 'array', - items: { - vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, - }, - }, - allowed_review_states: { - title: 'States', - description: 'Restrict review states.', - type: 'array', - widget: 'array', - items: { - vocabulary: { '@id': 'plone.app.vocabularies.WorkflowStates' }, - }, - }, - searchedFields: { - title: 'Searchable fields with boosting', - description: - 'Type fieldnames to search in field names. Type title^1.4 to boost the title 40%.', - type: 'array', - creatable: true, - default: ['title^1.4', 'description^1.2', 'blocks_plaintext'], - }, - batchSize: { - title: 'Batch size', - type: 'number', - default: 10, - }, - facet_fields: { - title: 'Facets', - description: 'Fields to filter on.', - widget: 'object_list', - schema: FacetSchema({ intl }), - }, - filterLayout: { - title: intl.formatMessage(messages.facetWidget), - // widget: SelectWidget, - choices: [ - ['dropdown', 'Dropdown'], - ['checkboxes', 'Checkboxes'], - ], - default: 'dropdown', - }, - extrainfo_fields: { - title: intl.formatMessage(messages.metadata), - widget: 'object_list', - schema: ExtrainfoSchema({ intl }), - }, - subjectsFieldname: { - title: 'Field name of tags field', - description: - 'Show tags to search for. Let the field empty to not show tags.', - default: 'subjects', - }, - showNewsItemPublishedDate: { - title: 'Show published date of news items', - type: 'array', - widget: 'array', - items: { - vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, - }, - default: ['News Item'], - }, - showEventStartDate: { - title: 'Show start date of events', - type: 'array', - widget: 'array', - items: { - vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, - }, - default: ['Event'], - }, - relocation: { - title: 'Relocation', - description: - 'CSS selector for relocation of search bar. Leave empty to keep search bar in block.', - default: '', - }, - }, - required: [], - }; -}; diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx deleted file mode 100644 index faad29d3..00000000 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ /dev/null @@ -1,438 +0,0 @@ -import { extend, isEmpty, keyBy, trim } from 'lodash'; -import { getObjectFromObjectList } from '../helpers'; - -import config from '@plone/volto/registry'; - -const volto_config = config; - -export class CustomESRequestSerializer { - constructor(config) { - this.reviewstatemapping = config.reviewstatemapping; - this.searchedFields = config.searchedFields; - this.facet_fields = getObjectFromObjectList(config.facet_fields); - this.allowed_content_types = config.allowed_content_types; - this.allowed_review_states = config.allowed_review_states; - this.search_sections = config.search_sections; - this.language = config.language; - } - /** - * Convert Array of filters to Object of filters - * @param {Array} filters Array of filters - * @return {Object} Object of filters - * input: [ - * [ 'type_agg', 'value1' ] - * [ 'type_agg', 'value2', [ 'subtype_agg', 'a value' ] ] - * ] - * output: { - * type_agg: ['value1', 'value2'] - * subtype_agg: [ 'a value' ] - * } - */ - getFilters = (filters) => { - const aggValueObj = {}; - - const getChildFilter = (filter) => { - const aggName = filter[0]; - const fieldValue = filter[1]; - if (aggName in aggValueObj) { - aggValueObj[aggName].push(fieldValue); - } else { - aggValueObj[aggName] = [fieldValue]; - } - const hasChild = filter.length === 3; - if (hasChild) { - getChildFilter(filter[2]); - } - }; - - filters.forEach((filterObj) => { - getChildFilter(filterObj); - }); - return aggValueObj; - }; - - /** - * Return a serialized version of the app state `query` for the API backend. - * @param {object} stateQuery the `query` state to serialize - */ - serialize = (stateQuery) => { - const { queryString, sortBy, sortOrder, page, size, filters } = stateQuery; - const bodyParams = {}; - const force_fuzzy = true; // search for `${word}` and `${word}~` - - let qs_tailored_should_notexact = []; - let qs_tailored_should_exact = []; - let qs_tailored_must_notexact = []; - let qs_tailored_must_exact = []; - let qs_tailored_mustNot_exact = []; - - const _remove_orphan_leading_or_trailing_quotmarks = (word) => { - let word_without_plus_or_minus = trim(word, '+'); - word_without_plus_or_minus = trim(word_without_plus_or_minus, '-'); - if ( - !( - word_without_plus_or_minus.startsWith('"') && - word_without_plus_or_minus.endsWith('"') - ) && - !( - !word_without_plus_or_minus.startsWith('"') && - !word_without_plus_or_minus.endsWith('"') - ) - ) { - return word.replace('"', ''); - } - return word; - }; - - const _removeQuotationMarks = (word) => { - word.replace('"', ''); - word.replace("'", ''); - return word; - }; - - const _make_fuzzy_and_enrich_with_word_parts = (word) => { - // EXCLUDE - if (word.startsWith('-')) { - qs_tailored_mustNot_exact.push(_removeQuotationMarks(word.slice(1))); - return; - } - // MUST - if (word.startsWith('+')) { - if (word.includes('"') || word.includes('*') || word.includes('?')) { - qs_tailored_must_exact.push(word.slice(1)); - } else { - qs_tailored_must_notexact.push(word.slice(1)); - } - return; - } - - // WILDCARD - if (word.includes('*') || word.includes('?')) { - qs_tailored_should_exact.push(_removeQuotationMarks(word)); - return; - } - // EXACT - if (word.includes('"')) { - qs_tailored_should_exact.push(word); - return; - } - - // Words with hyphen - let word_new; - let wordpartlist = word.split('-'); // common hyphens - if (wordpartlist.length > 1) { - // word with hyphen - let resultlist = []; - wordpartlist.push(word); - wordpartlist.forEach((el) => { - if (force_fuzzy) { - resultlist.push(`${el} ${el}~`); - } else { - resultlist.push(el); - } - }); - word_new = resultlist.join(' '); - } else { - // word without hyphen - word_new = force_fuzzy ? `${word} ${word}~` : `${word}`; - } - qs_tailored_should_notexact.push(word_new); - return; - }; - - if (!isEmpty(queryString)) { - // - search fuzzy - // - search also for word parts (LSR-Lehrbetrieb: search also for LSR and Lehrbetrieb) - let words = queryString.trim().split(' '); - words = words - // filter out spaces, orphan ", "AND", and "OR" - .filter((word) => !['', '"', 'AND', 'OR', 'NOT'].includes(word)); - - words.forEach((word) => { - word = _remove_orphan_leading_or_trailing_quotmarks(word); - _make_fuzzy_and_enrich_with_word_parts(word); - }); - - // fields with boosting - let searchedFields = [...this.searchedFields]; - - let searchedFields_simple = searchedFields.map((fld) => { - const fieldname = fld.split('^')[0]; - return fld.replace(fieldname, `${fieldname}.${this.language}`); - }); - - let searchedFields_exact = searchedFields.map((fld) => { - const fieldname = fld.split('^')[0]; - return fld.replace(fieldname, `${fieldname}.${this.language}_exact`); - }); - - // Construction of query - let shouldList = []; - let mustList = []; - let must_notList = []; - - qs_tailored_should_notexact.forEach((element) => { - shouldList.push({ - query_string: { - query: element, - fields: searchedFields_simple, - }, - }); - }); - qs_tailored_should_exact.forEach((element) => { - shouldList.push({ - query_string: { - query: element, - fields: searchedFields_exact, - }, - }); - }); - - qs_tailored_must_notexact.forEach((el) => { - mustList.push({ - query_string: { - query: el, - fields: searchedFields_simple, - }, - }); - }); - qs_tailored_must_exact.forEach((element) => { - mustList.push({ - query_string: { - query: element, - fields: searchedFields_exact, - }, - }); - }); - qs_tailored_mustNot_exact.forEach((element) => { - must_notList.push({ - query_string: { - query: element, - fields: searchedFields_exact, - }, - }); - }); - - bodyParams['query'] = { - bool: { - should: shouldList, - must: mustList, - must_not: must_notList, - }, - }; - - bodyParams['highlight'] = { - number_of_fragments: 20, - fields: ['title', 'description', 'blocks_plaintext'].map( - (fieldname) => { - return { - [fieldname]: { - matched_fields: [ - `${fieldname}.${this.language}`, - `${fieldname}.${this.language}_exact`, - ], - type: 'fvh', - }, - }; - }, - ), - }; - } - - if (sortBy !== 'bestmatch') { - bodyParams['sort'] = bodyParams['sort'] || []; - const sortObj = {}; - sortObj[sortBy] = sortOrder && sortOrder === 'desc' ? 'desc' : 'asc'; - bodyParams['sort'].push(sortObj); - } - - if (size > 0) { - bodyParams['size'] = size; // batch size - } - - if (page > 0) { - const s = size > 0 ? size : 0; - const from = (page - 1) * s; - bodyParams['from'] = from; - } - - const getFieldnameFromAgg = (agg) => { - return agg.replace('_agg', ''); - }; - - // Generate terms of global filters - let terms = []; - // If isMultilingual, search only in language - - this.language && - volto_config.settings.isMultilingual && - terms.push({ - terms: { - language: [this.language], - }, - }); - this.allowed_content_types?.length > 0 && - terms.push({ - terms: { - portal_type: this.allowed_content_types, - }, - }); - this.allowed_review_states?.length > 0 && - terms.push({ - terms: { - review_state: this.allowed_review_states, - }, - }); - - const filters_dict = keyBy(filters, (e) => { - return e[0]; - }); - const section = filters_dict['section']; - - // Generate terms of selected options - let terms_of_selected_options = []; - if (filters.length) { - // Convert to object. - const aggValueObj = this.getFilters(filters); - - terms_of_selected_options = Object.keys(aggValueObj).reduce( - (accumulator, aggName) => { - const obj = {}; - const fieldName = getFieldnameFromAgg(aggName); - if (fieldName === 'subjects') { - obj['subjects.keyword'] = aggValueObj[aggName]; - } else { - obj[fieldName] = aggValueObj[aggName]; - } - if ( - aggName !== 'section' || - JSON.stringify(aggValueObj[aggName]) !== '["others"]' - ) { - accumulator.push({ terms: obj }); - } - return accumulator; - }, - [], - ); - } - - /** - * ES post_filter - */ - - const post_filter = { - bool: { must: terms.concat(terms_of_selected_options) }, - }; - - // Exclude sections - if (section && section[1] === 'others') { - post_filter['bool']['must_not'] = [ - { - terms: { - section: this.search_sections.items.map((el) => { - return el.section; - }), - }, - }, - ]; - } - - bodyParams['post_filter'] = post_filter; - - /** - * Aggregations - */ - const filter = (fieldName) => { - let myAggsFilter = terms; - // Add selected filters - const terms_of_selected_options_without_self = - terms_of_selected_options.filter( - (el) => !Object.keys(el.terms).includes(fieldName), - ); - myAggsFilter = myAggsFilter.concat( - terms_of_selected_options_without_self, - ); - - // So far - let res = myAggsFilter - ? { - bool: { - must: myAggsFilter, - }, - } - : null; - - if (fieldName !== 'section') { - if (section) { - if (section[1] === 'others') { - res = res || { - bool: {}, - }; - res.bool.must_not = [ - { - terms: { - section: this.search_sections.items.map((el) => { - return el.section; - }), - }, - }, - ]; - } else { - // // Must section - // res = res || { - // bool: { - // must: [], - // }, - // }; - // res.bool.must.push([section[1]]); - } - } - } - - return res; - }; - - bodyParams['aggs'] = {}; - let aggregations = Object.keys(this.facet_fields); - aggregations.push('section'); - aggregations.forEach((fieldName) => { - let aggName = `${fieldName}_agg`; - let field = fieldName; - if (fieldName === 'Subject') { - field = 'subjects.keyword'; - aggName = 'subjects_agg'; - } - if (fieldName === 'section') { - field = 'section'; - } - let aggBucketTermsComponent = { - [aggName]: { - aggs: { - [aggName]: { - terms: { - field: `${field}`, - order: { - _key: 'asc', - }, - size: 500, // number of buckets - }, - }, - somemoredatafromelasticsearch: { - top_hits: { - size: 1, - _source: { includes: [field] }, - }, - }, - }, - }, - }; - const filter_fieldname = filter(fieldName); - if (filter_fieldname) { - aggBucketTermsComponent[aggName].filter = filter_fieldname; - } - extend(bodyParams['aggs'], aggBucketTermsComponent); - }); - - return bodyParams; - }; -} diff --git a/src/components/Searchkit/CustomESResponseSerializer.jsx b/src/components/Searchkit/CustomESResponseSerializer.jsx deleted file mode 100644 index cf770b3d..00000000 --- a/src/components/Searchkit/CustomESResponseSerializer.jsx +++ /dev/null @@ -1,46 +0,0 @@ -function _pimpedAggregations(aggregations) { - let result = Object.assign({}, aggregations); - let buckets = []; - Object.keys(result).forEach((element) => { - if (result[element] && result[element][element].buckets) { - result[element].buckets = result[element][element].buckets; - buckets = result[element].buckets; - } else { - buckets = []; - } - buckets && - buckets.forEach((bucket) => { - bucket.label = - bucket.somemoredatafromelasticsearch?.hits.hits[0]._source.title ?? - bucket.key; - }); - }); - - return result; -} - -export class CustomESResponseSerializer { - constructor(config) { - this.serialize = this.serialize.bind(this); - } - - /** - * Return a serialized version of the API backend response for the app state `results`. - * @param {object} payload the backend response payload - */ - - serialize(payload) { - const { aggregations, hits } = payload; - const foo = { - aggregations: _pimpedAggregations(aggregations) || {}, - hits: - hits?.hits.map((hit) => { - // TODO Replace hack: Add highlights to _source data - hit._source['highlight'] = hit.highlight; - return hit._source; - }) || [], - total: hits?.total.value || 0, - }; - return foo; - } -} diff --git a/src/components/Searchkit/ESSearchApi.jsx b/src/components/Searchkit/ESSearchApi.jsx deleted file mode 100644 index a9b20c1d..00000000 --- a/src/components/Searchkit/ESSearchApi.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import _get from 'lodash/get'; -import _hasIn from 'lodash/hasIn'; -import { CustomESRequestSerializer } from './CustomESRequestSerializer'; -import { CustomESResponseSerializer } from './CustomESResponseSerializer'; - -export class PloneSearchApi { - constructor(config) { - this.fetchConfig = _get(config, 'fetchPayload', {}); - this.validateFetchConfig(); - this.initSerializers(config); - this.search = this.search.bind(this); - // this.elastic_search_api_url = config.elastic_search_api_url; - // this.elastic_search_api_index = config.elastic_search_api_index; - } - - validateFetchConfig() { - if (!_hasIn(this.fetchConfig, 'url')) { - throw new Error('PloneSearchApi config: `url` is required.'); - } - } - - initSerializers(config) { - const requestSerializerCls = _get( - config, - 'es.requestSerializer', - CustomESRequestSerializer, - ); - const responseSerializerCls = _get( - config, - 'es.responseSerializer', - CustomESResponseSerializer, - ); - - this.requestSerializer = new requestSerializerCls({ - searchedFields: config.searchedFields, - facet_fields: config.facet_fields, - allowed_content_types: config.allowed_content_types, - allowed_review_states: config.allowed_review_states, - search_sections: config.search_sections, - language: config.language, - }); - this.responseSerializer = new responseSerializerCls({}); - } - - /** - * Perform the backend request to search and return the serialized list of results for the app state `results`. - * @param {string} stateQuery the `query` state with the user input - */ - async search(stateQuery) { - const payload = this.requestSerializer.serialize(stateQuery); - // Extend paylod with url and index to address elasticsearch server - try { - const response = await fetch(this.fetchConfig.url, { - method: 'POST', - headers: this.fetchConfig.headers, - body: JSON.stringify({ - elasticsearch_payload: payload, - // elasticsearch_url: this.elastic_search_api_url, - // elasticsearch_index: this.elastic_search_api_index, - }), - }); - // let results = await this.responseSerializer.serialize(response.data); - let results = await response.json(); - if (results.message) { - throw results; - // throw new Error(`${results.type} ${results.message}`); - } - results = this.responseSerializer.serialize(results); - return results; - } catch (error) { - throw error; - } - } -} diff --git a/src/components/Searchkit/ElasticSearchHighlights.jsx b/src/components/Searchkit/ElasticSearchHighlights.jsx deleted file mode 100644 index f21270b3..00000000 --- a/src/components/Searchkit/ElasticSearchHighlights.jsx +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Component to show matches per document - * fragment_size is set in CustomESRequestSerializer - */ -import React from 'react'; -import { useIntl } from 'react-intl'; -import messages from '../../messages'; - -export const ElasticSearchHighlights = ({ highlight }) => { - const [toggleDetails, setToggleDetails] = React.useState(false); - - const intl = useIntl(); - - let fieldmapping = { - title: intl.formatMessage(messages.title), - description: intl.formatMessage(messages.description), - subjects: intl.formatMessage(messages.tags), - freemanualtags_searchable: intl.formatMessage(messages.tags), - blocks_plaintext: intl.formatMessage(messages.content), - manualfilecontent: intl.formatMessage(messages.content), - }; - - const showDetails = () => { - setToggleDetails(!toggleDetails); - }; - - const fragments = getFragments(highlight); - if (highlight) { - return !toggleDetails ? ( -
- {fragments.slice(0, 3).map((el, index) => { - return
; - })} -
- ) : ( -
- {Object.keys(highlight) - .reverse() - .map((fld) => { - return ( -
-
- Matches in {fieldmapping[fld] || fld}: -
-
    - {highlight[fld].map((el, index) => { - return ( -
  • - ); - })} -
-
- ); - })} -
- ); - } else { - return null; - } -}; - -/** - * Get fragments of matches in a document - * @param {Object} highlight. part of response of Elasticsearch query - * @returns {Array} Array of strings - */ -export const getFragments = (highlight) => { - let fragments = []; - highlight && - Object.keys(highlight) - .reverse() - .forEach((fld) => { - highlight[fld].forEach((mtch) => { - fragments.push(mtch); - }); - }); - return fragments; -}; - -/** - * Get matches in a document - * @param {Object} highlight. part of response of Elasticsearch query - * @returns {Array} Array of strings - */ -export const getMatches = (highlight) => { - const regex = /(.*?)<\/em>/gm; - let fragments = getFragments(highlight); - let matches = []; - fragments.forEach((fragment) => { - const fragmentmatches = [...fragment.matchAll(regex)]; - matches = matches.concat(fragmentmatches.map((match) => match[1])); - }); - matches = [...new Set(matches)]; - return matches; -}; - -export const ElasticSearchMatches = ({ highlight, indexResult }) => { - const matches = getMatches(highlight); - return ( -
- {matches.length > 0 && matches: } - {matches.join(' | ')} -
- ); -}; diff --git a/src/components/Searchkit/Error.jsx b/src/components/Searchkit/Error.jsx deleted file mode 100644 index cf1ba620..00000000 --- a/src/components/Searchkit/Error.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Header, Segment } from 'semantic-ui-react'; -import { FormattedMessage } from 'react-intl'; - -const Error = ({ error }) => { - return error?.type ? ( - -
-

- -

-
- {error?.type}: {error?.message} -
- ) : null; -}; - -export default Error; diff --git a/src/components/Searchkit/Error.test.js b/src/components/Searchkit/Error.test.js deleted file mode 100644 index 2608ece0..00000000 --- a/src/components/Searchkit/Error.test.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-intl-redux'; -import { MemoryRouter } from 'react-router-dom'; - -import Error from './Error'; - -beforeAll(() => {}); - -const mockStore = configureStore(); - -describe('Generic Error', () => { - it('renders a simple error component', () => { - const store = mockStore({ - intl: { - locale: 'en', - messages: {}, - }, - }); - const component = renderer.create( - - - - - , - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); - }); -}); diff --git a/src/components/Searchkit/LICENSE_Cern_react-searchkit.md b/src/components/Searchkit/LICENSE_Cern_react-searchkit.md deleted file mode 100644 index 19aa587f..00000000 --- a/src/components/Searchkit/LICENSE_Cern_react-searchkit.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (C) 2015-2019 CERN. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/components/Searchkit/README.md b/src/components/Searchkit/README.md deleted file mode 100644 index 1893527b..00000000 --- a/src/components/Searchkit/README.md +++ /dev/null @@ -1,3 +0,0 @@ -`volto-searchkit-block` is an integration of `react-searchkit` in `Volto / Plone 6`. - -`react-searchkit` Copyright (C) 2015-2019 CERN diff --git a/src/components/Searchkit/Results.js b/src/components/Searchkit/Results.js deleted file mode 100644 index 812c067e..00000000 --- a/src/components/Searchkit/Results.js +++ /dev/null @@ -1,89 +0,0 @@ -import React, { Component } from 'react'; -import { Grid } from 'semantic-ui-react'; -import { - Count, - Pagination, - ResultsMultiLayout, - Sort, - withState, -} from 'react-searchkit'; - -import config from '@plone/volto/registry'; - -// TODO conditional Matomo tracking: catch case if app has not volto-matomo installed -import { trackSiteSearch } from '@eeacms/volto-matomo/utils'; - -import { scrollToTarget } from '../helpers'; - -class Results extends Component { - componentDidMount() { - // Dispatch event (on query change), other add-ons can subscribe to. - var evt = new CustomEvent('searchkitQueryChanged', {}); - window && window.dispatchEvent(evt); - if ( - config.settings.searchkitblock.trackVoltoMatomo && - this.props.currentQueryState.queryString - ) { - let options = { - keyword: this.props.currentQueryState.queryString, - category: 'Suche in Dokumentation', // optional - // count: 4, // optional - documentTitle: 'Suche in Dokumentation', // optional - href: '/search', // optional - count: this.props.currentResultsState.data.total, - // customDimensions: [ - // { - // id: 1, - // value: 'loggedIn', - // }, - // ], // optional - }; - trackSiteSearch(options); - } - } - - render() { - const { total } = this.props.currentResultsState.data; - return total ? ( -
- - - - - - <> {cmp}} - overridableId="volto" - /> - - - - - - - - -
- ) : null; - } -} - -Results.propTypes = {}; - -Results.defaultProps = {}; - -const MyResults = (props) => { - // Add scroll to search input field - React.useEffect(() => { - const el = document.querySelector('.searchkitsearch'); - if (el) { - scrollToTarget(el); - } - }, []); - - return ; -}; - -export const OnResults = withState(MyResults); diff --git a/src/components/Searchkit/ResultsLoader.jsx b/src/components/Searchkit/ResultsLoader.jsx deleted file mode 100644 index 64663564..00000000 --- a/src/components/Searchkit/ResultsLoader.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Loader } from 'semantic-ui-react'; - -function ResultsLoaderComponent() { - return ( - <> - -
- - ); -} - -export default ResultsLoaderComponent; diff --git a/src/components/Searchkit/SearchBarSection.jsx b/src/components/Searchkit/SearchBarSection.jsx deleted file mode 100644 index 7c4917d4..00000000 --- a/src/components/Searchkit/SearchBarSection.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useIntl } from 'react-intl'; -import { Icon as IconSemantic } from 'semantic-ui-react'; -import { onQueryChanged, SearchBar, withState } from 'react-searchkit'; - -import messages from '../../messages'; - -const _SearchBarSection = (props) => { - const intl = useIntl(); - - const payloadOfReset = { - searchQuery: { - sortBy: 'bestmatch', - sortOrder: 'asc', - layout: 'list', - page: 1, - size: props.currentQueryState.data.batchSize, - queryString: '', - }, - }; - - const onResetHandler = (event) => { - onQueryChanged(payloadOfReset); - }; - - return ( -
- - onResetHandler(event)} - /> -
- ); -}; - -export default withState(_SearchBarSection); diff --git a/src/components/Searchkit/SectionsSearch.jsx b/src/components/Searchkit/SectionsSearch.jsx deleted file mode 100644 index 70b7b65d..00000000 --- a/src/components/Searchkit/SectionsSearch.jsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react'; -import { isEmpty, keyBy } from 'lodash'; -import { withState } from 'react-searchkit'; -import { BodyClass } from '@plone/volto/helpers'; -// import StateLogger from '../StateLogger'; - -const _SectionsSearch = (props) => { - const { - allow_search_excluded_sections, - show_filter_for_excluded_sections, - search_sections, - currentQueryState, - updateQueryState, - } = props; - - // State - const [activeSection, setActiveSection] = React.useState('all'); - - // Helpers - const search_sections_dict = keyBy(search_sections?.items || [], (el) => { - return el.section; - }); - - let doc_count_others = 0; - let doc_count_all = 0; - - if ( - props.currentResultsState.data.aggregations.section_agg?.section_agg - ?.buckets - ) { - const buckets = - props.currentResultsState.data.aggregations.section_agg?.section_agg - ?.buckets; - let bucket_dict = {}; - buckets.forEach((el) => { - bucket_dict[el.key] = el.doc_count; - }); - - // calculate doc_counts of others and all - let count_others = 0; - let count_all = 0; - Object.keys(bucket_dict).forEach((el) => { - if (!Object.keys(search_sections_dict).includes(el)) { - count_others = count_others + bucket_dict[el]; - } - }); - Object.keys(bucket_dict).forEach((el) => { - count_all = count_all + bucket_dict[el]; - }); - doc_count_others = count_others; - doc_count_all = count_all; - } - - React.useEffect(() => { - const filters_dictionary = keyBy(currentQueryState.filters, (el) => { - return el[0]; - }); - setActiveSection( - filters_dictionary.section ? filters_dictionary.section[1] : 'all', - ); - }, [currentQueryState]); - - const restrictSearchToSection = (section) => { - setActiveSection(section); - let kitquerystate = { - sortBy: 'modified', - sortOrder: 'desc', - layout: 'list', - page: 1, - size: props.currentQueryState.data.batchSize, - filters: currentQueryState.filters, - }; - if (currentQueryState.queryString) { - kitquerystate.queryString = currentQueryState.queryString; - } - - // Empty filters for sections without filter - if ( - (search_sections_dict[section] && - !search_sections_dict[section].show_filter) || - (section === 'others' && !show_filter_for_excluded_sections) - ) { - kitquerystate.filters = []; - } - // Replace filter 'section' - kitquerystate.filters = kitquerystate.filters.filter((el) => { - return el[0] !== 'section'; - }); - if (section === 'all') { - // pass - } else if (section === 'others') { - kitquerystate.filters.push(['section', section]); - } else { - kitquerystate.filters.push(['section', section]); - } - // Do search! - updateQueryState(kitquerystate); - }; - - return isEmpty(props.currentResultsState.error) ? ( - <> - -
- {search_sections?.items?.length > 0 ? ( - - ) : null} - {search_sections?.items?.length > 0 && - allow_search_excluded_sections ? ( - - ) : null} - {search_sections - ? search_sections.items.map((el) => { - return ( - - ); - }) - : null} -
- {/* */} - - ) : null; -}; - -export default withState(_SectionsSearch); diff --git a/src/components/Searchkit/__snapshots__/Error.test.js.snap b/src/components/Searchkit/__snapshots__/Error.test.js.snap deleted file mode 100644 index a78b8382..00000000 --- a/src/components/Searchkit/__snapshots__/Error.test.js.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Generic Error renders a simple error component 1`] = ` -
-
-

- Check the configuration of your searchkit block! -

-
- - ConnectionError - : - - - - Service pipapo not found. - -
-`; diff --git a/src/components/StateLogger.jsx b/src/components/StateLogger.jsx deleted file mode 100644 index c8590d83..00000000 --- a/src/components/StateLogger.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { withState } from 'react-searchkit'; - -class _StateLogger extends React.Component { - render() { - return ( -
-
-

Current results state

-
{JSON.stringify(this.props.currentResultsState, null, 2)}
-
-
-

Current query state

-
{JSON.stringify(this.props.currentQueryState, null, 2)}
-
-
- ); - } -} - -const StateLogger = withState(_StateLogger); - -export default StateLogger; diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx deleted file mode 100644 index 05fa8263..00000000 --- a/src/components/Views/FacetedSearch.jsx +++ /dev/null @@ -1,826 +0,0 @@ -import React from 'react'; -import { compact, truncate } from 'lodash'; -import cx from 'classnames'; -import Cookies from 'universal-cookie'; -import { createPortal } from 'react-dom'; -import { useSelector } from 'react-redux'; -import { Link } from 'react-router-dom'; -import { FormattedMessage, useIntl } from 'react-intl'; - -import { OverridableContext } from 'react-overridable'; - -import { - Button, - Container, - Dropdown, - Grid, - Header, - Icon as IconSemantic, - Item, - Label, - Pagination as Paginator, - Segment, -} from 'semantic-ui-react'; -import { - BucketAggregation, - EmptyResults, - onQueryChanged, - ReactSearchKit, - ResultsLoader, - withState, - Error as ErrorCp, -} from 'react-searchkit'; - -import { expandToBackendURL } from '@plone/volto/helpers'; -import { FormattedDate, Icon } from '@plone/volto/components'; -import { addAppURL, toPublicURL } from '@plone/volto/helpers'; -import leftAngle from '@plone/volto/icons/left-key.svg'; -import rightAngle from '@plone/volto/icons/right-key.svg'; -import firstAngle from '@plone/volto/icons/first.svg'; -import lastAngle from '@plone/volto/icons/last.svg'; -import clearSVG from '@plone/volto/icons/clear.svg'; - -import messages from '../../messages'; -import { flattenESUrlToPath, getObjectFromObjectList } from '../helpers'; - -import { PloneSearchApi } from '../Searchkit/ESSearchApi'; -import { CustomESRequestSerializer } from '../Searchkit/CustomESRequestSerializer'; -import { CustomESResponseSerializer } from '../Searchkit/CustomESResponseSerializer'; -import { OnResults } from '../Searchkit/Results'; -import SectionsSearch from '../Searchkit/SectionsSearch'; -import SearchBarSection from '../Searchkit/SearchBarSection'; -import MyResultsLoaderElement from '../Searchkit/ResultsLoader'; - -import { ElasticSearchHighlights } from '../Searchkit/ElasticSearchHighlights'; -import ErrorComponent from '../Searchkit/Error'; -// import StateLogger from '../StateLogger'; - -import './less/springisnow-volto-searchkit-block.less'; - -import config from '@plone/volto/registry'; - -/** - * - * @param {Object} querystringindexes - * @param {String} fieldname One of the indexes - * @param {String} key to be translated - * @returns {String} - */ -const translate = (querystringindexes, fieldname, key) => { - let label = key; - if (querystringindexes && fieldname in querystringindexes) { - label = querystringindexes[fieldname].values[key]?.title || key; - } - return label; -}; - -// TODO Make reviewstatemapping configurable -export const ploneSearchApi = (data, language) => { - const cookies = new Cookies(); - const authToken = cookies.get('auth_token'); - - return new PloneSearchApi({ - fetchPayload: { - url: expandToBackendURL('/@kitsearch'), - timeout: 5000, - headers: { - Accept: 'application/json', - Authorization: `Bearer ${authToken}`, - }, - }, - es: { - requestSerializer: CustomESRequestSerializer, - responseSerializer: CustomESResponseSerializer, - }, - searchedFields: data.searchedFields || ['title'], - facet_fields: data.facet_fields, - allowed_content_types: data.allowed_content_types, - allowed_review_states: data.allowed_review_states, - search_sections: data.search_sections, - language: language, - // elastic_search_api_url: data.elastic_search_api_url, - // elastic_search_api_index: data.elastic_search_api_index, - }); -}; - -const _ExtraInfo = (props) => { - const { result } = props; - - const extrainfo_fields = getObjectFromObjectList( - props.currentQueryState.data.extrainfo_fields, - ); - const facet_fields = getObjectFromObjectList( - props.currentQueryState.data.facet_fields, - ); - let subjectsFieldname = props.currentQueryState.data?.subjectsFieldname; // "subjects"; - - const querystringindexes = useSelector( - (state) => state.query?.querystringindexes, - ); - - return ( - - {Object.keys(extrainfo_fields).map((extrainfo_key, idx) => { - if (!result[extrainfo_key]) { - return null; - } - const extrainfo_value = Array.isArray(result[extrainfo_key]) - ? result[extrainfo_key] - : [result[extrainfo_key]]; - - return Object.keys(facet_fields).includes(extrainfo_key) ? ( - - {extrainfo_fields[extrainfo_key]}: - {extrainfo_value?.map((item, index) => { - let tito = translate(querystringindexes, extrainfo_key, item); - let payloadOfFilter = { - searchQuery: { - sortBy: 'bestmatch', - sortOrder: 'asc', - layout: 'list', - page: 1, - size: props.currentQueryState.data.batchSize, - filters: [[`${extrainfo_key}_agg`, item]], - }, - }; - return ( - - ); - })} - {idx < Object.keys(extrainfo_fields).length - 1 && ( - | - )} - - ) : ( - - {extrainfo_fields[extrainfo_key]}: - {extrainfo_value?.map((item, index) => { - let tito = item.title || item.token || item; - return ( - - {tito} - {index < extrainfo_value.length - 1 ? ',' : null} - - ); - })} - {idx < Object.keys(extrainfo_fields).length - 1 && ( - | - )} - - ); - })} - - {Array.isArray(result[subjectsFieldname]) && - result[subjectsFieldname]?.length > 0 ? ( -
- - : - - {result[subjectsFieldname]?.map((item, index) => { - let tito = item; - let payloadOfTag = { - searchQuery: { - sortBy: 'bestmatch', - sortOrder: 'asc', - layout: 'list', - page: 1, - size: props.currentQueryState.data.batchSize, - queryString: tito, - }, - }; - return ( - - ); - })} -
- ) : null} -
- ); -}; - -const ExtraInfo = withState(_ExtraInfo); - -const _CustomResultsListItem = (props) => { - const { result } = props; - const item_url = flattenESUrlToPath(result['@id']); - const is_external_content = item_url.startsWith('https'); - const locale = useSelector((state) => state.query?.locale); - const querystringindexes = useSelector( - (state) => state.query?.querystringindexes, - ); - const showNewsItemPublishedDate = useSelector( - (state) => state.query?.data.showNewsItemPublishedDate, - ); - const showEventStartDate = useSelector( - (state) => state.query?.data.showEventStartDate, - ); - - return ( - - - {Array.isArray(result.informationtype) && - result.informationtype?.length > 0 ? ( - - {result.informationtype?.map((item, index) => { - let tito = translate(querystringindexes, 'informationtype', item); - const payload = { - searchQuery: { - sortBy: 'bestmatch', - sortOrder: 'asc', - layout: 'list', - page: 1, - size: props.currentQueryState.data.batchSize, - filters: [['informationtype_agg', item]], - }, - }; - return ( - - ); - })} - - ) : null} - {result.head_title ? {result.head_title} : null} - {is_external_content ? ( - - - {result.title} - - {showNewsItemPublishedDate.includes(result.portal_type) && - result.effective ? ( - - - - ) : null} - {showEventStartDate.includes(result.portal_type) && result.start ? ( - - - - ) : null} - - - {truncate(result.description, { length: 200 })} - - - - ) : ( - - - {result.title} - - {showNewsItemPublishedDate.includes(result.portal_type) && - result.effective ? ( - - - - ) : null} - {showEventStartDate.includes(result.portal_type) && result.start ? ( - - - - ) : null} - - - {truncate(result.description, { length: 200 })} - - - - )} - - - - - ); -}; - -const CustomResultsListItem = withState(_CustomResultsListItem); - -const MyCountElement = ({ totalResults }) => { - const intl = useIntl(); - let labelSearchResults = intl.formatMessage(messages.searchresult); - let labelSearchResultsPlural = intl.formatMessage(messages.searchresults); - return ( -
- {totalResults}{' '} - {totalResults === 1 ? labelSearchResults : labelSearchResultsPlural} -
- ); -}; - -const myActiveFiltersElement = (props) => { - const { filters, removeActiveFilter, getLabel } = props; - return ( - <> - {filters.map((filter, index) => { - const { label, activeFilter } = getLabel(filter); - return ( - - ); - })} - - ); -}; - -/** - * CustomBucketAggregationElement - * One single Filter of Faceted Navigation - * props.agg.field: field name - */ -const CustomBucketAggregationElement = (props) => { - const { title, containerCmp, updateQueryFilters } = props; - const fieldname = props.agg.field; - const querystringindexes = useSelector( - (state) => state.query?.querystringindexes, - ); - - /** - * Translate labels according vocabulary - * @param {*} bucks - * @returns - */ - const translateBuckets = (bucks) => { - if (querystringindexes && fieldname in querystringindexes) { - bucks.forEach((element) => { - element.label = - querystringindexes[fieldname].values[element.key]?.title || - element.key; - }); - } - return bucks; - }; - - // Get label from token - let buckets = containerCmp.props.buckets; - buckets = translateBuckets(buckets); - let filter_labels_dict = Object.fromEntries( - Array.from(buckets, (x) => [x.key, x.label]), // TODO Translate label - ); - // List of labels of selected options - let selectedFilters = containerCmp.props.selectedFilters - .map((el) => el[1]) - .map((token) => filter_labels_dict[token]); - selectedFilters = compact(selectedFilters); - // List of all available options - let all_filters = containerCmp.props.buckets.map((el) => { - return [containerCmp.props.aggName, el.key]; - }); - - const removeAggFilters = (event) => { - if (containerCmp.props.selectedFilters.length) { - updateQueryFilters(containerCmp.props.selectedFilters); - } - event.preventDefault(); - event.stopPropagation(); - }; - - const selectAllAggFilters = (event) => { - // toggle! updateQueryFilters toggles filter selection - if (containerCmp.props.selectedFilters.length) { - updateQueryFilters(containerCmp.props.selectedFilters); - } - updateQueryFilters(all_filters); - - event.preventDefault(); - event.stopPropagation(); - }; - - const dropdowntitle = - title || - fieldname + - (selectedFilters.length > 0 ? ` [${selectedFilters.length}]` : ''); - - return containerCmp ? ( -
- 9, - })} - > - - - selectAllAggFilters(e)} - onKeyDown={(e) => selectAllAggFilters(e)} - role="option" - aria-selected="false" - tabIndex="0" - className="select_all" - > - - {' '} - /{' '} - removeAggFilters(e)} - onKeyDown={(e) => removeAggFilters(e)} - role="option" - aria-selected="false" - tabIndex="0" - className="deselect_all" - > - {' '} - - - - {containerCmp} - - - {/* removeAggFilters(e)} - /> */} -
- ) : null; -}; - -function choicesSorter(a, b) { - const titleA = a.props.bucket.label; - const titleB = b.props.bucket.label; - if (titleA < titleB) { - return -1; - } else if (titleA > titleB) { - return 1; - } - return 0; -} -const CustomBucketAggregationContainerElement = ({ valuesCmp }) => { - let foo = valuesCmp; - foo.sort(choicesSorter); - return <>{foo}; -}; - -const CustomBucketAggregationValuesElement = (props) => { - const { - bucket, - keyField, - isSelected, - onFilterClicked, - childAggCmps, - // updateQueryState, - // currentQueryState, - } = props; - const label = bucket.label - ? `${bucket.label} (${bucket.doc_count})` - : `${keyField} (${bucket.doc_count})`; - - const onFilterClickedCustom = (filter, event) => { - onFilterClicked(filter); - - event.preventDefault(); - event.stopPropagation(); - }; - - return ( - - {isSelected ? ( - onFilterClickedCustom(bucket.key, event)} - className={isSelected ? 'isSelected right floated' : 'right floated'} - key={`${bucket.key}-description`} - > - - - ) : null} - onFilterClickedCustom(bucket.key, event)} - className={isSelected ? 'isSelected' : ''} - key={bucket.key} - > - {label} - - {childAggCmps} - - ); -}; - -const customEmpytResultsElement = (props) => { - const { resetQuery } = props; - return ( - -
- -
- -
- ); -}; - -const customSort = ({ - currentSortBy, - currentSortOrder, - options, - onValueChange, -}) => { - const selected = currentSortBy.concat('-', currentSortOrder); - return ( -
- - - {' '} - - -
- ); -}; - -const customPaginationElement = (props) => { - const { currentPage, currentSize, totalResults, onPageChange, options } = - props; - const pages = Math.ceil(totalResults / currentSize); - const boundaryRangeCount = options.boundaryRangeCount; - const siblingRangeCount = options.siblingRangeCount; - const showEllipsis = options.showEllipsis; - const showFirst = options.showFirst; - const showLast = options.showLast; - const showPrev = options.showPrev; - const showNext = options.showNext; - const size = options.size || 'massive'; - const _onPageChange = (event, { activePage }) => { - onPageChange(activePage); - }; - - return pages > 1 ? ( - , - icon: true, - } - : null - } - firstItem={ - showFirst - ? { - content: , - icon: true, - } - : null - } - lastItem={ - showLast - ? { - content: , - icon: true, - } - : null - } - prevItem={ - showPrev - ? { - content: , - icon: true, - } - : null - } - nextItem={ - showNext - ? { - content: , - icon: true, - } - : null - } - size={size} - /> - ) : null; -}; - -const sortValues = [ - { - text: 'Relevanz', - sortBy: 'bestmatch', - sortOrder: 'asc', - }, - { - text: 'Neueste', - sortBy: 'modified', - sortOrder: 'desc', - }, - // { - // text: 'Alphabetisch', - // sortBy: 'sortable_title.keyword', - // sortOrder: 'asc', - // }, -]; - -/** - * FacetedSearch - * @param {string} filterLayout default 'dropdown' - * @param {object} overriddenComponents Override with custom components, ignore to stay with default 'dropdown' or step back to react-searchkit default components with value {} - * @returns - */ -const FacetedSearch = ({ data, overriddenComponents }) => { - const { - facet_fields, - allow_search_excluded_sections, - show_filter_for_excluded_sections, - relocation, - filterLayout, - search_sections, - } = data; - - const querystringindexes = useSelector((state) => state.querystring?.indexes); - - let facet_fields_object = getObjectFromObjectList(facet_fields); - if ('Subject' in facet_fields_object) { - facet_fields_object.subjects = facet_fields_object.Subject; - delete facet_fields_object.Subject; - } - - // TODO Get config from blocks data - const initialState = { - page: 1, - queryString: '', - sortBy: 'modified', - sortOrder: 'desc', - size: data.batchSize, - layout: 'list', - }; - - const defaultOverriddenComponents = { - 'ResultsList.item.elasticsearch': CustomResultsListItem, - 'Count.element': MyCountElement, - 'ActiveFilters.element': myActiveFiltersElement, - 'EmptyResults.element': customEmpytResultsElement, - 'Sort.element.volto': customSort, - 'Pagination.element': customPaginationElement, - 'Error.element': ErrorComponent, - 'ResultsLoader.element': MyResultsLoaderElement, - }; - - const dropdownOverriddenComponents = { - 'BucketAggregation.element': CustomBucketAggregationElement, - 'BucketAggregationContainer.element': - CustomBucketAggregationContainerElement, - 'BucketAggregationValues.element': withState( - CustomBucketAggregationValuesElement, - ), - }; - - overriddenComponents = { - ...defaultOverriddenComponents, - ...(filterLayout === 'dropdown' && dropdownOverriddenComponents), - ...(config.settings.searchkitblock.overriddenComponents && - config.settings.searchkitblock.overriddenComponents), - }; - - // TODO Check if check on client could be made simpler - const locale = useSelector((state) => state.intl.locale); - const [isClient, setIsClient] = React.useState(null); - React.useEffect(() => setIsClient(true), []); - - return ( - - {isClient && ( - - - - {typeof document !== 'undefined' && relocation?.length > 0 ? ( - createPortal( - , - true && - document.querySelectorAll(relocation) && - document.querySelectorAll(relocation)[0], - ) - ) : ( - - - - - - - - )} - - - - - - - - - -
- {Object.keys(facet_fields_object)?.map((facet) => ( - - ))} -
-
-
- - - - - - - - {/* */} - - -
-
-
-
- )} -
- ); -}; - -export default FacetedSearch; diff --git a/src/components/Views/TestSearchkitQuerystrings.jsx b/src/components/Views/TestSearchkitQuerystrings.jsx deleted file mode 100644 index fb6dcba4..00000000 --- a/src/components/Views/TestSearchkitQuerystrings.jsx +++ /dev/null @@ -1,224 +0,0 @@ -import React from 'react'; -import { useIntl } from 'react-intl'; -import { createPortal } from 'react-dom'; -import { Container, Header, Segment } from 'semantic-ui-react'; -import { useHistory } from 'react-router'; -import { Link, useLocation } from 'react-router-dom'; -import { useSelector } from 'react-redux'; -import { OverridableContext } from 'react-overridable'; -import { Icon, Toolbar } from '@plone/volto/components'; -import { getParentUrl } from '@plone/volto/helpers'; -import backSVG from '@plone/volto/icons/back.svg'; - -import { - onQueryChanged, - ReactSearchKit, - SearchBar, - withState, - ResultsMultiLayout, - Count, -} from 'react-searchkit'; -import { flattenESUrlToPath } from '../helpers'; -import { ploneSearchApi } from './FacetedSearch'; -import { ElasticSearchMatches } from '../Searchkit/ElasticSearchHighlights'; -import messages from '../../messages'; - -import config from '@plone/volto/registry'; - -const sort_caseinsensitive = (a, b) => { - var nameA = a.toUpperCase(); // Groß-/Kleinschreibung ignorieren - var nameB = b.toUpperCase(); // Groß-/Kleinschreibung ignorieren - if (nameA < nameB) { - return -1; - } - if (nameA > nameB) { - return 1; - } - return 0; -}; - -const _OnHighlights = (props) => { - let location = useLocation(); - let highlights = props.currentResultsState; - let hits = highlights.data.hits; - - const regex = /(.*?)<\/em>/gm; - let fragments = []; - hits.map((hit) => { - hit.highlight = hit.highlight || []; - Object.keys(hit.highlight).forEach((fld) => { - hit.highlight[fld].forEach((highlightfragment) => { - fragments.push(highlightfragment); - }); - }); - return null; - }); - let matches = new Set(); - fragments.forEach((txt) => { - let result = [...txt.matchAll(regex)]; - result.forEach((match) => { - matches.add(match[1]); - }); - }); - let matches_sorted = Array.from(matches); - matches_sorted.sort(sort_caseinsensitive); - return ( -
- {matches_sorted.length > 0 && ( -
{matches_sorted.length} matches found:
- )} - {matches_sorted.map((match) => ( - - ))} -
- ); -}; -const OnHighlights = withState(_OnHighlights); - -const OnResults = withState(ResultsMultiLayout); - -const CustomResultsListItem = ({ result, index }) => { - return ( - - ); -}; - -const DocumentsCount = ({ totalResults }) => { - return
{totalResults} documents found:
; -}; - -const overriddenComponents = { - 'ResultsList.item': CustomResultsListItem, - 'Count.element': DocumentsCount, -}; - -const TestSearchkitQuerystrings = (props) => { - const intl = useIntl(); - const history = useHistory(); - const searchconfig = config.blocks.blocksConfig.searchkitblock.searchconfig; - - const initialState = { - sortBy: 'bestmatch', - sortOrder: 'asc', - // sortBy: 'modified', - // sortOrder: 'desc', - queryString: '', - layout: 'list', - page: 1, - size: 50, - }; - - const onchangehandler = (event, data) => { - let searchQuery = { - ...initialState, - queryString: data.value, - }; - onQueryChanged({ searchQuery: searchQuery }); - return; - }; - - const locale = useSelector((state) => state.intl.locale); - const [isClient, setIsClient] = React.useState(null); - React.useEffect(() => setIsClient(true), []); - - return ( - - - -
- Matches -
-
- {isClient && ( - - - <> - - {/* { - onchangehandler(event, data); - }} - /> */} - { - onchangehandler(event, data); - }} - /> - - - - - - - - - - )} -
- - {isClient && - createPortal( - { - history.push(getParentUrl(location.pathname)); - }} - > - - - } - />, - document.getElementById('toolbar'), - )} -
- ); -}; - -export default TestSearchkitQuerystrings; diff --git a/src/components/Views/less/springisnow-volto-searchkit-block.less b/src/components/Views/less/springisnow-volto-searchkit-block.less deleted file mode 100644 index d06a1ae1..00000000 --- a/src/components/Views/less/springisnow-volto-searchkit-block.less +++ /dev/null @@ -1,296 +0,0 @@ -/* - * @rohberg/volto-searchkit-block - * springisnow-volto-searchkit-block.less - */ - -@type: 'extra'; -@element: 'custom'; - -@import (multiple) '../../theme.config'; - -.fnresults .ui.items > .item { - &.private > .content > a.header { - color: red; - } - - button, - .ui.button { - padding: 0; - border: none; - margin: 0.25rem 0.1rem 0.25rem 0em; - background-color: transparent; - color: rgba(0, 0, 0, 0.6); - cursor: pointer; - font-weight: bold; - - &:hover { - background-color: transparent; - } - } - - .extra { - color: @textColor; - - .label { - margin-right: 0.5em; - } - } -} - -.searchbar-wrapper { - display: flex; - align-items: stretch; - - .ui.fluid.action.input { - width: 50%; - @media only screen and (max-width: 767px) { - width: 100%; - } - - & > input { - width: unset !important; - flex: 1; - border-color: rgba(0, 0, 0, 0.13) !important; - border-radius: 0; - - &:focus { - border-color: #85b7d9 !important; - } - } - } - - .button { - border-radius: 0; - } - // Search button - .ui.action.input > button.ui.button { - display: none; - } - - .icon.delete { - display: flex; - width: 3rem; - min-width: 3rem; - height: 3rem; - align-items: center; - justify-content: center; - margin: 0; - background-color: #878686; - color: #fff; - cursor: pointer; - font-size: 1.2rem; - font-weight: 700; - - &.unselected { - opacity: 0; - } - } -} - -// .ui.grid > .row > .column.facetedsearch_filter { -.facetedsearch_filter { - opacity: 1; - transition: - opacity 1s, - visibility 1s; - visibility: visible; - - &.cards { - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-items: baseline; - justify-content: space-evenly; - - .ui.card { - margin: 1em 0.5em 1em 0em; - } - } - - &.dropdown { - .bucketaggregations { - display: grid; - width: 100%; - gap: 10px; - grid-template-columns: 1fr 1fr; - @media only screen and (max-width: 767px) { - grid-template-columns: 1fr; - } - - .bucketAE { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: space-between; - // width: 50%; - .ui.dropdown.fnfilter { - display: flex; - min-height: 3rem; - align-items: center; - justify-content: space-between; - // padding: 0 0 0 .3rem; - padding: 0.7rem 1rem 0.4rem 1rem; - margin: 0; - background-color: #edf1f2; - font-family: @fontName; - font-size: 1rem; - - & > .dropdown.icon { - font-size: 1rem; - } - - &.selected { - background-color: #c4d5da; - color: @black; - - .text { - max-width: 95%; - } - } - - .menu { - width: 100%; - border: none; - background-color: #edf1f2; - box-shadow: none; - - & > .item { - padding: 0.4rem 1.4rem 0.4rem 1rem !important; - line-height: 1.4em; - // color: @white; - & .item.isSelected { - font-weight: bold; - - svg { - stroke: currentColor; - stroke-width: 3px; - } - } - } - } - - div.visible .menu { - display: block !important; - visibility: visible !important; - } - - // Hack for dropdown with scrollbar - &.scrolloptions { - .menu > .item { - padding-right: 0.9rem !important; - } - } - } - - .deleteFilter { - display: flex; - width: 3rem; - min-width: 3rem; - height: 3rem; - align-items: center; - justify-content: center; - margin: 0; - background-color: #878686; - color: #fff; - cursor: pointer; - font-size: 1.2rem; - font-weight: 700; - - &.unselected { - opacity: 0; - } - } - } - } - } -} -// No filter according to block configuration -body.section_without_filter .facetedsearch_filter { - opacity: 0; - visibility: hidden; -} - -.searchsections { - display: flex; - gap: 20px; - - button { - padding: 0.7rem 1rem 0.7rem 1rem; - border: none; - background-color: #f3f1f1; - color: #6e6a6a; - cursor: pointer; - - &.active { - background-color: #dfd8d8; - } - } -} -// Mobile -@media only screen and (max-width: 767px) { - .searchsections { - flex-direction: column; - gap: 0.5rem; - - button { - display: inline-block; - } - } -} - -.countlabel { - font-weight: bold; -} - -.sortby { - display: flex; - align-items: baseline; - gap: 5px; - - .ui.button { - padding: 0; - background-color: transparent; - color: rgba(70, 92, 98, 0.77); - font-weight: normal; - - &.button-active { - color: @textColor; - font-weight: bold; - } - - &:hover { - background-color: transparent; - color: @textColor; - } - } -} - -// No results -.ui.placeholder.segment .search.icon { - display: none; -} - -// Loading results -.resultsPlaceholder { - height: 10rem; -} - -.highlight { - margin-top: 0.5rem; - cursor: zoom-in; - - div { - margin-bottom: 0.5rem; - } - - em { - background-color: #e6f2ce; - } -} - -// debug -body.section-test-searchkit-querystrings { - .ui.segment.top-wrapper { - display: none; - } -} diff --git a/src/components/helpers.js b/src/components/helpers.js deleted file mode 100644 index 5b82632d..00000000 --- a/src/components/helpers.js +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import config from '@plone/volto/registry'; - -class NoSSR extends React.Component { - state = { - isClient: false, - }; - componentDidMount() { - this.setState({ isClient: true }); - } - render() { - const { isClient } = this.state; - const { children } = this.props; - return isClient ? children : null; - } -} - -// TODO replace ugly flattenESUrlToPath hack. Problem Elastic responds with backend Plone url which is host.docker…. -/** - * flatten url to path if internal, else leave it as it is - * @param {String} url - * @returns path - * - * "http://host.docker.internal:17091/Plone/news/sprint-on-accessibility" - * -> - * "/news/sprint-on-accessibility" - */ -function flattenESUrlToPath(url) { - if (url.startsWith('https')) { - // external url - return url; - } - - const urlObj = new URL(url); - const urlArray = urlObj.pathname.split('/').reverse(); - urlArray.pop(); - urlArray.pop(); - const newPathname = `/${urlArray.reverse().join('/')}`; - return newPathname; -} - -const scrollToTarget = (target, offsetHeight = 0) => { - target.scrollIntoView({ - behavior: 'smooth', - }); -}; - -/** - * @param {Array} objlst array of Objects with ... - * @returns Object with fieldname as key and title as value - */ -function getObjectFromObjectList(objlst) { - let obj = {}; - if (!objlst) { - return {}; - } - objlst.forEach((listitem) => { - if (listitem.field) { - obj[listitem.field.value] = listitem.title; - } - }); - return obj; -} - -export { NoSSR, flattenESUrlToPath, scrollToTarget, getObjectFromObjectList }; diff --git a/src/components/helpers.test.js b/src/components/helpers.test.js deleted file mode 100644 index 3887b2ce..00000000 --- a/src/components/helpers.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import config from '@plone/volto/registry'; - -import { flattenESUrlToPath } from './helpers'; - -beforeEach(() => { - config.settings.legacyTraverse = false; -}); - -const { settings } = config; - -describe('helpers', () => { - describe('flattenESUrlToPath', () => { - it('flattens a given URL to the app URL', () => { - expect( - flattenESUrlToPath( - 'http://host.docker.internal:17091/Plone/news/sprint-on-accessibility', - ), - ).toBe('/news/sprint-on-accessibility'); - expect(flattenESUrlToPath('https://nzz.ch/arosa/piste')).toBe( - 'https://nzz.ch/arosa/piste', - ); - }); - }); -}); diff --git a/src/components/index.js b/src/components/index.js deleted file mode 100644 index 73016cda..00000000 --- a/src/components/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import FacetedSearchBlockEdit from './Blocks/FacetedSearchBlockEdit'; -import FacetedSearchBlockView from './Blocks/FacetedSearchBlockView'; -import TestSearchkitQuerystrings from './Views/TestSearchkitQuerystrings'; - -export { - FacetedSearchBlockEdit, - FacetedSearchBlockView, - TestSearchkitQuerystrings, -}; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index cb15d57f..00000000 --- a/src/index.js +++ /dev/null @@ -1,119 +0,0 @@ -import zoomSVG from '@plone/volto/icons/zoom.svg'; -import { getQuerystring } from '@plone/volto/actions'; - -import { - FacetedSearchBlockEdit, - FacetedSearchBlockView, - TestSearchkitQuerystrings, -} from './components'; -import { - ReferenceSearchBlockEdit, - ReferenceSearchBlockView, -} from './components/Blocks/Reference'; - -import SearchSectionsWidget from './components/Blocks/SearchSectionsWidget'; - -const applyConfig = (config) => { - config.settings.searchkitblock = { - trackVoltoMatomo: false, - }; - - config.blocks.blocksConfig.searchkitblock = { - id: 'searchkitblock', - title: 'Searchkit', - edit: FacetedSearchBlockEdit, - view: FacetedSearchBlockView, - icon: zoomSVG, - group: 'common', - restricted: false, - mostUsed: false, - sidebarTab: 1, - security: { - addPermission: [], - view: [], - }, - }; - - config.widgets.widget.searchsectionswidget = SearchSectionsWidget; - - /** - * A reference block with default components from react-searchkit - * TODO set permission to restrict to admin as soon as addPermission is implemented in Volto. For now: set restricted to true. - */ - config.blocks.blocksConfig.referencesearchkitblock = { - id: 'referencesearchkitblock', - title: 'Search Reference', - edit: ReferenceSearchBlockEdit, - view: ReferenceSearchBlockView, - icon: zoomSVG, - group: 'text', - restricted: true, - mostUsed: true, - sidebarTab: 1, - security: { - addPermission: [], - view: [], - }, - }; - - // Test some querystrings - config.settings.controlpanels = [ - ...(config.settings.controlpanels || []), - { - '@id': '/test-searchkit-querystrings', - group: 'Add-on Configuration', - title: 'Test searchkit querystrings', - }, - ]; - config.addonRoutes = [ - ...config.addonRoutes, - { - path: '/controlpanel/test-searchkit-querystrings', - component: TestSearchkitQuerystrings, - }, - ]; - // Configure 'Test searchkit querystrings' controlpanel - config.blocks.blocksConfig.searchkitblock.searchconfig = { - searchedFields: [ - 'title^1.4', - 'description^1.2', - 'blocks_plaintext', - 'subjects^1.2', - ], - facet_fields: [], - allowed_content_types: ['Document', 'News Item', 'Event'], - allowed_review_states: [], - // backend_url: 'http://host.docker.internal:8080/Plone', - // frontend_url: 'http://localhost:3000', - }; - - // Fetch querystring indexes. - // See /effective-volto/addons/asyncconnect - config.settings.asyncPropsExtenders = [ - ...(config.settings.asyncPropsExtenders || []), - { - path: '/', - extend: (dispatchActions) => { - const action = { - key: 'querystringindexes', - promise: ({ store }) => { - const state = store.getState(); - if (state.querystring?.indexes?.Title) { - return; - } - const myaction = getQuerystring(); - return store.dispatch(myaction).catch((e) => { - // eslint-disable-next-line no-console - console.error('Fetch of getQuerystring failed'); - }); - }, - }; - return [...dispatchActions, action]; - }, - }, - ]; - - return config; -}; - -export default applyConfig; diff --git a/src/messages.js b/src/messages.js deleted file mode 100644 index 30a595c9..00000000 --- a/src/messages.js +++ /dev/null @@ -1,104 +0,0 @@ -import { defineMessages } from 'react-intl'; - -const messages = defineMessages({ - // block title translation for slash menu - blocktitle: { - id: 'Searchkit', - defaultMessage: 'Searchkit', - }, - searchBlock: { - id: 'Search block', - defaultMessage: 'Search block', - }, - search: { - id: 'search', - defaultMessage: 'search', - }, - facets: { - id: 'Facets', - defaultMessage: 'Facets', - }, - facet: { - id: 'Facet', - defaultMessage: 'Facet', - }, - // item: { - // id: 'Item', - // defaultMessage: 'Item', - // }, - label: { - id: 'Label', - defaultMessage: 'Label', - }, - field: { - id: 'Field', - defaultMessage: 'Field', - }, - multipleChoices: { - id: 'Multiple choices?', - defaultMessage: 'Multiple choices?', - }, - facetWidget: { - id: 'Facet widget', - defaultMessage: 'Facet widget', - }, - metadata: { - id: 'Meta data', - defaultMessage: 'Meta data', - }, - searchresult: { - id: 'Search result', - defaultMessage: 'Search result', - }, - searchresults: { - id: 'Search results', - defaultMessage: 'Search results', - }, - searchInSections: { - id: 'Search in sections', - defaultMessage: 'Search in sections', - }, - searchInSectionsDescription: { - id: 'Search can be restricted by sections / paths', - defaultMessage: 'Search can be restricted by sections / paths', - }, - searchSection: { - id: 'Search section', - defaultMessage: 'section', - }, - searchSectionLabel: { - id: 'Search section label', - defaultMessage: 'Search section label', - }, - showFilter: { - id: 'Show filters', - defaultMessage: 'Show filter', - }, - // highlights - title: { - id: 'Title', - defaultMessage: 'Title', - }, - description: { - id: 'Description', - defaultMessage: 'Description', - }, - tags: { - id: 'Tags', - defaultMessage: 'Tags', - }, - content: { - id: 'Content', - defaultMessage: 'Content', - }, - cancel: { - id: 'Cancel', - defaultMessage: 'Cancel', - }, - add: { - id: 'Add {type}', - defaultMessage: 'Add {type}', - }, -}); - -export default messages; From 31e71bd553be96734caa80b4416181b4c854b6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:40:23 +0200 Subject: [PATCH 165/226] New project package.json --- package.json | 70 +++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index 98aaa440..a1f4bea7 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,44 @@ { - "name": "@rohberg/volto-searchkit-block", - "version": "0.4.0", - "description": "Find content. Pardon typos. Allow search queries with and provide results with compound words.", - "main": "src/index.js", - "author": "Katja Süss, https://github.com/rohberg", - "license": "MIT", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, + "name": "@rohberg/volto-searchkit-block-dev", + "version": "1.0.0-alpha.0", + "description": "Search with OpenSearch", + "author": "Katja Süss", "homepage": "https://github.com/rohberg/volto-searchkit-block", + "license": "MIT", "keywords": [ "volto-addon", "volto", "plone", - "react", - "search", - "find", - "opensearch", - "elasticsearch" + "react" ], - "repository": { - "type": "git", - "url": "git@github.com:rohberg/volto-searchkit-block.git" - }, - "bugs": { - "url": "https://github.com/rohberg/volto-searchkit-block/issues" - }, "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" + "preinstall": "npx only-allow pnpm", + "start": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto start", + "start:prod": "pnpm --filter @plone/volto start:prod", + "build": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build", + "build:deps": "pnpm --filter @plone/registry --filter @plone/components build", + "i18n": "pnpm --filter @rohberg/volto-searchkit-block i18n", + "test": "RAZZLE_JEST_CONFIG=$(pwd)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests", + "lint": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --max-warnings=0 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "lint:fix": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --fix 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "prettier": "prettier --check 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "prettier:fix": "prettier --write 'packages/**/src/**/*.{js,jsx,ts,tsx}' ", + "stylelint": "stylelint 'packages/**/src/**/*.{css,scss,less}' --allow-empty-input", + "stylelint:fix": "stylelint 'packages/**/src/**/*.{css,scss,less}' --fix --allow-empty-input", + "dry-release": "pnpm --filter @rohberg/volto-searchkit-block dry-release", + "release": "pnpm --filter @rohberg/volto-searchkit-block release", + "release-major-alpha": "pnpm --filter @rohberg/volto-searchkit-block release-major-alpha", + "release-alpha": "pnpm --filter @rohberg/volto-searchkit-block release-alpha", + "storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto storybook dev -p 6006 -c $(pwd)/.storybook", + "build-storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build-storybook -c $(pwd)/.storybook" }, - "addons": [ - "@eeacms/volto-matomo" - ], "dependencies": { - "@eeacms/volto-matomo": "*", - "react-overridable": "^0.0.3", - "react-searchkit": "v2.2.0" + "@plone/volto": "workspace:*", + "@plone/registry": "workspace:*", + "@rohberg/volto-searchkit-block": "workspace:*" }, "devDependencies": { - "@plone/scripts": "^3.6.2", - "postcss-less": "6.0.0", - "postcss-scss": "4.0.8", - "release-it": "^17.1.1" - } + "mrs-developer": "^2.2.0" + }, + "packageManager": "pnpm@9.1.1" } From 4fcf9cddc9fa90d2bc15d02b4879581aee6dd0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:40:53 +0200 Subject: [PATCH 166/226] Volto checked out with version tag --- mrs.developer.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 mrs.developer.json diff --git a/mrs.developer.json b/mrs.developer.json new file mode 100644 index 00000000..87bf82a5 --- /dev/null +++ b/mrs.developer.json @@ -0,0 +1,9 @@ +{ + "core": { + "output": "./", + "package": "@plone/volto", + "url": "git@github.com:plone/volto.git", + "https": "https://github.com/plone/volto.git", + "tag": "18.0.0-alpha.35" + } +} From e9e831f9a13783f555f0c7cafcf0da4344a86ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:41:32 +0200 Subject: [PATCH 167/226] Remove locales from project --- locales/de/LC_MESSAGES/volto.po | 168 ------------------------------- locales/en/LC_MESSAGES/volto.po | 168 ------------------------------- locales/volto.pot | 170 -------------------------------- 3 files changed, 506 deletions(-) delete mode 100644 locales/de/LC_MESSAGES/volto.po delete mode 100644 locales/en/LC_MESSAGES/volto.po delete mode 100644 locales/volto.pot diff --git a/locales/de/LC_MESSAGES/volto.po b/locales/de/LC_MESSAGES/volto.po deleted file mode 100644 index 07eef637..00000000 --- a/locales/de/LC_MESSAGES/volto.po +++ /dev/null @@ -1,168 +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" - -#. Default: "Add {type}" -#: messages -msgid "Add {type}" -msgstr "" - -#. Default: "Cancel" -#: messages -msgid "Cancel" -msgstr "" - -#. Default: "Check the configuration of your searchkit block!" -#: components/Searchkit/Error -msgid "Check the configuration of your searchkit block!" -msgstr "Prüfe Block-Einstellungen!" - -#. Default: "Content" -#: messages -msgid "Content" -msgstr "Inhalt" - -#. Default: "Date" -#: components/Views/FacetedSearch -msgid "Date" -msgstr "Datum" - -#. Default: "Description" -#: messages -msgid "Description" -msgstr "Beschreibung" - -#. Default: "Deselect all" -#: components/Views/FacetedSearch -msgid "Deselect all" -msgstr "keine" - -#. Default: "Facet" -#: messages -msgid "Facet" -msgstr "Facette" - -#. Default: "Facet widget" -#: messages -msgid "Facet widget" -msgstr "Facetten-Widget" - -#. Default: "Facets" -#: messages -msgid "Facets" -msgstr "Facetten" - -#. Default: "Field" -#: messages -msgid "Field" -msgstr "Feld" - -#. Default: "Label" -#: messages -msgid "Label" -msgstr "Label" - -#. Default: "Meta data" -#: messages -msgid "Meta data" -msgstr "Metadaten" - -#. Default: "Multiple choices?" -#: messages -msgid "Multiple choices?" -msgstr "Multiple Choices?" - -#. Default: "No results" -#: components/Views/FacetedSearch -msgid "No results" -msgstr "Keine Resultate" - -#. Default: "Relevance" -#: components/Views/FacetedSearch -msgid "Relevance" -msgstr "Relevanz" - -#. Default: "Search block" -#: messages -msgid "Search block" -msgstr "Such-Block" - -#. Default: "Search can be restricted by sections / paths" -#: messages -msgid "Search can be restricted by sections / paths" -msgstr "Die Suche kann auf Sektionen / Pfade eingeschränkt werden" - -#. Default: "Search in sections" -#: messages -msgid "Search in sections" -msgstr "Suche in Sektionen" - -#. Default: "Search result" -#: messages -msgid "Search result" -msgstr "Suchergebnis" - -#. Default: "Search results" -#: messages -msgid "Search results" -msgstr "Suchergebnisse" - -#. Default: "section" -#: messages -msgid "Search section" -msgstr "Sektion" - -#. Default: "Search section label" -#: messages -msgid "Search section label" -msgstr "Label Such-Sektion" - -#. Default: "Searchkit" -#: messages -msgid "Searchkit" -msgstr "Searchkit" - -#. Default: "Select all" -#: components/Views/FacetedSearch -msgid "Select all" -msgstr "Wähle alle" - -#. Default: "Show filter" -#: messages -msgid "Show filters" -msgstr "Zeige Filter" - -#. Default: "Sort by:" -#: components/Views/FacetedSearch -msgid "Sort By:" -msgstr "Sortiere nach:" - -#. Default: "Tags" -#: components/Views/FacetedSearch -#: messages -msgid "Tags" -msgstr "Tags" - -#. Default: "Title" -#: messages -msgid "Title" -msgstr "Titel" - -#. Default: "reset search" -#: components/Views/FacetedSearch -msgid "reset search" -msgstr "Suche zurücksetzen" - -#. Default: "search" -#: messages -msgid "search" -msgstr "suchen" diff --git a/locales/en/LC_MESSAGES/volto.po b/locales/en/LC_MESSAGES/volto.po deleted file mode 100644 index 6b178ea1..00000000 --- a/locales/en/LC_MESSAGES/volto.po +++ /dev/null @@ -1,168 +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" - -#. Default: "Add {type}" -#: messages -msgid "Add {type}" -msgstr "" - -#. Default: "Cancel" -#: messages -msgid "Cancel" -msgstr "" - -#. Default: "Check the configuration of your searchkit block!" -#: components/Searchkit/Error -msgid "Check the configuration of your searchkit block!" -msgstr "" - -#. Default: "Content" -#: messages -msgid "Content" -msgstr "" - -#. Default: "Date" -#: components/Views/FacetedSearch -msgid "Date" -msgstr "" - -#. Default: "Description" -#: messages -msgid "Description" -msgstr "" - -#. Default: "Deselect all" -#: components/Views/FacetedSearch -msgid "Deselect all" -msgstr "" - -#. Default: "Facet" -#: messages -msgid "Facet" -msgstr "" - -#. Default: "Facet widget" -#: messages -msgid "Facet widget" -msgstr "" - -#. Default: "Facets" -#: messages -msgid "Facets" -msgstr "" - -#. Default: "Field" -#: messages -msgid "Field" -msgstr "" - -#. Default: "Label" -#: messages -msgid "Label" -msgstr "" - -#. Default: "Meta data" -#: messages -msgid "Meta data" -msgstr "" - -#. Default: "Multiple choices?" -#: messages -msgid "Multiple choices?" -msgstr "" - -#. Default: "No results" -#: components/Views/FacetedSearch -msgid "No results" -msgstr "" - -#. Default: "Relevance" -#: components/Views/FacetedSearch -msgid "Relevance" -msgstr "" - -#. Default: "Search block" -#: messages -msgid "Search block" -msgstr "" - -#. Default: "Search can be restricted by sections / paths" -#: messages -msgid "Search can be restricted by sections / paths" -msgstr "" - -#. Default: "Search in sections" -#: messages -msgid "Search in sections" -msgstr "" - -#. Default: "Search result" -#: messages -msgid "Search result" -msgstr "" - -#. Default: "Search results" -#: messages -msgid "Search results" -msgstr "" - -#. Default: "section" -#: messages -msgid "Search section" -msgstr "" - -#. Default: "Search section label" -#: messages -msgid "Search section label" -msgstr "" - -#. Default: "Searchkit" -#: messages -msgid "Searchkit" -msgstr "" - -#. Default: "Select all" -#: components/Views/FacetedSearch -msgid "Select all" -msgstr "" - -#. Default: "Show filter" -#: messages -msgid "Show filters" -msgstr "" - -#. Default: "Sort by:" -#: components/Views/FacetedSearch -msgid "Sort By:" -msgstr "" - -#. Default: "Tags" -#: components/Views/FacetedSearch -#: messages -msgid "Tags" -msgstr "" - -#. Default: "Title" -#: messages -msgid "Title" -msgstr "" - -#. Default: "reset search" -#: components/Views/FacetedSearch -msgid "reset search" -msgstr "" - -#. Default: "search" -#: messages -msgid "search" -msgstr "" diff --git a/locales/volto.pot b/locales/volto.pot deleted file mode 100644 index caa1758c..00000000 --- a/locales/volto.pot +++ /dev/null @@ -1,170 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-06-20T18:55:48.220Z\n" -"Last-Translator: Plone i18n \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: en\n" -"Language-Name: English\n" -"Preferred-Encodings: utf-8\n" -"Domain: volto\n" - -#. Default: "Add {type}" -#: messages -msgid "Add {type}" -msgstr "" - -#. Default: "Cancel" -#: messages -msgid "Cancel" -msgstr "" - -#. Default: "Check the configuration of your searchkit block!" -#: components/Searchkit/Error -msgid "Check the configuration of your searchkit block!" -msgstr "" - -#. Default: "Content" -#: messages -msgid "Content" -msgstr "" - -#. Default: "Date" -#: components/Views/FacetedSearch -msgid "Date" -msgstr "" - -#. Default: "Description" -#: messages -msgid "Description" -msgstr "" - -#. Default: "Deselect all" -#: components/Views/FacetedSearch -msgid "Deselect all" -msgstr "" - -#. Default: "Facet" -#: messages -msgid "Facet" -msgstr "" - -#. Default: "Facet widget" -#: messages -msgid "Facet widget" -msgstr "" - -#. Default: "Facets" -#: messages -msgid "Facets" -msgstr "" - -#. Default: "Field" -#: messages -msgid "Field" -msgstr "" - -#. Default: "Label" -#: messages -msgid "Label" -msgstr "" - -#. Default: "Meta data" -#: messages -msgid "Meta data" -msgstr "" - -#. Default: "Multiple choices?" -#: messages -msgid "Multiple choices?" -msgstr "" - -#. Default: "No results" -#: components/Views/FacetedSearch -msgid "No results" -msgstr "" - -#. Default: "Relevance" -#: components/Views/FacetedSearch -msgid "Relevance" -msgstr "" - -#. Default: "Search block" -#: messages -msgid "Search block" -msgstr "" - -#. Default: "Search can be restricted by sections / paths" -#: messages -msgid "Search can be restricted by sections / paths" -msgstr "" - -#. Default: "Search in sections" -#: messages -msgid "Search in sections" -msgstr "" - -#. Default: "Search result" -#: messages -msgid "Search result" -msgstr "" - -#. Default: "Search results" -#: messages -msgid "Search results" -msgstr "" - -#. Default: "section" -#: messages -msgid "Search section" -msgstr "" - -#. Default: "Search section label" -#: messages -msgid "Search section label" -msgstr "" - -#. Default: "Searchkit" -#: messages -msgid "Searchkit" -msgstr "" - -#. Default: "Select all" -#: components/Views/FacetedSearch -msgid "Select all" -msgstr "" - -#. Default: "Show filter" -#: messages -msgid "Show filters" -msgstr "" - -#. Default: "Sort by:" -#: components/Views/FacetedSearch -msgid "Sort By:" -msgstr "" - -#. Default: "Tags" -#: components/Views/FacetedSearch -#: messages -msgid "Tags" -msgstr "" - -#. Default: "Title" -#: messages -msgid "Title" -msgstr "" - -#. Default: "reset search" -#: components/Views/FacetedSearch -msgid "reset search" -msgstr "" - -#. Default: "search" -#: messages -msgid "search" -msgstr "" From 8ad07ec3855cada02018342dd700875058246b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:42:14 +0200 Subject: [PATCH 168/226] Delete LICENSE --- LICENSE | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 71c2d871..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (C) 2022 Rohberg, Zürich. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From 0bcc3aa8cade9266a253f5152bdcf27c982aa5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:44:50 +0200 Subject: [PATCH 169/226] Create .pre-commit-config.yaml --- .pre-commit-config.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..3c7d331c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +repos: + - repo: local + hooks: + - id: prettier + name: prettier + entry: pnpm exec prettier --write + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: eslint + name: eslint + entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: stylelint + name: stylelint + entry: pnpm exec stylelint --fix + language: system + files: '^packages/.*/src/.*/?.*.(css|scss|less)$' + types: [file] + - id: i18n + name: i18n + entry: make ci-i18n + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] From 1e895eace68b5ad51f1f41e470f9d872468dba17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:45:20 +0200 Subject: [PATCH 170/226] storybook initial --- .storybook/main.js | 188 +++++++++++++++++++++++++++++++++++++++++ .storybook/preview.jsx | 26 ++++++ 2 files changed, 214 insertions(+) create mode 100644 .storybook/main.js create mode 100644 .storybook/preview.jsx diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000..1b15d3b2 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,188 @@ +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('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 = { + stories: [ + '../packages/**/*.mdx', + '../packages/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-webpack5-compiler-babel', + ], + framework: { + name: '@storybook/react-webpack5', + options: { builder: { useSWC: true } }, + }, + typescript: { + check: false, + checkOptions: {}, + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + compilerOptions: { + allowSyntheticDefaultImports: false, + esModuleInterop: false, + }, + propFilter: () => true, + }, + }, + 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/registry/src/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 }, + }, + }; + + // Add-ons have to be loaded with babel + const addonPaths = registry + .getAddons() + .map((addon) => fs.realpathSync(addon.modulePath)); + + resultConfig.module.rules[13].exclude = (input) => + // exclude every input from node_modules except from @plone/volto + /node_modules\/(?!(@plone\/volto)\/)/.test(input) && + // Storybook default exclusions + /storybook-config-entry\.js$/.test(input) && + /storybook-stories\.js$/.test(input) && + // If input is in an addon, DON'T exclude it + !addonPaths.some((p) => input.includes(p)); + + resultConfig.module.rules[13].include = [ + /preview\.jsx/, + ...resultConfig.module.rules[13].include, + ...addonPaths, + ]; + + 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; + }, +}; diff --git a/.storybook/preview.jsx b/.storybook/preview.jsx new file mode 100644 index 00000000..1d2ac84b --- /dev/null +++ b/.storybook/preview.jsx @@ -0,0 +1,26 @@ +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'; +import enMessages from '@root/../locales/en.json'; + +import '@root/theme'; + +export const parameters = { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; + +export const decorators = [ + (Story) => ( + + + + + + ), +]; From 34b9e24aaf3cc9ef40cbf620396e3f16fee0ae8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 10:45:55 +0200 Subject: [PATCH 171/226] new cypress initial --- cypress.config.js | 13 +++++++++++++ cypress/.gitkeep | 0 cypress/support/commands.js | 1 + cypress/support/e2e.js | 15 +++++++++++++++ cypress/tests/.gitkeep | 0 cypress/tests/example.cy.js | 20 ++++++++++++++++++++ 6 files changed, 49 insertions(+) create mode 100644 cypress.config.js create mode 100644 cypress/.gitkeep create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/e2e.js create mode 100644 cypress/tests/.gitkeep create mode 100644 cypress/tests/example.cy.js diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 00000000..dba4b580 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,13 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + viewportWidth: 1280, + viewportHeight: 1280, + retries: { + runMode: 3, + }, + e2e: { + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}', + }, +}); diff --git a/cypress/.gitkeep b/cypress/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 00000000..6a44064f --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1 @@ +import '@plone/volto/cypress/add-commands'; diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js new file mode 100644 index 00000000..4ff23d6d --- /dev/null +++ b/cypress/support/e2e.js @@ -0,0 +1,15 @@ +import 'cypress-axe'; +import 'cypress-file-upload'; +import './commands'; +import 'cypress-axe'; +import { setup, teardown } from '@plone/volto/cypress/support/reset-fixture'; + +beforeEach(function () { + cy.log('Setting up API fixture'); + setup(); +}); + +afterEach(function () { + cy.log('Tearing down API fixture'); + teardown(); +}); diff --git a/cypress/tests/.gitkeep b/cypress/tests/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/cypress/tests/example.cy.js b/cypress/tests/example.cy.js new file mode 100644 index 00000000..f3a1fadf --- /dev/null +++ b/cypress/tests/example.cy.js @@ -0,0 +1,20 @@ +context('Example Acceptance Tests', () => { + describe('Visit a page', () => { + beforeEach(() => { + // Given a logged in editor + cy.viewport('macbook-16'); + cy.createContent({ + contentType: 'Document', + contentId: 'document', + contentTitle: 'Test document', + }); + cy.autologin(); + }); + + it('As editor I can add edit a Page', function () { + cy.visit('/document'); + cy.navigate('/document/edit'); + cy.get('#toolbar-save').click(); + }); + }); +}); From e4b92618a848eb189285ad63355e6fdca11db152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 12:22:28 +0200 Subject: [PATCH 172/226] github workflow except acceptance --- .github/workflows/changelog.yml | 35 +++++++++++++------ .github/workflows/code.yml | 59 +++++++++++++++++-------------- .github/workflows/i18n.yml | 47 +++++++++++++++++++++++++ .github/workflows/storybook.yml | 57 ++++++++++++++++++++++++++++++ .github/workflows/unit.yml | 62 ++++++++++++++++++--------------- 5 files changed, 195 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/i18n.yml create mode 100644 .github/workflows/storybook.yml diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index f8f99a5a..afc44097 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -6,7 +6,8 @@ on: - main env: - node-version: 20.x + NODE_VERSION: 20.x + ADDON_NAME: volto-searchkit-block jobs: build: @@ -20,24 +21,38 @@ jobs: - name: Install pipx run: pip install towncrier - # node setup - - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v3 + - name: Use Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ env.node-version }} - cache: 'yarn' + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- - # node install - name: Install dependencies - run: yarn + run: | + make install - 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 + git fetch --no-tags origin ${BASE_BRANCH} + towncrier check --dir packages/${ADDON_NAME} env: BASE_BRANCH: ${{ github.base_ref }} if: github.event_name == 'pull_request' diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 9bc221c1..79f96f43 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -1,40 +1,47 @@ name: Code analysis checks -on: [push] +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/code.yml" env: - ADDON_NAME: '@rohberg/volto-searchkit-block' - ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 - PLONE_VERSION: 6.0 - - INDEX_SERVER: opensearch:9200 - INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 0 - INDEX_LOGIN: admin - INDEX_PASSWORD: 'oxczBG).3xWyapLn' - - CELERY_BROKER: redis://redis:6379/0 - CELERY_LOGLEVEL: info - - PLONE_SERVICE: http://backend-acceptance:55001 - PLONE_SITE_PREFIX_PATH: plone - PLONE_USER: admin - PLONE_PASSWORD: secret - - MAPPINGS_FILE: /configuration/mappings.json - ANALYSIS_FILE: /configuration/analysis.json - PREPROCESSINGS_FILE: /configuration/preprocessings.json + NODE_VERSION: 20.x jobs: codeanalysis: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] steps: - name: Main checkout uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + - name: Linting run: make lint diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml new file mode 100644 index 00000000..c8e463b1 --- /dev/null +++ b/.github/workflows/i18n.yml @@ -0,0 +1,47 @@ +name: i18n +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/i18n.yml" + +env: + NODE_VERSION: 20.x + +jobs: + unit: + runs-on: ubuntu-latest + + steps: + - name: Main checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: test i18n command + run: make i18n diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml new file mode 100644 index 00000000..32a8b60f --- /dev/null +++ b/.github/workflows/storybook.yml @@ -0,0 +1,57 @@ +name: Storybook +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/storybook.yml" + +env: + NODE_VERSION: 20.x + +permissions: + contents: write + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: Generate Storybook + run: | + make storybook-build + + - name: Deploy to GitHub pages + uses: JamesIves/github-pages-deploy-action@v4 + if: ${{ github.ref == 'refs/heads/main' }} + with: + branch: gh-pages + folder: .storybook-build diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c5363526..0de7eba1 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -1,43 +1,47 @@ name: Unit Tests -on: [push] +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/unit.yml" env: - ADDON_NAME: '@rohberg/volto-searchkit-block' - ADDON_PATH: volto-searchkit-block - VOLTO_VERSION: 17 - PLONE_VERSION: 6.0 - - INDEX_SERVER: opensearch:9200 - INDEX_OPENSEARCH: 1 - INDEX_USE_SSL: 0 - INDEX_LOGIN: admin - INDEX_PASSWORD: 'oxczBG).3xWyapLn' - - CELERY_BROKER: redis://redis:6379/0 - CELERY_LOGLEVEL: info - - PLONE_SERVICE: http://backend-acceptance:55001 - PLONE_SITE_PREFIX_PATH: plone - PLONE_USER: admin - PLONE_PASSWORD: secret - - MAPPINGS_FILE: /configuration/mappings.json - ANALYSIS_FILE: /configuration/analysis.json - PREPROCESSINGS_FILE: /configuration/preprocessings.json + NODE_VERSION: 20.x jobs: unit: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] - volto-version: [17] steps: - name: Main checkout uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + - name: Unit tests - # TODO do 'docker compose with ' env: VOLTO_VERSION: ${{ matrix.volto-version }} - # TODO Remove matrix option node-version run: make test-ci From 079532584ff020404548fc9d4233e22bf4494a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:42:29 +0200 Subject: [PATCH 173/226] Remove old tests --- .../cypress/tests/basic.monolingual.cy.js | 43 ----- .../tests/create_search.monolingual.cy.js | 72 -------- .../cypress/tests/language.multilingual.cy.js | 71 -------- .../cypress/tests/results.monolingual.cy.js | 76 -------- .../tests/search.anonymous.multilingual.cy.js | 97 ---------- .../cypress/tests/search.monolingual.cy.js | 156 ----------------- .../cypress/tests/search.multilingual.cy.js | 165 ------------------ 7 files changed, 680 deletions(-) delete mode 100644 acceptance/cypress/tests/basic.monolingual.cy.js delete mode 100644 acceptance/cypress/tests/create_search.monolingual.cy.js delete mode 100644 acceptance/cypress/tests/language.multilingual.cy.js delete mode 100644 acceptance/cypress/tests/results.monolingual.cy.js delete mode 100644 acceptance/cypress/tests/search.anonymous.multilingual.cy.js delete mode 100644 acceptance/cypress/tests/search.monolingual.cy.js delete mode 100644 acceptance/cypress/tests/search.multilingual.cy.js diff --git a/acceptance/cypress/tests/basic.monolingual.cy.js b/acceptance/cypress/tests/basic.monolingual.cy.js deleted file mode 100644 index 2fa0c728..00000000 --- a/acceptance/cypress/tests/basic.monolingual.cy.js +++ /dev/null @@ -1,43 +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'); - }); - }); -}); diff --git a/acceptance/cypress/tests/create_search.monolingual.cy.js b/acceptance/cypress/tests/create_search.monolingual.cy.js deleted file mode 100644 index 703d7496..00000000 --- a/acceptance/cypress/tests/create_search.monolingual.cy.js +++ /dev/null @@ -1,72 +0,0 @@ -describe('Searchkit block tests- create search ', () => { - before(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'searching', - contentTitle: 'Search', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'garden-blog', - contentTitle: 'Garden blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'garden-february', - contentTitle: 'The garden in february', - path: '/garden-blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'garden-march', - contentTitle: 'The garden in march', - path: '/garden-blog', - }); - - cy.visit('/'); - cy.wait('@content'); - }); - - beforeEach(() => { - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.intercept('GET', '/**/Document').as('schema'); - - cy.autologin(); - - cy.visit('/'); - cy.wait('@content'); - }); - - after(() => { - cy.removeContent({ path: 'garden-blog/garden-february' }); - cy.removeContent({ path: 'garden-blog/garden-march' }); - cy.removeContent({ path: 'garden-blog' }); - cy.removeContent({ path: 'searching' }); - }); - - it('As manager I can add a searchkit-block and find a document', function () { - cy.visit('/searching'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Ausklappen Common blocks"]').click(); - cy.get('.blocks-chooser .common .button.searchkitblock').click({ - force: true, - }); - - cy.get('#toolbar-save').click(); - cy.visit('/searching'); - - cy.get('.block.searchkitsearch').should('not.contain', 'No results'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - - cy.get('.searchbar-wrapper input').type('Februar{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - }); -}); diff --git a/acceptance/cypress/tests/language.multilingual.cy.js b/acceptance/cypress/tests/language.multilingual.cy.js deleted file mode 100644 index a604af69..00000000 --- a/acceptance/cypress/tests/language.multilingual.cy.js +++ /dev/null @@ -1,71 +0,0 @@ -describe('Searchkit block tests – search - multilingual - language', () => { - before(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'searching', - contentTitle: 'Searching', - path: 'en', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'garden-in-february', - contentTitle: 'The garden in february', - path: 'en', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'der-garten-im-februar', - contentTitle: 'Der Garten im Februar', - path: 'de', - }); - - // Add search block - cy.visit('/en/searching/edit'); - - cy.getSlate().clear().type('{enter}'); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('.blocks-chooser .title').contains('Common').click(); - cy.get('.blocks-chooser .common') - .contains('Searchkit') - .click({ force: true }); - - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - beforeEach(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.autologin(); - - cy.visit('/en/searching/edit'); - cy.wait('@kitsearch'); - }); - - after(() => { - cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'en/garden-in-february' }); - cy.removeContent({ path: 'de/der-garten-im-februar' }); - }); - - it('I can search', function () { - cy.get('.searchbar-wrapper input').type('february{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - }); - - it('I can search within language', function () { - cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - cy.get('.block.searchkitsearch').should( - 'not.contain', - 'Der Garten im Februar', - ); - }); -}); diff --git a/acceptance/cypress/tests/results.monolingual.cy.js b/acceptance/cypress/tests/results.monolingual.cy.js deleted file mode 100644 index 07c848e5..00000000 --- a/acceptance/cypress/tests/results.monolingual.cy.js +++ /dev/null @@ -1,76 +0,0 @@ -describe('Searchkit block tests – search - monolingual', () => { - before(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'suche', - contentTitle: 'Suche', - }); - - cy.createContent({ - contentType: 'News Item', - contentId: 'brunch', - contentTitle: 'Brunch - Viel gelacht und lecker gegessen', - }); - cy.createContent({ - contentType: 'Event', - contentId: 'ausflug', - contentTitle: 'Ausflug Matterhorn', - }); - - // Publish News Item - cy.setWorkflow({ - path: 'brunch', - review_state: 'publish', - effective: '2018-01-01T08:00:00', - }); - - // Add search block to /suche - cy.visit('/suche'); - cy.get('a.edit').click(); - - cy.addNewBlock('searchkit'); - - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - cy.wait('@content'); - // TODO Replace this desperate wait per seconds - cy.wait(3000); - }); - - beforeEach(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.autologin(); - - cy.visit('/suche'); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - after(() => { - cy.removeContent({ path: 'brunch' }); - cy.removeContent({ path: 'ausflug' }); - cy.removeContent({ path: 'suche' }); - }); - - it('I see the publishing date', function () { - cy.get('.searchbar-wrapper input').type('brunch{enter}'); - cy.get('.block.searchkitsearch').contains('2018'); - }); - - it('I see the start date', function () { - cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); - cy.get('.block.searchkitsearch').contains('.202'); - }); - - it('I can open a result', function () { - cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); - cy.get('.searchkitresultitem a').first().click(); - cy.get('.block').contains('Matterhorn'); - }); -}); diff --git a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js b/acceptance/cypress/tests/search.anonymous.multilingual.cy.js deleted file mode 100644 index 7d06c31e..00000000 --- a/acceptance/cypress/tests/search.anonymous.multilingual.cy.js +++ /dev/null @@ -1,97 +0,0 @@ -describe('Searchkit block tests – search - multilingual - anonymous', () => { - before(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'searching', - contentTitle: 'Searching', - path: 'en', - }); - - cy.setWorkflow({ - path: 'en/searching', - review_state: 'publish', - effective: '2018-01-01T08:00:00', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'garden-in-february', - contentTitle: 'The garden in february', - path: 'en', - }); - - cy.setWorkflow({ - path: 'en/garden-in-february', - review_state: 'publish', - effective: '2018-01-01T08:00:00', - }); - - cy.createContent({ - contentType: 'Document', - contentId: 'garden-in-march', - contentTitle: 'The garden in march', - path: 'en', - }); - - // Add search block - cy.visit('/en/searching/edit'); - - cy.getSlate().clear().type('{enter}'); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('.blocks-chooser .title').contains('Common').click(); - cy.get('.blocks-chooser .common') - .contains('Searchkit') - .click({ force: true }); - - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - beforeEach(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - - cy.autologin(); - - cy.visit('/en/searching'); - cy.wait('@kitsearch'); - }); - - after(() => { - cy.removeContent({ path: 'en/searching' }); - cy.removeContent({ path: 'en/garden-in-february' }); - cy.removeContent({ path: 'en/garden-in-march' }); - }); - - it('I can search', function () { - cy.get('.searchbar-wrapper input').type('february{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - cy.get('.searchbar-wrapper input').clear().type('march{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in march'); - }); - - it('As anonymous I see only published content', function () { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('/**/@logout').as('logout'); - - cy.visit('/logout'); - cy.wait('@logout'); - - cy.visit('/en/searching'); - cy.wait('@kitsearch'); - - cy.get('.searchbar-wrapper input').type('february{enter}'); - cy.get('.block.searchkitsearch').contains('The garden in february'); - - cy.get('.searchbar-wrapper input').clear().type('march{enter}'); - cy.get('.block.searchkitsearch').should( - 'not.contain', - 'The garden in march', - ); - }); -}); diff --git a/acceptance/cypress/tests/search.monolingual.cy.js b/acceptance/cypress/tests/search.monolingual.cy.js deleted file mode 100644 index c48544a2..00000000 --- a/acceptance/cypress/tests/search.monolingual.cy.js +++ /dev/null @@ -1,156 +0,0 @@ -describe('Searchkit block tests – search - monolingual', () => { - before(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'suche', - contentTitle: 'Suche', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'garten-blog', - contentTitle: 'Garten-Blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'februar', - contentTitle: 'Der Garten im Februar', - path: '/garten-blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'marz', - contentTitle: 'Der Garten im März', - path: '/garten-blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-mann', - contentTitle: 'Testseite Mann', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-manner', - contentTitle: 'Testseite Männer', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-lsb', - contentTitle: 'Testseite Lehrstellenbörsen', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-s', - contentTitle: 'Testseite Stelle', - }); - - // Add search block to /suche - cy.visit('/suche'); - cy.get('a.edit').click(); - - cy.addNewBlock('searchkit'); - - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - beforeEach(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - cy.autologin(); - - cy.visit('/suche'); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - after(() => { - cy.removeContent({ path: 'garten-blog' }); - cy.removeContent({ path: 'suche' }); - cy.removeContent({ path: 'testseite-mann' }); - cy.removeContent({ path: 'testseite-manner' }); - cy.removeContent({ path: 'testseite-lsb' }); - cy.removeContent({ path: 'testseite-s' }); - }); - - it('I see all if no filter selected', function () { - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); - - it('I can search fuzzy', function () { - cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - cy.get('.block.searchkitsearch').should('not.contain', 'März'); - }); - - it('I can search with inflection', function () { - cy.get('.searchbar-wrapper input').type('Männer{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - - cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Männer'); - }); - - it('I can search with decompounding', function () { - cy.get('.searchbar-wrapper input').type('Garten{enter}'); - cy.get('.block.searchkitsearch').contains('Garten-Blog'); - - cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - cy.get('.block.searchkitsearch').contains('Februar'); - }); - - it('I can search with wildcard', function () { - cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); - - it('I can search for an exact match', function () { - cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - - cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); - }); - - it('I can search for a compounded word', function () { - cy.get('.searchbar-wrapper input').type('stelle{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - - cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - - cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - - cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Stelle'); - }); - - // Blocks text - it('I can search in blocks', function () { - cy.visit('/garten-blog/februar'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.log('when I add a text block'); - cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( - 'Montags gehen wir in den Zoo.', - ); - // cy.toolbarSave(); - cy.get('#toolbar-save').click(); - cy.wait('@content'); - - cy.log('I added a text block'); - - // Searching - // WARNING Do not use cy.navigate TODO understand difference between cy.visit and cy.navigate - cy.visit('/suche'); - cy.get('.searchbar-wrapper input').type('Montag{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); -}); diff --git a/acceptance/cypress/tests/search.multilingual.cy.js b/acceptance/cypress/tests/search.multilingual.cy.js deleted file mode 100644 index 32bb80dc..00000000 --- a/acceptance/cypress/tests/search.multilingual.cy.js +++ /dev/null @@ -1,165 +0,0 @@ -describe('Searchkit block tests – search -multilingual - fuzzy etc', () => { - before(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.intercept('GET', `/**/*?expand*`).as('content'); - - cy.autologin(); - - cy.createContent({ - contentType: 'Document', - contentId: 'suche', - contentTitle: 'Suche', - path: '/de', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'garten-blog', - contentTitle: 'Garten-Blog', - path: '/de', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'februar', - contentTitle: 'Der Garten im Februar', - path: '/de/garten-blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'marz', - contentTitle: 'Der Garten im März', - path: '/de/garten-blog', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-mann', - contentTitle: 'Testseite Mann', - path: '/de', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-manner', - contentTitle: 'Testseite Männer', - path: '/de', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-lsb', - contentTitle: 'Testseite Lehrstellenbörsen', - path: '/de', - }); - cy.createContent({ - contentType: 'Document', - contentId: 'testseite-s', - contentTitle: 'Testseite Stelle', - path: '/de', - }); - - // Add search block to /suche - cy.visit('/de/suche'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.get('.button .block-add-button').click({ force: true }); - cy.get('div[aria-label="Ausklappen Common blocks"]').click(); - cy.get('.blocks-chooser .common .button.searchkitblock').click({ - force: true, - }); - - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - cy.wait('@content'); - }); - - beforeEach(() => { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - - cy.autologin(); - - cy.visit('/de/suche'); - cy.wait('@kitsearch'); - }); - - after(() => { - // cy.removeContent({ path: 'de/garten-blog/februar' }); - // cy.removeContent({ path: 'de/garten-blog/marz' }); - cy.removeContent({ path: 'de/garten-blog' }); - cy.removeContent({ path: 'de/testseite-mann' }); - cy.removeContent({ path: 'de/testseite-manner' }); - cy.removeContent({ path: 'de/testseite-lsb' }); - cy.removeContent({ path: 'de/testseite-s' }); - cy.removeContent({ path: 'de/suche' }); - }); - - it('I see all if no filter selected', function () { - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); - - it('I can search fuzzy', function () { - cy.get('.searchbar-wrapper input').type('februax{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - cy.get('.block.searchkitsearch').should('not.contain', 'März'); - }); - - it('I can search with inflection', function () { - cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - - cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Männer'); - }); - - it('I can search with decompounding', function () { - cy.get('.searchbar-wrapper input').type('Garten{enter}'); - cy.get('.block.searchkitsearch').contains('Garten-Blog'); - - cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); - cy.get('.block.searchkitsearch').contains('Februar'); - }); - - it('I can search with wildcard', function () { - cy.get('.searchbar-wrapper input').type('Feb*{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); - - it('I can search for an exact match', function () { - cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Mann'); - cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); - cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); - }); - - it('I can search for a compounded word', function () { - cy.get('.searchbar-wrapper input').type('stelle{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); - cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); - cy.get('.block.searchkitsearch').contains('Testseite Stelle'); - }); - - // Blocks text - it('I can search in blocks', function () { - cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); - cy.visit('/de/garten-blog/februar'); - cy.get('a.edit').click(); - - cy.getSlate().click(); - cy.log('when I add a text block'); - cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( - 'Montags gehen wir in den Zoo.', - ); - // cy.toolbarSave(); - cy.get('#toolbar-save').click(); - cy.wait('@kitsearch'); - - cy.log('I added a text block'); - - // Searching - cy.visit('de/suche'); - cy.wait('@kitsearch'); - cy.get('.searchbar-wrapper input').type('Montag{enter}'); - cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); - }); -}); From ed2b3bcde63052128b5eab3a758450d354530947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:42:58 +0200 Subject: [PATCH 174/226] Remove fixtures --- acceptance/cypress/fixtures/broccoli.jpg | Bin 102057 -> 0 bytes acceptance/cypress/fixtures/example.json | 5 ----- acceptance/cypress/fixtures/file.pdf | Bin 74429 -> 0 bytes acceptance/cypress/fixtures/halfdome2022.jpg | Bin 319364 -> 0 bytes acceptance/cypress/fixtures/image.png | Bin 1185 -> 0 bytes 5 files changed, 5 deletions(-) delete mode 100644 acceptance/cypress/fixtures/broccoli.jpg delete mode 100644 acceptance/cypress/fixtures/example.json delete mode 100644 acceptance/cypress/fixtures/file.pdf delete mode 100644 acceptance/cypress/fixtures/halfdome2022.jpg delete mode 100644 acceptance/cypress/fixtures/image.png diff --git a/acceptance/cypress/fixtures/broccoli.jpg b/acceptance/cypress/fixtures/broccoli.jpg deleted file mode 100644 index 456706d6627d9a911219187bf6dd6bf8eeed22ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102057 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 diff --git a/acceptance/cypress/fixtures/example.json b/acceptance/cypress/fixtures/example.json deleted file mode 100644 index 02e42543..00000000 --- a/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/acceptance/cypress/fixtures/file.pdf b/acceptance/cypress/fixtures/file.pdf deleted file mode 100644 index ef9c79819496798012ceabadd92415c3b1923c26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74429 zcma&Nb9iL$w(lFOV|1KU>{N`7osMnWwrzK8cWm3XosMn$_HV7d@7nh{_nhbaQ#EJ3 zLp7^rJ>U9_@si02i_$UDv%rzwgwh(+}uD>b1Nrf2LMpiO5e#?*x1n4 z$QTYNWo%>WWCmd6WMG8@D!JPk1Arpd2F6B4#zuebjp2BC;T)YDjPHVqTtWzF_Cf9S=Hlj zZiaACO1IzVufF!$eLwf^2(!D!m-jYpeeT+)Wv{w6|~e%ucu<@mf`+T8 z&z;bt*y)^YZ;#hE-7Vjb`Sz=^l4Xt9DT+0F?Se+9m%F>|>@ME!kMq4y+3v$tU*G5F zo8@qIhwev`r<;(}C(~+?7xihM+Ej0yw}~PxiL~X{l1cXLP2m^gX`jwix<zvi9a4^01_5G^>og~t2%KK_vMS*q6s z9&*g_Mcu#EFXz8km;w!^2)>}^D%0nV7jp`tHcrQECDNDeFX$$nMhkRREi;crX1I@> zC*1cXdEsJX+UUuWEVD?!BS^G|<#9nb&zu{W$Sti@)=E+cKVLZ?OTzJfEm5W{EH^eP z4;3xBOBcM1f}bFMek$O4;&I+prdJ`Ta$b7gJ59E}S{+)BHj-`;F1L9|K!E6ua6U*L zoK-%8qPpx?w%T}3I9Nw|1C7b<7v|cOd@5IQbdQvZ4jqDWl9wz6#$2rNk-9EspXWwM z-iqw;4Hw-7bVqnZcY!Y+Y465DO9K6%_ayu#uwtm=XsPag^l<#Ja_|2zCXPT?;c)EZ@zSZ=Qb$|4xf||VZQh? zz#$R-q!{BZ)THoMgy2)z#Kx)6HAONNz)tu~)s3ox2<&U!+|#?5xk9`+s$Mv)iuJ_XsZ=~UtD_F1-B(}cm)t#GYZ-~&Jmsm3|F8`E6h?@UM)NPd8$yZU<^Bz z$kr$)Aj>}igt@F6baH-sYj(&oJgFVW;sNou=&7EYp?cJ$a;KKrHvRD$C=(8p$zA^K zU+0#iG=DqL6iplc-QhUR7U*(UK3w2yHwE#imm;viRsdIxRqABr7lTNuv15qf`P=$} zpc$49Xzl4sh?iNuhy(9JB(}Xggl@kUBJgC2(7qhNw@9SQ)W$faEC;FB_L#kft2@Cz zM-EM4f@IKus_W*cjT^JLRfU07?vI1EcdcWY-(w&9q0yTw+%T*O{pr6U(#^}^Ik+CC zSR0+%!zWD14Y&Sy>-W271#q*cu7*6RT9J}MDHoC*@HAx80#b)TDn?l;YldTO&JVy;@@o&lfR?dno9}_01hMiXk zRVJ@MYIkj)f1Kw`RvaWj$q@VtaKti&*pw&!NcqiE)lrDNJ%8td_B5k}EP$yiuqMF; z`4{qmWE$33D(pB?Wd)MaER09ZD&z$5qp28TV0WmaQ+?T*pmrqCv0xSPK}Ib`ZLkjU9RrBA6ehqL-VH`ucg+7kVO963JhACLx^ zvnnl{TkRL}di1$cJWqW0O<H(hu=hyt=%CTyq8$32QiV4r$hUsCV8YE!pAeX(&VF^TSr1qE- zyoTLP+K82**B65^%DBivjDlejS%^P85NmZib8 z1DuwOTN}-mA?{PU%mj78N7W>2|vhFvk=+#}~ zpkR&K!fv6!46DCOb6?#m-&M=F16}?pHUzqH=0}i-Ci|veWfcySecK^)mFZ86-1)*X=5LMNGqOPoDU3=a`1?9Ml6}ayx72TbfrSW+C0q zvXY*Nfj^$zAn&rde1z%^$da_n9Jqe;{!QIM3Y;#q?en;CRfP{4l2}0}Plf*>2tgg4 zdThMUQ>|VE@c_hf{UEoN6tAe#sFt49?vHl+H5QURd4coy#MC`fl_B;F0k1H;S>2HN zBl|l>hb_$?jSA^Eljew8uWi^&tPcVQKNM{&QoO>j#N1?4vaL9AWzXYBZI14}A`5BX zdcht4fUXia)EN<97fFS;9!SK6MtmA!?ayZS_0ws*jv}8WxNqHNTKZkAy|bLJ7|Xfg zZY7Kze(P;WSj_W*Q0*0DsvHnKKw18^*qA=-`0Cc1K~GQTZuEoT_WLVfd=dYf0Jxu- zDBiuzhD{6mB&xDyjp+9TE-zmg z7&EPqhxqL`3odco?Om=@&qbX@yhRI}8|>TglxL40!Da-Nh_DGsmZwoZkT}ZTG-xL4 z$+ZsqKWK|ql?Rruc+gwem9?65@J*bu2iDGSJ4_&X4$IaF1_=W3bI2tHXgXvY*y?#$ z;JSz6FuNObp%!WYF$_)P{;EC2r8JehZ9X_xj^EErZuw*HAj`*uWx%uOj4p2ydNmxp zof0y8N#zz;0~$3HDau{kn6jm87_LII-CNi=5k2WI&VAwY9(Hk(Jn!|p9;`<^?Np}h zPM$BthllVh!0H+f1K2M7@Ky8}x(Tk>xngS;N(A*L_a&GxjyX>aee}Q4EM(Q4)$>bj^J=U)?miNV|&m>y@?s}$lSKEhfQQqe=U#tLf4 zk~kBj)d_C#QYqd4nCcQyQI>%Zm}n2p>P&pXGrkxi#OMBYn2*;xY=9t4Vzzvk1Ri9BFn* zn7NZ;;BkuJ`<>Xd^_xP6>^xv8qAB_kl|a+|t7feqM{UJBTyI*8hn1Z^I1~w#qKh-% z6$*Thb|eZR4wd?n;5yu#1;K0CfzR5(>ns{X!D6{Xe2Vwk?i;=`zuvheRh`MU#lw0% zqH9Sxn`iGIXvoZ?z&j8Gv2#$S;(D<*%8};GNjNWj^LA%$dN^vAiVk6wvZdpyrsH{( zv`2MY%h(SixL=`90@))sk4l;W+qKCTsJlCxvwi2dN5f*_DdyA3rkS0S$Cf!SCEddB zQBy=iASC=0TyUDPlcpzOy!5&OPtl=I7q_?5tVT2{bOT5mtMo(Ki0z%u*x2xc_EQ8&M{ut^+Lu-UFc~f0Yoe!HsX7WcPfs%bWSl>Ff|xaqAG_ z`k@Hn($W9BHohfI0Eq7O8OZ12vcj`kz|n;#`WgO5soVGXm9)Y52gtd`3II<9>z&H> zY3s)-YR-~Jn&!!qv;0xf?qU21fSt5BRhg6x!aI7`^YksRm$z%473Hk(8d=t#5to9bMyW74=+; zt}DiC5(Wq~DTaG3zY#LQ5#DTvQGBTB)w{!fqj$fyz1*pdy7`0tx;;HS%V!C{2NV7K zhp+{SOUUKvHyT1uN2XO=(0X$t&trTt(E3Uf8l8q~%)?44d1|^_fn`~uc?JfDX_*k@2@NH0ju`g_B?eQQkbAi_0 z_iBo-?Y+6k+LtSogi%Z#Ng3a1SW(>{Ziru5I@{f6FG3 z-Nxo>^Ck6DegyotcEy?_n@#VRwD0Jt>yKtZg;TtjW+rR2GWYfSRZkakAU**!!2ijA+lky0R{>z3|<%&hlSGDd3%jO;a0SgpObS$|uz zMv60)@>E%WTeLx+n(A7V5uc@(AoV?um)6+wo_MjYzuX;o=H`^>wj54t?y~n6o`_eT zc$*bS$0g)4)*6QYjNDulTd@xdW|wJxnV^X&k9|1KNAPsOG6~_6@1JBgRjf{Q)OD!6 zN?VM;`>dK5%y!)RhAs9@$90C!1WCtnFF$PsIwR~rpFCL9%9%||hkB8OzCk)0Y#4t_h^=s-Y1tHdxb?b*i}79!@kf3n{Ip*ZBJCBr_gOWe$}f{Q zu4TVcz1af-rp!d``pDkWYfw{-0;^~HrMH?t%&{gYljbq^V|cPIk*DnaFY`)-@=wZK zGOpfEwjx-LMoFl5W0@0stDdmXsi@dIT&lW0d17M4HqJ^=2pyqwQ?y*2!+I0Z`nH)Y zqow=TD506BVH52hYqfH0W|EN;m%=X_hHB$OR;>`jB5a1EQUedH!Y13{bX-qfTbF5I_YyJQwhFglSO z&xo?Su(JA9J8vBOP39| zK=DqL3?Fa9&)PmkQnR)OOWYS;-`aNOo_6qEQcWD9i%Ln1k5+X6FYa``aY)qNaWn_R zeU?!Bxx7@29QrUABfpQidxC7Qd;I1lkxnL9#37Jwy31f?YUW|L+X!!PO-%Uhgk<$m z6FRZ()4={ME`6k)9qMLuthm@=AXhxGawQ(qY4y!(@uaAe(N-8a!U1X38T(PDvsoz% zI-5F4Y*X1|d#|xgcU73_h^z`dT23j+*0rmmXy=d1zVXszX|ij*-piDS%0zBwTCSM? zTbC(DMr>!5bCW4>G#ud^uKm)|j4@v(Tb&AAa)PDgs);v`*y035+p45yTNrcG;5rk> zlQE2yWj)%7YuzZyaIb05tjr}ZzdS8kK^;B>=V+s>(ZNAkE!|WU@o{W@6UA0kv9NQ3 z3t&#N?>C6Im+8tca(F;N4cR%~ukKTJsmfMc8uMKmVRTs<2xPNQhfW>r1UKy?LKp$V zZBCh7E87kGD0V6IkTIO^&+L1_eHgJ~e1aT3-@{y`5_#iv3|e1Ir4A6537H5HTQ?0f za*RBxFOaoy`8>SHC1B_2c;feW*aGyDGTz+t4&DI7o`yylr@7GmGpF2PetF1nQ&rqk zFAc%J{l<2Md+JKhIz(5ruyw`?WqeAXU6gcr73Dm-5gV6Tv%R{wE65b);flG){HS8&Qu zV=8s4R~WDg5?}@|xtt=2&AB|WwM5T!B$*@bSYkbBWk5^|lu21~l`*t7S%eNWB4avE zWu_;x;4tq3Bs%A)q$GC)`8EYn$VzeINfHmVFWnq!0=Os_!*j~4&X4)pNkZd5j8xvk z+cN!35d*XW3}|f$}1hFTlM<6 zn0TBx662=A)1$8H$=i^dI?@@H28tR_yYc6p`N7iW*fnZ+Zqi9Gq;UCsj{N_DC~-e94Ngu|a4?{l3%93DJc+XZlNeuFE$nYH8D_1eYk zHt=4~uVOa3=IyR?qNS87xIEf{v~2p!QxA7An1s1BR9vw3I+6OO`_1sy<_>lSwW~kk z;_|VsS}w&UIH~V?iRV%2+En!F;CLB}dK+&ag{?d6=lg6CxYl_<^#gzpD* z_@$u_>k=%9nQ#U>4%G7@Rp}Q#O%L z9)28;laJ!BK9Nu`wGS%!ynflKrUs{H))Hp|=LcHqQ*Z{EomsmpJC*f4RcA0ZuoZEO zudTo!-l@6?*H-JgO)i6_*gDtgpM!Iq?grU6XPnToG)G!pLqW#~-W-9$tk1=0-nedz zDYoNpw@VJma%Y));Hy0M+@;V@xbMdWtBOc3gL=s!#U<<0yb@Bb63XJKaI_;2_BEvj$ST(==@M)6)& zy#~jra|P#H@gw}++RIPNv0|lD;r+)+kz~jl+m+pP2j=rhX|a^Lt_T5a1qFFHJr^zRp=HvEk(tCA|?2&KK(c{OU;t!vP zJ-#h3FAudtp2?4!P{vF5V!}Hws^^F6yQN`9gl!-9w}LLIo_6Z!SKiWnLqY?Cm{s=&=}G3P)z0}@z7tV^lmeRkIwP5-#vPndq8f9GlULd zM{(=c`k6xAIG=s=(3@baBeb*P-a;FkcRS?Dta1O4VV@(|Tm8gWTg|M2bylJcdHCHi zFUO4aL5Ni>{mu#3r@i-bjAO_S+c0NIJm;%rh%S+5raV~XNi1d%8HmoL&_bsxX9?hPp1-^gi3$;!0T^V$-XpbB>;o`Wh?^-|ee!ng7fRarB z--l)TAV?+x?ywBG0}Al_dpr884xfYw`o04>C*9&Y1Y9nuvYR=!KeIc(RD4r)|`VXOG=A@9-4OwPaEf8kI^H3`nT5#C?qelDDc&A|u?1#}kW2N0CE$3*H-ng97&jgt^kF4Ez zQS}pqHN3v)$?jsF`X?$~%8p|Sf3lx}r>M>#>bE${QW+lNnJTHQbF@cHqKE~o*-T;q z2$%+(cW3Rzs4qD@{jkX%5jB&)#PNo1qE^1k)mEGj^dySKl;N~C#lxOZdtRm_?Fi|$ z{6ZmA8B3dx%&LnT=&E5QMD4o<*YBO`ICaoLSwOd38aBS7MV$n}gaUT{wVK;jfJe4m#s6!&9|h_MaAZPmwmHkxStRff zycdgPdVrVS9>BgidI}C11du#y5XY)TzV4cr{?QZlvnQ~#Qq)QzB&zOFM6VPUIX%lK zSDI8r2{{^s4~Q?M;M0pF{wsV-eZ1>Cd3e<`gl3YaHyzTt)wpdS1NmQ<8+OrDyLE8QAgy3Hh~;`Dv?9NOj)rRN;xZO zaM}SKrwWfy?S#0nWrt`yy(rW+@QiBtV*2Gij7$RYcc+NDGLIk)-oZP=fi0)0hCg&; z^oc#}XvB`!ua3;*I$Vx?_)s7#kISiLcXOJvjJU_XkQ zx$)$uqG!yVL9RQOV6k8$P}*a_qQoVFugQE3vQ5Mo%q>M#&UEKVC?Mo5H3r;>w_vUa zPx?vP`QRKB!;|x;h93ucSKe>I@T!T+LMmJO7D?7+#Q8A#*}+Yd#@rUT$La*?ZV#d6 z-4_db1)aCI#t^pM`#A@hG)-?A?)8QcdEJJ>Jh`5LX_aF&2Fggd$^O2zBA&!QL%tD= z6>FHFQmu%i2BGCo2p|n6j=6Z{YfaaKOjOir{S52;u^NO5DluFfg&0OVN>fUdMmFK3 z&IZ?37*Sq|Bwt(OUU8}9ZHl9kSZ}N4{ai6&{X8tEz44~5ZHGQ}j$j{Ekp#|?fhg~6 zCiz{w;@c=*YW`g!UB+B+K=Wfwr00D%375<{3G=Wm`p9cuU-R7@@ ze_DhFNzH9wD)Zwj3Nyab1u=y%g*wAs_m>@S)|dz3G?2AoLr!5M(28%N7$`I?AoC~S z8N{Rg>WD3kqYkLjrVA%smPQv8QQssE@d<*i)iYo}2)M6Id@}-gL%YGi&G(re_S9KxsM{omhhx zl%t;>(CN$%xLGOV7(3Rf7sR9Nz`7Oo;xY;;ERKFy9|vn9r(iZ#m8WDtUPL|U2FS5O zR$t03w%zl`1FBH$g3c<*cEO5DK~sA(9kd@Cy=*oC#vuwBb`K(vSSVYw`?(BxsS3I+ z^9HLeIyJ)W-}*QZI{8&q9<(L7UKK&{XZ?1EzsNN1{-{VoE50?li<-kPR4J9zr>2TX z;A!`*8T{%&Z9M6?u#Psp6RLo@zwxk?)F-jxcMQETVP9ZbedoU!;Iz=YlysaVdR7sE zns9IgmvXR;;GzbD1)~mbx)@S4@v-VDY;jO^b|>R6`C(9 zVHX252!9RgGg(H0T%0S;lQ45MXmn3&kh6rZy-Gu_g%&@pfQ1+sh=43wp~CPR)5U5D zYmTn6D#PQzhe&D(l_A>R7DZ4}X5)KAV_v-kvwWYjkg`njskccA9PV7p%=P0ABlZZP zRRK*65-#`+31Kc%H7r`J@!>bA0xeu}DO8#tqgYY~ILFu2EW2qH=R0GuOkJ<=MkI)D!~yky3?}&`NCs-lKf`7!T0)?^ zXE`?JdCivw8-eeYA^y75jgEn%q~w_WzMPM9W4U5NlG~>b#qYZ#UWib#dlZPd<|4mI zuQ!qbH=&D^SY?1-8Y}nP>qw+&ihmNb8ZPh7q5F%woX|=IHrUcp zt;qZ-+@m!2Ng-N(rogsPL5Z$RW4`8kH4P6ZJxiGoKG3BK&}y}dA*xtft)adcBp6JU z#TEZrrYPK`892i}@6IJo*{@X60_Ig{3N{t&V?ZQT#oY96|2vr0nF1kn5 zub@17sg*?i=E$V-us4Z_D<_>dH{&3sLd`?KPVTkgWH|^XhQ3e`Ai*`gmIEi~fgS{N zBcyoa6B?m`B6j9R3c?=UlbMh>me|#sw=blBrma02@mgG?8<|zbJTWs@ZBD~`5^edW z1&(%sda=-h1dRBU_uPV4BO`PhIbavH!?0$}_KzNSPJ-@Y1v525`v7J4G!?+Tby4V; zs-;^(=O=}74|nl$Aw|P^_Ae&4+@G}2<3ae&g51J$%s_=R84qbHo)VP zuC?wOrj4dEvb1%Sd3ERax;9KA!cMpAU*(r>-{r@# zc_ufrSjnhcd7PFqOUI;R0(f#7Qp@4$ACiVtE7enH5C-czL??-vk-)@tyUOQbZ4U(mOef+D}`bla7&Ww>wa zq~rLsAf87oMRvCKqBmw^+$}&_^oJRbM{fBn*JUn~CnU<$j`6-my@)K)0dM0ms=JQyq zP!eyE9jgew1BA;1_fOn>Q`{Dw)pgX?f@R8XqiAxoavH~Fy$H4254Xbx?L5WyCDvG&eQq9A35Z9xLOw>_zO~CkQqrI*+g2Vmvsng z%amD(BsnRATi!Sa)f!>J;rr=J=jvKDbPIFbr~Pt$X|aw~`ex(KlAQlCQZ6>4Y%g<^ z87A45C#0dELN&I1V34Uv^Wvf3Ph{_8s6lV`K#-Xx*!(5#Q_>Rw60B(#8$Gc3#R#^= znUAup*@VW0KalzIRDrqtZM@ZrxW^$zw)60!0(B~|eYKmyzvKMqWAZk!pY1lsVttfa zQ;As{Sg7Z4U2xDe2ps&_tqU=K;~$^Cq^g0^L#uUyW~?Vs@C+kwG`|r|PZQaD;cHpE zbrM(xU1R5#`HCroz(M?EX4_{H+Arq)!bIw`$-O#?;9MM9r+&uJ+kqYhLb!NE zWh!0ykNerm(R4JzEa^$*N($*Z{QjhaC{J$hx3aUEc=DfhlGKpRa$D|61&DDOK=?uJsX{ChC0Lp^X<0-oQF@#Y(Yn;2jO?^iWJwrbX)GND3U1k1?M$TIiF=< zMgdt8!jcK2p+eg4HUUR}x3yP13DRc@8(V3*>j9Pre!WsNF z9%;qy#~5fs+SJS(2Aa*V2nEhnI$-G>?i+bkN$hzTO>>`%)Vf)1Z2p92~! z)8=nfx;~w)IaI39bw`u2zC{c~Mn#Jq9|`k3s3KYfqo1~2pQqB#4He*DAEC{*-EhBk zVL2)KziRw16vX}?Ls45Br+=X&7G?m)zs!K5EKC5-|MmdNIoKL1 z8an|rfqy4I0YD{VH>dxZlab*+vE=j}jQ>Vs{5O;WN*f!Q>kHbt0W|-yVdZ3{XJTgq zaIi4ab8u?I0TqlLZJiwq|6YTC%29v=3jHnRufHRJ@n7%kzaf;IzUjZ_ME|Sye=YuB zjGQvDFthy6@zOPKb@kZ2zb&sE-Pu^1p30FQUfksM?LAOjk+8+4x&hvL<5uk8_%=VC z;(T+e5fv0P>wEOHDyS34>SC%_zT7_Vn{^}3^;OA--=DAFU+pLphiIT-j_INQr{sh4$x`mKn zGOVpFMcKppq7z5em~2C*X(ZTWUA7K7nB~=3P>c@VdXe&LYL4WrP~~3;4$scZc{Fz# zv}r1~21Rr>BCV(OUrqv1QN9)$n>5r~O=*Cgt*!O`b5!fkUB=Yg@Q<_=Uv0f)f}b~P z$+)h}2)3|AvyDkyZ!YLB$8R)*aAq&hito>qON~B*;X2Rvj2v9^#}V4GzD`i<7@AzE z6sn)kiG;2ksg2zDG3Q^{6MVM&81VGWg*s6?hy!a|)?#EWTWy^XAP?y@vZ zQ+V*7ZFUSe<$(u-Hh1mSmLwaN*3|2`@kSZbo!CLQ1@8u$e8(Nrjo3<7RRz|{#w3u&Q>q#SI}V*;*GZp)r8?^fhPF*iVbpd|p7`wo zqPAa_$fQks--ac+pY!U74Sa`e4e2YZ+#CyPX;iX#ynjX@U7eZIHH5Uier@vI%5IxDn*X8ED9jY?SgGIphS%Mc0P-gD4-tgXna=`-5V z`!8o{Q*@-PHJJWBPh#Q!!Kfr28o+T$P3!u7O0R0Fi6(U9j2QWOWj?y}?*%VTx)OBY zDSQk53~xJ?+A0p=s;s*4GeDNW!~PZCkG{M8iV=ee^MYEtcePO>8v|8k-;}NX>pm6*&|erGi6QYQY2O8$!M)ZQ}bnhCuy&F$aiB z>Vr&`^q9^OU#)gh&3#D0tsR$Eaf7rR?snD{M9fi1#$CV^Jbc_KH}TI74i^J0t}=BQ z)z7N+-Y?5_$?GlLC5h5PT5sq0*Anh6$b zBneXiKGpfmKTtrl^6vq?!Bk*5VY_`qM(}w_QRH2f0>4tAM*+{BXl(pVEHYd`jsMeeRc3*RwF z+=7G^BUxm?VE_#;<}_f(ZLk7$0kbad3A^5dBIU9+t%rLkRbyR+s*g)}kk$ANFEj!* z4r#XILUUP#R~%#9ggH@^M0E7rhcvv@63muxbwB1_b2@j#t7QovgcPA*HvQw0wKF@p z6%)4K14AmQ6mVar6%9h1QUn%7WjcH8x}d3kyZ8vEt8}aR)S%FBQc{*Gr_7jFW&o*- z;aXDm>{k{6tBk??C$Kl4kH+Lfy%u&7@LtbB7sJTn71=K8)^tmA1Gok0=`qQb>0p*S zj$HnUFj&QIinb9b%T*QfjkVX+g|HU85Nae}k&<>R?Qs14^9fdKMt~J%1t4hUN0#?q z8_~97FK^LAjKN;o5v9?pV>Xol342|sA23pjSfyLdO*Li=0b3TFGT=k8Smj3v+6x%i zB2=kErhH>T$0T1xMgG`B;fVXP4I=rhm&KgMz-p_%#ncf-qqmpnCji-v9o|HA_W}%P zh9(Jx6!pJ;%d}~X^&Pug$;=)49iZBqQG}!qVbzS$$r0*wkQX@O7bhNkDekIgBn)BG z-$`sGQh@lUCjF4vsVjEc6=<3`jJ0xPNf9S`UDk(g|8~LyrNo0sMBKL=+^pB;O z#Qs=y;+3Ue$XHb~mZmrLwb!bU8#OXk^59VL@k+~e&`Qe)I0Sf=*Qqj`N2t8PY$5s` zc~L4W)zi}Ck7QcOM#E9L$``6{c;3;GGkEBO)fT}P;c*K-=r3i(OPf%tPro1k;FIy7u5Xw z^4dOwsnrg55{QJ8$&q^aQTqqW;c{h^&AF5+-@N%z*C#q0)jRWait8)ycz1-%DNucLKUHeTrjXD?tt@S&FvCVEbPT5v;ZFD?wwj2*93BqZd-vz z(H&ceXv44xP7Q4_UWE?K{+Fclea6TBUDQJ(Mrxsi-hC#?KM1dwR4}|B@9!sHH3qSw z|Erp2{GTn!zvAy7MGOF{D5whwOVY@gTN^k#D(c%v(FxjG8NmS+os6wj0E{e*f0d${ zzQaG7loDubV+@o68UxLN)<9dJ9nb;j2z36dy#JB4|7L(zz$$!U|Lab0S`hE)q1(|BDFaoqFl$0Mp8W%*~2`7$L zhzWWk9Y-$?eIhLkQAh$d=lLMowfXVr<@&X@*?rl{NpbYPmFAw~Zj$XD`j*_4SAz;+ zj-V_=%+R+>Y~Tvo#vo>i*+2x7M^_OQBm|;Xz(U2|39HRJPV5J# zAPN@kK)3fSG90Adc`gVp79O13?7VMTX*uE_#brpag*+HaI0)$DcC~(eJwG;hc#+(^ zcMx1sIFPSB5D*d+n~|IygKkh>+h7ecU!gjSKER1`!21D(+6$gMROQycs-BQ~;+w$V$a)nT7B5z=}UZ zw_}068as%2U~~|mm%dxF?N(pEMh6}ygddzi&UNBSYe^0SBE%&ikh`X}R!@d<1p@(- z1z!J+0-Oj0&f=S(z#lmloNs}HpPN~HXAtj-4*&{^mf=O~edPr{jvfXx$_>#VB`vTF zw5Z<0reGp*5nrST@;)e7WX!jIM0@+!_GyfWHVfqKtF)cr1Q3BZm=@ngmp7^hCs9&lOpVcF8IpJ@r(Vr(MU%#(js%I?d?ONzZP`E&RX|X_D`g1|o z{IZ^zCAOQ9hRW4UY-ozc9kcp@OUfQTx8r1gD0r5Q<`HARHnFANMoparq&@ z#9YiK;fMM0%iCdrMJ(QyMT6+uo1b()6Rh|4Ack#Achh+u1P}v!jboCIBAQFGo6--3 zoO`=%=F2I)D^62~31rj^e9MKPVPA%K*hA`==J5Ye(bAK^A}6E&>g8o){OMPbSnqS?O0MH{oa>dCw;D?NSZuZfC-Hh0ishyi% z24c<#%c#y+N<*^(8Qgd3)d*e_{rvV~ai1sWN|}0p#(_$UX_S+g{#Qb{Z7x<@tSu48 zY-5}!>b`JLtXIRYo@b!{pwP5({$x`tw79-B?i@a1{T)e0=YMed-Gjr{5zL7>axmjp zH|yIFdo~ny?*WK>q*uhg^B&A}CF6xU*spa1(eb+vs94(Dsp?upHu3zh=9uRX4(<96 zvsT0`O7EWVtNat3(lWb}@$HJryMYgmANv=_FDr|y9YQN@1GK3q(tUJC68yb)ebbzQ z>a!4P=h)_16a>rik7Z z8xKD^i?U@8hrqXFnSj}3L+hlDKWk_&*5QSzUWFUYEv#SZygZBJG5eh4x;J$T$=c-T zlNy$|{=|{5E7!EA+E?b`v5?MPK8fx-wXl)j=sS1!X(p&SYZO2T99tO)-!w8&b(|Dq;DffYhf(JCLO)1YnF9hstc12-E^Tq1El z?_2&w9A>KO{G`JlQoRAuawX4=@+w3f|J-15Zm4pVB&7$uUoU9^g6rc#dgAdp@-;uc zJ&l<>2RJwdp-cQ~=su(w4rFtD7MHS99sD&E!@511Tgy z%@05{Yr|3HoJ?L@4ZY-cLx!Or<;=CQ)CIhsJkK9+$(e{sM1T#pf)8pMwD^w999EFE zCu=E*OJKM^l*ev!YUMCL{M!U=hu%ws#u1W~n$O35rC@4F5eu@I|15Dsxc1l25(y{Y z(F#_#Tr8KMyWMD0G4$H!!_KboiSrfvmpcZguKs8EPgKq*wS-XME31VZKV`NhIK)PILs?lNTJ*THdY>)?#Ak&5ZsC2V_o_q7rX;E#1C{1 zB9&%_`|`k;rI~DPJKNQtP%4Zb+bwima)nmAk_u#$%VdH(+hT;{RUFH9L)8$-h~I^) zULz^haLd83b=;7xzr)f6l=o-;+@YZ;CI2LgbM#_v{8pPY);~i!-QYvj0+M3C?ewkM z<*Y*2;euv6z?%M)y@E^uiInYn6YNHadelyJtRGXc-SWgiGp%s7Dn9hdSLxn|h+>rj zzYzA7pp1LLW{}&1GjD#X(xs*~see_jN&NY7a+OZ)4{aD9*}mUsa$^A4oibmE5#vz% z_TF3jBSj8IDwrlbiEC^tBtGhX04@jB6f{0PdO(LGBG%c8y8O)B{rtMe1ePHZ3-dv)8_m7xJ}#Hyr(xQvY)?s9M`DP@Je5{ez{T|?%Y6JW1Kuz zY?yqPwRh_l*Mf|j4BpL>m`Jhab#w;8Ld<>hi4a$pMd2}fusf@@8jKD$yALRdvDI%60R~0Hm)@)6Uk3) zQH@Y1Otj^;8YEFKFi;%(h~|VRxa)A??A4jQrcr@^_7e;ruKC!bMel`8jvv3RfwNn7 zBzfYcUz7+gY#YtVcgRX0Zy-bYXch^b_jGA&M6urUgoRzg#=k_Lp5Y{u` zYdmQUjfUhis-tkpE9WH4;S$bacSAz&j#v9v7Ns4$)OsP_L-IC^T1#rNEgPxuJRO;@ zCFn2GHGMbR=q+p`yOue&Nhln?b;S9QV!tNX_NgOg)vW70#9vyUvLNoOJew%rFN|Tq z-Cj(GM=1lFO`!(`cgVTCV6&T2QV$xJ6hw@8JukU`_=DUNflz9?LNEgHN3pNk%TJ^xJ`eX$SgR$ zsQ!(u6rrSJFY#%nQuq?TFb~Bx@rf`%Kit^85YB3-LYAs)xX6xt#)%<}mgOZH=gi%K| zD%|J<)&-vtHvuYlaMJ)KM2_jk$>Q`E~e?w<*hir?u^+YjJynMr3>jxy(Pi%*i&Ivxt}3^Yc>g z_mfOiUw}>o5)+CVLCG2pfJ9;5E(Uswr$^pO%qM5D?cCpF;bJ1VI~}LWx#5$^d)QerKPcQu*xZL_DW`j3*c>uiV&g((lKlCa$zEr=LX_wv zr3rO~;)Y{%A*P;DCx>M`4I%VYv``ILpeLYly3}(=r56u*GGgYb(8Se69OF;=yW#Z^ z{zC&6W7+DF`xqYn6pPVBd9!D+>T3DCW2ITkvkpxNU>jXf0Csety3XB|on)>cyF-wF zoA(@u8k!U+UuY*U&w&Cxdf5C(x?l81zg~H`=9lPRc8HktpLRfbhL;Xc7G3;Rmv6BL z&o7T{VtbNo%;`_!fc?sJ5B9AfkuRPn4CCc1R*ai>x#?b5IQ@0lhkGuW8jG}=BQuXO;a~2XuawJ?w;F%+ zkw4}vkMv16LC&nTeP*1&kz4T~m87o@eHQu(huBMk%p4UNdGzBRl>#;pz;tA&*Uh5H zEt464pO(V*zD14mG#4$HvQUerjh^;W2**$46^fH(kYl(4z6zxmsWJW5rB^309foF{ z#k;{y<;UkannZxZCJd@k&K}T@8%O@j-5Rn^#WpG_Zil4hl#$P~cOvp551fmIgCFWs z{|{sL6rNest_wP5#kP$vwo|d~q+;8)ZQHhOJE_>VSwZJtYwzy8yRYu6&*s4#bDWKX z_kPARh8He0KBC2d3bk@lDS?=Fu_iQkG9 zYl*SSsbSXYE7h$2w68AnW_Ips;@s7>*MA zLl=ZGE^b4jUd<`(nY8^XEc9l3zM-0xjF_s_DR34uDZ9QWX^V1Fc?Bb9Z?{)7)3muf zV`spA$y~$XR2HBTFV}JcJ(wjTxE#S2eZO*pIR>sQ+;)HzeU3IF? z$3s6jr9USzoPRE1WUIxy{Y`piWntmI|97nFwQG3}ol4WAo8df3{mUVPXIo7$($2vp zq&I@6tMgI#8-&pw_!yQogDKM@a}9C`ca0eB!h||3V``WPf2kp!Wh$czn*xv6O{1Ug zc9YSnNRsq+O%T(Ok^WCphYpy2`+O6Bqeo}fG-Fry^bt3IU1O8?4$1zuPfej1(q=*V z#K*P73o_nkUV=Q@G9-`X9k_qfp70n3A}QJ4ndE04Z-_j>MB=Q9z4D@7N}zz7 zzBf1HwMv4NDzLhdNBC6I`jKLergafX$?HCoLpzVx)oPo+V|7?m zk?v8rT<}y&N+`F;7njWvt6k<+x<%Ukyh5!mf&z-EF}#rQa)ZJ;oHb*Zrq~|&W}pZRp&gf!|vv> z+E-*mOmeR)@-xZ=lR>jcpzT1B0{>=bI6_aRA4>xDH|9(c?B`i)DcEQ3B0moZ3EFxR z!?Wj0PH(3%H&W5G(8U_ui-WQneud>Y^q?m|m*V4W{*67ADKr@j7NR9Sv&&tJy#whA zylQ%f`Z;`Wk0~IxkZ4~1zQ}Es1};`Vm$;?Odk=sZSl%;Ut2*9&*)J6Z;9(H-{AJcX zAohSVlYEkD(c7+l-qf$%`*TRx` z7yMl;mY9)3QxHoPN5w1wou>&nv1t4I6)0%r)o8Il|8y>g1?3^xNpBEuCd{F7tjz=H zna9!d)&uS`M(u|pO`Ln()E05D+RIgl5!-HlnXokk}z}X(rE3W5Pm*4BDk7%gh*0({+Ln{PGFGV_9y@m+7rDSA7*Jrs8QoifLd7CfH%oP# z<}m^yMQfis>!;*>$+Q4Ok%3N`G-p|>@JAQn{H_KZ`gjC8piF_=n6aR^`1U;&&0~?V zsd@13+|(MDzDI^DAy#9Acj<=7u|{cEMy0{xAFX)tG0LJcmJ&-eX&+Gh@$lHoiuoun zo}7}xuvJ1OOu*{UBkj=4Ax_)RR;g9&<7w+6T@-XdqGxVUc>I}Znvn-J%Rb&I#002! z9>j*Ido)7y#@)WuK zS;AK>9T5#T?JsVkNSGo_6`oL{y`r2X6(ONS7b{sp5Z~nTyyD2mDq>I>l|aHNxa1t- zg_G!5h4$*yPq*7i-06&Ka8&8~L(`_$xfL-d)UP0Sh~JN*)XCL8lBxp`hBmm#OhZ2P zsNPp-?o3V%Qg?q*wR+WZAk;NtBqgso40IoZr_%O;02Sw)eOk0G3u$Qo-3FSY)7km( zN9y!0anejxzaG;4LyF8*T$N6t;E z{IUH|2I4DTG?lWIJ4+0FP`Zx6{mdNF<*9pnh5sD)?{)NB^hg>~$pnqEIY#za1-6+Q zuUo7X!&o zc5(ZhJear&?=YnT;$GPdS_-2iK1>_$f@h&b&+i4s)W5B0&W5(XSU1-y+&tMEk3IaZ zcPSV1J)rO#&W57jDAaann=JO5KVXponvAWJEY1iv8|n0mo+e{%nE&{jkues zZmzIG>9nK zbn*V{Vl+qo(6G{%T~k+2{>_F0%$@ry*FvxU`&R7z8|a$?sLzfPDp-&!Kpwv68}ty% z+VubF68{Om{Ewl%kiMhwe??^eUs*z0-_hwm9`O@<`C$VkX9K7IYB~E)*V&I${AcZd zV8OxM&dK)Yo&Qb?0o2TmoXmcLF8>|O0f^ZAw56HbnEs6S|642myD9BIi+?_0?(jc$ zVB!2r)X=f|3CSJ<3~OIFFi4|wYJs= zn7P}T8QTEtj2+BvjQ}3T4z~ZrDo(Dp04FmCV`G5HPfiD5V(wxLurdGf7DHPrTN{8N zKp5~FAOa8thyf&i3`PbZ3y=pW0F(eK09Am2gTA4qv6GdtiPQglcK8pq0Q3O{07HP$ z580RiOaW#;aAN_m`oSBU|Kc0_{{kE*fHS}a;0kd6-yr9Iq?7(9{(&4O#vjOG{@);njpJwZ^?zQTWBkwV|34syk%N=zr|Is$ zx#s^SIgA_(3_q0gzfcax|3*1gu8vM7dW|Ml8}5dgcpEOG9)=q&*-o4F)*F|r*J*f; z@Bh3VPi8upeWy41XEaUKmVM64J)Ucv;laZbr~~WDyBXyNwr7Ip;K-5t2K_Xr#bP2{7kC5_x!Y<6{tchZa_b<{)v6EWqoWpyog(&$2+7z5pQd zXP1(cZ|dpvJ3vYub05_1B@j#fM2fMnv57X99eueMIz~V^e zxsfQOnBXT{S(Mb?_G!AS-Lx-f<)W*pOJLfm@0QFCu8+`JxYI|uz_M>=6SJ=b2BzlL z#yhYykm=nt)JvVO5-|Di-twu}9g2J3z@cwCknh&9f8P|>zRRb+okQQfFK@j3+U-gw6=^)uwv{!z^!os>C z`!fB56H)s5CZ?ZXCh8l2dg>eBzVw*BK!@&gE!{~lD5MblW_B9VHNL36eEGiH7ybbl z_Fc%IOTM}2D(FRBN_-JkznMN}@zuFHK#GUIRA(jz2EJ~z$!la~eV4yXkw2`5eXG9P zfduj(=fG^N*S3S$hp0&|LZ$Frw#SmvW&{XeU&YIW%?mXZ1O=kKYkftBu3IGsU)JSE zO0aTfU1p1!Lt}-p)(h+TXmKjGS$3D@K=hb0zCf1fCS>V53JJ+S6^95;G8ng4A|1F9 z*R!Uu#XOZJsNczMY`}pr4`boQeaO$??$UH9WeCljgH2D8#wp}t zm4y!qNd~)H<32-Qo!al}P`hjp!;*3Z%QRjRSA|8;X|kN6;85HLm1yX9+4 z%h42m6{4A|sUCRAqX=sF=h8y{+7WBH@N~B>ae7NwU>iJU7_cqSq{U|~Q9D?#vuFv^ zC9`Gd=^1s8^1R&T;m5Xc*p1ba^0(NzI)fn3Fb61B%Y-(p1-MJUbj&V z$P!JiR5^^%L$^E3E5pAy*Jm2v=(x43j!UQs9l{V+2wlO7edhfx7ZSSTdD*yaqFJZ7 z`ofSARsLz!&M!ntYnOC!P)FcR)pac=9A&D`y%z!q?1vR9kNx`^?gc6dL{crrphOJk z5(PQY;;XgR%5@Cpdw6@Vlg|5kGe^#owd>?}aa#HJRZB5LHgx1hPU19V87d+Dh09^? zktFHSv`cZ0;G!U$s>}Pq1*Pndy_Jv4#F_I+`ru-&VmLc%H{v#JB8?1HNln{AH$pM7 z(?Z$Qa*G@HcaF3j$(Jk6^>Y16aEj6@IP~M>F??c{U9&gLTkL@{LR)A#*$r9<5E_Yx z$<;$122e`C0nIk{m`q}9II2!XAem`mURrdS+K-VUt0nwDaX<4#p!9wM#Lo{V5mx05 z%O2G6Sc2i$gfa}>CH@Mg4nq$mHOT?wv-`RX|1P_2kR7WD_jpuJ*wOC!prVLabFrynYV|GvU$uK-P%7#M#w@-MsyNElN-y-#O)49mO3)c8 z)VjV7kN?ioWkY}0$LybltVhMtB(z4YgiJtm8oEC&vqqyy}{R zo{Pfwo|En=MGemvIXT|#qrxb@Ia7x%Z2-1=ya|itYN;h@2OPhVDW7>%3~+7OUGgE^ zr;NKU($Q#Gw!`F_s$#z36xL-S>5G~6y%>qxIk;JLthcNt;npiuaKqpaD_77*S%Mq>Ie&y=xj)L+B z=ODe(Cr!uakiEV=6r3y%{ZoBuEkT(Az^*vk(Y zD~cW3#U51G_VUmweg>kzYn8wQ)8VAlU%+|-Mb#hi%FM%Y9i+>eh5lMmSdsG=Zl8Hn z@SFemq5$61_AYx_T2o|8{Ir+NfO(%x1G^N9ha#9oeaB*}8r9`H$h!+`&yW|5KAfMl z@nb%!Cx1&%3r3DB!f{OP+50L3zZ3)~Ytd@*Ew~|fCX@CZ-*4VvuF9{-Xuu~m2E;H_?y^E91335x~B`9vF4+IG^j8?5g)O@U~PE1 z7K=jED8RVT?x5l3SOzr%^GnK=*p(4WVkne98l0Z&R8d>V zW}Ya@MXJOdHy7^l4&E$d&{gSoEx{&iDvs9{VoA|vCE`H7za|#-J+e4f<&941!V+$d z5$W<_xi^I`NYMDofZu5SDkOA>3Cpqf?y+tK9Xn~S7|iEnm4e(U`oC8yt`ROgW7TZB zR$&?@4Ttc8yr;6RpV^H7OVNrITDEjS zsXDO)+2ci;!IIC^v67Q)w^O1wD@_1drlCIU`NOT+#XTi4Z>mlr^q&h&Tk$ zaU71M3Yhm8oAsGh*rRK=Gn+By@;C{vSFvDfZ#<+z!!pO@49jcxawPsfZiIj+d(Ql% z^;%BPzUkzB7J29_CURHxocbkpr3qYsPB}WYmvZ3A;{#R@5>=LgVA>zZElJLo_nPR$ z6fAT>@l5K6iv3X{*|jHj%Bdyk-(*AdSVpSWgkUafbW*61$Xl^d(Sv+fj9ro(V&GF9 zjGKQeeJbSy&9aDyoG_=SJ2|^U4tV5-uE&O$GQn)`*^$602PXeQ3@_SAF!m&Jrpk%V zUr9^USHbtjSyb$_9ZrH4$9)7rt+phFVnXL94no_Vre0{{x`buimkBj{NE7`sPngFQ z=7-ivn7~Q`5HE2Pf}`%)%lh6tY;^98?k2yf8>+|;mx@jw@)4ZtR6I}TKdjR~4|CfeUTWOA5iI-?0FwQ5h5WI$9W{#7oT9Yev)Gr& zKg^exq$O76r0cnF1bsFUqH`zna6c;enGcN393(%Z!b4kZ$h)GMqP3wDUz*($dlPFQ?8i?f3~WcOcVAe-a@bU0*VJBrU^82v(afafD1$zXtfElT zS_Uy;u=h0&z9nqzt&_A2y+@;2XD+dt=w*zZFzDcB|bUJS4$hS&Kl zuS~CFJ$(yh@d6Zwso!g1R_iqme(I@;?&qwCqWZwO3pOh4@luZVSS78|7LR*Wf^~xI z{&F*{5gXR+W-nLbq~2KN_i_k%_Thpx@G~aj3p+(vQqx_{?duPYOl6s?0TLhf&)hw? zW6a5cW`XKPEuWSj)m2Z%&V_t}b!fF@J(~W*9_c^k<~UDDK}@@&E#|B*AMk9p!;TBN zt<0qQr+D9>=pRV*pJ`?T@F3zsxm3m@oHYJZ1W8hx{@9s5IGXEw5N*%LYetBOvzDk9Q|R^EBp zqcd$wH?Iy)inZ4B%o|)j5*kl6BP&#se^TH+ua)HQr3v6j^GpgO8p^@GU3Bo41#9mP zRC(CR2*Gjf%dY0VP14WP5NHCT;s?@v;X|W4)MPW~s`};^n1q^{0rgaGm;viZruSRt*0#SpFO`jkR=8z9B2OVwUG! z&fXNy6X$If0|? zIgo~|~RU&{jd*hOR3(0GJ(hkTzZ5-n{95Mx40HZ_a^rzMNo1+59r=DjX z%W8S5Lg^?-xolJe2i~Z^KwsMkRzNLhGr5I2+}FS_1M_g72W?!s<2n+lfTEn0QXKN~$(vPL+~(?UFRUs#VNRf{321iH7pu!9rx z@+u^wSc@Xn(5vlWsz3@pV{bDvn#lq#-i>A?Nex$c5_EGCQXb5I_zGzPcf3}bdW%#A zIeq?G)vyiGO=;X#BHo!6rFN7b)g{bd^^B4w@=*5p6N>)mrGCyt6}JwWaNvlnUxici z(I`xvKi$ZC%wZ0364%thWuZ`QCTO=wt=9L~QZ$K`7K1t`*aA}%BFtYR58StdIKU9H zVh?fnc&BqLVN#gIh*3fqZ4zAF86|XiC+}s9_L=su(-)u6&LPJ->b~wbreOCV={Q{Z zAtM}b%Y8RT9o2>?)taVbJIdNkrk6VLEfw~y$56AMSclHP*(9u^WfscXJA&AIWReIw z_G8AvGl#-hr36o>0$7$t2?P`ugcW~3F`j&}Rx{ux2 z1UyaDgvAVeY?_>G1~-;}ha5SuCX$j#`1Dg!*sX&rcf%_N8`k7MTOgazJ2*?*%{CeY!3p}KqDlhsFKx37IxsiFMc(Amf$ z{hiBIzRp%Rs4b=uN1t6q21hF&V>GjGsl7_c7PHt$?m3cGi2yv)(7UAb`}wFtmba{t z9gc9zjG=NlC5&8x{OQdzZUcIM46j&qb}& zGO+qDm5c=1CQ^lQLqteQP>$6GtcaahZ-L9Y;g1)1$@PH;T>`aMWfUm`QFrN=c@hjy zlHaNqEFw%FI`8Gx`sqMwl9>F1Pog+5XB5R8@H+;}ew01h6K4t$FP-?c`o;Kc3TEVO znP}B|B)z%3q~8Jucd^x#+|nr3d?EqocG8zVN2L9nwrp}wcyj+rz>_Z0U&M(@Zn9u& zpDcYT!^WxXew#-d$kgz)gayNcti5MSg+NiY-)jBjpO8%HWVkYz za?lI{{dFQVB9$omf^^^KvIyq`7CZC<-h9wBC%EOu2xZiFo2A?xMc)qc1Y{U^5nAp^ zJUnO&6(_W;iog8R*Q?Z>s;&=ybEK*vr^1A~K{82Lg?L+i%Q4W%u{-QaM5gQ}jK0@a z0+a0PjyYkb56)v*A!;EPkAlalapKnZsFw+Y6|1l4_CWz?7>&jv8DkHkV~VpJ=ces@ z|GZ9#yU;gXB<{_jN&ayNf~0egG_EFpg);%3z=kgts}>CTNVmX#KU%@jm!fhW=%4^G zHYjiF4pF&vJ;8=(o7dJ~bEOpbE{pBJgV=I^g6>uja#fwHs`C+=rJd#Pa$cjm9wN8V zd(h<&Weq#gAr-5q!Bw~$;m^qR>LZh9pn^IXnD9wPnOh(fOHs$QsA_D~&B0}qtBzOR zTB=FnRv=_XCz-D>l~TtYP`Bd?3!=3ZcQP{jXmWX5rk!QnfXm1koZ!oS3M5JX8m$b1 zO#dxcj~!(nkXTukfMLoG0&#>t$B?qN#)=s1#qATR;%{z>bTx4-XhyaGT|LKo6@^hsArzeQ zf-9S%L0hK$GH6b@YRmWK5v&_12&OSTHmRC+ot9Cz(Nw`bRS$%9Q%Urm4LUJ+&v*JO-(uIZ+<+BJ}8 z52FyqXFD;`AW^PMjxQ0|*S;O3Nxan-rnXtC_OAhd6>%bZtI`^pT~ejuA(>ddiRz6~ z?BjYbcb8OWo2Vug_Lf8{K3M~X?IMsnV#`1)3pJvnEkv?H<)K89bC$#uQs%}lbr{Vi z-0)2aTzn2@Bl3!s2N~A3>rw`vcIz?OH^#;a1BZ!6-j-bIY3zO{D#h_O&A9x(87Xw} zkPet{gYg(AQaY`Bah<=}>a9+jWPpj?ngtNa~9RvkK|S zJuPABS2e0+aoiQVU_knnmX7-S!)fsBf?>Io1#$HN#d8!F{R7sh7AxpvFY70=dD)Ap z6*uZZJ7K>{jjxK@&PsdfUxLvCMG!%V<)C)lIgOiU;E^P zz^z+YNYM@XFw@Bn*^dwSO~eJbJXJl(@1t(Ehq6c;Mo6d9{;fkmK0ZHN9d#BE)%=~8 zX7NVSk!YG4yZj{zA*A`T0EKXN$}=~Ae6)+4bkWVY-Gu4Fwv<3gT6}`q5vP!kZ=Q~k z!vF)0>d%gqltM5>;XT}ux;%j|6u0e#WFZ_6Qt9#;iB%TH?awrs4Jn+}@IVr=HdKaL zGowb!M9fQPsVGW4HSFIv|AA3DDfDkHac1L>qzd)v>MerhOeNkx0YUzzxZ7W;?jidT zSl~jUHE#I@Ck*@pS#U#?sSOa~=FiVhC6`JvmnVNB|Eq!f% zr1}~R!s#6zIdX?vG15SY2N`QnZLU>(9V@2vg9Xm4h=f&uYpV_69mD~MMPgXv?mpFk=tu>6#kYdQgpb(uogd<6RgWs*>|7U zQGHh1c?Z*i``Bi@|I7jNq@l-eQ5}+pguNQfx#8H1rL<(s2)Dqf;Q3hxrjC8)X?7n| z#v8m&lOy-ro=B^ME*>6;^P0Hw<{?pp=4s)sNK44|kYsyIM{g$2l~;`C6S6rO zl(r+#?YS9nCHyVgYMEEATlHF+SEc9>?LdpU z9C91{7VhlR204)4^u?MwV#U+4!n@pCkz~j>GmcQ2IXig{<%}Td7Qm3I-NnhVXUuJp z3F9Cug+dxuKp>WbB!9$dDLKehLq8`2#0EeeDkW{sB|t3^kbSH~KJ=O!$?u8OF5t_- zk13h2@j_Nq8h=t%Z(NO32T-~792CStj6!z9>q~)5M2R8v{|gurSx=Hv8{uca%0<#X z8{Q`A26aE+AQp<`aq_{a42sukyuqdBC__Q>{G@(9k@MNo#h$%d-1a8J(>a@e~4 zND$Gt&M!nnFEB2)`|B=xFPH?S^bi)ss3Kuqj&V=5y&ylcL^T+7K@HZ4q?pk$t{1Vk zhZX%Q!2gHDv;ML~PW0Ro?`7g&SO&q3&j>sO0O?l-sZ)=7xI{tJl1JTsrQj%YN`j)e zOw1)&@6$>+JT7P7PPk`v{^bC@<+K-?=%mb*_NpkJ_3am!AfoTwWlH)tyz^V%RV%No zkU@f7D6+S`Owx^WG`bho&o4xbDb(^aBZHcEAUdQH&q?g!rv#DKx{4b^FZzebAqb}IH>jD)Vh{~vsQ&Rx#jhLKVufhU%|R>q zq|rEbI8|Xeh7QRxnxQFjX~w=H@uOqG4E@3)idIZ;Dl4abUHAeHZo z1>IyMj6Jjz%QwpJ!?Dd?iu{g~m3#MtBcddxg)EpMgQ>9ic3BsWuNihAg1RcmNoWRd zo&Hl1UWQJSteU1EV+ePs_>duatpJ$IlTwBmtmyJ`UnvFJn?RFP)b6Nm;g@P0UE^Yq z^qa!!F|iwR$RXL%AD%e$;K`364z2_a%3z6z5w_iwLpu{9$xAT*Gcjfyy6}il6RD5> zaIq~A>n)MtU-F2S`GIxEr=~aJ&X7pQ-TCIA$wBq+t+?GYkukH6)n6Aup^JOtv!`|` z*%xEabrpGABM?2tr(LNgamNzYVm(^xC}1^jSGHr*w%c#_mt72Z{k zadWK5y}_4~4v6MR5#0Fg96a5urX#ZvlZS|N9h`5{eL$ECcKID*&x!Cs&n|9SsPvG} z7Z!TM=@K4nF@_bEbh)cTJfb@4E#|x7bCk1x5PL*>girZL^XQ*F;!|}<&EEGy7M7W2 z&*!0Y9NWT$A@WgopL|SxG~FuGT=Z)Gr`Kv*25=(Aim3zf#;B3SBFj4c6wIE`=~5}A zTK<_9Wsh)_?=>Z{u`UyBB_p}yt6+9bN$>Xhs64Y4-5TH;6~Ff((tRUPmLc+>(4am? z{ev9fWY~ml1SoJ=xv>pBK`mmZG~P(y0uU&)IcJ(prwXeBS+-+0+o`omhMrV}4Sg@c zUu8)i)5q@?g)KF-+dlmVu4Iz@}$A=7uip+wve3mhe$7PY%Gz+kzj= z0uzO;sKgycojcH+UB2 zuflv(6Z%%9MwG&HY$7>Ey0b>%vm5bs2sOZeoO*X*@o_u&(pN~4j>8MimR84h7LuzZ z2$+=>4weY7;Q=Y2*h2Kt=^8VPN!DLLBAt*i+M9GQvi3d}y8O3F;rgD>qki%A5*82Y zbwvSd21-VFO$oG+(%YKyi4++_j{1g8jpl74n>9Y*QggTvBujGQo+h@FcV^40M-IJP zR0y+pl20+j_Xk|+y)?t-R-@>Y5l-d3`zWw5e&|{)olksy=s9Q+NZ{a!xzZS~=z&O- zJMv-OyEmwjb*!nn3T>$8pGN$QL2CQ+`1jo{n}rF1Bqn{a#mW_=4iqcjua|6Slj;XL z6Tw0+k%!TB_ebx62!3sCA=Oa6T7pUt1CL0-1;NOH7l1Ibla zd_?SY?F!p}$a=Gqg<0d`63bA-IDa>^(fXp#@YZBH)+niHel{@{U~T6+KG2Yo1@kfD zIh!bY8|aQ&dQ3F^q6uCRBnCNeoB17U#yQ!1WOl`qv^O`PU2w=nSB1_J$AkhfSqBuv z>*phF?qnrf?b78mg+$?xo-$M5Mk@$qL`i!u8Hsj^8hB9hl=Z`smdSW3r4Lcl$o~S5>vbQ;?uKU+X)`Eeg=X0+z@|?PHd3rTSfml6UbF|9 zJWQ@&iisVttyd9;eDG+hTJp(-*Hn{cHtzu5v$~yRM;@7hiq>`>05O!1P zn^Xh{a0RZ=jTT(Y4OjT$s2d?ruVpGgJtaIDgaGq>NsL*Z(;s#FIxTY2HR^+s{w?kt zRP-DNjvgroZy$q)b5-i%d(lPevp<2(T+8hTw2w3Bkaq3XxXC=_4ja%qaEQVZ*5YB< zJ+4P|v2(XzTn>TPpvo%8@-`^Z|tyQ!44O56m`a(AJpXLbK({LA`g4R3C9}SItTM!Fe8@k~N zAGaKK-vhXpBy);lTult8KEFI0OT#-Q`FCa7#3mG{b?*og49tnY_tKxsm!!`5aIIsz zGr--RyfohOB#azy zBM8A;+k`h}oO83$!$4n`GaP2ZcQc?n`qyd+kU&xvX8ilW->ieoJQeWfRm)4#lmmJ% z#Oi}=FWc$XJ59aI`y=INecpOw7K71D<#UWvj5sVD0_S=pk%;ncmXAiM#v1k7_MVDr zop*I};mczcBam>tib({rUpj9JEQEFELhVdkFqas_>G^c*?Iuy>%gwVB`l`suVWyZm z=$=|;^UXtas^O69Ctx#FoV}_E?L>(<2mmF3ufZD*nEQXU8#off0>%%IJu;d4^;gw^Ymq=*miz0{{ju@Ezu zZ;*$6Usxz#;4>P!FefNoY7!yvKi2VIkK6{lmu*4t@^JcmKAQCfoe9S-{`B{rmG1oi z;;zVGuc1=3iVUqClTD zltqy>#5aB}#d~t)SbVi z0Z?j;;9ch?8z2WQ-!C}gM05}`ll{{L=Z^JHQkOI_Qwt>47N4x{9q)4i(E5S!mT1omSSqY)Vn?e zyJ62fQtgn`qNvA-c6R2}%5&bdKqAm;*s#%Kzg|}oxTj`6GNjYUr1v422-d6^iKV!$ zvzqd+qfg?e$j{+{xD^R~Cq3d8_2~5O8RId?7q`8qk!$*KwXHXH|LG3jTM_D|&8i zb|q;fh)k4$cKUjErww-CbuV$ClNp4!?^QyxxQUn+)d;`ow0+Fdsr46nl2ukLRN}6a zI*L(ShjU#0`r>*&*(Ac>rlBI>>T|Pfo{AS=EeQ--k?{~6rzT_Ry!!Uk|0Ic-2$nqU zJU+kjB$hsu*vk-mhmlcqJVHqx;(owf3qVl zm5=zz5llv~VjvV7(GUK|PO1k>@zy7-EzPsAt`j4-2pS|OGam}EBMQmeC`Nkw?o%_e zhd-n@h9I9d`)HDcvGs;akuEq4GVcNtk7Cn>y+I`$ype+0{A45-s?#bkSxrS^6wAZo2aL+MNDc41mG}WH0_<<==Jpx8X3VFh8db8lqoXBnUMB>(NBqfUB z@2jr1&+Y8rCI={InWK=fs2L8;0)LHU5r?L4Yn411d(-r~0!~>x*@IfKL9H}X6yQWg zX*JwiixtO!F_>2C$uz{8N;cl*7g`3IHCBAR47 zc+_gna6;-3XF_x2c9)J7emfgPyL5Q=EEf3xI?CH-pz+r%iiIiHm~}}a(AZ>W6j)Fb zeX`=$sNS4l)t4!L_tzZfOh)w@A9ouG$GQ-Vz(T*7UpBxaJ)Y;ORa<;3K-ee%T>+hM zQkVht(Hl8O*sOZBRQJLQ_DkTpYBaiK&F!$ZV#rJF!D#bDgV(SY}@KV5DVu5-s=v$O?`IqtqTTe?dzgJN%FyxXZivW>28;P_dHtx|oh{YnRV64T!?Pfw6J5Kt1)Adx+1mB=Y23=^HFgda#kxLqTBm^plw65ee zqgYWDF8Myc=|U^b-pa*8K#MS&jYC^kfby@E=x&vE4F z24>!9YA?JQk{fQ;>Pz|jiAmyFbWdtCVrGh=(P@E$2>L^Eon*QG31(}f%@u$6H11;$ zvw`8%mmqiNqwl-ZHdybyux*cY@}Vuz)T$c#f$JYZ4W8L~4Y?0^X0CBw%zMKB#A&_PRNhW3UU6 zM|Ja0QuM`=B#;6Glm0F~NP39U+KBG2*Fy;NTmA6nIR-AHXIq>Y0LmF>Rqap^*MJoy zNE2Wb)&CDc_9$jkrV5rVbqEasZq7uq_z-U_|i^)Rd+y@TfLQ9*iQF z>~!l;-O^*hBLWmfp?2OGBJH3Jm9)g%9WMI-d?MkIOZ#TFyGEDR+H+bk)W-~<-}lT8 zD39b%!W1-RYD!8tB2>oc`SPH$9j-d-DT!W@`B%9u(#Rx_WDB&!byTcSq42)=$l-!s zBjrt@vJ3Zk^U6t3f&(nf?5^E1bT*4|tz}}y?W}&AUDen0iArlHt{R*890nuz=0Dk)4~OKd5nrZ;lZb zxl|ZZ&v%j^!I__T^ZIzZAQOXKU5H7T}#$`xkiwt ztj+;yJ^(D<2!3OZXBAZSi<|~IIm8ioQr#V9&Y^UCWB~tuW=NzUE6Rq+>0*rS^^n+Q z>Q|=0?%N(lTf$J`p{&?a9EkS-q`8$Bv?7$CYqm!0KW7qt{5fDFVpe`1@+$@rXj)l9 zCsEW5g7w0FNEx|~B!a6(zB{FO%tMworm@bQR!i(cNJ|2Oxdmt>J9v8VS>%QO0@hjy zjf;^D+W6t7NkXvKNmZ5zrN$sSZY3Eh_tH@Sk%^3apWUfN(|NiwlP%0W8>^1(I=!X$ z-!F9ckHBWZhHwh0SLH_bmLx;|h&(~>l5NayLuaYk`{@p>PR@1+>7CZ)7&V{}-~I&S zVNHQ}H%8R4@}1ys{J0C>r5@JYNlT^HxKa5XoW3YsO}TN_$U&3(TT} za&}>&=?4!SrDx}^`v)VlvP0b6a>e(i|CK4{y>Q-^|&rMOv zTcwTn@Af(ilFFWclWC)PFVznOU#uwXNagm^E$|d<5L0p@X4T_SSY{QIsm75tFWek$ zf|4m#SUa46_P;vD6|yu4My2k^Ey6wTP!&Rif#_0}&1Sx+UfEj!qeasXy=5XaT>@X9 zLdN%BZtii`%S<{Yqw-NEyq%l=pUVGB&P8DtT>eozK0c9`%FzV<9NbuStQE=bk`$#k>P2s zjNv}g^@K382q*4H3$J(TRDjwfWbZE(0s3Kf0sUsUmiIrSB1L>pfc`xVrNYFJK!k79 z5*;|lVkY>QY?IB@?m`a)iTU0n0V2@u!ayLp;^`#;FShTue}{l4DJ!|;O>9;pbqnd( z7}O>F40ePNd0hd%^DOCg*Panu#_scLbmZ_lCe8tBOof3BzBO*}N;Qp=`wO4VvmmDp zqn}(jl@Lg0NCy#2F|5qAISc>i-%6C+rNNvVsEbEuYz6~VRQAKg9_c@M!tk&=<^ET8 zY-ycEYSYK1^RYDKBEa$qn%sc3j(~+ILUP*|lp3lHvsuYN)D6^=eI%Kn{HN2)0%p1u z%IUc5g-tQ1(0%oSA*lLj%G?tDbRSZ=hgvU^Cx$6s$rI;hU#&u50f@D6IE@kVE04IWKjRw^efmqZ+k97$m+>u_BrqAF;(zSC~IlU`K9 z0?cOlR=%Z|aRQ$`w-Q0dGdnqg-~j8Fs}W24Aq5&esqPz!n7y=#!460lcRJOHsBtvM zcRwm75W;Z)mkqKORXg1xW-KixMiS9B^t7$FmrJ`Vory-%&}-mj;5tE(7yy46VO&5- zia%2b@e!i3a_mb^YQOv3#-t0%(OX1bkd>`q9j#71+RdlD z1!D*c{=89E&(QyQ$xONs>imwqMZaVV2;ok(6JWqO0R_EcqesLup1;ysrMTb~bvt~v zZ#Sbx1z0f>fXD>*g%+4 zZ!z;*4m#`BQVN#fXw7R)1?{2F^^hV&&>mKyt4#h(ONEt3zsb1$P4YQIcdgRaHLOdT zkD;3)qlUB(a?A-$U-&bm`q(e#V*YqkpE~G_D~CAhz?QZ$6FfLYiST)^!~i~@d?I^n zl|mIsQsg;6!NlM88d@>g8P{B3ENUNg3JA*XuSUz)D++jVwaECr229+~0T_ z7zHT9Iteuc>#Rj{VpG3!(!PHchQdq;Z}oLSp0Ca}=?Du(rYpdh1m~0oM^MLX#L06X z*GC)QLkbO*aH_eUD5-r$Pc33EGE;L&p>|j`GyqTbkYNpk3WBp7y}oYbp6L)rch?ev zeAEW z2-WEWF{m&nN@8z{ER&;?H&^b?nJ``sj$snaw7%kx^(gk%IlhN(t9|0$h zFT~$K8|6GT0vl)gGY^xxi(%B;JFMIz-gwBn(a8!fDDgsl3a2!>kuM%l=yAT{6e%ns zO<~(1z2Pxb&dTcTe(GNNo%>ho+>Y$IlvX?uNl~JjA}Ipf17qIcRC4$~#6lTiHTv<8 zA(5^*HarH4(l@%u>XMA1^4wojevYl95o=h$Z@^ZTN5t9fTS zPV8evHK3Mr(58%Ks09kSGx+x9w1Etq$hB|cO75V?p0l9pkDj~ zDydhWHi-A=(M6XjMS_b)S|XGlObjg`fn@4-03T5@|zB_WF~OlJu~ z9@}bdQuE7fODN(_SnCo~bsNQ@XyUWL5O1cYC`~{Y5-u0LyUwIB%Em@(qI>a-o3Fm+ zeZi&Z^vxI={JSij^?ZYieaz2}OahCbmh22~+6eY&VTVXSp0J|nOBKkKy;+rJ(a#} zoLz&v>^qoltsxk3_z8vmax=-YElO$`^6-F*E7q(3Q2vOR+~gag=3HcY5ssyWp&+

?R4wK8QIEP1Kg{=O1nOJ{EcekN1_8U3@io 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 z783�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 U48wH?Nr*?2Ra2&FRGOUQ_d|7Rbr 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�+pL!JM{~3S(yj`x%%zS9$qGJK_@Vy*1n#r9@Tr$t@lE~Kv!Y65f$Y^Ij!Q{p z$qN<6b2EZVNYZgBv~5nO>4V9`$VpOSC_3u)ld7{y_|py0@CA zY_H}4<$;{xGm*G@8rINt4O>dnp@&U^Ip(=90gdEmUD+EFNP*hhw*tF?9YEwYee3Z9 z_B}t?B>X2AnvaI{0+%K!=88+}N!9-75;OtS5XwO;$i%J+?P|iY{f_=5cyq)4eUv&n z&f%oHOUc!yD48iCPce`H8G!N~T}U}6IImJ!y%|H2cW?AK9$!{7(H;}wF(#=vwXld! zeQ$6Y{o_KZ<`eQ3b@G*Y;1J45UKorT^^I3i@PCLkEka#J3rjsZD`2W+yves=3`%A5 z?Q+q({F{`v?aV}ns2Q?+71BH#WhCG5l49)5|D?(x+Qg0}HrAI(ZIoiai z;DOQU9|-(c<82PY>sy(uJU>0mwl|lyp%PhGknzY~Ex{uy8aWm5y)sY-U~9R?+o;5o&h>#8#R;?Y5b3ZFEv-+TtXJ@>gw{jovv~VvW}$aNpDd zKT&=h-_7Fqb&m|mZ+Q$1qT=4+@;0ootdTSAJs1uGlHV_=d-nr`ZT3_ zzliPZS5WfqVUF$;m&%$;OF6zqS$D>=M=?oC7@G>|wa3a9wWiw&v5&-j<={(?5Jhmh z4xpO7xYQo<;>sDC{{VfvENd)z+)+G}ZUNX;n__P$K_UBx(_^>4H=4|vq5D)z5=*Gb z9l$`+`Al{~7Bt8M3hE@@TjhSg)=pn5Xu3p>ZUlOIc!cxO<&G)&i8N*Ed$e5H&u_IU&{nG|`BPW`OT zTMd!7wosLtIaFM>J`?z{ZQ<)(GsC_X(=@w?oBKrsk{H=U%BW%>f;`9EgzO9Qs;&SS z8RNBCVDbL|#61H-irsYkD3U8?Ss7wPw}*2xZf+G^rU55t;YY1|Ux_{)c)r_Qy4SV6 zB^KfU#xJ#8G**bK8zdI+1Z8GpH%JsO!EUXd-*N zi`(l%V+80{Kvz#yjb7am1Tn=PRaA@&0BhEzj-wQ#eNJkWXMIhd4d|MGiuIT-M7Jww zionj3KvF=iT?0ZOIYW%9sT>t2I2rn9O&4ipajEI^UEOK=b+l61+S|;fH=;^fITn1t zk^Rz*m32FbWCLLNI@86s_kJjz)?0|JZl}DxiYrLAw%d3u1i_3^vxB-oV+pt%;0zH| zym#Xt6>Az@#-FX&_;%w-V>k9)!^?QOfs)*f*4uZ(3o^2|HxMr$y(f1y%T|7T<2)u*8EH2 zkBTvA+J>WXFoMo2pqFZd%I^%};`1ZR?Hd#ZV5fAL-G=e)FTr~LudBOj)|c9?)UXDS zp}I$oXN*j$mjvO!8M z%^Q~txl+n`KX$G-Xi`?EEmjOp6AS%!)({CS>JKO2Vy|NmOTpeCmiEE zS7YKW4l6nBB+zeDQ2yAFSe1}$Qsjh;%OrtG`3p0?%nlonYd6AP6x6&=YkLihksyLJ zyHhB}=GkNuh#-Oj8I%=t zk7D2x`@%9!d-ZB^w61wDTC=4W-X*=4Rg~$r&3AJ!dBv>WbbDirM9QWHlRLKuCw|oK zaa`|(n@)?wTHThrZcANRC)s11%epy?w(pcxLeAwDP!xa}vNsIXo83~=L%6xsH1?L? z?YGg{v@v)#BVueQ?mi zv9pmQP8f+Jj4(1j*7Hn`s+lFR*nAb&J|lR)#4DgpsZVEQ*CzVoP_}{@=7QSNIn#7$ zIx<8hov< zo^u%4U6xz`7ytz1mdRBG_#BTH2JIuFojbG%(e%k+#c{xVVU(Yrd|#_;R~Fiam9FbAsB0f- zMzd1u1mXoHEvIs*GDuyF!!QCt&p2965_XF;vp&A?uZp}asA?A4Bzmp1VPuUVf#Z@X z2GYy5#TG#Vq?7#SHkv153vhOvhYHHTd$)xAOui)1Ej67S z{iXsWvXwSmK@5*&DYuj~S7bD5T~5&81J!b+Ot+q&l_x6^z%PzyEEw$8F`;eWo zO9aqc1W9lT$g;V?-Q|&xKSA~RyeEBR_c~iCftndaizDuiV&MZu>UN!xw^iJS({}3f zPaJ$O(EM59H@3Kj9Zn+eG}coh${efYJVA;)E(kyB-{#4{=V|K0xa}hetq+NDKpoPqZi6Awa~3K_W^P86B8yUBoWqg3FLke+ABks$9iq1^oK2+TRXQJmG=|lDE2-Mq%Lqr`tV1pG?B6En%w=51 zv=!SdBuE)SVi$3)A6@bH#0>{q-QuXVJ1bbD{nhovd*T@bCKCv7!3zw&^liM9QONI6 zjJ^Y!&#CmqzLBN)R>~a*Nw$*GYqm1n-(RZ9B+|&|Yiwe~M1ufEnE64^%vq0&-rHVS zUrDAJVzIw-HRKc9$ra71k}f1w4*vjXj!3}_1d*!88CDKRCa>vV8+;G0+f6D-eGSZk zVV*0fktLN_5(HBtJIX*f3A-ggINUQ{A@MWDS}vidTHE+XQ@D~C=bH=_qj=V6*(Zsa zvmg=%IbG#k9DoY&;ZA(kXo)k;{ASX}JbG}}-&T%r)5;?yQAlE_q$4zLqZv}9f^t}7 z0(m%^ODSW!gdaZL#E}qB%2a|67~o^nX02OVS=%T?!n5@U1E*j+04Zgf+ADXsm4J-_ zC6C=0>QAt*O)D!LQg?SD@dl@T;xPB~qX^Z+cCm&out0^1TgFHqB5z_kb?7U+@K1*P zFK6M2JZs}oA-9Ry?QKJ%i>{Mb5j7xE}fS_(Cjw$$d%FcfeeWrUeaUI0%K9eq0bbnwiT^ zR9tuFXVFvm6XAZN;5{e)5sQ61-rYqd<)ys9CihNmEn?IoCOe7z(Y82l%u;U3sugrb z@UIVO7nTO|#2O}-4y&ZS#KwCmrbsTVwkt^-$-Uy-VcZn3LJ!>_2Mt{x!6>XRG!1`5 zmefsq8okOijUkRWonrZxH4K~DWGKu);DAmIcHRS)N&G85v9IXRTFBG=rVUc^Z?j0` z%+baT)a4>ec-uHCN!&sC!je4qDN}AO-0O-`*2hg_qoL*!nV5Kio8~r(Op^#X<^eYUi(bk%2ZuQ zE)Y7-nUV*RCgmI?#|yZ8duguO-CA0Bd*ZAEUAB9+v9y-JD-f0}a8M%_80s_SVT+n54NQ%bZ*yro)iYHdTXQWxhD^ zFYM9r&rzEGEh-BcH7zbyxxcYnn?;^Qk(TH=}MsRJIP$yNc(bj>SSrM#j}Jw5Gk|@cUSK?6nKHHHaS4_S#<}Naq1u zO1~*mqmXdDz~`-VU)mQ?+*-3TgpOU@fsS}Tp*(i4)W6yn;r{@IzBlMM-X)7ik3#X5 zkzj8H)r=__)K#KMl-+IGGKfeLs$_-O*>9MBPtR#`k;d$-+kR(Wq!4-F@=bky2Nmt% zds&}3jH2mE(bXOGzOATOTfpY$?Fy1Kh225MN!l_9I8ehJ_7ojr>r=OsMmJ3)Y^BBs z+IY$8*&km+j`f`6UfC2j@-#EX3OpnN2m`7f4sn{NptQPU&n3FxOoSuOxlx0ho_c|i zfnB_;a`So4bgbX$*|sVJKbl z81c%IJplCSTFY_gPrFZ7BVzEXKm?PJ+4)C5<5FM$0BJ|3u!c51OK)ryJAgPO4!dxD zGsjA417{gyX(Ts5Vb}>U*=7oH*o^e_Bbtj>ww5MWClIz(QzY&7&N_c8-xp}N(noa2 ztnndGKmf5TlC9AC3>;R5w*kGej!0J#Atgf$AD4rlKtZL-D;6}FEfY_HZjxmOgbv50nE*f?EU?1YrAAH!&>HG>(tvLxpBs z=WcO>$pmqbPv=b{=4pO;Ut#DDMleC?T2P=a@g{$Gq?6CnoK*pb_GqJR_Kp|=7?}YF z9Zmq?^G=@WsifY z@ahq27VZ`xhBr_+Q0J-j8OBd)T&xK!RLu(wo0$~39AMz=>V3cz!oE;dCzV_<&U*7q zv~n26z#)c4M<v9ohUtQ}qCmT#S**fw_GL>A|T2_LPyJl*mu+j>gCL7?1(>&p)kA z;*oCm$$M)n&vA7PlE(~dlFsp}5P1991M|gaq#_5n0l1%Jsz<2ck4zj@y$)TP#f{h5 z#mCsB7eXf8wZvb%DoPN+a=euwfzZ=TxGU&=@uq3f>0b@JL*UN_!=NkpZ%RSpNFtWv zNo0~sZ!+p~@;JCbBs`!grwDEQJmvO4z!x3Mx!*MFfbqq7iRp!c%EHGx| zoRXwwzD?4#?QcW0j_XO(WVZ9Om53(LUj>5)Qa2r|^cCXMrOzozGtlNy(I5ZR``T|G z>5}WWN$~dEc)P>{%#PM5=4*H*LPwb$?DtY5M%L^?w2`4309NO4s=hS7@kWKHs%p1d zgqq#`vPD0Jd=DzcXePMHhC8`j{jw#MfQmWM;RG-$pa5&dHILd4;fKPzEe}EPP3X60 zlzpCSC}X~n1nS8lM3|2|?>ncOKv@{O{J$ye{u+4aRJDdb5cqdU(=M>hS2IJXNYk z4b0cm$>p1pktCBoT;b(rL<8l7Y*ba?ok~hvsh+d?T0xD0vIh<_Gpe#vcj(J>2Mbnp*r*w($FEw*{`hvUJy$8#KGQBtHAA zf@ImeMuCv;TyI^2Bk@be-URWVj%{_{ggzC5>iX-!nzi8ZPN_UmMGfOyTuTF8J^+#t zBOnnjHfIsB47ogb*vZ8>y#-QEOLMRI1M&M`_UBek;l^RJT zV5;PTe7QISp3yqd-Vh6?7+TV3m98XbRr^QErT}*gd7D`63czPI~gut{So-# z@zcb5U&m?uJE7|`T4@@kt&QE-w`k=3#iN_FG6Wg_09A7FNHQ31X!PA`B^l- zw%Z>fVG|LPy-RwYO|J)PN<${^gX}8KeT7Y{{RHo$*Fi_Lc8%~UL?1Tw;M5W4VCor zPU@z4Opw53ZL*z;8cZLUaBH^l_rTBEQ{q>NG>;T~Xw-Bsg;pu1TVEvlPug!T9U_)h zh)BMCkt~t9WsO=$&~J`0mumCR9cu~Te}ieGcmQes8PV-^>upZ)wHy0))oxzWWj6N~ zR&6ZOJaEJ|N~IK>s}RxebIfUfwI_hQcRsPBe0|h4KM-lU-PCbfoi#3ClTNo;%!kT` zAd0}YCSV->&@fIm`j}`q!6s^-k)h(Rff}d8y$0XKR~|0$w}|`~rd&@AhNG;+wze~Q zZ{|B%S_Ww#j#6bAF{x0kBVl%j@x4=C@W!R$3vs4uiKg4fbs8bLxlv?R+ZzLr=K!+& zrMbsEU<&;R@O}4)^rnwM_(Sk-T=8y=K8teuJX0_B1S3tng`kpq6MJ~`+@;8Uz&KTo zMk;`B8-EUUjT-C2z9hWx{<&{)X8LX8!E}ar>mVU;=;}mkAY>}xgY*6v@ou-QK9i=WgEbve8+*fd4b7dqZruCi zERq5uGOz?Wm>0ly-M|CN#N}(Bnmdis=+9%h*Co|g!?yk*itjPRi=m$rDG?W3;>vc??J%E4KR?fmN1M#Egym;rqe!0NF<` zuQz+x(lA@Lc|4vszVfw?4fvk+YrQW;f(=I6<5_l@qn+3{&KxvxurIbfr-cJK99GVm z;q7j1H&@fVO4oNwdLz_ytGHr$ZWbpQc&+@X84kkO4(DkY$T{MySNFGGC9sFb);gq5 za&BXu-t=3^XB>9IaRj0#cAZP|(n!USvk<7bE$BszTGWSTw@ zAc)1aK_>-r4*(9{mFCu^I&;%r=2a(dhbi&*#~vPkhqiXsntz$8+m*667amUWA_B4* zBV28ig~{3m4`I^0*G#sLUid*TjW2bJE6cqG!dN1L;u!?+F_Z*I=LCmw@`1Ss3fSBS zB5#8}4%2=v_gC-GhJ*IuY0PuX2tRLO#uJf56A7`T_eP_{U7tekAEW8}QDZe{p@P z!FJl*wmS=%&G=sq8Y?j$kG73HK~Xhr~S zZ!&Di$1B}GAE5SHr-rWOXs-0VD%#?8Wpa`jjHL)A4qxRhpOkdR8SFH5KO1-+E31g~{ZjWz zyJ+HBTFsVcL|v++!FMynRY52=Al;9cWCDJ6&VnQIukG&j2T(H;>XI{|>IsiF%m`dD z2O#H%JXf`RGm}!$tgbKoJ)`J$X*7)_vfC^%&p8Ss%&juWfQ&zaFvSMeBLwnK%vE|;Z_O95ns@C5qiSx; zYp+=9mhub>yC;d}1xDbr+hk0P2Tbku-O%qHcUAF)gX$U;+iQADgBv}eON3xJb$fXf zE>w&>YzqU_S1NQ(8^!mcMYq##8f$&i>K8VRWh9#jD!*&8TX`1)1Tzu=^&Kr<-fR96 zw^$!e)h{hAmnim9KBFegkbavawX6O@3c!q|?fotH)Yk9Q&Tf)|DZt@3{ z?AGx?8#5>*%Wa6_KH;M|=togf+;}s>SI;HBjp6MQNLT%25y2Id$-5ZDwgTPMwgz_+ zIP1p{_;#ox-VxU#y?_mcS)!4Z)9<9H$9l4yf~=9a=tg<2uHRBisNUlG{>JGT zY?c5vILOHil12z#4lsWn^ql3fT&t<`*X>2(eMiF|3pFiMM$@k#n#wj=V_6pJB0(qt zh?Dno)pnEoSv;S^xa|Bd;_Y#~Np+{+cxG83y1l-NB`~-wzFIshag}W07_k{El{mo1 z?|;QF9Q-lxmW`WJxzSaIEY2F#}WRU5W zvO3vMc{GAFK^b!_f?|akA0N4GoeJay!LHNB-?5&rr2H)LFUD_%-YGi%ooRO;nQd_r zCFq7XWi#8oyplyCfVf9`mOK$0t>f)GK^Arz zm8%F^=H}bXXsw=HZVqwsY$3N7J9s1-=`|mQegaR4y2zU1X>H-uMB5`nBwke5l*c5I zN`z$_YV81==eIs(HxF$mQQJ?E61rTh+V~}Xs`yfUVs%0|^P~|s}4>`a*^v^Y=>AnU! zX=${}n@uU>GU)cR`9d=$LL6-(=3xrvT#}K6Qd=cS92J~?BGvqDs%n>V8@AK!o-3Gb zRn$QX9HD5?0fM+BDc(H@!LL6sbt+;VSv}2d?pYp1Y2u#}{5;pBpTYkCV1nXHPa@<9 zl_9oi2zQFy67I}FVA5?x+(}*73~+pTt7+Q)taZITOv2_d6w)yp46&*fR8#W+yMQ}~ zCy~cbMX~UHqcxO%7x0_u*V>PWmT5IeUI@O?6oY-jENn@5lWK2PJ2w(T5;z`(;D3W2 z7Swz<7L($uRnupTn~SKdCYZJ8j$hqOB#~xyKubnrQ5!Jf8I=6LqUA8c;L^p^e)Cq` z^RX&*e|C}LegW_wiM}G-L8bV6%UF&Q=K5?$1TBp1UByh(5%*$`aXn53O?{W8{4e-j z@GDFH*wVEaW$`k@AKE-kc?;@)Wip@Nx(4#V%({_q-fqN*6s994)|lk zQg}DRIz-`a7EoZ+bqI{UVz2L12vS0DFwyTUNp{NT)4X%zi;X8rgHF=nhSKunmn$Q$ zmn!1V2=^ow8sNs|}>U_E5IJMs&CAW!u4{c|zS)|uC z5Zhl`OQ%dFNn~`MG-AJLlgpKZs`i?m;jf8aBeL;Ek!>W)cP`nL?c(1a;VDAFjDtYG?siu$szbxyq?xR&I45kUAR@xX2aq`E- zFWLV9<442a3G4S?4&GVJ^T&B@H-{}`GbBvF+>3isic?>Q&Tzh?gc zjz1c7nQgo~;(Ogj_ftWeM7p)Ogh3Q+JmC$HirOhkDf0KcMkI20CN^r<5eHp5u+f{) ze5%bJulrDVC&oG!mi`KdN0BsZduZjec>JF<#Q;^O4pZsF8B-a zO3%XDf5dATt*w_$)@?Phr~RTceR*o9Ys8RBs*o9@ketZC0Go#-WAtY8;#Y+{aUPNI z&*CNOMdJNt7u6$=Mpe0wOAdvt<9Pv=Ib;BZ3_&jWU=emTe97V&W&NJ~Z6rP;jvo+1 zeQygcqa2}bC$)&V`%p$7JEMHRXEF>)8Du+2Q^vC?(Whzs=CZ@ne)m)JljBaA@ms?0 zq$iAgC3T=Sm*R`txFL9CWR^RnRx1>!xOm8mdli(X=LKb?g2L-7=TA)80n>}=mj zvb1Z!_-9mSOGx#I?h08o+q*On%BdW% znWjVgmXQLoD(+y)E4DeClvG?!cZQ4BEc}rDr8O-#PS)(Lp|OU-=1aT#vhlQ@V_VB` zlA|-T7BU%2G0s>pI3NHkwfIT#8&MiH#lEYpOf4ZV`}RXKFe+z^kc+&Gqbr;p+mxJ; zN%bF#-w}doxBmbQyhGr4EHvxkIt%?$Gd#0B#_Zhr(M7Q24>CC|+?60FGB(hpvE6EL-bX%U#1qNpFO?*6 zE8}wwq(_G5VuE^Om6OB%GWd6>c!R*2l1-!PHxWa1BsUQ3xtb7#<7mqb$i+#>AH*4Z zS5+#ss>aJnBbjq4Hr9vHnzZ`N-ZSv${3I7JU!60=5n0VAGr+Om>Hh$1jn*mDD2<(v zl_RSVaBwS))#B79)())=uFIWSJi`P^?vgN4?9n14EEAEL0pUh|VS-7@@b|=RGgZ^{ z>pe~pABc3TC=@iTR7!1TNbM$Qj@4l#PQ+p7XJWj>61lx+JU%& zYbjbrw=v!2JgJg5PdX)L`D11*TWKMYaa?%Dp!r_r@pnfdqWHe&!&>xOhNr0NR@a&p z+yYHD=KAs|OszVwBuIaA=6uVQaG)PCc)>p5ropcGCtAArX&$lQe-**v9TGngT^X*e z*h?INs>0Je#UtJF#7SY0j8_@^ z>)>a^H?!4lybUy79@TVT6Y4g4j-9S(&_17QHT+DGM ze$6z9ORF*SCy}IBaM8Mik2oN?UD*wsbm9~x6qiEj)@yV2a_aL_w6?vrfLrC6=UXXD z$rQ24mXahR_lYb|mqt(y@Ku$8!SkCh6X^GvR2qJzaGHLnr{4y#)CO}LyFBP*S1Pva z9Qzl`b#07EMjP^{$^Hv`ZnyZuW8z;C8(1{RwB1mYYklakppSBwidklwIT=RdCU!Z& z9Fi;4Z)cxH3F25G5k{~5opiS4c0Ikzmrs!8Sz~4n008aCAc30l6nS)KY-FCO=X_rc zpNFp@@zt%gFJr1*nIyJVC9QAOL)-|CFqxJ;=)mLW>B$x9Ukg4bX`U(5?pE{cnm(mv zDYw;hJBx*!TS(3t77hU24Z;+a8v%hJ9i$rO{HqO3XS3I??aiLE5-7Iv_PjEq7_FvR z0}_FqcF4H$xytPXf%E(pcK-khejvo!ew+4-$!_AexzsmiSz{%@ir_PrD+A|gE4f#d zI}`z>@1P%V_^ZMC-l?I@AAy<^J-f?rmb#;vBS~b-$q&ye<_nI@926%7fxxeXt@W#q zhQ1)Zmsin-fpHD-isA{AiRJT>!evsc z2(}2qT)Cb<qy)D*#Rf?-}VjSHw*R>^IkztE@(%6}*ZyEMrWcBH}BBcrK%2 zIgPWy-5-{~IEcFyEmo)I-^4!+=$dYsed50g+le%=*LN(I^2IOM%&|!{o@6U8oR+UV zi#iy-SY`7Wl*1@v^UjrLpth&st6fi4xLXU2Q8fz)uAX@!YuO`tA|Y-hP@+xpBV>Tg z6L}p^&^|x-UuWP?33!Ii9TwWbH3gD2zlw4je2tg2h!lS^GVFFmC=)JJW>f*k#Qq-e zhP~ol1j*tHnUhApyB79RT?ct$y@UIwAR=xrZ6@ub%y{@Dao7FpX@mxAwlIc_2K#@1uuB9z$X};WuBmlc-Cw3g?lAz-w-MmL-@mkyVgl%ub{{Uo$ z-q%cy`p3>>vVvYK-)tWV9kaklfm_0jhb(cd_$sXJ|v4+g3rT7M73v}PiSG9Va>$W(k5m{ zVpb4YoF>$MFi$2n3Nli%+QX$MC^OD}G5E4i9(eD=7Mj)67W*#aw-0Cq+>orqmz!)X zC6JdqVNO&ITOT&x(7ZRTY4>*#+5NuqJLj4kdzh?mBa!1X#~5@n1WoFw zr#VB<&0aaEcr!CatH_bq>JT)Rw)Yo+wWPo|GUZ|nWMD`tgu0f&UW9t5fHYqpOXBOD zZ^zARr(4aWm2F2;0!ZCN_<(PZ_ku|>0tvP;U9EzER~>lMR8)Dp@BM0F8_IiXL2Gm4 zEeB7%)UBsFY}Xotz0Q?kEy1_GVT8Og+j)^iCAmz*!Ivc)D2=cY(c1hzK|GpgfFDxv z3BM7^1nh-unj6PHLx`mc+X|^mko$Jbtg3*972fC?+dqUZbZu)&GU~b&+nM6Hovxx- zWVjBCa{9yh#@Z(x*$zXLoI7x4)D0~Sa+|fBH8+?g8 zU^v5YPS7_1EA!LC_qt5_6If}N@ao!!hf-U{whkWQRE(8%7zJc$1GYC1Uyqb=Z}wgB zGiiE`m1E+G=bppGSNfsUt#!EDdds$CM28_)%8fxq#|n>m2o@y3oPMYJZH-a z``uS=waYk6aE6udGj z?K{~V1_#ZXNz^s{2HVD$uobzz2G;H+iD#BG45Q4E5)a)hdy&AEP?ua_gMd4tm98hM zx;_!nTfj~6-&F9<&tH^$I zkQoq|z>(X^$vFTL7P;q7nft8HN(ot`#=Hx!>t76YnRR~;YSYgOk_`MA(zV~j6{qvQYxx@MES08#m*DZ{aDqtIes8~KD7SM zXy+!*yy^A?dFliQ zW3*e6_Zugj9n{Et-P>ZyEIkz%l^7S=ud~&(e~nkym%3!@70tDq+T6Uz-)IrQitiy*V)6o~ zb8g&lUoI$c3WamI@RM5_wTzlItar9Lrk66`*h{F+vOkq?Cf&*8J~VaLa|m-TVs>3;077kGwi+?YqnZ_w~DR2 zLuw>PNko>x=1|#J9#pUw+{E<~q+|QTAk@@vW@8!XoKEA$UMlgvk!7jt-Y3^WjsC`)HLN`n|u3f`zE-G8PYQGFt@n#W8i@9#9=@=V0jPm z=ZSRvJHxTvTI!d!x3Flk!41}#Wfk4LGRYcwi!Swe6t|Wmj3W)9pNwIaW4?z|g15YU zf#6*)SG(2p?Mmv}3oUC?zqqw)yVzxdNG7^xd8BBhAxILE+e6_|xWE{%OZdU1j}G_` z!+#F$EF!wS)mT|tY7K1j+{rDQBgpYQgqY)$Y$ZlsM>0sv06FWtDR<)ycl$nlJ5rNN z(NYygiKh6gRJfWe2L>HR;ssa^ z_8|$GfmliC{{S)!ed4@l(VcgqMhc8k<(hAZ??vd+yhk04jpej#U})FMNY)Q1&xCR# z2@(_g){sWXtjb+i73X@7#2bGb>H61&AH&NPoD*EyS?g~suB=rg1frZYs7gsL4r2?n z0lTNRY91$_!?9}j{vZu&V1sq6@Y@AjhS}yv16-KG6*90JbGRW5yMd3NwGAu8{{Rpn zz141H)AaDm_Jzb&=yqNF%w?K1jh;}La)n7KH)E5#I#twN?P1lXtz>-fuJ|8N&^3#F zKEWg>J{Ixq+*&oyh*Qe7un}9ud!i!CEV21Hw{~bF0n#|p1Z+TM zVsLWR;`Z83iEI6#qQj$IYM1QNEZ1|q@XF2pv&@pnmf0*)g2bE>a-~^G2iK*Ur=?9# zF0LKL-5zV=E90rpWG{`wE0eiid8T#y+ut&%V9ZEx0V~vkL9ePl71-(aa>J`@EsJaG zWVgCI*C@`5X$+0iX=jQy3UTt}la6b}{7I)?c#BlhbvtXTSYpy-j$v&Kj~s}IGRlFA z0>OYhoc{m{05~3;zA$}D!4P;v*+*$}ZGP9$>ei7YQ+@F&tFk5u^LKpC2-;KtjG(fw zn9}7*bqXrd=4a5Kv=4xyz4&Q)samVp-RZh|!dl^#PnB*Hk17$2IeH zzL}xuuX(2FFvqE{inOS$Z|yEi!3DL|%qXsG(Hh~d6wkjBlsC|772PX7QDzA9-? z@XuDd)wQ#7>3bcz%I!GD-!4U&{&$BAtV0tIL$f0z(mW&kKKQcg-&^qi0L0;O;ropu z+TQC;7NXX8?;zY_oIH&3Dm+RAfmkZC9ti|z%AI!MXq|e@@lOJHZ(G(isC+Ns^q6aL ztf^z=oXO|gqZ1Omj{*j0(Um@RA+SDTMSV-*4~!QY5O0Uy7cJ(ncnYFiU)yYuDP=1K z9-d9-VpEcEF7$CFQ$q*DtPa*{-MIFKnDfm|{IzrqMj z6)7#9jkG1uw9knaI*dA>jjr!JJq!cvUvAP;+TpG&;twPvY6n>FM45~$Mr})s{k;D;Jv-t%7p-;C9Bc>|-i-S|2?4tHl~mhBW)X65SIOubdI} z2;7V;?}R}Tt2~V5_Jv$OrbcOWEmj?F;Lg%q+1^{j6!OH9k27m3N|8$(>hOkmjC+Yf z$+#h4d9FLh*QZ3#bqlxAbsawKZdyhnC%Lv=l6lZL5JpfXz}m48sLS)Q#@5e^XulfZ z@z$vnmkF%t+J2QP+r5NwrOIB)rOmoYEG1=Uh2xe;_NsPlH9SQ;j@Ge+*9nA$jqh@sjYWobTUDj037{{W3Y7$Whdgc>)6tzxvX6A9hnGWm?f z8A)?0DZnlJv6HlM-n>%U`td9TD;mhLGWltOgf0NwSmd06pG?=KLNy$1EshGZN$6~A zn((*1nmc6PNUOo0Oxk0cM7TwwI&MhH10IW^T<-NkvP+$8b&Ozn`lI3$%h2LmJ8yG~;d zQMJ9$1+CVJJ1koOo)Ae#!vMc8IqH4KVk$d%gtqpx$BmMzvnrA{wo07my5OEO!5-qe z4Q4&|unTOc+>QsZB=^r8RxE|0w6`jwg<{};by#BqwrQpc=b-op#Cq?9d|3{);kAxw zQ7$5qInE(yB#D+!H|9~bG9(#bHzI@A73ex7*Au+bYu6~#-Cr$*?DsAuh(&ssSwgXN zM2mD}d1#>kAuzmGoJoJA$MEV)D|;(5s%jdH$u!<+dzh`EMP)+)1IbpHf~~mun@1R-0{ae6iZaW2TtpokPm9G)SRtdgiqLZ>cf5JZ!ym6&H zrN4-7?UVZtPq@=d14S*djn*kthw}baaB#=zdoPK4O!_1@`W>(K zqT5*8+rcK6Y%Zq}#Qs#GSZ*X9ZJI0Jf#@<*uElSY4ZxhEms%#~F2wFj5`q3~L~b zmvJ%`D(W+mK^5m$!^THsD>Qq___{Pp9b3bi$A|4S`xLM(6Y6pOmfbe1DzSz~iDP)k zz*zUYf(BW69e08>MDW(BqWmS)BwcsIjc0i@e`|-#nI}nA_oAQ#GNY+vUCPQc!C#l< zpAui`x<;R+FNkft>EgB*))#k(C64Y(If_RYkus1#Vq=qQXUoXtkrutfL-GC2sXAUs z;yYgtU0B>jc(GnWlBL3`E3Ku5p=r3W4nYQO(`?LSF~W{H(NS6*Dyx=^_Ps3*gRbBB zPT-4+SX5kT*N(rtZ8qvcjSwg1+YF3G7?Kp@8&Eg zLlQm#{i8G{(!M0$J0m5v`b9hv^R@RWE!Unv-HiQv*Q1lxYNLda#hBB)ZhXt7+Qe;T z8$b;pku%N}xd3OJcB|jnI%CFd?k%zz{#j9qxK`{Ac)>Z{$4+xoX>l2@q(hCU58d?K zK*#0y=Cm(j*&MOj<>g(-i^otnEO-C|{{Ysnr93+$2S$*+w2?~*Mrj)aDH$M?Z2Rt9 z@y2tC(0~vgIX-N&0voVBsy#Y8h-~ea>;XJm;*{=4wwb~ut5xC!WjXyTR%*( zQ`p5CUqEB~qF_kJC+6qrNtARSXE3SxiQeV1-Fc;f%PAQwLF9q!Rp6M zSm`6CSxyQ4d8h%`DI{;^v0NX!jP&=<^sD+?n9EN&8zfcQPhvSW40B5&fY@ap-sc}m zS=v`fAVmN?iy0?@gU14$z^oj+jO;iv6O-H%pXFCW2-!*v*vR|={-UZhZw%AJHqenU zUHtxCKb2awSRILt%khkn$3xPZkTVCwcS^EDGb0oWwf?I=Qj7@!p);#f%!JpL6G0&kUyvvUr{?!103{5a8#k*E?@; z%oPzjMnEmQIY#NvpzTX*ZFViMwF^ZomePf}900M(R0ZJVW$+F=5!$9&ri+Q{J~Hti z_J)nE>30Suy47FIOKTQMJghWpvHZ3mZc(>r&){pp*5KS*Lu>;NC~_AgzklWHR$`Lo z-b1L_{E}KcQ-@#R*&r%?KK9z)@K&#DXQ^D=Us=83XowL;oA6op7Tb&f4;lBYVNFw3 zM*h<7=l|954;uVs{{V!RUm0q828C&3YvFr%G`%$Gu|!^2F8EZ+}2DdJ6j?hA`aZD)qY(17M?G%<0+j%OH7!q4`Xk=Af0PiHA=jOu#PFIP1W8s_Mi~1C+1e!D# zJJDtN(PfB0=+i3}VH>)!#7Gr#3#b6>9~)BxID6er$+qQl>wkzJw2#Iu7sS3Iy_3au zH#RnD{g+{9ZqjcrsKl;()!Y_CW*7Ho9%A#5GhYt)WA=ve=fKa29wG7Hf$tz+5_sBX zk5;k0Q!FzpJabIadF1Uf6(kmX<;wImZap{QhPkgxrP*9++NHIW&vkooYSP?Hvyj`a z)-ppsB@`%OmM3Wg1bE-XQQ>P}8vUEX7P`&->PyYmv2P+y%3>`ynP(!uml~idRgQ9` z;a8Ee3tL->R`6K~7jal4jitx^b4EWm7!}S>Y5xEU z_f_#$oeUQdYx-%KW!2+Z?XCck3bX>=Ibw|=ninf7OAGDBNeW0FojOTDH0^R6neBfH z^#1@C{9LrZ@YFin*hAr2q||hm)-SDE7_KpyVJ&?7$as`Gh|qw?=OK9=Ux)r3TlkyA z+D*2PFq2tGBD%R_r(J4#ecZ7XjypfITd0EJNe=CC1k%UyK3Pr;VSEeym3%4jTj7=W zjWvHCOX1k#Np3D8)$Q8e0k@fao#BP(eX4Sx1lkJ~z+vm20{xJ_9Q;qS)~|dU@fTXv zd>P>_wxd>S+iN(Yj7J^htqd~6o8w!FpKLqh%z&_Nr1aCF71Z7_OH?LLJ zVm};wJJ59mg37zvX52Mt@Rsa(=`w6DBc^8StWVeS)>xa33QaKig+r#SLnyZ57`sor^Cx# zP91Af)8@X?t!=ekN=toVZSE!$qCjl)-8Iacqo((x8F_>eJvzZ}vNZf`1J1EEi-~F3^ zXy1kZ01NzSs`!3uyZ-w4`$<(QWe}ucoD>QGEsXrdnm07063XE-w^Zd3LhT<+R1szDUwCVstG!n3d#-NmASE>dz7Q$M$OQ zC&imRSKu|}#jLt+s}7}qqWDKoS>ci)5?jtCnlR^NE=-I=6d7%!A1URj%I5Vof>uo6 zd{^WD0E>G60D|WDfhM`2_%_PH?%zhxG>slBpAcAB-5CrCBTiw4C}jpf?F%;7G6@xA z1sCT(4*0X;ZMKu)trJu6FNkim{VP<0<4l6=Tm7QZRg)7zBn(vC@zKZT^CbdhgNHRqkiv1&t%D%F>gtV}|%wa*3W5*=0sFpmJ&x%L! z!^XZ8@HU&{4~P+3SnGCrRF*m-JW<1S4WmOGP^^GNk;fx|95REIU{2QHa^o=(r4;YE z5}SJHhxkwXXU8q$t1W9un)}7}aot$K;ydekWb8Y zJ{|l{{hxeyb*21G((d$mZ|$VRGKLddTx5CBF$OmZNFK+i1abN;qWo+4{dJ{TpNF0! z{{TkUu7%8ER}UlHTrZYY(2jiB4$+_DP%*Su-9Hi$>$&4?U9x_ zgDQkL8+S+774j#>uM^(fc*jxE^$AShVpg#ZZm#Ao$om>b`&^FTHVy{>j1kthgzY6x zmqR~+YJE@PpM$zT!mo#V55*4=%c<)#>K0=4G&%J3Yk6Y1fH)9cdGbW@51Y(YSmp{J za;HDq3Ur!}&OkB6== z?C)k~bF(bY@`8-0U;P}$%mHk!RDAj2U)mn-e~H!68$TP@Ux>*qR)QMLCJ1Ds7F zw*>}x$@D*SYIoioxzqmuwykgA@V>LW6c=;C&|SFC4x~J3Aj$dgq;a?qO?o&iTx%{! z^w8eRq4Tbxajf0EbNE-rT85jblw?>gw7p6RU6cclv?iY&w9S$-B(_rc>fNKU@dT@L z6y7QD+sAh%OxnJQXuwL*0%BKe^ z{#VQjU}a>4mG=iY(@z)cGec~%Z+NYa=v}iRix_X2FwzhR#}XCCVU9gW=tGtI9R8cE zX%bo)JT-Z7J+|$F>dIj*#&yG^p^?fSK6u%89A!rp+G%&X&7{!{Zu%dwGKnMkSX$1F zfC-gd#4H95V~k;kJ4R{H-sP9kNtmb}R`qj*qw-N)DGh2ns5)cS1gK2KVB!kU`^*tL`@TbF#dhNAK%UfjE zOgkaelg_ul3a#=h-`fz=#f}%u-UdfJV!3<24OoSde%Gm;LI+)u-S58n5E+$M$uQhT z3Hz`6-lGcYNUs?9h4Jf5&~%HqGz;+8x?Bx+CA@+;0-`n?rU_sP8;YH|9l5U(_)Ys! z-}tlbn%|8in&VB{e5A37AOJLxZ+8+m$gv@3Dj19m0hZ^J=i#r9POoig zu0eHcx(MYh2IA2^X848SCf6_YA#0^x+cu?h=S3UZ zw%@QxmPpt=$lDZPfC=0bZ5gjE@L*AUscN1wm`h^?<+Zeu!5n5$Yk4x;PU>8$mX0Y) zB4Ce~X&WRb&Nanki>Q;6!M;vUQJEA*)t%A}BoFEKbbj+&qsXl1*CN9m`v#IIv zcmv}#&xGx@yQ_mS-x(J81vKko8rCZkh&j&d~c)pe^S4+ zk{IuNM;Hw)^^_pT1=BlcGZ>d^?~f0F%E(xO?w=2QW8iOvmwLU-{v?yd*H<=Lf?Haw z&|J%>T}=-CIL5FR^VjUM2;NM;mmwI;WyvLs(#mUVzqB>kcG&~r+S=)I+aob#u{mh-$obF2r^K!C3|RPzT|zBd%^|b6i&>5B zu3&ZJ$z*-rSW?3nP`vKj*FN#Cd_?hu)}y9rmN#uvk7fWEb(BN_k~S7Ff(;>*l$TviYDeTZav}sSv8Lj=q5S zS@2ikUH<^YYpXvW_=+7i+S<-nzWX$S+DPEEMv{4@l1RimAsbHCQdlyD+zA6e;Sa@M z8vH@<{-@y$15ncR-?wSf!R6{I*Y~#w6{cw8m5rXCZ6}&=Jo!%U08&U5^(XAd`yu=r zuw4(v{xPN~>aa_2JWhgeEz4;epqhsqkPuT}Sn&tdy z@WaJ}#kyXDY?0Yo-d=eKk}7W-?gE>3P^=2RB_Z}A3^qZG! zNa5Wco_xZ#(}t2D(m)t|;kOPBR}G9FE_^Te&EjS8v>pNQNb<@t%7K>Q zSYVCHyMWFflLhCFtu((8n^`QSaMv-~PdrI0AaC7*90p*@0U(jX5J;jUM6VAKpzk0z`>G5{=Kx1?qF3x3%xtL&EK-+Ui~e)#kT> zgum$XKiWr<@sfo+&5l?S4g{Su!RUSbvoVEiKeL+KLz;Mbb2g91j~TwX;C~h8SJPKc zz27{{OUOj39z2-I43Z*+94ma7#?jRMo$wRJzY?r%Tg18z?Z=31BD!{aT|(ibUFJ7A zhCTaQ5D5$tMZpJf1Q02FNBxaHEoj=Vo2>XU#yxi1Nrv_|xYMp4?(SyP5G0}Cl)~d+ z7zZaH?Z{9u;`Y83_=Vu@e^u4IVQZ>*e^kFkN$>RyFk7rhHlvavPbjpY1um*f<%Z@i zb3D|j*0fvxyvz1;anSu^(tadbc#+}0)_g^6rfM*R3u|RNBv3~9P|>j^Woa@Qq+-fP z%A;rm`X;}mY8uUo{jO%ej!2ex?t{#&8Tm?yf-)C{zE%U3&tgEw2{vqCaXP;Fh|H?sq^K*uBWQ{flHj|_ODUH<@tPfy=#u8VS-Tz7Na z%WAI@5kKx=mtq06nWP|wB!WTWy=pkFeQjc8MaJFFWAN94bPYC9f1{f}4PG>?*AwZ` z+uUAAuEtqa?ykeSG_vlKa8PlBfI!FRC+(rH=^i5SzMCeKVLbjE&^22-2`??nASVeMq|lOA(3294FXi1-{S zO8XB={ib{^@t@)U0NVZ_-Dy@!s9Jr3N7U^+_;5(d-fRLigt;swc#o2QevGTSGxO$y zCcb;$fjcQW4G-?PEwz$R?xEa-^?fSVzW4nS3A&eP&hcqc&cSB|c1 zH7#P+LvX7#wyEIz$?c(v6bzGFJ+z=C%OQ=0#0?9g65)XW?e{X7Q^a8-2|Ku}T;;1S zO;bNfrtlt#ulzMW0rCEkV|}IRaRX@3+(C3FjcGL4~X9Z zWB6S&zl1LCH47Vi39gZ@-YdzXy|!@n=GhzB>ALNu zm6CZnUZXR?C8JwRH%Aqn#kI`Et>bO4405x1d390%+g&$@J|*kd5Vn=AUU_gd1J0Ut zX&AW$Nmpi34pfeJ0fE=FlneG~obHJjNCJqw#$Pc=e^H;)9}U_aZqD5__Yw&gcz>LU8Hev; zW_D2{EAH}3C^-2@2Q~5+?KkirS-97(ekN#l+Ptx~oJ%c*oJK^1#2H`AZ#8EMBV4R- z$MZMw4yUG`Hcj%qj4ELgO|8$6f!uN;B7AQ;Vf)?iz&o%-B`7fua!J<6M)%`P_caHYkZ&s+=}`?S^b!Gj~M(k(7Z$N z=TN)VuP!GId8WxVywXZ$-!igDvVjpK{{YKe43)@N2RI%Q{hfX+c*@4_M7y%M&|%Rd zgd2TH=C*kxb}b)~CdYx~h@d2w!(;+MI29a9rTfJz9CO0XZpmEnjV3FLv1J|2{1!eS zocRv2BCnnzS4fLIV*#*6(~vQM$PP_*{{RiVDRFP%{cB5_NaehWYiaHgnMnd!O|z;v zAxTi>h~<>-Bc^Jf#1DqQ5qt-GJpLfCywYK|XdZcQ<6~(Ou~3Y%s!0H8oq#G`vUuZz zP=9Rcrta@R@SViAhVF4C&4!ySaTb;0Q7grasU6lG#9)*{I2GGV6&D_8YjYP$PZ(PX{>3t z9}T>7bECuJNMxGAT_V+tW);o0kRchMg5^BC;KZ$hG7g7r!l4pTfM;+K@-5_|2ZG5w0H%G?M@h!PuvqJJTA8NIfuvd-DgUtXbJg>}v zs{#go8u15%^$!!Uv)3cE(*z90TP;RUHp+WxC21a1k_k-K(#BbYQk7QR5lLoH53EPv zn;#VXPD{OCP;U?TcH8|g{t@pCtB4Hnqq@!|c|_K`-B1*a#<85DyC&Be58{gm2(g5n z8s^-D7V$)|7{e3$q>fn2yllA1eauRdSp3zB#HXz-N>tO*N9RVj;XfK^o-?|GRI$H; z*{@~$9QTqfyqA`?jl(3+;u#sn12Tel0LPr?=$mbOUBA*Mmd?vi(&p5ln^e^G8+pFf z9fgcj2$>>|cac`&Pb45rvzdNuV}b0S5bV4^;@<*K;q6~ex}Gg(TC=jX)htmR?e1q) zX&eBXRyFw<_NO^u#P0^aR`I`rd~4wQD>${ScpW-R_~e2JB;NN?8;Hu?OXaYPERKlI z1i2DtcO5Zz?3SQ0KY?--wrWeE=2Hf|W^gH=;Uew4{}ft0SLY)4X}D zOQl%b>Ju%+#g3tPj*F_?T*CfP6Fe*gVo2a9k;KRYj3`jcov1DRE1^ucrg?l4(cIgu zuF3w}1Q+|_NOst6c}JM2VRc4aqqcrvxbTzXO-o4E2=M;^i##1X$v2bzhiqOMe9NO3 zl$Qrlg5DRo3B3;4!D$!}PStAJ&~#|^9V=DVb=8LEeNO&uTK?7}71VRw*no|170Sqx z#WYdu+UX%s-@9NOIiX`G1@EZ>ik)n&ZS)HyT@|urs79ZF4FC zBo2|=q^!G{gc30&LZngxN6qs>TX?g>+P1f%$EeMyAW?4>q}P(P#Um=xG+tWJ553tHV^-SrztMdQO`aSy& zr{W(GK{lPI+-!$Vxt*^PFjp*|Ma)3PJiG)^GO!@vWQH~RXX9TFcpG1rSJVx}=Tee6 zuNz3XOEmjHKo8n&8_O2+@I;NZiu~c2D}$enr0|ZRXEpAJb>fA)vXV7xd38xGy!0#t zjUChiRNWGj%cm!H;Dn5ycL7!^CoU>CvqJ@q{{V_@bp2w&XSKQ1)$U&6&4syka>|J; zR?=*gpD31vCC;Kw5sn25t)??ERMJv zOE!3BCe81J?tT;NBx@Qy?e(Ih?;z3cVxR2x(*`@?wwR^VQ88m6JmG^BAm<$YzQ3sW zhQ?if(*D-&H@Zd>+&eYGq=#WL3Aec1nO0-L{t>ujcTP8tyQ=PS-5;f13)en1_~*nh z_^Uy%@K($TEw3Q6y0Tl8yk;dC<-GeuD4>N#iV3jo^Aofa^WW{i;=NMyN$_pPr7e__ z+rxB>bgRZ?5?mlF1dQnv?jwtQi@qdIP61^mzLEWxekABR-jS#HPV2`PQfju3ZwlM1 zK+I#xJWwX|klaP(1V#)u##B6P69Txu+Uw!Bf;?=o_*r!ezY1&GY|D3XJ^t^r7~SSb zkZ+MtqeU|+NxfHS%8kIN8w#2b+Y;ur&+ z{4n`T;GtlHez#wW0s?1??QnQa7}Fj>xI^AIUg2rHbPpjXtt2>eg*uTRn? zk5c~8w$x&@$l`Shs`)|Z| zdZbn`t(BFmw)%lr}P#wDD=el0zEbT#LD&UBBpY%Cqf77*plQ(eD9)40&&v z7}u*?R#?Ja5{XkcY0%m|LaZBPIw1prbjQyvHEc!RhtyWSFV%HV4X>YbqG=K7 zt8cqb`&Hn$l^Gd!;aYQs3Vs@cv?dVo0MDp7IVl_ewJ{u15zTSmLJwYk)_t848+ zJoysx7<`LR5!yzI>L-$IvY(z9gDWXu2x0RKU2oz)#SKfvcU~Uw<&EZ{_GgmJt@W>) zDoB#FyX29;0ZszOGB5~I7+@Y!_r}&brNk+Al4<%Syb!}an+(VmQXCUBFSIhtkC-_O z!G;Mc%%*$a7wY~d5LrdyxvcH1UP&R6@&|S{?6`RDqDGL)S$W)qazVi4*E*X=WLir@ z=x+i1PSz!w{@cYCO`=GEv^MiBu_WlEK=OuU%A|~NR|OP>TrO0e>%+bx_)p@EUg|AM zdkAE;k?$j#=ViJ@BEHg4{IFvLE3{>digq>zMSR8KZEH!3RJghECb0&icc|SF=0$pD zf$ien6_zO#!IWTTDIIz%M8WnmdnB5ViS#Sq59*#0@ZO)RX?mQAZGCGcys^zYxISK? z3fw1|3ao1DnItI3@2eN{DY+d`ZKI^|&X0TIOVB(e;y({TtNo?!B7t&kZ)1rXAuMxS z`I18MgXM0^4XP3F*imP~I^V}F9{R_^+W!EA;J&wsmA|xw%yO(dRJ?h)-jfIzsb%vJ zx_sIBK?57bo)hu*kD_Zg8Z^?_+{Rk&+HE@CI9U;$NtOVG+t^!tPVnxfe=WYDYjdREX)yV- zMXE*uTO?8}u}acgw2c}|9`YEExCtqR#c^L5ekJJ>{fEb&Z?IntUD`L=wNR~jY=whF zlFRmHV)kct$CY*hhj19$!Sc;)H!U?sAs51r3*1?1I%TGWG~Fsik|2p92xXRVE*;h~ z3n>zvfr_v#f}>*qep0*mCE!mKYuew#>p5S+nwE%yLp|S+t9WmcIcD-BirJn_!yeMZ z%kufo3qE<@6?}L7pgceEMYR6_4{CaDt7|f))}L==AfCeKN>YlBv)maNNij@eh>*Kv z5)E}97k)EccxU2fho~l<r%tlp*`6=15wjAvlT1LJK! z!TuwQ#Bumi^3m+B3bMg9%F7%wY$S$BN^LPRZ6S|M!*OckBbh9u#$7V%$O+v=VR@xF>|;j&3DwFnNK9+z=! ztg%ZHsFQ4SAqG5<$tcSFqoaZ=)WhbIgl#u-j;0bb_mTL~sN0KpRV4}@26&z~+HtYU zFeOP-h8!>?5y;IXQu!(#1GXc^!b<_~++!e$`wPb(3Yz}_LQ!$>`LBFM6t34(_;9>) z+}k)J3)B($)|U~qimPzVY~&Uslx4{I$@~H0tv1T*#FqMd>RJQHsJC`_c_H2~sWrOh zM0H@u;4cy>9Wh?T3~f2a??ca?7IAtU!)oq__sO*a;28Sk^cX#{#W*;)w@Gc-NR1E- zxWf){f(Qh2#dY2nwa|6DeMd&Nj@nr@2a49lOQ%mh+Uo?${%+M}RS-#-+iH!-u1j)r z{70nR==ayQw-)W@LeQX|Oh*3zIuDl$p&xcbXOg{%9G;a?UdC>ek;^Wjx)rcsn}%g+ z0ATzNFKId3w*VfQ$j(4Gt++f(()!K_&LF;$SWLK(7YN8Ys^o%rAn}um_NUPNBKLFYdVZgyrS6i_B-X|_ zgTzD&GfL)5c_TS5`?DXGWI(%z%UM*d1ng-}lR2Fq#o9)np=-K7i8MJanDw!cZDNv;xWdxefXO0#B2Hl@2W zc>x)f*pOlj2sKs-Mb@7dp=IIKllvf$FYGQwyTleY45@D`<=6$z)Qw2VA1NlXF6Y)) zK=BTTq%ytj?X2ExH*>}d3FnS1t4T9%X#gxXoCa)iMo*sSt*ms$9Sl#3UN*E%UVjKh zqTDhJBBOB>BbZSpfmyhS5GuRKe# zceN1^;p9^uLNEkI$B@u`7vOD6;63-nDI(NAv^2S{<2FWJ)`imGGF>cB&Jrn3;Kjz$ z$K^RS%PQ3FTu5TLF9zAgx!r{!p)#GUS%F`a<9IT7f55&s z)chNL;LjIm+INWM@^l%rykA3WclHLjx|I^j@NA5^jpPx;?qelYFh&5~_>=H^N%(K# zPY>!J4B@%)HLdN;$$u5y=(%NUNMs9dHO0KC<-0^&c`QLd8C8l9qGO6snrlW}sJTYZ zsqkA%4wvFOMgp%spy%tF(Sp&e)IwW&y6@aL3|I{Qy?7NZ&90}STU=Sh zQ*F($4p~Xg-oqqdM_nL8--hG-hISuXJrE%;GTk~ zm}u&?>BJ@CQMYsL{HSB)6^b8?Tl9weR+15 z6DsYN807glmK4EZzCr-CxOjv26N=M03$cu;QYXhA6!-ru{_aSBjH&=%BknSJn=%1G*+4L z-eJxdZ1nA%)mbBdBhJAL4-5E#oceXES29LXSp2FF2i$|e6(*GI_R>2fj;P0S0x$vK z9=Yq!)~K5nE}GssOpuQ>3@`6FP{ndsV+3$Ies$P@>%)pbmyXT&{>S-igrJ?`U^?%ySzI7W-1Swz^w;fWVTl z+Q^4rMWVXQo{g4FDT_sjj|e@?==sK33qxol}e<6*OZM zx7L0V@h-m?_T|Do;IzJ+{{UwB!bV((qS}NgEPw?O7aO)@2R!nG66R-gtbB*!AK6F8 z+UNEi&%vv?E%dz~NSehpE9m01n^nHJnrnxG6ody5K+o4i9wJrAlp8T&TrehKjWpB#0d z+AoUq{{ZbhPfEAZrHkwyeZ+h~x3)#65i82#29&eN53$5QcGvn zHEU??wAV?chDlW;w}~N^Nd!&1YslcKRsetnZrgks{hxJ>Gf(kTLu(^=lTeb<-s@IK zOq!JNO6hKr+A{eP?MU3p$Pl`*aQn~|&Q&R`E>x-Ld?(=zd*YA97S=u<_)A#PHCqo9 zTw_e|%xSkz((V@BB8ouLE36UAZ5t$s7%pW}tcFJ>zP2EtKxHb`uoAZ2>eR^FZffh zFt#_g_RA{Tz01b;GK1wvkd9|T|Ge6 zbjV~9Kqg-9*YtaP zfpI8!!X&dp0eg11F*25tU8gIZx!MjLkHx>*6F~4Lr>@`l{@L|!6Y6%y$*|Ne?)*b- zvS80K+CA9-Pu0^ThF$lH|b)vlIxyS*zvWjQ;=#^)CbX+f49}i0ov))RJ3* zw(`O(gAK9CvMCJCvO>G}E?b3OnPLXejHo;GFs-WPO!B{l9xU-Yt^SD}zKN*Iacd*n zUhD9+fRfa zYzY;MEHXyC?kvuWzyOxub$$r=Iq_Z}7XJWld@{Ya@ZX6oEE4dDo=9HEKowl1!_D0C z>y(M(Cp#2^7V_$r_Af;#}AnzO(d*ZRy&Hwgb>6P^xhr- z_{ZS=e^kEG{4XB8;pile!L<9Ei)J?x%sj9oll@^TeqgOCG6x$$Uy{7&x^=5r?zS^< zyRpoC3HYR%U7g(DV2a{qk`J-Yp_oJEI;dw>agQQScn5jfdB;DY3;SnW{6g@R=7Zv$ zLr&5)*rlE}p4iD5xVBX>?2XWEX&Ka+QHBX%0NlB+%I}CbQl-4GS?DdLLv|UW3S)bO zi_cTGBCx_xtdZfI#T>29qCTD0{x;a_-ZZ-!WFp6IA-BBPA}J(!Z#a<5<%1Bww*-t1!n`}< z4uPra&p*UXR`M&By1;29e9JLisy1*pV0_(v@1PaS_>WI@gHP2gB3Q3vj!AA>)<9#( zZL#@ha{{La2PdaJDu;__n^m>aWz@CFZs!_8tX7^{4=!OLjKlk|szWc%UzQ{99OniP z2I*9UsmjZ$Ipo_vTEArf0FRn3tMF$|@NTiK>Do@ar)l3{(e(B)q!XZV5uWMUT194g zNRTe(Dggr-Bgz}XmLCLsL#X(0YY}*ZR@Lo+o?APodwUnTlg&t=0gfUJ>x6IK0CK@d z$oS{>bJTtk*m%~_J|f#)+}b)Mb27;+3lpejEDRj7t8MGKh{)UCzgxUdulxz{kHN;9 zz`h@oU%1t0hWAUixQYwUI^yZrD+fkc=_;>Jc z;m^X)AH{#GYL`};&FzuBTN{azNT-?LAkS|905e6gm6hWKlpJki$RBQK3F3Hc7Sm6* zvec|D6=o7mKb1I*ut?b7>lhwFvV6bK@Q=fuJ^0P9d~EQ2{{V~auTGrzlHF-`;vcle zsdpyHQWCyzndJug<1WP;a4H-TU$VOIi+ow7UH<@MXwcarS=%DXd3x7Yaw`>8UnETo za7un*jneG|jNk?pt`3x=Hx#UnsOpaLeL5{dX4dTQq_j4Y;G}5&Vz49?QoDS=m}HP} zcMPaGs5PUgSYZO}taCYFTzrB|58@{^#`q$5F4(Q*uC-;V zM$Y$5x;b|-87(YWWAhR)M+YE-jt@(DrRZ_rM}2>G_IA<8s%Dl8kg>P`g7D0t(y~ic>35deJ>Ah3!dV>_8Rc*|!ZVa-?4dFJ`Q-g4*ZAUyT~T z@hj^3cZ4skXOH_{TXbKtutL5;bjIl9W6Z>1IWl2+9#>`X#@pe3yRCR*#Co2mr}!66 z)L%q8^vey~ET-plLF2WObc!LlCCp)iuX4-~eNpjS;+@CBzXX44MXg-LuSXnW-A|O` z?M$rEsFoO^Ne~_jluwtEtH`e>wfMQ>gWSwrGTEeuy| zV}yCKq`M=YF#8eZMst|}C+8|gjIRo1wwgX{_^;zVZTvT;FM~Xby$N*%u(+1S28C|c z$$?gqU6G7r2aOKLRb>sFabr{Xh2ia6OG*4Yq0QlU3~a8R+IgCLi)j-p1g2KmA{&_1 z_U`-b!w?012W{d*t$Y-HJI6X-gM2~am^3f#D=S@A^{lR)Ras5Qu~=>45+r%XCsr&+ zL&5lZ^Ify?RM#)0=mj*Jc%I~lZc0gNrdDP1B+lv9HrlI@%%3RES2Vt_7-(nh8#BBy ztjpv3No}klTLx)mk!C8-Bh4ZR)e92YF2z+8;c`La74#;N@ZVJMwvDFvlf)Wb-NuLE z>_ez&PLNyOG@mrmwYQw4#_CaANUEtUYUr~o6<7J|!G0#b@OO?a@0#wwTwCMHn&=}y zlDPwLf=Jpk8#yk@@ix<*1_F;%__wNf=i*G?+WJ3=XE*wVhuHNv$Cn+fkXuR;22@S0 z7yx8t0m%7OApkx|Ql75J64LiP%U|)ogse5KYhCePlRu2E?*tK8>KFHuM;)*`8fc}o zX%~C4gkjembI$DmQ(3g>1(H2iEN%rdzO+JCT-HORFwIUuEqmv4WteaYO$gC z&iCxvrI(2>eEmB1(m@* zG|Oo`IP77+)8%3o^5zLKBy7%~E0fze0eAyE*VXasI){iBKM?q$+SbonHm5?;;?t6A zMKQv%ZVq=xADa7%3Bu(@H$D(!=KHUPR~{PhW|!lsHLFMsv}#V2`ykZ(+4iNP2$(}B zm$>}$@4_kGGQ3uwhBeJM#CI1OFOBuKHky8&Bv8$w!zJ`GHPkGNE>riuYAaz}Amv$H z5?elYDwSDD+1WE_$CS|cfo&&(ybyFw0{zHy))sI zuXEx3H&D50G|QW*qPm*OHBhe}*JzH}CW`>Ns6bf87><8KJURO@_z(MEN0x0uIs7Hz zc{X0^HqoWk#J9#U&E}Rs@S?8YEfv_1cee|XFu*Ctl^e%Zsnh%$@ZX308{oM9D(jyQ zZQoGRrJCq!S27r{1&of~QIp6hSYY{qW=3ReU>-5%zZX6{Yu+^YQLgy6!qW}sS5${i zpApL~sz>G-Apuv+i4Z)p~+dXPYR^8WzY>)?Nkwci2Xc#qjEVn2l* z8Bd0``sIg;Q(sHpG(~i5c-b7RvAlCiB}9;b8*->*3|Bqt?d!cF&dS*YS2rf&+!XU~ zBA)Id>I#PQ6WEcUJ3PBv= zFh>f-fEgfXkzd4q51&~nESKWp10ZHA4f>s!K3c5tJb{Q93z43fIj@*MYLAPaBKTuv z_8$+!4fW0CjL&m-1aha8lAsXza&KixpC>3(5AP`XO>~+MjQnq{d^+&wiu^UDP5zw{ zTk4i~&lGU9@nLX0)MrAhH;Dr-%mxLy2euOCe5AL_sq&ZY5AgfNJ{R$qf;F8I{NE1| zc#`8x(e1A6H!!)98A|TAiXk#aBl9xq50rjz+?~C*!;gnQ7Jd=-dWp~|kCA^Cr#kgkqOp!AL&e9Lfo;FX59~3?yd_?evjdd*&KkN^N7dKZocNbDh zZsnU&wvx!3WK~w1?5M_5ARz+d8<-JaX?S1b_kjK&uh{%6e)C`XPZ^Hl(&p0TSw>{c zWTZ?K?kY@eo~84VTS-csE>@CPOH;(|KWTkqTCwp@fF8oe=Tm!0XS_N*Q^O^@Tgnr3 zsLl(v0Xbye!~vEAb$&ozc>7xMezU3Tdez+e=BXCDVH6sicOn^44f4kndrPA_70P8n zWNeaEb6=!BKYs~)2Kbla?;c&lJ;j%cGO!U8m+JJu$Sa_Q9@(n*s(`I!bc@oBWLet`|e^>i{xdx-={f6TTRHH?g$QFBio+9hS2;u@c>0TH8aXY0Gam;)P{kl5R|G+^2pE z7s&vh68OpBy+6VpE77H~zy8=*rh;X*j%aS!J(k&y_^x3WT5xFAN9Lp8Wyol#35fTI# zTrUa1URO$Y`-w92xwsCQ$@%C=R0Qn>S{N=ezBN@`5>{X!wAcr#SdJXfb_`i+rE zAy$&!28Gaqs3b>F0+aH(vgJo3pvUBIjJ_Xumr?Pyp<_RVZM-X`+CivX!*y*8hA3_{ z=o;obNZ!>X%om7uM9Mb0$TpQ^A%8=3FCW2StVO5a&2c2?O03tC?PPvIX@Uhphk$Z0 z0UL?IBTK|uMeoE7E5tfphSu>*5ro?AVQjH3**w`i!p1377BQIqZKDSus0UhcPx#1ml*HrNSq-?x7t0eGRw358e9@;ZABl)UKGRrfr(7RQMBoXr((fA)? zHD3_fOXG>2%S4M80mMd0ZX;$txsA56gqheXZpaKy>@H1xb9>--i@=%>fxJ_vDAY7h z?71}o9nh6zxwnmE1({s=yX7CfQV4bLgU&SH3iK#eNmpR{$y+jN-HeI|QtQrdXnB1`r`8phF}U6VYEDH|Uw%D5qz zSJ`?;?00dfT5EnT{{V#7TGYH#s7h~bH0@sUMzM-FaVfKsQHcc0x#JQ@9u&&&g@7zL z>rjWp&YE|(Df8Z^@pJZx@Q#%ZovhjDdaOEb$?oO1WYlE3K_af{8aI%{vH^hTh{#)T z37j9ZJQ-(U;*q5IiM%y@-fT;9){S%`xf5p|OB9qvIxou5%6ZBKV5Aiv7R}>(PlSFj zm%$$wyeog<%|cX?7;jZ0yN!eMy58+Vt;8g4%j{)W$k@2WeQEF?T=3V2E+fBvXHWjg zzP4K+_O7x^6A3rQWR`f;!y=X>qo~|l2b9U>ohvKJ9T2+F7@jlHASPRRt;-m$CAYb| zw3=&Yif!_u#dRbyF(r@_)Q!rcrZM84Gx%?=Y5M%|csAl_KE-7nxP3-hCz=M3gXcph zCS@DWV~~d#$`khw=x>dF75p~T-~JN~COLHb_m((srn8m5c@_+2=6HcvWOqjmA;4fm z7AK(apNO9hJZ-1yH+QzsNv%UOTS&jz3@0*299&`v2bzB3zE^u^jP|0Fi-#<96;&&q zI=>Zs6Q}5Mcwa^l%_fs-x~1WbBTIP><+6=pn1GJYD(o?!%8||q74xr(ZZCBSEH!@@ z!*K=1k7G2Nh_XpO%Nf1$?(PJy2H^5r1GuKawTWg0L9G}(Ip7!m)zIc^_++<&3pR&R zSw2}}m8bKgj#z?j4%@Onc&&oVfEaSEVSj6RccjCv{{YAB@VdA+OOj!5j{aJx5tebc z=g!b_2+HEPXN1N{CpV{LXU_$rXPoLfmZz*OwYP)sUUJZ}W!VUneWv0+E*VrjOn|a7 z4jw^}>^T_oYrA`$OG>rVC(*8TeIb^4q=8oJ&1QxfW|{W+eq1XWI{^78B4$+~iv;v1 z@K%fAU$j7?%TUo$;wQY*1b|LZq9#a=Pc~62hml_@PcMb}RGu2tC)72$d==pcA=2%< zS2U}CY?sE&i*+i{Tg`I}a*r-YDur4WW;u+ImuJHoASI>kuSGmOnL` zSfZH6C)x_AE4=^%_k?FP#ffr-p6I%DWTj+ccyGY^Y_jN@_M521YyGor16h24&>MZoim{^SUCTJ8UB{=Q!~zR;yLEZEL%-?yKV|+r#(q zY1*c>DO?3e(N_TFj!t5h z8dFzSxh_d{KHvC%@e9Lx2ZH0$G))%bdzW^*ZAVIuRE6ceUob^`4ei8=t2)T8afn1< z;bYnq_+Q73GE3_O)Naf=2BW7frQ{k8phSOaxV8p3*2+t1CAXUCPS#vWheMSmNKxS4 z81dGf;XD0iZDRiR(cnq0t^UnCs}Q;p4=y`|g`-uE1Io(o#AGX@rs55E#`Yf{ZLaj4 zCGyKKee?SYVWXBdDpAKm z2|W#;jG?=NX-%Ya--~Hq$;!zbks{|RvW1aYeBUWK7|1-=qbhYH{5?-TTk3mX!ViVI zm+cSY?MpwvLI#-2)B?(c>{0+pQU~`_6-lO9j=Y3!fw{; z;$&BZrR-68K4gRmB8pl3=Y|j?2QUmR=WVUFsBrgY#ru-fBJ#pZhLt8s6IDR5tes0E=MqoTD<7EJ*WN z6tLp}fq`FTc>CcGjXVkQ1}#Eu8sA!5{ZZ~C7m)8uBX19v8!gSmvJ(o!8mcUlF~A_H zRtCN%@rulvjlQtJ0bzAKGD&X2a*G-HN0`e zZZ4sbcf?6tV=YmLgk!2#2wkJid_&?nm%!JT^2&7Qw7$23T`~zA!qH13Hs@Wek0Q#! zw}w-&6(ECy^dH2(3;3Qd+4sUeJ+V(aOttXjZ+B^_NLAvwo@p2DXl2;)Nbvb73Z_6@ zf|%s-H(vv^FCS^v`b2&pxUkjkaWwWiqFGITjAmWdDI~EpjBt?2KJBrtNW^SQ5)E=xa8Zj}-0G!HQBstTlC;kb z>V6<=Eh9{}lKObyW=1aR3~(v)NDfp+h!uv{7|sSV!l)!M#T-n&NR8C)M9xhW;WlbI%pC@X|{+@uU$g(X)~pnC_&L zQjK2Fqiu=98F3q=idjr~4g7rI)^}~Y9(}Ita|o?;M0av2f)z40^4yQQ6>zL`gPw3W z!8oaH{5>VScS(69HwYvRZ7N%bc-47j7Z{915lr+1u_0J=HSb>%z8Yv6J)ej#((*kv zLnXxVu2v<9cAcTp`2pab2Aox@XwHsy^+%{`-aGIHtMGq8gI@61{{V!p+sU(s zSA_XW){I|lS4kd3n_^(8khxH&B&ikkr@*fqXwRl!cvDuE)vR>ahCLc)oI^Zt&IT>Z z-?90YM1L#y#fpXjNC*$*o~^A)o)(tMHvQN3j0AuP^3hU7UAg37uTOKD^-D{*VbZKT z#fg&o5aQZ5-21XV^kk3$Qdt?!O6PAJR~>vevRuZH#5E)IUsm{+qa#P*W${hEg?TOU zgBO->I>i&0S#9NJQ8K%23}g?8&nUZ24`1RtSno7jT^`2kPM=iL!hxpv20dEt<|6J= zD6TKk=F3gXGYK}_g2X91BvAPLF(Y-`rZ5_jsWsx>6eAfOZC0it%n(HlYLfd@bGaxM391-oD3<}Y? zHKC_ES|og9efuMLkHpv3jpDBwJen=7#L`Kk>Cp=)FB=bU9G8+u7?O83#3Y-K%tl$5 z<o+lm!QuAzJGe>$4l{9zaO zaOu7gRK3$l)oiXbdx&o=<%2BJBxNFG@=opV4poqlcAz3fz2Se0Csoz0wcioxT4tMh zX7L2mW;l*{VkTYUYo{<3G7YU7J1`)Z-N3KgKlns6`|U;_v}v~(giC)6&Mxo3&6 zbCAcH+v{A*m7*O5(EMf8G>vynyZT%ejW`0@GNNwo7Ow-6AZi_%lr$;4@?I<2YgfA8a@5Vc;WS z6|7Tf^Jy&pY)x^c+Qq6s{4dFQYRL&*%aT$Un20q$V7((gvQE&a{Bt1CXHuw9jDS~#R(=fKQT zNb|NfIL8}@Q|LNRg*06sSa;GbrqX}3m&QN!W(d=Q={Fv1IHKl7_Dnj~`T1{-18gzP{h17H0-9q2FlE}QVEyHcxe7BY{ zD2#3MW-bL;@t&-FU8$#sC-A7biXSzDMJR=CC9+J1Hf&AHw2*~($QdA(1OzBQZ8iS@ zg+B^>bzs-p$A@&?VF--_%A{RK<&p^Vn2Dp3ES4LXGB$c1YvXNq_8+s+yhA+S3AI~v zdyBZQhxYtJ>&jv0BiqL;0vo7fksa1dIaxPz^MFsOM=@JVLylN<_CGT;j}};IdTyz0 z9-kGqnQgBzBh?XS+F+K|@3aEiyvc>r)tx4g%U&d3eyKQOWEh_fL2<94bCz#Ezv*+&Eu z#d)vnwWoORUigTX_2?QNDBr&jYMkv7H_0T5`yEAE*Bg&eTvr3lJ1 zl1A{Jgi{-dw2Wi|uG`uaTcxzL&)O5{0_VO<}>7{rt z<_POH)JdbYl2l0S{Kx@UiO67byO(y~o%QJL)!$9GxH2`J;|OAP2*Nv*-74)Fz>Ut_ zV+<7sc-%MXlUvvD!?~TB`|LGevP`&athygRmcw6RUmOHARhokr_ z<3x6v?x=4xj~JMgOL-K&cK-k_*)X?I%KK$m8bwne5xNjg=3IsJFg4vNMy*Hg`5tB} znw8XdJRZYC`!A9%8LrBW*=8dQI0GP#*vCOp+iKSK_j|1-3PfyPH9W3(*_?lS9rN#9 zCb?;O;oVa6LDUG3P_mJrkpOHL0FYFWH!Cm9r=bSCe@)h5xV(Kap^`Sal1591U7?0c zWP$-a`fz=XeRL|qe*-)zMoUI}A-&RdJ8N~lmlp(Z*aepzLt~&}>MIHrSR$icLaklx~jT?Tvx?3Iq+e9DvM2V2lnhd8}rdch+v9wvAdK z`+^^puu)0*ff?_L-_os?+9EBaAltAixfuiR56I+xHK_U>yXp(#YdHaeO|i=uUA|0( zLgNFdC#T>K70mQz@j3RoiXn46cCGfit(Z~)+Hv|0IU}j6@IgJt%4B$ox)Zm6!z7g> z01@@Bi&;@?Bv)~T1gJyDJh%STZ9CYtiUT2lb3-kx4KhV}q*bS~31F>Uo8{iBD%79ey&Oa)e-dS0g8a+U@83vIq&tWnoxqv0%vcs?>m|?lOa=`qts`);<7GT z%wAHQ6*&& z74A;o^}BsQ`9a7Yyc&M37nNrBFp;?iP@aVL1a&_3pKEax^G55*bqpKPhf(y+HuNnb znz^^Uk#US1W9l=-X3ZS2pxKS3n2(i6>&7_k?OWRAl)826$1BAm&HKpSB1*JvkO(IS zIRp@S1XVj5$AaCgm@k$edgKpGbu@(gAOF|%PaOWynm52ps~tm2hAmcIcIwhQWs5RI zv0NC+$kR4tDmO^s+n14*0pKNzLuG2S{Ut~;mx~9 z6>+%z&OvfV1%^L7{vpBR-xq0me~NU^?ENAy3{R$Mk7a9wxRPryB0~zzkk~&y**twU?%Y4Lrd%hYUUHl<@M3{k^_8xZXayM`*ecV`|o zX6GGE`P`3T@oc&`kM*lxj9Qkd2Zxu#-ej6ByjyOsZ=$!JNTz4BjI?ma7nauy@}Xx| z8$ebJd`IJFjP5)&@l#0fzL9mQLie!P>$iH%#QtUEuR5%g%M|!lRfwqnz)-@bg zXa4{VC7^5ZrnGzsrCREizIKsytHzpz+_73bW(aMqWVA)Jj^xC|Mz<1qMSwB{1hVme zww>3Ad{^;mJx4;)FZ?;-i(MN|gxYFQ!Kb@S!gl*Y!d<>;0R8NV^A;aC<#VFLD_%p} zQQkWJrEdILuC?EVd`03L%{CoHR>xA;4W#!{m5jk~QhBW!MEg>x$`>PPA;D%OdH2G9 ziC!wxwfm0~>6f+h(e$lJ?Jo6(xOgPnfU3Artw#(xyCS5geOdGmfd2q(Plr~&3$%}fe-8c~THrc@OVA=ZF~$%p-|*?O}pS)A)PwBga-FM$+}On~PV>KeQ!=iKlCR9I(-~Tdh^1(4b4FySnG)Z+G%S7On2@9C8 z>#7we=XFlb7-4f9lm6HB?_3`_X+|S zu_XMT@FT~TKMQqxza02?Pm5E!eL7t&t))ki;S-cTa-FZ`%B>#dDb5QnHj|HX)P5g$ z-&*lI&*RG!@az}zCaW?%l1*mA&LzB(41xl~EBBO)@S%p$#VYZqbrob{otHC3&1d3Q zhP1DQ-WG{$^=&^%)g$t*t!*wm<;}B^9Ae^cnn=Wv!o?VFE1Zl0pQ1h(d`R&xgZvk# zTFIkLsa$K5dCzfka9}d0G8uo>yoPAi3nFxsWCOuk{LJ{Fs6^f-x$vKc?xwfUtxy|C z1joyfkbU!j6`1u5er=!)38j5*;?mX8ZPd!r zA2nVu?6H04Welo5_7|G-h;(0wH_EcyT&AK@<|*zj;k6Pz$#BEVc3&@H$N?9oa=c{M zw99v`d@{Yf@UDw|z^x!%>|-8WXr#ofpyUPsB9CPn(S&(c{n$^EO!%|nAH%N{_-g7owVA9m zdo6Xv!XhiDl^aUTtvo_DWq;uW514l&n)1yh$l0 zzA~HP`+P;WxwTChZv+CF|zrlA#_vIYAzi-xwjg+Ybm4Y zGD7m46;xLY+kyZFK2R~p`hETI`{R9wfc_n8EAamSQ(ZdxOU0SmOL4MgjbjTK+PT_vLiF?6b{!JUfEI~Da$9{6Lz z_Lg#LaoOqjyUmK+lJ-mF>>(k7NbW(8ILfTJ+D6cuxEwX@t(Eqv4~XscJx=yiQeMXL z_6T5hE4LnGF^0hh1j!)J8=tLfUU-=`8%<9`(l4TdIbmxChWTFJ9+;9vi^-N3F}t%2 zqbqW*ROHhC0K&ZR#m0p$pROjOaU}64*|i%VunAIijF(k=NMm-1ij-L0NKke@1}mpg zt1XX)e0!_d=>Gr}^#1@8+juuvlTfhNZDfF5GOfG_){O+~Yb-B1T}K=x0jHo)+Il)Y^`o)wY#;JM^Nn%FA7E!Nax6vN^;&=!_lfk~ zbZMGynXWE{q}$m9j`MVJHG}!8;x|Q-?ab|+$k=8(S&)x25l&E*;h^O7yEre}Sl{@+ zUDLm@bb0NpbqM30&c^2PB)ythcQkJmmi3l2d~vyV9QNZsAzNDO7TVsWqwCjZ(pY0z ztkT~yv}}@mij5g%L%B#U#NZMS-X^}co8gs`THWeVc<$XUZ0zlCZf6tRIs)Rs7j-XeQUf5gOc%Im_)>?($rwgPvH!!;+ZH$2g(OpLl(Js*GApi~- zw$jz-MRMwMS-sCK*R}0y#X9eYj<@F>Rn;vu2*Nhg7{(MXOKso*kCXwlas_=GY4G>P zmVO!WME(rWw7qM?Hq&2T&u6sW&bIcT$29Uo8kwPqb`9=M(%Bfapo=Zr6`bj=P}BZooJx7*DI z#1oh^B$FXx8KZeiqBcJ2uHXjaTy?&=;?EUHaMr$4HM&D(btXK!b|5N8=19b>W=Fz? z1x9hoitV)RQ{i3jg>)SoRT@83mBlcf&>yFV%d2mV(dXx7_X}| z+ppQTPw@4oxu*CvQ%t$CHmo4LzPFO{?%l({W?>-@9o$A}<_{2z6ptH5cRH|*uQBX= zn{R3HTfo;hc3vCO?<}Ievh(#jJv!koB)~>pvy^gV$z9kP$5J|q<9u7J_=8=w(Yzh2 zcym$HgD&&@#;$M=iuKjnC%DV+|{BD#Ra^S&1J~Yu^+v^u0F!0KuOR?<3Sa zE2r5*X&uJ91FY~R*-O+Q-F zZySBvEYV#^s}dDr9YIX6IKU2!e)9ktyrnhWKI>b#yt;|mE<;G%2^`4CcR6pDCwVM$ z*1th~f8uY3R-Y5^HIEJ7L$6%S*3)Sa+AA4a=VEz_GO9%+eq$)al14sK2@2d-z~2%4 zJK|3X{6N*TUlqspU1BK`;^B6*VVdO65^|3GkKA zhP16C#8Elab&Y1$MOV78hy{x6HM}cj4c~yK8-F(CGS7 z*lGHdNePLyDQEJcl~Op$+`6K^5?SJ-vs8-_5#; zY2gr?i>vZh($%9?K2Mo8Ks(BoAuHv@(!Xc@XG_y&xVhFfdGyKF_E#GGS$vs-1ug@SLT4VE@o!1L_=WM#8&4T{q)BIH4U{liM)BA}kdq`xn`4p8WPr%a zyssiYGHb^)?+y;n@|0t zG;fDr4%frJ7>Y&K?CeMGqO^uPNhc&DPaLqx41tgO)4C>RJREagiSQ%iuDS6NM7Px} z3SQkRyk0}e=BbV_C3aZ>Q|2)zZbnJqX1^kJjW@ROprG= z0M2UZSH|e@uo6ys0kjC9t(ii6bE=w7h|3 zlIlY3w5+IzHkDkHn);viUB0ln_*qgqkCA0Zb2`%p<2*Fz@#xhqU@_W>O6%`Nr|GR7V3H?})BF&}!Gph^c)zDDNUP_RgqJ6R zFhM0Sk^7TDFaO`R-$yMr50Y<9O@FU)UG4yTf=uuHwbbOT-7(x1`0v)L`&nJ^#~>?3 ze|*g(_2#{~J@4gbpbYKp1R)5MfvW&dmoSW>|Au_OVxmXs;8Gz};Tusm$=D_c-v^ye zQw@!|w2#5k8qCL@(5ifc=9GdAU?)G5yM5`$+P~(M;d|o2EamgA+w6|>lfCad{;#oY z+ZVX1QU+X3;Q4Z~{|z#l82|)Q9$$a#;G-dyx9KqvE86Eh2=h-1+6u_&x~95ltyIc_ z`uZfkN?c+A$)e#mt%i9mXzV)gI42{EpXQyuYkZ;i4jDGbeRL@FA5^{&E-!th+B>V^ zU+z&hNj!;VNQG6F;yx+7nxXlv0{T!iit!+~Ei>?&x?5Uc8cFRW?s!OLTkmok>IMdK z<-PPJlKQC6?xb=)n7*sFT|hU;4%i?k3=%@Du2XhASTFjW(dL!3k>$@Qv?-lh3A}`i zzMod3lK+x6PRMHEXKJ#VpfWCMcOq%i#NgS=_V)jbs??0HfJTXP_RZ#2aDC&3(?id)hB9_IH z@0ocKkz?DKc3S;|yzn;J%)8W7@u&w?Sbl)aYmY-tHvJsgrdd}ZQ(zf0(T-TMe*JyE zi3KiXqlllttD#7GHlLjIfa7hQQqZCiB;}tO%EIgR1%|G3+cG!q#-XHVkjB(ffsXxx z!C0X6x>q}vuC01fh#tpIq^!kDG%8L(~xK88QXhFW|-9cipfL3^ela=PfFn$|=rzl0)-=P>rE&gScl?l4Ss zJ2$@^sKUN+=IslON7Y98WXAL#)VO@ct76F8g>*VkJTXRe(laKh1iHcPiK1=QV0yDA z7dll%h(X#htTgfqU)-h%XnJfR){7$P5GO(&G_X|!al*CPu7 zFH;Bo+d4T00=}`N)R)!V`KmsN#-77CLphmN?Lyl!4bqjBhP^Hx4%V{=SoWt^ZAwfr zYJXV9DoTYZY|v8j2GP>d77VIXcAwVax7tyE?|Gb_$l=_1kk}4JKN|6bZy#XLVxFL* zFVUdfevWoYau%13uzZWH`52*f#oKXhh5Ff=z0?U~|BhvAeM^oH6oY%1CaD+7Uq_a4 z!*j39Gm%UeWF`2~-Wufg{8CXxCD4ZT;L@cvF10OHPLRrACiajr-b*ynJH=TUTp0%` zUc;FiDMej?3wN=D)7g!u1}Ypto*B{aR@9kdnshffo*{}g=zUUBF`+y%u`G`l>d(a;mPjS4jUFT8De__FgtH#}I z(%wg0$wa}0CdL>cwD7fE+~!d#jFqs~#iv{(Gkz?Rc>X3)WgX{b*A`<=?>G*)Drwv@ z;-FMf?^{DTbHa$|T^vgDi;}7Rh3h;!z4NhQ`sLDgr-P}U59RLVJE)Yw6=o4o-#4|HIHu=HqYs!qZ$R1{mPe&hhIi5cRDkF zClZAR$L${{#3^-SClm)27S?@4m#tj7|8g6Fpxiu*l_t&PB?L58og=Bezp9K(iLzjA zv%Mz&v=RI^U-r(k>=L%UiP^UiO|_Y}mDGh%1B}-Sj{9UTd`@iPdVliPjcGT;oUacR zC1p{jtdky5TZFh$dvJ{fbGcFD7?1j5;nt>NywrbTnuF-H)#2M^tNKUgB|QufOtB2 z6oIao7S|gd5~-J*Wpn2!Wf-YahbO0ee~n@Nu)&ME@F;(=(UaL`Tj@q~t}b=6eb_{& zzPMDqE|NT!{+=b@N%?~@#b;sceOEEZuJdX0etqq~da5Od_q1~*%riCDL)G9fM3DhD zQA$Qa0hFrW3ln8O`{C|D#GXne88)zZI*2RmsqpfO#slC=Rr_r-iyDm)(o~xY=W=dX zmhFz^1Lwlg->$1oLYVlX8_Q_B7=Ly}?aOTQ#udSNLNOm-!))eKHA=CrS4KxET~6Z~ zp!L>U%^7KK<8(;S#A+C4Pe(wR#t;8MB|W&q!k)4J2U`KbAtF|8ipwwzStgE1_HWAT z`@Y+U(tL;#*0bmi;pXg&-3Bs!%{9l-GG!daU`o#0*1Z5=^XcdJ<`Es#b`VO-BISih zzmaQ~%Pmj@3pCxKGB%<9qI`qKdD{-*j$K-)WBNA<1s z5^b?-N!w&&(vNAJ$HM_&8~^S%n@c>!^t}R{Pha4gYW*=wua+UXbzO1ZS9)eE4)@;t^Nihp@sBcog@3GUEUkQ5<6P`M1&pqYG-D`u zCuHK#cwmg#M=qzkfU@sVyKfik4cGm{4UC~5;p*>u#y_VZXKGDFqvj@Y+S4OH&Iiac z@Z4~{PcbFv8*uTg{7*al<6t6!ObTpJkpenwE0Th>Ryet*tegTNG4tc==gLn2a&DTg-0-{J7ZQ;c|&5G}9BRoia>(%_nB zS&HCa59U5Aj@maCwM*?U99YJFzwx=Fg>{Q#iC>XUl{ObtWOSe$Bu+j1&!*rM{1VA$ zB7a{0VbP~?ws9ImFei~h4i6$&|Bfx{RodXb{2SpvRPvc?*lkrs9<0T47j_8%H6*o^ zBdsZqpo`u_ug07=>6|H3o;5WQe8D&&#*RqAaJmQOzr}wQO4?W8KVPI!I!NtY;I!}W zhb`M%<_jC*gfNVk;Pwpe$4Nud<7^}Tddw-`XaX~nH^bqkzP)A(9j1kcRMKT7r%eqV z@j;sdXAhY%cM>y{1bYU>4j2mLC^5T9<{wq_P7@tJ8wJH%9VB9Q%V3nA`{q;vig+=$ zO)W%fympOE2G$*+6f6>cExGDMb)4TUe6>u&zcpUr<$=A#oKypPxo2`xU(*@AXG_&s zD_9?CXJRN!OK|_*U86JqXjj<#g)X$z-*(pMyjYmu{#M_2q)hbr_tT$Hm$%7DbuJIG|5SGt5S}o=Hjb zLWRPZp-!#Ykh5Hqc5SIC;S(1Pem)*-kVrL5+Ob_9CpI!pl!wh8wne{He>8<5X(yn`D=;`jo-LBl!3@=)D{=|2 zf*Y$DF@wamjma`^CjbU<4VRkR|4<3SrHwnRi7Z1zNQM=M zwZjA!X%>pD95}O8F|j0$Ll_a{4+8r2(K=xp^3z+hj3R+~^Ux~z=@iklT3w}}{m|it z7#jO3s&$y+j5PhlS$GiVCb#~e47&GGeEg{L$ysBfgih^)OJTj(!tNQ9BeMVAkhI!~RC(WNBNGN^Xx$?IW zuG>4XfHbpOZIIptB=S*{HbRxB{7pcWB_6ZVcdJNC+XUyd;XmGl1FAbTG>fNzK%8H% zOmY{F?KyqYwW-+*eA4-oV_>fD^xe)_Q;N`s#KyNbkDf_y{&1V6@an z^+bH5pTIHR(qrEYaKDaUo!Mh!jKnLZ$~Z=kKl0%3cWA8BnC!}M#0lHeYBL< zuLkw*MS7kTXmJco1IOA7j7Pzxs}76qQVojiGz)NbacwM)pzk&U1f;cJ1>hl=b1s`* zvxJR0jdMPnr6MA%F)#OVf3-2yZ_{MD4~;DkF$-PG^t++?uWV-QZuEeo|J~0|h6dT~ znHUi>4W$NzNjM~2a$(22M`FKt5lg`N275<;LK<~?yYAxEeuU7Ghr>@Z<2ypLjmnVi z<4cP9tTCKM$+^2c#W#5Pqo&t$G}MZyd)k_{|L`NEX@UT$#Rv%lL~M=yEmdjWZQ_7L zz}&h**sCcsWBj+wjByzLyD4lSq8~~4S&xc{w4eT0X5mV|GKW@%S%_-gPscqxDEKcY z8;NAZy3tz)#hmrMsu6yGtY??)iQ|lmC%aD<`39^X(8RQdKd|3bf7fZ_3T2Mf-C3E@ zU1|3~-J=%$@vC*(Z70kr@%8%sA}>p)&0Mo)xIk>eyB~4N{D(Qor!kL`%RE+fMGag| z{;3k^7rG0vE(}YxKkHHjFFR&kM@c4`%H7qHsz-lqUQC+oH9{NbD;2%#)P9E8_oTFN zg5D9)5dVutv$&_4Xq{Oxy}%Xm%rQ0|F2f?iFz5}r%a`!rN*a#hB8gU;jZUD%k@JriG2WT=v*~;`TLp^NCO=@g&!p$ZHt$w}@=gqTYFD zUU`GEbb)hH?FefMu7S(0dvU6gRTweaMLUhYb$~8O-_B~IxCUAf>&0Xr=aUgY7t4*K z5%==cDo3Y{u$N(jisGb|e`rsEix6W*=cmN4vWi8p4L#joKLRs*OG(Y5L%uo*V~*uj zd6~6O3yaPPBi334-@3$JHJk@>6Zel7B0fL#O0$Qp^pW{pu!p`5v*)=UZAzyXW-m#o zqi6TvOl3;vk23Au`_~oS`FdVb@#izhl3HSvR#7>Om@7?1t;@y+yPlHTc7O0p^$n6=Io@#V0DdxB=x`dl0XuK$h^y0SZ(%8WaBa>3Fy~X8>$tMXXhUcYX?C|&QOztWi z{`v&h<5uzzN}wwl4)%a9nGAU!ae8<;;jHxv>SL9Yq!j5GYDx}`4H4NYy#47uqBH>h z6h!OYoEhs9Xk=%tuO*x-{xupu_0L7v=zvt6(-e;&zyL`|QJoSYU_e{<0~8gBIW*bK z7$b#BWATeZbO487{XQpH4@d4|V{`C0Z@Vk84w1ds7p`^QcdynoAd&N+{6cDUXqZbY zHiLzMkwwrtH0k5ZN%QTENIFpj+ZVSVM`TxnR@P(-uRh{uE?BbIB(P4Hy&mv{Ml)Dr zXkk71Ap8QVy1OhKUJF-zm1P~ph{^b)$<%s5V#SGr2A`56eRMLQ;tK~ zSIW$y&u2VQHEoB(LjHD?5}PJ6{nbHk6i$m~+<;1Hvc-Km<((lQ!pb&(vq5%f#y@u* z%T+x4pK5`kCyivGL5BKRh~psboH|VcX}qT>kAJ1%?RzWhn3B4_pFdt^=<&T{X>iolHGV_- z5wpb~tWi5#60Z%&*SJa+xp5`LMeSR8R+DgS25$|bw>V?(Y9IT&PmKuB(DrBJW&Qp0 zlKjP|8D>5ctJ6Y@w}Ujo(v5-BMleF!QTERr&I+IS0xifUwZK0rviMDuiH2<}E~`J# z85efWw2qv)e5XmE`%luZ^sjh&)~9}<9r6^x@k=NylQonBI~_ylp>>}ozo)_T*v`tx zP29TYogPt{H#BjZR(Jqh{Z>y8M(lbhYnw_;EjHx&Qu4*yX3A}fes$J94-Uz@~APSVwKfme7U zTqaaFG@}ql(H8;_@ZX(Rq-0BMbrn5@W}ohfmV7HqM=-8|zPXR&6zSP$VfY1Yyeli5 zqW(fD?$dF*kZPW%{&+nU5USuniluWExV;-Z)P8izOmrF%Oc zYy7hj_t-i^*gwVAIQR!&>QkPjfhvT)?^hZ=>me=O5DS?IeaUUQsBgYf7_dqGSuW_b z8Mngi?$@@C6kW+13*!RyN{UhRcS<-(J82h?!4vb|b>a>n_&W7u`ocMfwa!P0DD>9y z7Bq3S3URSs$26b*hnkoqTqC2shA5r+Bb2k|`o+mq;=Kls{p#@sr((=N;PD^qcbwsa z%r*T;hAT;V-WVZ!p-NvmCaqf8vU;^pn^+H!Oc0v0i~hTpoXv6RHNW81)stD!vqyS3 z`MfLM_cEc?lu_n&(|*>S&mUmjOiC?{yK3Ki^72w$FfP?AI=a;Zp?YK@vWGHJ{sP2XK=GXVKhSX)I$VF~(40okB z%AP}2AK#`1UfxF~LdVh^{^h%}DavvsjB_=4_3o6fM*8kc+n!>folYo-v#+%aP{j^v zJN4whn(62V!-sv@-OSZ&kAn<Ib5n;(?5I>1_e^KyT{+CarKT)fEI`pkE|Ef5TiX2lgmaeqH2OcF1aJ)0S^Pmd08& z%XR#kgW21D?R~CrPftOQ!(WwbJ6OmofT`2PN@qrChNk2j%Bo`a~~o8i<--@KAm8o499PxX1MybIE(W)(wP@q-bp2W_ae^cbD-F{epH9< zwiKuQbg@Chu=nJNQ(2PVGf020>eDc#n6&A#v}R&FqjU5pKEOlY_^9$4CQ__dB|mR?JW+=iZa zHbPgH5&7@~+))x=Xa`Fvb zl-Z5KhM|0DGbx~E{*^ej=)xaGxRZEv;(~~|0JX+2(@$1`ZVMEOYUAT(0UkOfHXpn85u= zI_~uWk>NZ10xcu%4Q_G1>_6#w&k4@ru=f)*oR|Y%%}HQXL$XxPNP{HJ0e!kBr=r?E z$CoC~)EczOS2e|wo|NR?vMCC(tN7)UUVohwEN?5trchf5-|XOZ+%#$v6wxwe?0i_6FR(y81~Ph!OWVFk8@3& zs5BSIi+Xjw-YWMeypXyy`bgq!U4S-QzGJ7!Ye-jw<$>x652NwqqB)*V8l z(~Y5Ei~@^8(Gi{BH`62YS<%W5wNV}RYiNTHNsNq=)TI&~bRjw-s4{{tF%;<;R~E`} zqHPcTkF>;5JRA={P7_~DVA*Qlh30P1 zMWvNJogs1QiG#w2rQWT+=inB+4Q@01dbEK`D>Kl1#(e^XZnee-DqMseyk+A)tdhPA zpSn=PBRhb>ccHeinVidbjW%$ko%54xj;Ur@x5#!_Y_&g@f9A_ddhLB&nQQYwL7Ts{OXSp|Put=s{ZO-Gbz;I%Bif z^VC_VI6`sDB{>2riG?4-?M=(>L;Ab3ZdSb?P|Avz4jL-S&BUHTX;uEu>oQVuu_qHC zt_cb*dNiBWm!0)CUc>Lx8mw4XTYzsG-;ISheAxaci^D;XNR_oRaAP45WC-wchXXvU zx)5Z9)1i4H{Ury24cSYDkIYT&bd2I~BRpP*Au-*US}!ICx~Pwuqei#seYZi>`Ts!CDIjJkDsb{;nkfY$9mi z;O3(BHvSabXjT&MP)}r*=#i>;UC*G;iZRci>vuJwAso-3VXX_hr)Zw8>w=@@#Nrd? z?9Oa@+aQ8PL2&Fk<9Ty0gvc=_d%ebq6#~naEi;D6OX|P;6d7yg(-t6PASg> z&4+90fmrq_rb8934Td_|skC*Hzj7&YsK~rhMEkbN@@-8S$84N?N9zw^qKnz?UKEyG zjA7Uhgm!P&rI{on@RN?|?^T?R&z3Ne3dtDYr5hP!+Nw@dgRe-L6qkknkuuPa43ISa z9K?vm%yF9o z_?|;$Jq;;Kj{)$3TVJptv$a|->KhU*(i^1*F0w+wR(I5z2#1hyYe|8!Rn=fPBSTZv zR4gf0>bamtM~|97@7RAnV30`j44HES-aATZKon!Ky5<>F1g)Hk(3;xLeN2XcH*IZc&W z8$U;#9S`rW6pvHY|JSK&TBl_A{T)cm0(4+55i}?e+mcaKoxYlXk~V6WmLgC zPWu=0QYC)!P+S{nH;;TD>2P>^yNkYlZEci`h}x)z=U)v#-*ot+(lf}Qzcw_NeUf76 ze)5kD>59e$U3^u4ksMERv3=9R^vBwUR)4r@l5m9H<+q^z^q)9abd8Wr{5w|Un`cms z_cLh5&@rp03vp@wj|Y6My~t|h=-v32inYLN5Zv&wc^eW($K1N8f_XWVsi$65D^FCO zK$uE;8?+jlW-jAk-mut*CCHgaFhOVv)AjDQC4{Y2#g0C6P z#mlsjH$Gc+_d?pYaSxP3Pq$CG9dcWc#cANlyzArxT+@qnr!gv_gMT^U(++lgzW(eZO$YmP*RsBvKxG< zED8LR=IVeiWh02;kjEhZaAeowGf0kf+AKjTyJtmCu}Vm)rl*U5OH%Pi?`ERy{VO7R z(s8=HyVBz`+#IHn4RMcTCIxqc zkAR8p>e$DM+p=d6Gi~xygd%d>+~uSC$k<;ux%z2IV4x(?@!wZY_1(lC z!5~2|1qx4+)Grl;gF|4N8s|<*#Gno$wD516bb%H0AlB(s`U21kb!)tfQjNa}Q|p;7 zwvQV#9@F}$=ygKAaRGGDZZ5&s90l;RJJ$QMr-;I5&~%CxR3qoTZu008-2gAC|F=Wf z$xTMulIb9C49jeR@T9Y4JxVE=xovDvalS(IaI?$62itD6+QSfT1o;--@AG;k#P{>) z!Cy{+BfsJ<=ZmA(uXrY3TGzH{oDh*teP$4eaNlr`qL);D2C?BmZUupmcfcK-bBZz$=QoyhQVoXFBLe7`WJQm^ziLvv5~6oui} zkoGGB5au#84Frs zpg)B18G>AKghgEMLO*$!)$$JTqC4IPN9Ca56dEqe;i8S$cxT#zlyU<2=IM`d+F2zB;Y@`b1-{Zc8lFsDl^X^%aT3rbz zPE;&|@&oQ5pPoVAoWVEi8c!ho`XI4qP>L%M61ri@$iznIF1-~Q{B_l&4?@mqdoqT| z)Nvi}ye#@r0+5XuuQzgRLl!lyuOG0v1N>=TOarL|sDy-e&M(L*YtlQ}4`G9`0JY0~ zR;-Vx(6}1^`^8^Ph!SJ}qDaZim~;ywz6uO|(BeT^gv`!l1MkDD8f2uOd5Y>Yh~DYO zZO5rJL!Z^x0kE%e2YGeKWIir}(B`MVhQbWQT-;jo5O(cp9^86r7pp$v(i(>U?X8m+ zi8>SLW6vYiHWJ(aXv_M(ET-`+W?F+T{&Sj&O;a2tmBE(#i~nr2`YAn26ev8>60+~G zH;^AH!T+)VQM$M`x5%!`7r=vcj2~<+7N1itzgP^@+IxeVyU@5~@T2o}%kreyjc#d--lg1ja1w7={c~WyJlh6ZUe5!8?Td zVj%JdVnKov0Fk4WPULkDD_qTL`Hjmf6pv$u(W#lQ4z z-12oH6G$&LfltT{o4hn4FJ{YS?Q^4b0n;yD+_+6E2Mzh(?nC+R81 zgJ=Y*xEu>7M6y(tDyMjqAFtD+Zp?*dZ(dGHECly6h?RZ-4BYAK>{b%s;5OTC_h&!@t|-!dzF$`r&?$k`&|^PZ7W)5( z&_)uBE;Zyy^akpU{-(3fP$*)Fi=u@2FB?{q=6nfoxysxjOfmm}XddjupMs~ozfdUS z$=i+5g{L{ki#jm(Joe?*w}nx--div30Ty0<1~Gu6A-5FfPnm6#Fb$-tMl|@?u^#EZ zSROGrzSRm_xaARWei;080gmP>TayWQEpGdEhs9w?{`JHJbq~fh1kxTk5knr-#30!W zHXO{!0Fg-kS)&FwEPsb*P{WNnI_&UFBP1*Y*BXdKLh zROpiGa;dqazR|ef#lKRyuGP(DXOGw~|BK|FlO}xBI`@0zP9{U(_j(8&L)~!yWfAoF zy!nuCa{lV;(3mnONlNO0XG-&kSj`wcjMMnsCOO=@D>?*ap^CRb2JdLun zMzX^2Q@NkBN>4fn(xM=di<#9U`tuCgvctgNW6}%@f>2iJ;o`C3hnRw#6Lk;48FU4H zv=q`IY!QVje4irZoY~8Bw(#o4(x&{gPpS8ZJ@b0*zlt+G$BEtz`ZDXh&8z+Kvb^UF zg%LvWR`g*IIUof*Dev>^k!;|hA`c*$$t-^cmH1^0O6n?c4tmaf2Dly3Mz6gj79?x^WcZIseb8p(VTay9sn+XwP!N2=1s!BFQF^gXfrra8hh0fpn&o{%+tDRCm*wD zIgzdF;J>j>x8|G8k6&-P?*Dm>F=6Ha&Mc_BpSASKmJE%){80`A=S)d(J^De6k^( zqixvEzxfl-WoX2&n4G{U(<+o2HUL>Y5}WtFJ=AnE$17={%HzshSBmTRab4Fs_xIo* zW%yKbOESqS%QgSct#9Lw5Ri2V?ns4W&&)6AuAk?4zX1uue1B&4`D3&>Sgg;<v1(5obP_OcZboAy|M0sf4Yw?|d(rg}uBTr&4u`B}^we z3#(~OkNCjgvRbvDFcrdX_BvLm(n&aT`sl>NO2TxoF^z~f&5N3Sjbz#tg(8`2r_zFG z{`Z4%@G}TVDi<67A!AhTn0q0Y+<)nBiGb{GdQ_Fz%(2~%U(<;B;Oy0-nt4=+NQbG+ z$Hw`>xHrh9h1u9@SLW208B6bK_!3%@z1}!iroQ{sjo;oObU`(+t(fXxl5LLbpGl08 zqy(8vtY)@II2T*Fjg$>*?>wjP|MS>_U;|(oj?7aefdAiRcJ`j*KL-0B{gt^c6z_+# zs+h!AIJZhH*ZY~zNXl%T=$~}b&Sk4sw&E;rK!mZ1zsC|pq*>}_`H~do3VXFg35}W_ zc48vhw=`%~1;vpNxavZ@q7(EJ_(ye_-HRHSvrF+B7r{iTz|ckM(h`P6(H`Y}^8I8K z_?Dm+sqg&&zN0+rI=VG~V#sc-rHJPBD5@|vxg#Fn-jq@glKnXtgnpW3gO`qxLz%S1 zWlKP0%*Q`H(rHrn*iFVf=6rMM!rZf%ouBAqQ)qSoqf03t2@zk4;(y1OLDo}Rg- zIfaKX>2~Kf>H~2nhe^^SIX}_5t8eJ9os?_9(`9yv6H_z6e_~0$++*aykIZooiHsf1 zm}g(tY23(khH-vBDM>)#WX;!?HZ)irTA})&g~!*s@(5s4md#)x-Y1~EqVx0%yz>)* zi5$KKia~`tH5GpvDL9!(NE)T{W`k_b2M3VE3k^qxW%DN`sw+vCOsZd?1gu&hDB0P*BH0 z?N&bBVvryNvmuWRhckX$Z&0VBJ@^AEF44+hD0jnHYsQ~AEu9yUsAt;kpkVnzfI=>x zruMu)GwGo4uTglLq#xPsmFXXW`n!~oDP-K!NqSQEfvCG4ix4(ys@jw-Wu>)TrxW%$Xv7g6;U8fEJT#P7PSJM1@`vwiz6 zKD-=a@3JK+vhmDq9t+logQFTWi2H-9%?%veup0>e_;}zbl)`SMYPoLXLbVJqR2lJb z3R%>mJ(d>^*90e_(Z~!bFE9moM|&Q8%!-dpnmtOY%1?k%nOs1q9wm)kj5X&vCBE8|VE=wXNFp-8(3!a{GKIp?|)8!*AxT38>hE)9Nez z9;*IY!)j;VAy7^Toif)M3+_z+o4Z)IlwywFS=X+epmXq3j34CwOG`~!2t81Kvp!dR zm;$mSD}9<0|3kfTWqPlYvsg)dpP!Eg7q|yrmaJ`wV3lU~BPS0cg^GpUVx2DzSzhc6 z|Lt%@fK0Pald&fKzZo4m1sbw68CIRKplq}AGKmR4LP_G&#n>DuT&i2?nGi)t$xbF9 z(-waQJ%c_#gK^GPk;)BSiomn8sLLU4a;Pg#YZov01x&c2errHAhlM7hH*`Gd;;yiH zG&t$Tu(SU)+WikkhobI<EHFf~T0A(6BX;MT z%F+knAoW2vjCqft5+zpN$!<0dC-JbbiL=6oP}bQm*yXQQc2Mu?C&KedY-~20$oEXF zgBmv{vd^9(0jxOfV6N;R{r^Ps@5a5_d zgl)km)wbd59+SZvl&{R#Jk-wMKTQfY%q+Z61Q@CKP;>tTv@~Zt%%zZk>s)Gi*ZlK7 zd!nROpv(55pS0CraAYDoW9p=Q}9u zCAwemX5(&__@jqoEzG_-Et%Dr$gpp$tKB)8-+~C6@bVNEFeyd~f#cs%SOCe-qRc|} z#s#o$cQfF)U)rXson73pK@tu7VwuoERUEO0y<8)!m-gg<;*9r0eq}BwX@zSVD%tV-kmrOK#FqPo?T z7U0zf2xX*`rO;nHiP@y9?Q@)-u&Df~Aj}n3{)8nu{4bAOvx8rP(U66`c3an5D?$2- zXbex_oBwwH{O((D&AiMpv)5x}U=rXqj1WLAVhMmFX^S+;>ityr&XA?H`hHI=ONq`v zYYA5x&mt!~2jr;~=K^6A;EvbWAXSY8 zoTp-I8Lnb$D@?nJHtExi4z{j`aZVs37pCPvK1dLo0$Er5*u3UarnV6Yp z2I0`Ir(ghja{LA8A(EFvAk!6TEyAXa{KM`SW0C{0W0q^aU-_Wl_)t+(8WM8%S*%Ff zV$q5TA?t4I#V6bLlBqhIbl*33&i$*tfbVdcpFHCMM3dCiwa3c)Dno< z@45v6yZhJGj@NEP!AIu*fSCLa%3MKcr(v+Q2%DC?P#n?JT2bZDogdZna)OQohxPn+f9_%#pd&8MFi>JnA7!ew%6 zWTa7~`7loHBr3i+pb&G7S!)L2S=K{` z(Kus!!iClvRU-qao6R*C&j%mD%}=2Kk;8Q3jq%eF`2Mi#{>8is2Ro@(KXtDjmFfKw zD$eilJ@v6aA9!-)hJL(w!Gm3c%0j$|ns`rN z>1=?HbIynUhN|W%sQJq1JkbrQTvGNF#teMXi9U`=T#j&%UqJbR2$Flz2Q!sz4(jUi zBTD1O!PyMOC%7Ja?BTPXcJlY(B{4?DRzx-}Z-3ou2wQZP!DZ%(L8{(c;|5RT{yk&C z*;V}SBLr16M7qD0U~eyNfi?+qH53@mgGvCASJvF&0CFiLGW*CmbF1v=As7S@BAw67 z8aY)a$LRgzt}t-Kcdt9p?&}_@z-H|ovQcJ`cZswzU6EZ{Vu%-aY3vF=TL)vv@uc_! zpsuYQ3|t1lo0FJW{ZmS8z3t+pD%*c_PrM3TgFJ#AyKb(?k+DYbs;-?MPF*)Az^htF zl&fW9v>)KU<*M~Cl4NL88?EJ|u=Us{21=RO1$j+q2sM@+Xp{^6{tK=vUYMRZqDoCT z{qc)gDt17Tjq=C@37J&`;3I1@GPb!!9tb|$6O+i_Kq*dENNZM8=yOtK)rx%}Uh~hx zDYaVrrerVP<#V*@LJaDVGnSOE#YsU=Gkt(x&mxMN>S0zLVi4< zAgC{RgyMHq=V#h(8~s$B>^m8_$YUx{r2}5armlM?j`{~_05k3S+ade&G=Te(U6(nY zEo&ws$$9%9LKK44;ezW+@Sk_058dqq*E8s6ib3qwC-)DItAi^}C{rLwaTZzZrk8jf zR|iP>5CDGv`2gM#*`ojF+I4Lj@}=hSiz4#N;+#!miB^P3G7h!6LLfPGwUKv>&SzAtC0K^aA84EkA2I>$dz!z2@ID5*jE$s%$1#!c$E`BeO z*l6)%@0{*af&z5{ou>|OU6;%SUFozS5XWt&KsqqT;DzF2&>-@$>w#N-YZ7samT$u7jzk7>itNxrFhgU+N z)-!07{TXyLp>brmP6<4#Vlqc3GS@2emtmpn2eZ`eLP^%Wj0{nmcJoRDI{DGfax=x< zKDwLbmIxKVdm3J?-3QC#gG2YmFGnWY%>nOue%)!;=nA;>@4_ zhcm4EaSj`M=hb?dRIAuviEkVDKI@S)?gH~Mf*RRcb5qbKCiiicTG!Arl9ClC!z=9j zguDN?O!Ag|Y-?12;KAg71tTpJJ7^xc`QeK%3Z}bu*qxAwXV8>cTR_*&TQFi~7f&m` z7h~It>5PAqXJSbdb%S)swCBW|XFAkOCvaOb{QC88ESWhVaaaTj2~(AEUBH2Jy6*1) zGsp=Z3y~za?=hlT0#Tv(5kV>8uDBg~F#c(wpq?yFYimaKt%+qIC@?JW`b-}%-yO~Y bfN0LUh8FbW+rTTmU`7)yzw!~;=jHzaFbeZ? diff --git a/acceptance/cypress/fixtures/image.png b/acceptance/cypress/fixtures/image.png deleted file mode 100644 index 4c109bab8dbadd00ceb082694f3416b715055935..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1185 zcmV;S1YY}zP)=xnvsnabWDk;T{N>i~nkE||zor_QO-;(NTvH2D426-nUu@_7pLQpkYy_MZTyNstbb5ZZ zwqx{xEQCOPq!bL>d=r~XQr|(z$J{CUw@0HTe<-H69xKkDirw2{ZFwlK-*d1^i(#2< zZdBfJ$f$ydeoc4S)mON7eLJim#v9j1NI?#R@upr~6xDF?Osq{SklySZY|9{P>H((P z`_IIpd>5zt1#+K6%5}triu4b;lFhklRGZ(O-CA(TH(592LivBkRT?MLfBr3vORg5K z?&8YkTrRZ838i$k$XU2LZhDxqU(mI)Ok&p(Gt zFIg1VKIzh2-EAACm_>`mgctiiTzjxQ8leQa$z?AWkGY&3CS6N=JG6$2FugPF>rm?z z#BRDZf7!{U#i4-E)hd;v*qzu@>3yU1DP%$=aQWyzNrB+rxId8cvMiKsCmoEY4;7xsu7lJSs`q zq@vs!E~A?@hOhg}y0(AP4leWtZUTp!=Dl2b2y%%t*wjuiQ68@RUB}vp(xr0@t{_iO z*B;F~Xz(n4GRP$yz8}9-ajm^eR}XT(Bq=9wAr^Ake&kCZWGpys#Ta1r(SPlS`?+jT zE!UuJ-%kpRUyL;6;Ft8OxPdZBea3~RZ*#87Ww7yZAkIfUjq@OuJ*GLABpExoP}|q% zWxID??y8~(xcE5Nm-8rdB#%0uYnJzN&UT?kA;e+c`@eGGx&xOE{t4@p7k*zf+z`NZ zrGXGQUsoRIDu?5tP@u+j0ILtw0YJ~6zdQsx(70|B4%cl^`}2({R{$Ray`av98s>)v z^1~@t0&fDnsxFE`Q!m}%vwXW_E)`+2F*thMGAz%jW230=W!+5q+DMy2E-}RDDukQE z`wnZqi4JfHM!e9UdZ4e?%U;&Yb1rQk?@}t=m(#5G?h~2eKGA+I^aR>h-#{+uV$?F; z*K>BVuXmKoRb0b*)SOFs#42g94=Z&E>-CX+*nPY6G_D`cxp4WwdcJS2iAW#YH&-LA zw&POTz$W`XE=62Z#qg?g4uOA54W)zOs~pznqgkOo@0AE&?|jMd)1&87UZdm3RUAFz z9MR9Yub*>zKj$ic&cUM6_ASmW&(E2McZB){V+fV(8gbsY00000NkvXXu0mjf Date: Mon, 24 Jun 2024 16:44:48 +0200 Subject: [PATCH 175/226] Add tests --- cypress/tests/basic.monolingual.cy.js | 43 +++++ cypress/tests/create_search.monolingual.cy.js | 72 ++++++++ cypress/tests/language.multilingual.cy.js | 71 ++++++++ cypress/tests/results.monolingual.cy.js | 76 ++++++++ .../tests/search.anonymous.multilingual.cy.js | 97 ++++++++++ cypress/tests/search.monolingual.cy.js | 156 +++++++++++++++++ cypress/tests/search.multilingual.cy.js | 165 ++++++++++++++++++ 7 files changed, 680 insertions(+) create mode 100644 cypress/tests/basic.monolingual.cy.js create mode 100644 cypress/tests/create_search.monolingual.cy.js create mode 100644 cypress/tests/language.multilingual.cy.js create mode 100644 cypress/tests/results.monolingual.cy.js create mode 100644 cypress/tests/search.anonymous.multilingual.cy.js create mode 100644 cypress/tests/search.monolingual.cy.js create mode 100644 cypress/tests/search.multilingual.cy.js diff --git a/cypress/tests/basic.monolingual.cy.js b/cypress/tests/basic.monolingual.cy.js new file mode 100644 index 00000000..2fa0c728 --- /dev/null +++ b/cypress/tests/basic.monolingual.cy.js @@ -0,0 +1,43 @@ +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'); + }); + }); +}); diff --git a/cypress/tests/create_search.monolingual.cy.js b/cypress/tests/create_search.monolingual.cy.js new file mode 100644 index 00000000..703d7496 --- /dev/null +++ b/cypress/tests/create_search.monolingual.cy.js @@ -0,0 +1,72 @@ +describe('Searchkit block tests- create search ', () => { + before(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Search', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garden-blog', + contentTitle: 'Garden blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garden-february', + contentTitle: 'The garden in february', + path: '/garden-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garden-march', + contentTitle: 'The garden in march', + path: '/garden-blog', + }); + + cy.visit('/'); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.intercept('GET', '/**/Document').as('schema'); + + cy.autologin(); + + cy.visit('/'); + cy.wait('@content'); + }); + + after(() => { + cy.removeContent({ path: 'garden-blog/garden-february' }); + cy.removeContent({ path: 'garden-blog/garden-march' }); + cy.removeContent({ path: 'garden-blog' }); + cy.removeContent({ path: 'searching' }); + }); + + it('As manager I can add a searchkit-block and find a document', function () { + cy.visit('/searching'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('div[aria-label="Ausklappen Common blocks"]').click(); + cy.get('.blocks-chooser .common .button.searchkitblock').click({ + force: true, + }); + + cy.get('#toolbar-save').click(); + cy.visit('/searching'); + + cy.get('.block.searchkitsearch').should('not.contain', 'No results'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + + cy.get('.searchbar-wrapper input').type('Februar{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + }); +}); diff --git a/cypress/tests/language.multilingual.cy.js b/cypress/tests/language.multilingual.cy.js new file mode 100644 index 00000000..a604af69 --- /dev/null +++ b/cypress/tests/language.multilingual.cy.js @@ -0,0 +1,71 @@ +describe('Searchkit block tests – search - multilingual - language', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching', + path: 'en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: 'en', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'der-garten-im-februar', + contentTitle: 'Der Garten im Februar', + path: 'de', + }); + + // Add search block + cy.visit('/en/searching/edit'); + + cy.getSlate().clear().type('{enter}'); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common') + .contains('Searchkit') + .click({ force: true }); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.autologin(); + + cy.visit('/en/searching/edit'); + cy.wait('@kitsearch'); + }); + + after(() => { + cy.removeContent({ path: 'en/searching' }); + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'de/der-garten-im-februar' }); + }); + + it('I can search', function () { + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + }); + + it('I can search within language', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.block.searchkitsearch').should( + 'not.contain', + 'Der Garten im Februar', + ); + }); +}); diff --git a/cypress/tests/results.monolingual.cy.js b/cypress/tests/results.monolingual.cy.js new file mode 100644 index 00000000..07c848e5 --- /dev/null +++ b/cypress/tests/results.monolingual.cy.js @@ -0,0 +1,76 @@ +describe('Searchkit block tests – search - monolingual', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'suche', + contentTitle: 'Suche', + }); + + cy.createContent({ + contentType: 'News Item', + contentId: 'brunch', + contentTitle: 'Brunch - Viel gelacht und lecker gegessen', + }); + cy.createContent({ + contentType: 'Event', + contentId: 'ausflug', + contentTitle: 'Ausflug Matterhorn', + }); + + // Publish News Item + cy.setWorkflow({ + path: 'brunch', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + // Add search block to /suche + cy.visit('/suche'); + cy.get('a.edit').click(); + + cy.addNewBlock('searchkit'); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + // TODO Replace this desperate wait per seconds + cy.wait(3000); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.autologin(); + + cy.visit('/suche'); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + after(() => { + cy.removeContent({ path: 'brunch' }); + cy.removeContent({ path: 'ausflug' }); + cy.removeContent({ path: 'suche' }); + }); + + it('I see the publishing date', function () { + cy.get('.searchbar-wrapper input').type('brunch{enter}'); + cy.get('.block.searchkitsearch').contains('2018'); + }); + + it('I see the start date', function () { + cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); + cy.get('.block.searchkitsearch').contains('.202'); + }); + + it('I can open a result', function () { + cy.get('.searchbar-wrapper input').type('matterhorn{enter}'); + cy.get('.searchkitresultitem a').first().click(); + cy.get('.block').contains('Matterhorn'); + }); +}); diff --git a/cypress/tests/search.anonymous.multilingual.cy.js b/cypress/tests/search.anonymous.multilingual.cy.js new file mode 100644 index 00000000..7d06c31e --- /dev/null +++ b/cypress/tests/search.anonymous.multilingual.cy.js @@ -0,0 +1,97 @@ +describe('Searchkit block tests – search - multilingual - anonymous', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'searching', + contentTitle: 'Searching', + path: 'en', + }); + + cy.setWorkflow({ + path: 'en/searching', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-february', + contentTitle: 'The garden in february', + path: 'en', + }); + + cy.setWorkflow({ + path: 'en/garden-in-february', + review_state: 'publish', + effective: '2018-01-01T08:00:00', + }); + + cy.createContent({ + contentType: 'Document', + contentId: 'garden-in-march', + contentTitle: 'The garden in march', + path: 'en', + }); + + // Add search block + cy.visit('/en/searching/edit'); + + cy.getSlate().clear().type('{enter}'); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common') + .contains('Searchkit') + .click({ force: true }); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/en/searching'); + cy.wait('@kitsearch'); + }); + + after(() => { + cy.removeContent({ path: 'en/searching' }); + cy.removeContent({ path: 'en/garden-in-february' }); + cy.removeContent({ path: 'en/garden-in-march' }); + }); + + it('I can search', function () { + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + cy.get('.searchbar-wrapper input').clear().type('march{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in march'); + }); + + it('As anonymous I see only published content', function () { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('/**/@logout').as('logout'); + + cy.visit('/logout'); + cy.wait('@logout'); + + cy.visit('/en/searching'); + cy.wait('@kitsearch'); + + cy.get('.searchbar-wrapper input').type('february{enter}'); + cy.get('.block.searchkitsearch').contains('The garden in february'); + + cy.get('.searchbar-wrapper input').clear().type('march{enter}'); + cy.get('.block.searchkitsearch').should( + 'not.contain', + 'The garden in march', + ); + }); +}); diff --git a/cypress/tests/search.monolingual.cy.js b/cypress/tests/search.monolingual.cy.js new file mode 100644 index 00000000..c48544a2 --- /dev/null +++ b/cypress/tests/search.monolingual.cy.js @@ -0,0 +1,156 @@ +describe('Searchkit block tests – search - monolingual', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'suche', + contentTitle: 'Suche', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garten-blog', + contentTitle: 'Garten-Blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'februar', + contentTitle: 'Der Garten im Februar', + path: '/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'marz', + contentTitle: 'Der Garten im März', + path: '/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-mann', + contentTitle: 'Testseite Mann', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-manner', + contentTitle: 'Testseite Männer', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-lsb', + contentTitle: 'Testseite Lehrstellenbörsen', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-s', + contentTitle: 'Testseite Stelle', + }); + + // Add search block to /suche + cy.visit('/suche'); + cy.get('a.edit').click(); + + cy.addNewBlock('searchkit'); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + cy.autologin(); + + cy.visit('/suche'); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + after(() => { + cy.removeContent({ path: 'garten-blog' }); + cy.removeContent({ path: 'suche' }); + cy.removeContent({ path: 'testseite-mann' }); + cy.removeContent({ path: 'testseite-manner' }); + cy.removeContent({ path: 'testseite-lsb' }); + cy.removeContent({ path: 'testseite-s' }); + }); + + it('I see all if no filter selected', function () { + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search fuzzy', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').should('not.contain', 'März'); + }); + + it('I can search with inflection', function () { + cy.get('.searchbar-wrapper input').type('Männer{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Männer'); + }); + + it('I can search with decompounding', function () { + cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.get('.block.searchkitsearch').contains('Garten-Blog'); + + cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.get('.block.searchkitsearch').contains('Februar'); + }); + + it('I can search with wildcard', function () { + cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search for an exact match', function () { + cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); + }); + + it('I can search for a compounded word', function () { + cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + }); + + // Blocks text + it('I can search in blocks', function () { + cy.visit('/garten-blog/februar'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( + 'Montags gehen wir in den Zoo.', + ); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@content'); + + cy.log('I added a text block'); + + // Searching + // WARNING Do not use cy.navigate TODO understand difference between cy.visit and cy.navigate + cy.visit('/suche'); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); +}); diff --git a/cypress/tests/search.multilingual.cy.js b/cypress/tests/search.multilingual.cy.js new file mode 100644 index 00000000..32bb80dc --- /dev/null +++ b/cypress/tests/search.multilingual.cy.js @@ -0,0 +1,165 @@ +describe('Searchkit block tests – search -multilingual - fuzzy etc', () => { + before(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.intercept('GET', `/**/*?expand*`).as('content'); + + cy.autologin(); + + cy.createContent({ + contentType: 'Document', + contentId: 'suche', + contentTitle: 'Suche', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'garten-blog', + contentTitle: 'Garten-Blog', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'februar', + contentTitle: 'Der Garten im Februar', + path: '/de/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'marz', + contentTitle: 'Der Garten im März', + path: '/de/garten-blog', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-mann', + contentTitle: 'Testseite Mann', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-manner', + contentTitle: 'Testseite Männer', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-lsb', + contentTitle: 'Testseite Lehrstellenbörsen', + path: '/de', + }); + cy.createContent({ + contentType: 'Document', + contentId: 'testseite-s', + contentTitle: 'Testseite Stelle', + path: '/de', + }); + + // Add search block to /suche + cy.visit('/de/suche'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('div[aria-label="Ausklappen Common blocks"]').click(); + cy.get('.blocks-chooser .common .button.searchkitblock').click({ + force: true, + }); + + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + cy.wait('@content'); + }); + + beforeEach(() => { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + + cy.autologin(); + + cy.visit('/de/suche'); + cy.wait('@kitsearch'); + }); + + after(() => { + // cy.removeContent({ path: 'de/garten-blog/februar' }); + // cy.removeContent({ path: 'de/garten-blog/marz' }); + cy.removeContent({ path: 'de/garten-blog' }); + cy.removeContent({ path: 'de/testseite-mann' }); + cy.removeContent({ path: 'de/testseite-manner' }); + cy.removeContent({ path: 'de/testseite-lsb' }); + cy.removeContent({ path: 'de/testseite-s' }); + cy.removeContent({ path: 'de/suche' }); + }); + + it('I see all if no filter selected', function () { + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search fuzzy', function () { + cy.get('.searchbar-wrapper input').type('februax{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + cy.get('.block.searchkitsearch').should('not.contain', 'März'); + }); + + it('I can search with inflection', function () { + cy.get('.searchbar-wrapper input').clear().type('Männer{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + + cy.get('.searchbar-wrapper input').clear().type('Mann{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Männer'); + }); + + it('I can search with decompounding', function () { + cy.get('.searchbar-wrapper input').type('Garten{enter}'); + cy.get('.block.searchkitsearch').contains('Garten-Blog'); + + cy.get('.searchbar-wrapper input').clear().type('Garten-Blog{enter}'); + cy.get('.block.searchkitsearch').contains('Februar'); + }); + + it('I can search with wildcard', function () { + cy.get('.searchbar-wrapper input').type('Feb*{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); + + it('I can search for an exact match', function () { + cy.get('.searchbar-wrapper input').type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Mann'); + cy.get('.searchbar-wrapper input').clear().type('"Mann"{enter}'); + cy.get('.block.searchkitsearch').should('not.contain', 'Männer'); + }); + + it('I can search for a compounded word', function () { + cy.get('.searchbar-wrapper input').type('stelle{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehre{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Börse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Lehrstellenbörsen'); + cy.get('.searchbar-wrapper input').clear().type('Lehrstellenbörse{enter}'); + cy.get('.block.searchkitsearch').contains('Testseite Stelle'); + }); + + // Blocks text + it('I can search in blocks', function () { + cy.intercept('POST', '/**/@kitsearch').as('kitsearch'); + cy.visit('/de/garten-blog/februar'); + cy.get('a.edit').click(); + + cy.getSlate().click(); + cy.log('when I add a text block'); + cy.getSlateEditorAndType('Montags gehen wir in den Zoo.').contains( + 'Montags gehen wir in den Zoo.', + ); + // cy.toolbarSave(); + cy.get('#toolbar-save').click(); + cy.wait('@kitsearch'); + + cy.log('I added a text block'); + + // Searching + cy.visit('de/suche'); + cy.wait('@kitsearch'); + cy.get('.searchbar-wrapper input').type('Montag{enter}'); + cy.get('.block.searchkitsearch').contains('Der Garten im Februar'); + }); +}); From 806989f188eadddba863713a9c38e06ab22a6e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:48:37 +0200 Subject: [PATCH 176/226] Remove old cypress stuff --- acceptance/cypress/.gitkeep | 0 acceptance/cypress/plugins/index.js | 17 -------- acceptance/cypress/support/commands.js | 1 - acceptance/cypress/support/e2e.js | 14 ------- acceptance/cypress/support/reset-fixture.js | 45 --------------------- acceptance/cypress/support/slate.js | 25 ------------ 6 files changed, 102 deletions(-) delete mode 100644 acceptance/cypress/.gitkeep delete mode 100644 acceptance/cypress/plugins/index.js delete mode 100644 acceptance/cypress/support/commands.js delete mode 100644 acceptance/cypress/support/e2e.js delete mode 100644 acceptance/cypress/support/reset-fixture.js delete mode 100644 acceptance/cypress/support/slate.js diff --git a/acceptance/cypress/.gitkeep b/acceptance/cypress/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/acceptance/cypress/plugins/index.js b/acceptance/cypress/plugins/index.js deleted file mode 100644 index dffed253..00000000 --- a/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/acceptance/cypress/support/commands.js b/acceptance/cypress/support/commands.js deleted file mode 100644 index 44a50003..00000000 --- a/acceptance/cypress/support/commands.js +++ /dev/null @@ -1 +0,0 @@ -import '@plone/volto-testing/cypress/support/commands'; diff --git a/acceptance/cypress/support/e2e.js b/acceptance/cypress/support/e2e.js deleted file mode 100644 index 48912f7f..00000000 --- a/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/acceptance/cypress/support/reset-fixture.js b/acceptance/cypress/support/reset-fixture.js deleted file mode 100644 index a9a5fad7..00000000 --- a/acceptance/cypress/support/reset-fixture.js +++ /dev/null @@ -1,45 +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/acceptance/cypress/support/slate.js b/acceptance/cypress/support/slate.js deleted file mode 100644 index d910f6ca..00000000 --- a/acceptance/cypress/support/slate.js +++ /dev/null @@ -1,25 +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 - .wait(1000) - .get(selector) - .focus() - .click() - .wait(2000) - .type(type) - .wait(1000); -}; - -export const getSlateEditorAndClear = (selector) => { - return cy.get(selector).focus().click().wait(1000).clear(); -}; From f8807cbfb1324375e496583b39216f57334bc974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:49:20 +0200 Subject: [PATCH 177/226] Remove old acceptamce stuff --- acceptance/.gitignore | 16 - acceptance/.gitkeep | 0 acceptance/package.json | 6 - acceptance/yarn.lock | 2013 --------------------------------------- 4 files changed, 2035 deletions(-) delete mode 100644 acceptance/.gitignore delete mode 100644 acceptance/.gitkeep delete mode 100644 acceptance/package.json delete mode 100644 acceptance/yarn.lock diff --git a/acceptance/.gitignore b/acceptance/.gitignore deleted file mode 100644 index ac385657..00000000 --- a/acceptance/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -node_modules -cypress/videos/ -results -build -*~ -project -addon-testing-project - -# yarn 3 -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions diff --git a/acceptance/.gitkeep b/acceptance/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/acceptance/package.json b/acceptance/package.json deleted file mode 100644 index b54f8659..00000000 --- a/acceptance/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "@plone/volto-testing": "^4.0.0" - }, - "license": "MIT" -} diff --git a/acceptance/yarn.lock b/acceptance/yarn.lock deleted file mode 100644 index da3d1445..00000000 --- a/acceptance/yarn.lock +++ /dev/null @@ -1,2013 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" - integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== - dependencies: - "@babel/highlight" "^7.24.7" - picocolors "^1.0.0" - -"@babel/helper-validator-identifier@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" - integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== - -"@babel/highlight@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" - integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== - dependencies: - "@babel/helper-validator-identifier" "^7.24.7" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.9.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" - integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== - dependencies: - regenerator-runtime "^0.14.0" - -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@cypress/request@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" - integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - http-signature "~1.3.6" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - performance-now "^2.1.0" - qs "6.10.4" - safe-buffer "^5.1.2" - tough-cookie "^4.1.3" - tunnel-agent "^0.6.0" - uuid "^8.3.2" - -"@cypress/xvfb@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" - integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@plone/volto-testing@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@plone/volto-testing/-/volto-testing-4.0.0.tgz#172d1cb0c174c722b8b4f230ad204528ee4da8b9" - integrity sha512-0fnrxZEDMg04Ml5NGGkDai0DX3zfOZz1RQ7XjZg/+rhRMemiu53CwU1QOsnOoXhhoPx9/h+UHgLFEQhlR+3MvQ== - dependencies: - "@testing-library/cypress" "9.0.0" - "@testing-library/jest-dom" "5.16.4" - "@testing-library/react" "12.1.5" - axe-core "4.6.3" - cypress "13.1.0" - cypress-axe "1.5.0" - cypress-file-upload "5.0.8" - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@testing-library/cypress@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@testing-library/cypress/-/cypress-9.0.0.tgz#3facad49c4654a99bbd138f83f33b415d2d6f097" - integrity sha512-c1XiCGeHGGTWn0LAU12sFUfoX3qfId5gcSE2yHode+vsyHDWraxDPALjVnHd4/Fa3j4KBcc5k++Ccy6A9qnkMA== - dependencies: - "@babel/runtime" "^7.14.6" - "@testing-library/dom" "^8.1.0" - -"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.1.0": - version "8.20.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" - integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^5.0.1" - aria-query "5.1.3" - chalk "^4.1.0" - dom-accessibility-api "^0.5.9" - lz-string "^1.5.0" - pretty-format "^27.0.2" - -"@testing-library/jest-dom@5.16.4": - version "5.16.4" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd" - integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA== - dependencies: - "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" - aria-query "^5.0.0" - chalk "^3.0.0" - css "^3.0.0" - css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" - lodash "^4.17.15" - redent "^3.0.0" - -"@testing-library/react@12.1.5": - version "12.1.5" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" - integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== - dependencies: - "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" - "@types/react-dom" "<18.0.0" - -"@types/aria-query@^5.0.1": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" - integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - -"@types/istanbul-lib-report@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" - integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" - integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@*": - version "29.5.12" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/node@*": - version "20.14.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" - integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== - dependencies: - undici-types "~5.26.4" - -"@types/node@^16.18.39": - version "16.18.98" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.98.tgz#3554bb7911ea2bbc3a528be0776d6ab16b7674d2" - integrity sha512-fpiC20NvLpTLAzo3oVBKIqBGR6Fx/8oAK/SSf7G+fydnXMY1x4x9RZ6sBXhqKlCU21g2QapUsbLlhv3+a7wS+Q== - -"@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== - -"@types/react-dom@<18.0.0": - version "17.0.25" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" - integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== - dependencies: - "@types/react" "^17" - -"@types/react@^17": - version "17.0.80" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.80.tgz#a5dfc351d6a41257eb592d73d3a85d3b7dbcbb41" - integrity sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "^0.16" - csstype "^3.0.2" - -"@types/scheduler@^0.16": - version "0.16.8" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - -"@types/sinonjs__fake-timers@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" - integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== - -"@types/sizzle@^2.3.2": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627" - integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg== - -"@types/stack-utils@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" - integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== - -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" - integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== - dependencies: - "@types/jest" "*" - -"@types/yargs-parser@*": - version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" - integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== - -"@types/yargs@^17.0.8": - version "17.0.32" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" - integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== - dependencies: - "@types/yargs-parser" "*" - -"@types/yauzl@^2.9.1": - version "2.10.3" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" - integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== - dependencies: - "@types/node" "*" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ansi-colors@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-escapes@^4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -arch@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -aria-query@5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== - dependencies: - deep-equal "^2.0.5" - -aria-query@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - -array-buffer-byte-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" - integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== - dependencies: - call-bind "^1.0.5" - is-array-buffer "^3.0.4" - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async@^3.2.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.0.tgz#d9b802e9bb9c248d7be5f7f5ef178dc3684e9dcc" - integrity sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g== - -axe-core@4.6.3: - version "4.6.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" - integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -blob-util@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" - integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== - -bluebird@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -braces@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -cachedir@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" - integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== - -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -check-more-types@^2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== - -ci-info@^3.2.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-table3@~0.6.1: - version "0.6.5" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" - integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== - dependencies: - string-width "^4.2.0" - optionalDependencies: - "@colors/colors" "1.5.0" - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.16: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - -common-tags@^1.8.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" - integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -css.escape@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" - integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== - -css@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" - integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== - dependencies: - inherits "^2.0.4" - source-map "^0.6.1" - source-map-resolve "^0.6.0" - -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -cypress-axe@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.5.0.tgz#95082734583da77b51ce9b7784e14a442016c7a1" - integrity sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ== - -cypress-file-upload@5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" - integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== - -cypress@13.1.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.1.0.tgz#18f268e66662cd91b1766db18bd1f63a66592205" - integrity sha512-LUKxCYlB973QBFls1Up4FAE9QIYobT+2I8NvvAwMfQS2YwsWbr6yx7y9hmsk97iqbHkKwZW3MRjoK1RToBFVdQ== - dependencies: - "@cypress/request" "^3.0.0" - "@cypress/xvfb" "^1.2.4" - "@types/node" "^16.18.39" - "@types/sinonjs__fake-timers" "8.1.1" - "@types/sizzle" "^2.3.2" - arch "^2.2.0" - blob-util "^2.0.2" - bluebird "^3.7.2" - buffer "^5.6.0" - cachedir "^2.3.0" - chalk "^4.1.0" - check-more-types "^2.24.0" - cli-cursor "^3.1.0" - cli-table3 "~0.6.1" - commander "^6.2.1" - common-tags "^1.8.0" - dayjs "^1.10.4" - debug "^4.3.4" - enquirer "^2.3.6" - eventemitter2 "6.4.7" - execa "4.1.0" - executable "^4.1.1" - extract-zip "2.0.1" - figures "^3.2.0" - fs-extra "^9.1.0" - getos "^3.2.1" - is-ci "^3.0.0" - is-installed-globally "~0.4.0" - lazy-ass "^1.6.0" - listr2 "^3.8.3" - lodash "^4.17.21" - log-symbols "^4.0.0" - minimist "^1.2.8" - ospath "^1.2.2" - pretty-bytes "^5.6.0" - process "^0.11.10" - proxy-from-env "1.0.0" - request-progress "^3.0.0" - semver "^7.5.3" - supports-color "^8.1.1" - tmp "~0.2.1" - untildify "^4.0.0" - yauzl "^2.10.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -dayjs@^1.10.4: - version "1.11.11" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" - integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== - -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@^4.3.4: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== - dependencies: - ms "2.1.2" - -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -deep-equal@^2.0.5: - version "2.2.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" - integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.5" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.2" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.13" - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -dequal@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: - version "0.5.16" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" - integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enquirer@^2.3.6: - version "2.4.1" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" - integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== - dependencies: - ansi-colors "^4.1.1" - strip-ansi "^6.0.1" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -eventemitter2@6.4.7: - version "6.4.7" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" - integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== - -execa@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -executable@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -expect@^29.0.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -figures@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -getos@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" - integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== - dependencies: - async "^3.2.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -global-dirs@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" - integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== - dependencies: - ini "2.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-bigints@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -http-signature@~1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" - integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== - dependencies: - assert-plus "^1.0.0" - jsprim "^2.0.2" - sshpk "^1.14.1" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - -internal-slot@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" - integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.0" - side-channel "^1.0.4" - -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" - integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.3: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-ci@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" - integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== - dependencies: - ci-info "^3.2.0" - -is-date-object@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-installed-globally@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - -is-map@^2.0.2, is-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" - integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-set@^2.0.2, is-set@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" - integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== - -is-shared-array-buffer@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" - integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== - dependencies: - call-bind "^1.0.7" - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-weakmap@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" - integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== - -is-weakset@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" - integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== - dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -lazy-ass@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== - -listr2@^3.8.3: - version "3.14.0" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" - integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.16" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.5.1" - through "^2.3.8" - wrap-ansi "^7.0.0" - -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - -lodash@^4.17.15, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - -lz-string@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" - integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -micromatch@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" - integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimist@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== - -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -ospath@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" - integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -picocolors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== - -picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - -pretty-bytes@^5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - -pretty-format@^27.0.2: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -proxy-from-env@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" - integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== - -psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -qs@6.10.4: - version "6.10.4" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" - integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== - dependencies: - side-channel "^1.0.4" - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -react-is@^18.0.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - -regexp.prototype.flags@^1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== - dependencies: - call-bind "^1.0.6" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.1" - -request-progress@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== - dependencies: - throttleit "^1.0.0" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -rfdc@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" - integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== - -rxjs@^7.5.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^7.5.3: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -set-function-name@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - -signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - -source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sshpk@^1.14.1: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -throttleit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5" - integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ== - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tmp@~0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -tough-cookie@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" - integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tslib@^2.1.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-collection@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" - integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== - dependencies: - is-map "^2.0.3" - is-set "^2.0.3" - is-weakmap "^2.0.2" - is-weakset "^2.0.3" - -which-typed-array@^1.1.13: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" From 5357306fec8a06d736a1396dbdafff269f2a9416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:50:51 +0200 Subject: [PATCH 178/226] Create acceptance_default.yml.txt --- .github/workflows/acceptance_default.yml.txt | 102 +++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/acceptance_default.yml.txt diff --git a/.github/workflows/acceptance_default.yml.txt b/.github/workflows/acceptance_default.yml.txt new file mode 100644 index 00000000..574f1ae6 --- /dev/null +++ b/.github/workflows/acceptance_default.yml.txt @@ -0,0 +1,102 @@ +name: Acceptance tests +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "cypress/**" + - "packages/**" + - ".github/workflows/acceptance.yml" + +env: + NODE_VERSION: 20.x + CYPRESS_RETRIES: 2 + +jobs: + + acceptance: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Cache Cypress Binary + id: cache-cypress-binary + uses: actions/cache@v4 + with: + path: ~/.cache/Cypress + key: binary-${{ env.NODE_VERSION }}-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install dependencies + run: make install + + - name: Install Cypress if not in cache + if: steps.cache-cypress-binary.outputs.cache-hit != 'true' + working-directory: core/packages/volto + run: make cypress-install + + - uses: JarvusInnovations/background-action@v1 + name: Start Servers + with: + run: | + make ci-acceptance-backend-start & + make acceptance-frontend-prod-start & + # your step-level and job-level environment variables are available to your commands as-is + # npm install will count towards the wait-for timeout + # whenever possible, move unrelated scripts to a different step + # to background multiple processes: add & to the end of the command + + wait-on: | + http-get://localhost:55001/plone + http://localhost:3000 + # IMPORTANT: to use environment variables in wait-on, you must use this form: ${{ env.VAR }} + # See wait-on section below for all resource types and prefixes + + tail: true # true = stderr,stdout + # This will allow you to monitor the progress live + + log-output-resume: stderr + # Eliminates previosuly output stderr log entries from post-run output + + wait-for: 10m + + log-output: stderr,stdout # same as true + + log-output-if: failure + + - run: make ci-acceptance-test + + # Upload Cypress screenshots + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots-acceptance + path: acceptance/cypress/screenshots + + # Upload Cypress videos + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-videos-acceptance + path: acceptance/cypress/videos From 0b6ac5c24d0d5d998cd884e05079d3d1921b0e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:53:39 +0200 Subject: [PATCH 179/226] Rename old Changelog in project root --- CHANGELOG.md => CHANGELOG_old.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CHANGELOG.md => CHANGELOG_old.md (100%) diff --git a/CHANGELOG.md b/CHANGELOG_old.md similarity index 100% rename from CHANGELOG.md rename to CHANGELOG_old.md From 069adf6dd26653d76744b6d0558212dcd998ccf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:55:19 +0200 Subject: [PATCH 180/226] Update DEVELOPMENT.md --- DEVELOPMENT.md | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d4cab141..f8416e69 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,42 +1,5 @@ # Development and demo -> TODO Update to new Makefile +TODO Describe development setup - -## Index server OpenSearch - - make dev-opensearch - -## Backend and frontend - - make dev - - - - - -> OUTDATED OLD STUFF BELOW - -## Backend - - make dev-start-backend - - -## OUTDATED Elasticsearch - -Run with Docker. - -Change directory to ./development-searchkitblock/mac/ or ./development-searchkitblock/linux/ and run: - - docker compose up - -Inspect with - - docker exec -it bash - - - - -## Frontend - - make start-addon-testing-project +TODO Describe demo setup From 759047c8f44347a924fb26a2069fd11d53058d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 16:56:39 +0200 Subject: [PATCH 181/226] Create README_new.md --- README_new.md | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 README_new.md diff --git a/README_new.md b/README_new.md new file mode 100644 index 00000000..7775c3f1 --- /dev/null +++ b/README_new.md @@ -0,0 +1,203 @@ +# Volto searchkit block (@rohberg/volto-searchkit-block) + +Search with OpenSearch + +[![npm](https://img.shields.io/npm/v/@rohberg/volto-searchkit-block)](https://www.npmjs.com/package/@rohberg/volto-searchkit-block) +[![](https://img.shields.io/badge/-Storybook-ff4785?logo=Storybook&logoColor=white&style=flat-square)](https://rohberg.github.io/volto-searchkit-block/) +[![Code analysis checks](https://github.com/rohberg/volto-searchkit-block/actions/workflows/code.yml/badge.svg)](https://github.com/rohberg/volto-searchkit-block/actions/workflows/code.yml) +[![Unit tests](https://github.com/rohberg/volto-searchkit-block/actions/workflows/unit.yml/badge.svg)](https://github.com/rohberg/volto-searchkit-block/actions/workflows/unit.yml) + +## Features + + + +## Installation + +To install your project, you must choose the method appropriate to your version of Volto. + + +### Volto 17 and earlier + +Create a new Volto project (you can skip this step if you already have one): + +``` +npm install -g yo @plone/generator-volto +yo @plone/volto my-volto-project --addon @rohberg/volto-searchkit-block +cd my-volto-project +``` + +Add `@rohberg/volto-searchkit-block` to your package.json: + +```JSON +"addons": [ + "@rohberg/volto-searchkit-block" +], + +"dependencies": { + "@rohberg/volto-searchkit-block": "*" +} +``` + +Download and install the new add-on by running: + +``` +yarn install +``` + +Start volto with: + +``` +yarn start +``` + +### Volto 18 and later + +Add `@rohberg/volto-searchkit-block` to your `package.json`: + +```json +"dependencies": { + "@rohberg/volto-searchkit-block": "*" +} +``` + +Add `@rohberg/volto-searchkit-block` to your `volto.config.js`: + +```javascript +const addons = ['@rohberg/volto-searchkit-block']; +``` + +If this package provides a Volto theme, and you want to activate it, then add the following to your `volto.config.js`: + +```javascript +const theme = '@rohberg/volto-searchkit-block'; +``` + +## Test installation + +Visit http://localhost:3000/ in a browser, login, and check the awesome new features. + + +## Development + +The development of this add-on is done in isolation using a new approach using pnpm workspaces and latest `mrs-developer` and other Volto core improvements. +For this reason, it only works with pnpm and Volto 18 (currently in alpha). + + +### Pre-requisites + +- [Node.js](https://6.docs.plone.org/install/create-project.html#node-js) +- [Make](https://6.docs.plone.org/install/create-project.html#make) +- [Docker](https://6.docs.plone.org/install/create-project.html#docker) + + +### Make convenience commands + +Run `make help` to list the available commands. + +```text +help Show this help +install Installs the add-on in a development environment +start Starts Volto, allowing reloading of the add-on during development +build Build a production bundle for distribution of the project with the add-on +i18n Sync i18n +ci-i18n Check if i18n is not synced +format Format codebase +lint Lint, or catch and remove problems, in code base +release Release the add-on on npmjs.org +release-dry-run Dry-run the release of the add-on on npmjs.org +test Run unit tests +ci-test Run unit tests in CI +backend-docker-start Starts a Docker-based backend for development +storybook-start Start Storybook server on port 6006 +storybook-build Build Storybook +acceptance-frontend-dev-start Start acceptance frontend in development mode +acceptance-frontend-prod-start Start acceptance frontend in production mode +acceptance-backend-start Start backend acceptance server +ci-acceptance-backend-start Start backend acceptance server in headless mode for CI +acceptance-test Start Cypress in interactive mode +ci-acceptance-test Run cypress tests in headless mode for CI +``` + +### Development environment set up + +Install package requirements. + +```shell +make install +``` + +### Start developing + +Start the backend. + +```shell +make backend-docker-start +``` + +In a separate terminal session, start the frontend. + +```shell +make start +``` + +### Lint code + +Run ESlint, Prettier, and Stylelint in analyze mode. + +```shell +make lint +``` + +### Format code + +Run ESlint, Prettier, and Stylelint in fix mode. + +```shell +make format +``` + +### i18n + +Extract the i18n messages to locales. + +```shell +make i18n +``` + +### Unit tests + +Run unit tests. + +```shell +make test +``` + +### Run Cypress tests + +Run each of these steps in separate terminal sessions. + +In the first session, start the frontend in development mode. + +```shell +make acceptance-frontend-dev-start +``` + +In the second session, start the backend acceptance server. + +```shell +make acceptance-backend-start +``` + +In the third session, start the Cypress interactive test runner. + +```shell +make acceptance-test +``` + +## License + +The project is licensed under the MIT license. + +## Credits and Acknowledgements 🙏 + +Crafted with care by **Generated using [Cookieplone (0.7.1)](https://github.com/plone/cookieplone) and [cookiecutter-plone (5546534)](https://github.com/plone/cookiecutter-plone/commit/5546534df628a86d1e720069e17006e0b8eb66a4) on 2024-06-24 10:22:32.851752**. A special thanks to all contributors and supporters! From eda9cd093f73f4c19d8f92231e7f48190b31e670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 17:14:29 +0200 Subject: [PATCH 182/226] Rename tests: first language type --- .../tests/{basic.monolingual.cy.js => monolingual.basic.cy.js} | 0 ...e_search.monolingual.cy.js => monolingual.create_search.cy.js} | 0 .../{results.monolingual.cy.js => monolingual.results.cy.js} | 0 .../tests/{search.monolingual.cy.js => monolingual.search.cy.js} | 0 .../{language.multilingual.cy.js => multilingual.language.cy.js} | 0 ...ous.multilingual.cy.js => multilingual.search.anonymous.cy.js} | 0 .../{search.multilingual.cy.js => multilingual.search.cy.js} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename cypress/tests/{basic.monolingual.cy.js => monolingual.basic.cy.js} (100%) rename cypress/tests/{create_search.monolingual.cy.js => monolingual.create_search.cy.js} (100%) rename cypress/tests/{results.monolingual.cy.js => monolingual.results.cy.js} (100%) rename cypress/tests/{search.monolingual.cy.js => monolingual.search.cy.js} (100%) rename cypress/tests/{language.multilingual.cy.js => multilingual.language.cy.js} (100%) rename cypress/tests/{search.anonymous.multilingual.cy.js => multilingual.search.anonymous.cy.js} (100%) rename cypress/tests/{search.multilingual.cy.js => multilingual.search.cy.js} (100%) diff --git a/cypress/tests/basic.monolingual.cy.js b/cypress/tests/monolingual.basic.cy.js similarity index 100% rename from cypress/tests/basic.monolingual.cy.js rename to cypress/tests/monolingual.basic.cy.js diff --git a/cypress/tests/create_search.monolingual.cy.js b/cypress/tests/monolingual.create_search.cy.js similarity index 100% rename from cypress/tests/create_search.monolingual.cy.js rename to cypress/tests/monolingual.create_search.cy.js diff --git a/cypress/tests/results.monolingual.cy.js b/cypress/tests/monolingual.results.cy.js similarity index 100% rename from cypress/tests/results.monolingual.cy.js rename to cypress/tests/monolingual.results.cy.js diff --git a/cypress/tests/search.monolingual.cy.js b/cypress/tests/monolingual.search.cy.js similarity index 100% rename from cypress/tests/search.monolingual.cy.js rename to cypress/tests/monolingual.search.cy.js diff --git a/cypress/tests/language.multilingual.cy.js b/cypress/tests/multilingual.language.cy.js similarity index 100% rename from cypress/tests/language.multilingual.cy.js rename to cypress/tests/multilingual.language.cy.js diff --git a/cypress/tests/search.anonymous.multilingual.cy.js b/cypress/tests/multilingual.search.anonymous.cy.js similarity index 100% rename from cypress/tests/search.anonymous.multilingual.cy.js rename to cypress/tests/multilingual.search.anonymous.cy.js diff --git a/cypress/tests/search.multilingual.cy.js b/cypress/tests/multilingual.search.cy.js similarity index 100% rename from cypress/tests/search.multilingual.cy.js rename to cypress/tests/multilingual.search.cy.js From 4df801b9b885a4c573db93ab0182839c31120f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 17:14:57 +0200 Subject: [PATCH 183/226] Remove old cypress config --- acceptance/cypress.config.js | 9 --------- acceptance/cypress.monolingual.config.js | 13 ------------- acceptance/cypress.multilingual.config.js | 13 ------------- 3 files changed, 35 deletions(-) delete mode 100644 acceptance/cypress.config.js delete mode 100644 acceptance/cypress.monolingual.config.js delete mode 100644 acceptance/cypress.multilingual.config.js diff --git a/acceptance/cypress.config.js b/acceptance/cypress.config.js deleted file mode 100644 index d841d7ab..00000000 --- a/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/acceptance/cypress.monolingual.config.js b/acceptance/cypress.monolingual.config.js deleted file mode 100644 index 12fdddb2..00000000 --- a/acceptance/cypress.monolingual.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { defineConfig } = require('cypress'); - -module.exports = defineConfig({ - defaultCommandTimeout: 8000, - viewportWidth: 1280, - e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, - baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.monolingual.cy.{js,jsx}', - }, -}); diff --git a/acceptance/cypress.multilingual.config.js b/acceptance/cypress.multilingual.config.js deleted file mode 100644 index 488260f2..00000000 --- a/acceptance/cypress.multilingual.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { defineConfig } = require('cypress'); - -module.exports = defineConfig({ - defaultCommandTimeout: 8000, - viewportWidth: 1280, - e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, - baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/*.multilingual.cy.{js,jsx}', - }, -}); From d1dc442c1c699cfb17bbdc1c54c4efc0642ae46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 19:15:44 +0200 Subject: [PATCH 184/226] Add language-configurator --- packages/language-configurator/.eslintrc.js | 46 ++++ .../.github/workflows/acceptance.yml | 102 +++++++++ .../.github/workflows/changelog.yml | 58 +++++ .../.github/workflows/code.yml | 47 ++++ .../.github/workflows/i18n.yml | 47 ++++ .../.github/workflows/storybook.yml | 57 +++++ .../.github/workflows/unit.yml | 47 ++++ packages/language-configurator/.gitignore | 12 ++ packages/language-configurator/.npmignore | 16 ++ packages/language-configurator/.npmrc | 6 + .../.pre-commit-config.yaml | 27 +++ .../language-configurator/.prettierignore | 3 + packages/language-configurator/.prettierrc | 12 ++ .../language-configurator/.storybook/main.js | 188 ++++++++++++++++ .../.storybook/preview.jsx | 26 +++ packages/language-configurator/.stylelintrc | 32 +++ packages/language-configurator/Makefile | 138 ++++++++++++ packages/language-configurator/README.md | 203 ++++++++++++++++++ .../language-configurator/cypress.config.js | 13 ++ .../language-configurator/cypress/.gitkeep | 0 .../cypress/support/commands.js | 1 + .../cypress/support/e2e.js | 15 ++ .../cypress/tests/.gitkeep | 0 .../cypress/tests/example.cy.js | 20 ++ .../jest-addon.config.js | 17 ++ .../language-configurator/mrs.developer.json | 9 + packages/language-configurator/package.json | 44 ++++ .../packages/language-configurator/.gitignore | 3 + .../language-configurator/.release-it.json | 25 +++ .../language-configurator/CHANGELOG.md | 9 + .../language-configurator/babel.config.js | 17 ++ .../locales/de/LC_MESSAGES/volto.po | 12 ++ .../locales/en/LC_MESSAGES/volto.po | 12 ++ .../locales/es/LC_MESSAGES/volto.po | 19 ++ .../locales/pt_BR/LC_MESSAGES/volto.po | 17 ++ .../language-configurator/locales/volto.pot | 14 ++ .../language-configurator/news/.gitkeep | 0 .../language-configurator/package.json | 39 ++++ .../language-configurator/public/.gitkeep | 0 .../src/components/.gitkeep | 0 .../language-configurator/src/index.js | 5 + .../language-configurator/towncrier.toml | 33 +++ .../language-configurator/tsconfig.json | 29 +++ .../language-configurator/pnpm-workspace.yaml | 4 + .../language-configurator/volto.config.js | 7 + 45 files changed, 1431 insertions(+) create mode 100644 packages/language-configurator/.eslintrc.js create mode 100644 packages/language-configurator/.github/workflows/acceptance.yml create mode 100644 packages/language-configurator/.github/workflows/changelog.yml create mode 100644 packages/language-configurator/.github/workflows/code.yml create mode 100644 packages/language-configurator/.github/workflows/i18n.yml create mode 100644 packages/language-configurator/.github/workflows/storybook.yml create mode 100644 packages/language-configurator/.github/workflows/unit.yml create mode 100644 packages/language-configurator/.gitignore create mode 100644 packages/language-configurator/.npmignore create mode 100644 packages/language-configurator/.npmrc create mode 100644 packages/language-configurator/.pre-commit-config.yaml create mode 100644 packages/language-configurator/.prettierignore create mode 100644 packages/language-configurator/.prettierrc create mode 100644 packages/language-configurator/.storybook/main.js create mode 100644 packages/language-configurator/.storybook/preview.jsx create mode 100644 packages/language-configurator/.stylelintrc create mode 100644 packages/language-configurator/Makefile create mode 100644 packages/language-configurator/README.md create mode 100644 packages/language-configurator/cypress.config.js create mode 100644 packages/language-configurator/cypress/.gitkeep create mode 100644 packages/language-configurator/cypress/support/commands.js create mode 100644 packages/language-configurator/cypress/support/e2e.js create mode 100644 packages/language-configurator/cypress/tests/.gitkeep create mode 100644 packages/language-configurator/cypress/tests/example.cy.js create mode 100644 packages/language-configurator/jest-addon.config.js create mode 100644 packages/language-configurator/mrs.developer.json create mode 100644 packages/language-configurator/package.json create mode 100644 packages/language-configurator/packages/language-configurator/.gitignore create mode 100644 packages/language-configurator/packages/language-configurator/.release-it.json create mode 100644 packages/language-configurator/packages/language-configurator/CHANGELOG.md create mode 100644 packages/language-configurator/packages/language-configurator/babel.config.js create mode 100644 packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po create mode 100644 packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po create mode 100644 packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po create mode 100644 packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po create mode 100644 packages/language-configurator/packages/language-configurator/locales/volto.pot create mode 100644 packages/language-configurator/packages/language-configurator/news/.gitkeep create mode 100644 packages/language-configurator/packages/language-configurator/package.json create mode 100644 packages/language-configurator/packages/language-configurator/public/.gitkeep create mode 100644 packages/language-configurator/packages/language-configurator/src/components/.gitkeep create mode 100644 packages/language-configurator/packages/language-configurator/src/index.js create mode 100644 packages/language-configurator/packages/language-configurator/towncrier.toml create mode 100644 packages/language-configurator/packages/language-configurator/tsconfig.json create mode 100644 packages/language-configurator/pnpm-workspace.yaml create mode 100644 packages/language-configurator/volto.config.js diff --git a/packages/language-configurator/.eslintrc.js b/packages/language-configurator/.eslintrc.js new file mode 100644 index 00000000..96916a14 --- /dev/null +++ b/packages/language-configurator/.eslintrc.js @@ -0,0 +1,46 @@ +const fs = require('fs'); +const projectRootPath = __dirname; +const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); + +let coreLocation; +if (fs.existsSync(`${projectRootPath}/core`)) + coreLocation = `${projectRootPath}/core`; +else if (fs.existsSync(`${projectRootPath}/../../core`)) + coreLocation = `${projectRootPath}/../../core`; + +const registry = new AddonConfigurationRegistry( + `${coreLocation}/packages/volto`, +); + +// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons +const addonAliases = Object.keys(registry.packages).map((o) => [ + o, + registry.packages[o].modulePath, +]); + +module.exports = { + extends: `${coreLocation}/packages/volto/.eslintrc`, + rules: { + 'import/no-unresolved': 1, + }, + settings: { + 'import/resolver': { + alias: { + map: [ + ['@plone/volto', `${coreLocation}/packages/volto/src`], + [ + '@plone/volto-slate', + `${coreLocation}/core/packages/volto-slate/src`, + ], + ['@plone/registry', `${coreLocation}/packages/registry/src`], + [ + 'language-configurator', + './packages/language-configurator/src', + ], + ...addonAliases, + ], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], + }, + }, + }, +}; diff --git a/packages/language-configurator/.github/workflows/acceptance.yml b/packages/language-configurator/.github/workflows/acceptance.yml new file mode 100644 index 00000000..574f1ae6 --- /dev/null +++ b/packages/language-configurator/.github/workflows/acceptance.yml @@ -0,0 +1,102 @@ +name: Acceptance tests +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "cypress/**" + - "packages/**" + - ".github/workflows/acceptance.yml" + +env: + NODE_VERSION: 20.x + CYPRESS_RETRIES: 2 + +jobs: + + acceptance: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Cache Cypress Binary + id: cache-cypress-binary + uses: actions/cache@v4 + with: + path: ~/.cache/Cypress + key: binary-${{ env.NODE_VERSION }}-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install dependencies + run: make install + + - name: Install Cypress if not in cache + if: steps.cache-cypress-binary.outputs.cache-hit != 'true' + working-directory: core/packages/volto + run: make cypress-install + + - uses: JarvusInnovations/background-action@v1 + name: Start Servers + with: + run: | + make ci-acceptance-backend-start & + make acceptance-frontend-prod-start & + # your step-level and job-level environment variables are available to your commands as-is + # npm install will count towards the wait-for timeout + # whenever possible, move unrelated scripts to a different step + # to background multiple processes: add & to the end of the command + + wait-on: | + http-get://localhost:55001/plone + http://localhost:3000 + # IMPORTANT: to use environment variables in wait-on, you must use this form: ${{ env.VAR }} + # See wait-on section below for all resource types and prefixes + + tail: true # true = stderr,stdout + # This will allow you to monitor the progress live + + log-output-resume: stderr + # Eliminates previosuly output stderr log entries from post-run output + + wait-for: 10m + + log-output: stderr,stdout # same as true + + log-output-if: failure + + - run: make ci-acceptance-test + + # Upload Cypress screenshots + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots-acceptance + path: acceptance/cypress/screenshots + + # Upload Cypress videos + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-videos-acceptance + path: acceptance/cypress/videos diff --git a/packages/language-configurator/.github/workflows/changelog.yml b/packages/language-configurator/.github/workflows/changelog.yml new file mode 100644 index 00000000..62bdddd5 --- /dev/null +++ b/packages/language-configurator/.github/workflows/changelog.yml @@ -0,0 +1,58 @@ +name: Changelog check +on: + pull_request: + types: [assigned, opened, synchronize, reopened, labeled, unlabeled] + branches: + - main + +env: + NODE_VERSION: 20.x + ADDON_NAME: language-configurator + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Fetch all history + fetch-depth: '0' + + - name: Install pipx + run: pip install towncrier + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: | + make install + + - 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 ${BASE_BRANCH} + towncrier check --dir packages/${ADDON_NAME} + env: + BASE_BRANCH: ${{ github.base_ref }} + if: github.event_name == 'pull_request' diff --git a/packages/language-configurator/.github/workflows/code.yml b/packages/language-configurator/.github/workflows/code.yml new file mode 100644 index 00000000..79f96f43 --- /dev/null +++ b/packages/language-configurator/.github/workflows/code.yml @@ -0,0 +1,47 @@ +name: Code analysis checks +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/code.yml" + +env: + NODE_VERSION: 20.x + +jobs: + codeanalysis: + runs-on: ubuntu-latest + + steps: + - name: Main checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: Linting + run: make lint diff --git a/packages/language-configurator/.github/workflows/i18n.yml b/packages/language-configurator/.github/workflows/i18n.yml new file mode 100644 index 00000000..c8e463b1 --- /dev/null +++ b/packages/language-configurator/.github/workflows/i18n.yml @@ -0,0 +1,47 @@ +name: i18n +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/i18n.yml" + +env: + NODE_VERSION: 20.x + +jobs: + unit: + runs-on: ubuntu-latest + + steps: + - name: Main checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: test i18n command + run: make i18n diff --git a/packages/language-configurator/.github/workflows/storybook.yml b/packages/language-configurator/.github/workflows/storybook.yml new file mode 100644 index 00000000..32a8b60f --- /dev/null +++ b/packages/language-configurator/.github/workflows/storybook.yml @@ -0,0 +1,57 @@ +name: Storybook +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/storybook.yml" + +env: + NODE_VERSION: 20.x + +permissions: + contents: write + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: Generate Storybook + run: | + make storybook-build + + - name: Deploy to GitHub pages + uses: JamesIves/github-pages-deploy-action@v4 + if: ${{ github.ref == 'refs/heads/main' }} + with: + branch: gh-pages + folder: .storybook-build diff --git a/packages/language-configurator/.github/workflows/unit.yml b/packages/language-configurator/.github/workflows/unit.yml new file mode 100644 index 00000000..0de7eba1 --- /dev/null +++ b/packages/language-configurator/.github/workflows/unit.yml @@ -0,0 +1,47 @@ +name: Unit Tests +on: + push: + paths: + - "*.js" + - "*.json" + - "*.yaml" + - "packages/**" + - ".github/workflows/unit.yml" + +env: + NODE_VERSION: 20.x + +jobs: + unit: + runs-on: ubuntu-latest + + steps: + - name: Main checkout + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable corepack + run: corepack enable + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: make install + + - name: Unit tests + run: make test-ci diff --git a/packages/language-configurator/.gitignore b/packages/language-configurator/.gitignore new file mode 100644 index 00000000..0e623cec --- /dev/null +++ b/packages/language-configurator/.gitignore @@ -0,0 +1,12 @@ +.*project +.settings/ +.vscode +*~ +acceptance/cypress/videos/ +acceptance/node_modules +.storybook-build +build +core +node_modules +results +yarn.lock diff --git a/packages/language-configurator/.npmignore b/packages/language-configurator/.npmignore new file mode 100644 index 00000000..eb6fa944 --- /dev/null +++ b/packages/language-configurator/.npmignore @@ -0,0 +1,16 @@ +.vscode/ +.history +logs +*.log +npm-debug.log* +.DS_Store +*.swp +yarn-error.log + +node_modules +dockerfiles +acceptance +build +dist +yarn.lock +.storybook diff --git a/packages/language-configurator/.npmrc b/packages/language-configurator/.npmrc new file mode 100644 index 00000000..71c68438 --- /dev/null +++ b/packages/language-configurator/.npmrc @@ -0,0 +1,6 @@ +public-hoist-pattern[]=*eslint* +public-hoist-pattern[]=*prettier* +public-hoist-pattern[]=*stylelint* +public-hoist-pattern[]=*cypress* +public-hoist-pattern[]=*process* +public-hoist-pattern[]=*parcel* diff --git a/packages/language-configurator/.pre-commit-config.yaml b/packages/language-configurator/.pre-commit-config.yaml new file mode 100644 index 00000000..3c7d331c --- /dev/null +++ b/packages/language-configurator/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +repos: + - repo: local + hooks: + - id: prettier + name: prettier + entry: pnpm exec prettier --write + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: eslint + name: eslint + entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: stylelint + name: stylelint + entry: pnpm exec stylelint --fix + language: system + files: '^packages/.*/src/.*/?.*.(css|scss|less)$' + types: [file] + - id: i18n + name: i18n + entry: make ci-i18n + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] diff --git a/packages/language-configurator/.prettierignore b/packages/language-configurator/.prettierignore new file mode 100644 index 00000000..16f05c73 --- /dev/null +++ b/packages/language-configurator/.prettierignore @@ -0,0 +1,3 @@ +.storybook +CHANGELOG.md +README.md diff --git a/packages/language-configurator/.prettierrc b/packages/language-configurator/.prettierrc new file mode 100644 index 00000000..c56f6269 --- /dev/null +++ b/packages/language-configurator/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "all", + "singleQuote": true, + "overrides": [ + { + "files": "*.overrides", + "options": { + "parser": "less" + } + } + ] + } diff --git a/packages/language-configurator/.storybook/main.js b/packages/language-configurator/.storybook/main.js new file mode 100644 index 00000000..1b15d3b2 --- /dev/null +++ b/packages/language-configurator/.storybook/main.js @@ -0,0 +1,188 @@ +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('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 = { + stories: [ + '../packages/**/*.mdx', + '../packages/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-webpack5-compiler-babel', + ], + framework: { + name: '@storybook/react-webpack5', + options: { builder: { useSWC: true } }, + }, + typescript: { + check: false, + checkOptions: {}, + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + compilerOptions: { + allowSyntheticDefaultImports: false, + esModuleInterop: false, + }, + propFilter: () => true, + }, + }, + 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/registry/src/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 }, + }, + }; + + // Add-ons have to be loaded with babel + const addonPaths = registry + .getAddons() + .map((addon) => fs.realpathSync(addon.modulePath)); + + resultConfig.module.rules[13].exclude = (input) => + // exclude every input from node_modules except from @plone/volto + /node_modules\/(?!(@plone\/volto)\/)/.test(input) && + // Storybook default exclusions + /storybook-config-entry\.js$/.test(input) && + /storybook-stories\.js$/.test(input) && + // If input is in an addon, DON'T exclude it + !addonPaths.some((p) => input.includes(p)); + + resultConfig.module.rules[13].include = [ + /preview\.jsx/, + ...resultConfig.module.rules[13].include, + ...addonPaths, + ]; + + 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; + }, +}; diff --git a/packages/language-configurator/.storybook/preview.jsx b/packages/language-configurator/.storybook/preview.jsx new file mode 100644 index 00000000..1d2ac84b --- /dev/null +++ b/packages/language-configurator/.storybook/preview.jsx @@ -0,0 +1,26 @@ +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'; +import enMessages from '@root/../locales/en.json'; + +import '@root/theme'; + +export const parameters = { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; + +export const decorators = [ + (Story) => ( + + + + + + ), +]; diff --git a/packages/language-configurator/.stylelintrc b/packages/language-configurator/.stylelintrc new file mode 100644 index 00000000..09d4a276 --- /dev/null +++ b/packages/language-configurator/.stylelintrc @@ -0,0 +1,32 @@ +{ + "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, + "order/properties-alphabetical-order": null + } +} diff --git a/packages/language-configurator/Makefile b/packages/language-configurator/Makefile new file mode 100644 index 00000000..3551679f --- /dev/null +++ b/packages/language-configurator/Makefile @@ -0,0 +1,138 @@ +### 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` + +GIT_FOLDER=$(CURRENT_DIR)/.git +PRE_COMMIT=pipx run --spec 'pre-commit==3.7.1' pre-commit + +PLONE_VERSION=6 +DOCKER_IMAGE=plone/server-dev:${PLONE_VERSION} +DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:${PLONE_VERSION} + +ADDON_NAME='language-configurator' + +.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 :)" + +# Dev Helpers + +.PHONY: install +install: ## Installs the add-on in a development environment + @echo "$(GREEN)Install$(RESET)" + if [ -d $(GIT_FOLDER) ]; then $(PRE_COMMIT) install; else echo "$(RED) Not installing pre-commit$(RESET)";fi + pnpm dlx mrs-developer missdev --no-config --fetch-https + pnpm i + pnpm build:deps + +.PHONY: start +start: ## Starts Volto, allowing reloading of the add-on during development + pnpm start + +.PHONY: build +build: ## Build a production bundle for distribution of the project with the add-on + pnpm build + +core/packages/registry/dist: core/packages/registry/src + pnpm --filter @plone/registry build + +core/packages/components/dist: core/packages/components/src + pnpm --filter @plone/components build + +.PHONY: build-deps +build-deps: core/packages/registry/dist core/packages/components/dist ## Build dependencies + +.PHONY: i18n +i18n: ## Sync i18n + pnpm --filter $(ADDON_NAME) i18n + +.PHONY: ci-i18n +ci-i18n: ## Check if i18n is not synced + pnpm --filter $(ADDON_NAME) i18n && git diff -G'^[^\"POT]' --exit-code + +.PHONY: format +format: ## Format codebase + pnpm prettier:fix + pnpm lint:fix + pnpm stylelint:fix + +.PHONY: lint +lint: ## Lint, or catch and remove problems, in code base + pnpm lint + pnpm prettier + pnpm stylelint --allow-empty-input + +.PHONY: release +release: ## Release the add-on on npmjs.org + pnpm release + +.PHONY: release-dry-run +release-dry-run: ## Dry-run the release of the add-on on npmjs.org + pnpm release + +.PHONY: test +test: ## Run unit tests + pnpm test + +.PHONY: test-ci +ci-test: ## Run unit tests in CI + CI=1 RAZZLE_JEST_CONFIG=$(CURRENT_DIR)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests + +.PHONY: backend-docker-start +backend-docker-start: ## Starts a Docker-based backend for development + @echo "$(GREEN)==> Start Docker-based Plone Backend$(RESET)" + docker run -it --rm --name=backend -p 8080:8080 -e SITE=Plone $(DOCKER_IMAGE) + +## Storybook +.PHONY: storybook-start +storybook-start: ## Start Storybook server on port 6006 + @echo "$(GREEN)==> Start Storybook$(RESET)" + pnpm run storybook + +.PHONY: storybook-build +storybook-build: ## Build Storybook + @echo "$(GREEN)==> Build Storybook$(RESET)" + mkdir -p $(CURRENT_DIR)/.storybook-build + pnpm run build-storybook -o $(CURRENT_DIR)/.storybook-build + +## Acceptance +.PHONY: acceptance-frontend-dev-start +acceptance-frontend-dev-start: ## Start acceptance frontend in development mode + RAZZLE_API_PATH=http://127.0.0.1:55001/plone pnpm start + +.PHONY: acceptance-frontend-prod-start +acceptance-frontend-prod-start: ## Start acceptance frontend in production mode + RAZZLE_API_PATH=http://127.0.0.1:55001/plone pnpm build && pnpm start:prod + +.PHONY: acceptance-backend-start +acceptance-backend-start: ## Start backend acceptance server + docker run -it --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE) + +.PHONY: ci-acceptance-backend-start +ci-acceptance-backend-start: ## Start backend acceptance server in headless mode for CI + docker run -i --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE) + +.PHONY: acceptance-test +acceptance-test: ## Start Cypress in interactive mode + pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx}' + +.PHONY: ci-acceptance-test +ci-acceptance-test: ## Run cypress tests in headless mode for CI + pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx}' diff --git a/packages/language-configurator/README.md b/packages/language-configurator/README.md new file mode 100644 index 00000000..ab95c24e --- /dev/null +++ b/packages/language-configurator/README.md @@ -0,0 +1,203 @@ +# Language configurator (language-configurator) + +A new add-on for Volto + +[![npm](https://img.shields.io/npm/v/language-configurator)](https://www.npmjs.com/package/language-configurator) +[![](https://img.shields.io/badge/-Storybook-ff4785?logo=Storybook&logoColor=white&style=flat-square)](https://collective.github.io/language-configurator/) +[![Code analysis checks](https://github.com/collective/language-configurator/actions/workflows/code.yml/badge.svg)](https://github.com/collective/language-configurator/actions/workflows/code.yml) +[![Unit tests](https://github.com/collective/language-configurator/actions/workflows/unit.yml/badge.svg)](https://github.com/collective/language-configurator/actions/workflows/unit.yml) + +## Features + + + +## Installation + +To install your project, you must choose the method appropriate to your version of Volto. + + +### Volto 17 and earlier + +Create a new Volto project (you can skip this step if you already have one): + +``` +npm install -g yo @plone/generator-volto +yo @plone/volto my-volto-project --addon language-configurator +cd my-volto-project +``` + +Add `language-configurator` to your package.json: + +```JSON +"addons": [ + "language-configurator" +], + +"dependencies": { + "language-configurator": "*" +} +``` + +Download and install the new add-on by running: + +``` +yarn install +``` + +Start volto with: + +``` +yarn start +``` + +### Volto 18 and later + +Add `language-configurator` to your `package.json`: + +```json +"dependencies": { + "language-configurator": "*" +} +``` + +Add `language-configurator` to your `volto.config.js`: + +```javascript +const addons = ['language-configurator']; +``` + +If this package provides a Volto theme, and you want to activate it, then add the following to your `volto.config.js`: + +```javascript +const theme = 'language-configurator'; +``` + +## Test installation + +Visit http://localhost:3000/ in a browser, login, and check the awesome new features. + + +## Development + +The development of this add-on is done in isolation using a new approach using pnpm workspaces and latest `mrs-developer` and other Volto core improvements. +For this reason, it only works with pnpm and Volto 18 (currently in alpha). + + +### Pre-requisites + +- [Node.js](https://6.docs.plone.org/install/create-project.html#node-js) +- [Make](https://6.docs.plone.org/install/create-project.html#make) +- [Docker](https://6.docs.plone.org/install/create-project.html#docker) + + +### Make convenience commands + +Run `make help` to list the available commands. + +```text +help Show this help +install Installs the add-on in a development environment +start Starts Volto, allowing reloading of the add-on during development +build Build a production bundle for distribution of the project with the add-on +i18n Sync i18n +ci-i18n Check if i18n is not synced +format Format codebase +lint Lint, or catch and remove problems, in code base +release Release the add-on on npmjs.org +release-dry-run Dry-run the release of the add-on on npmjs.org +test Run unit tests +ci-test Run unit tests in CI +backend-docker-start Starts a Docker-based backend for development +storybook-start Start Storybook server on port 6006 +storybook-build Build Storybook +acceptance-frontend-dev-start Start acceptance frontend in development mode +acceptance-frontend-prod-start Start acceptance frontend in production mode +acceptance-backend-start Start backend acceptance server +ci-acceptance-backend-start Start backend acceptance server in headless mode for CI +acceptance-test Start Cypress in interactive mode +ci-acceptance-test Run cypress tests in headless mode for CI +``` + +### Development environment set up + +Install package requirements. + +```shell +make install +``` + +### Start developing + +Start the backend. + +```shell +make backend-docker-start +``` + +In a separate terminal session, start the frontend. + +```shell +make start +``` + +### Lint code + +Run ESlint, Prettier, and Stylelint in analyze mode. + +```shell +make lint +``` + +### Format code + +Run ESlint, Prettier, and Stylelint in fix mode. + +```shell +make format +``` + +### i18n + +Extract the i18n messages to locales. + +```shell +make i18n +``` + +### Unit tests + +Run unit tests. + +```shell +make test +``` + +### Run Cypress tests + +Run each of these steps in separate terminal sessions. + +In the first session, start the frontend in development mode. + +```shell +make acceptance-frontend-dev-start +``` + +In the second session, start the backend acceptance server. + +```shell +make acceptance-backend-start +``` + +In the third session, start the Cypress interactive test runner. + +```shell +make acceptance-test +``` + +## License + +The project is licensed under the MIT license. + +## Credits and Acknowledgements 🙏 + +Crafted with care by **Generated using [Cookieplone (0.7.1)](https://github.com/plone/cookieplone) and [cookiecutter-plone (5546534)](https://github.com/plone/cookiecutter-plone/commit/5546534df628a86d1e720069e17006e0b8eb66a4) on 2024-06-24 19:10:42.029315**. A special thanks to all contributors and supporters! diff --git a/packages/language-configurator/cypress.config.js b/packages/language-configurator/cypress.config.js new file mode 100644 index 00000000..dba4b580 --- /dev/null +++ b/packages/language-configurator/cypress.config.js @@ -0,0 +1,13 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + viewportWidth: 1280, + viewportHeight: 1280, + retries: { + runMode: 3, + }, + e2e: { + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}', + }, +}); diff --git a/packages/language-configurator/cypress/.gitkeep b/packages/language-configurator/cypress/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-configurator/cypress/support/commands.js b/packages/language-configurator/cypress/support/commands.js new file mode 100644 index 00000000..6a44064f --- /dev/null +++ b/packages/language-configurator/cypress/support/commands.js @@ -0,0 +1 @@ +import '@plone/volto/cypress/add-commands'; diff --git a/packages/language-configurator/cypress/support/e2e.js b/packages/language-configurator/cypress/support/e2e.js new file mode 100644 index 00000000..4ff23d6d --- /dev/null +++ b/packages/language-configurator/cypress/support/e2e.js @@ -0,0 +1,15 @@ +import 'cypress-axe'; +import 'cypress-file-upload'; +import './commands'; +import 'cypress-axe'; +import { setup, teardown } from '@plone/volto/cypress/support/reset-fixture'; + +beforeEach(function () { + cy.log('Setting up API fixture'); + setup(); +}); + +afterEach(function () { + cy.log('Tearing down API fixture'); + teardown(); +}); diff --git a/packages/language-configurator/cypress/tests/.gitkeep b/packages/language-configurator/cypress/tests/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-configurator/cypress/tests/example.cy.js b/packages/language-configurator/cypress/tests/example.cy.js new file mode 100644 index 00000000..f3a1fadf --- /dev/null +++ b/packages/language-configurator/cypress/tests/example.cy.js @@ -0,0 +1,20 @@ +context('Example Acceptance Tests', () => { + describe('Visit a page', () => { + beforeEach(() => { + // Given a logged in editor + cy.viewport('macbook-16'); + cy.createContent({ + contentType: 'Document', + contentId: 'document', + contentTitle: 'Test document', + }); + cy.autologin(); + }); + + it('As editor I can add edit a Page', function () { + cy.visit('/document'); + cy.navigate('/document/edit'); + cy.get('#toolbar-save').click(); + }); + }); +}); diff --git a/packages/language-configurator/jest-addon.config.js b/packages/language-configurator/jest-addon.config.js new file mode 100644 index 00000000..984c267e --- /dev/null +++ b/packages/language-configurator/jest-addon.config.js @@ -0,0 +1,17 @@ +module.exports = { + roots: ['../../../packages'], + testMatch: ['/../../../../**/?(*.)+(spec|test).[jt]s?(x)'], + collectCoverageFrom: [ + 'src/addons/**/src/**/*.{js,jsx,ts,tsx}', + '!src/**/*.d.ts', + ], + transformIgnorePatterns: ['node_modules/(?!(volto-slate|@plone/volto)/)'], + coverageThreshold: { + global: { + branches: 5, + functions: 5, + lines: 5, + statements: 5, + }, + }, +}; diff --git a/packages/language-configurator/mrs.developer.json b/packages/language-configurator/mrs.developer.json new file mode 100644 index 00000000..87bf82a5 --- /dev/null +++ b/packages/language-configurator/mrs.developer.json @@ -0,0 +1,9 @@ +{ + "core": { + "output": "./", + "package": "@plone/volto", + "url": "git@github.com:plone/volto.git", + "https": "https://github.com/plone/volto.git", + "tag": "18.0.0-alpha.35" + } +} diff --git a/packages/language-configurator/package.json b/packages/language-configurator/package.json new file mode 100644 index 00000000..44e69221 --- /dev/null +++ b/packages/language-configurator/package.json @@ -0,0 +1,44 @@ +{ + "name": "language-configurator-dev", + "version": "1.0.0-alpha.0", + "description": "A new add-on for Volto", + "author": "Plone Community", + "homepage": "https://github.com/collective/language-configurator", + "license": "MIT", + "keywords": [ + "volto-addon", + "volto", + "plone", + "react" + ], + "scripts": { + "preinstall": "npx only-allow pnpm", + "start": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto start", + "start:prod": "pnpm --filter @plone/volto start:prod", + "build": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build", + "build:deps": "pnpm --filter @plone/registry --filter @plone/components build", + "i18n": "pnpm --filter language-configurator i18n", + "test": "RAZZLE_JEST_CONFIG=$(pwd)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests", + "lint": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --max-warnings=0 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "lint:fix": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --fix 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "prettier": "prettier --check 'packages/**/src/**/*.{js,jsx,ts,tsx}'", + "prettier:fix": "prettier --write 'packages/**/src/**/*.{js,jsx,ts,tsx}' ", + "stylelint": "stylelint 'packages/**/src/**/*.{css,scss,less}' --allow-empty-input", + "stylelint:fix": "stylelint 'packages/**/src/**/*.{css,scss,less}' --fix --allow-empty-input", + "dry-release": "pnpm --filter language-configurator dry-release", + "release": "pnpm --filter language-configurator release", + "release-major-alpha": "pnpm --filter language-configurator release-major-alpha", + "release-alpha": "pnpm --filter language-configurator release-alpha", + "storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto storybook dev -p 6006 -c $(pwd)/.storybook", + "build-storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build-storybook -c $(pwd)/.storybook" + }, + "dependencies": { + "@plone/volto": "workspace:*", + "@plone/registry": "workspace:*", + "language-configurator": "workspace:*" + }, + "devDependencies": { + "mrs-developer": "^2.2.0" + }, + "packageManager": "pnpm@9.1.1" +} diff --git a/packages/language-configurator/packages/language-configurator/.gitignore b/packages/language-configurator/packages/language-configurator/.gitignore new file mode 100644 index 00000000..b462bc98 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/.gitignore @@ -0,0 +1,3 @@ +node_modules +build +README.md diff --git a/packages/language-configurator/packages/language-configurator/.release-it.json b/packages/language-configurator/packages/language-configurator/.release-it.json new file mode 100644 index 00000000..9ef4f97b --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/.release-it.json @@ -0,0 +1,25 @@ +{ + "hooks": { + "after:bump": [ + "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", + "pipx run towncrier build --yes --version ${version}", + "cp ../../README.md ./ && cp CHANGELOG.md ../../CHANGELOG.md", + "python3 -c 'import json; data = json.load(open(\"../../package.json\")); data[\"version\"] = \"${version}\"; json.dump(data, open(\"../../package.json\", \"w\"), indent=2)'", + "git add ../../CHANGELOG.md ../../package.json" + ], + "after:release": "rm .changelog.draft README.md" + }, + "git": { + "changelog": "pipx run towncrier build --draft --yes --version 0.0.0", + "requireUpstream": false, + "requireCleanWorkingDir": false, + "commitMessage": "Release ${version}", + "tagName": "${version}", + "tagAnnotation": "Release ${version}" + }, + "github": { + "release": true, + "releaseName": "${version}", + "releaseNotes": "cat .changelog.draft" + } +} diff --git a/packages/language-configurator/packages/language-configurator/CHANGELOG.md b/packages/language-configurator/packages/language-configurator/CHANGELOG.md new file mode 100644 index 00000000..e24f3a8e --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + + + + diff --git a/packages/language-configurator/packages/language-configurator/babel.config.js b/packages/language-configurator/packages/language-configurator/babel.config.js new file mode 100644 index 00000000..51bd52b5 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/babel.config.js @@ -0,0 +1,17 @@ +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/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po new file mode 100644 index 00000000..796f3cf1 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language: de\n" +"Language-Team: \n" +"Content-Type: \n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po new file mode 100644 index 00000000..38c369ef --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language: en\n" +"Language-Team: \n" +"Content-Type: \n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po new file mode 100644 index 00000000..7288b050 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po @@ -0,0 +1,19 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-05-09 11:45-0400\n" +"PO-Revision-Date: 2023-05-10 11:34-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" +"Preferred-Encodings: utf-8\n" +"MIME-Version: 1.0\n" +"Language-Code: es\n" +"Language-Name: Español\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" +"X-Generator: Poedit 2.2.1\n" diff --git a/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po new file mode 100644 index 00000000..e01604d6 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po @@ -0,0 +1,17 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-04-06T16:01:32.969Z\n" +"PO-Revision-Date: \n" +"Last-Translator: Plone i18n \n" +"Language: \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: pt_BR\n" +"Language-Name: Português do Brasil\n" +"Preferred-Encodings: utf-8\n" +"Domain: volto\n" diff --git a/packages/language-configurator/packages/language-configurator/locales/volto.pot b/packages/language-configurator/packages/language-configurator/locales/volto.pot new file mode 100644 index 00000000..ea538b12 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/locales/volto.pot @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Plone\n" +"POT-Creation-Date: 2024-03-22T12:43:34.158Z\n" +"Last-Translator: Plone i18n \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: en\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8\n" +"Domain: volto\n" diff --git a/packages/language-configurator/packages/language-configurator/news/.gitkeep b/packages/language-configurator/packages/language-configurator/news/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-configurator/packages/language-configurator/package.json b/packages/language-configurator/packages/language-configurator/package.json new file mode 100644 index 00000000..39e7dbf2 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/package.json @@ -0,0 +1,39 @@ +{ + "name": "language-configurator", + "version": "1.0.0-alpha.0", + "description": "A new add-on for Volto", + "main": "src/index.js", + "license": "MIT", + "keywords": [ + "volto-addon", + "volto", + "plone", + "react" + ], + "author": "Plone Community", + "homepage": "https://github.com/collective/language-configurator#readme", + "repository": { + "type": "git", + "url": "git@github.com:collective/language-configurator.git" + }, + "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" + }, + "dependencies": {}, + "peerDependencies": { + "react": "18.2.0", + "react-dom": "18.2.0", + "react-intl": "3.8.0" + }, + "devDependencies": { + "@plone/scripts": "^3.6.1", + "release-it": "^17.1.1" + } +} diff --git a/packages/language-configurator/packages/language-configurator/public/.gitkeep b/packages/language-configurator/packages/language-configurator/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-configurator/packages/language-configurator/src/components/.gitkeep b/packages/language-configurator/packages/language-configurator/src/components/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-configurator/packages/language-configurator/src/index.js b/packages/language-configurator/packages/language-configurator/src/index.js new file mode 100644 index 00000000..cb042f0d --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/src/index.js @@ -0,0 +1,5 @@ +const applyConfig = (config) => { + return config; +}; + +export default applyConfig; diff --git a/packages/language-configurator/packages/language-configurator/towncrier.toml b/packages/language-configurator/packages/language-configurator/towncrier.toml new file mode 100644 index 00000000..4d41a433 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/towncrier.toml @@ -0,0 +1,33 @@ +[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/collective/language-configurator/issue/{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/packages/language-configurator/packages/language-configurator/tsconfig.json b/packages/language-configurator/packages/language-configurator/tsconfig.json new file mode 100644 index 00000000..ddb0f542 --- /dev/null +++ b/packages/language-configurator/packages/language-configurator/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "module": "preserve", + "noEmit": true, + "lib": ["es2022", "dom", "dom.iterable"], + "jsx": "react-jsx", + "paths": { + "@plone/volto/*": ["../../core/packages/volto/src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": [ + "node_modules", + "build", + "public", + "coverage", + "**/*.test.{js,jsx,ts,tsx}", + "**/*.spec.{js,jsx,ts,tsx}", + "**/*.stories.{js,jsx,ts,tsx}" + ] +} diff --git a/packages/language-configurator/pnpm-workspace.yaml b/packages/language-configurator/pnpm-workspace.yaml new file mode 100644 index 00000000..f9c04858 --- /dev/null +++ b/packages/language-configurator/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + # all packages in direct subdirs of packages/ + - 'core/packages/*' + - 'packages/*' diff --git a/packages/language-configurator/volto.config.js b/packages/language-configurator/volto.config.js new file mode 100644 index 00000000..58a00562 --- /dev/null +++ b/packages/language-configurator/volto.config.js @@ -0,0 +1,7 @@ +const addons = ['language-configurator']; +const theme = ''; + +module.exports = { + addons, + theme, +}; From 2fdf2239abefcb4aee0e53144c970ca54313bc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 19:19:59 +0200 Subject: [PATCH 185/226] Remove unnecessary language-configurator stuff --- .../.github/workflows/acceptance.yml | 102 ------------------ .../.github/workflows/changelog.yml | 58 ---------- .../.github/workflows/code.yml | 47 -------- .../.github/workflows/i18n.yml | 47 -------- .../.github/workflows/storybook.yml | 57 ---------- .../.github/workflows/unit.yml | 47 -------- .../language-configurator/cypress.config.js | 13 --- .../language-configurator/cypress/.gitkeep | 0 .../cypress/support/commands.js | 1 - .../cypress/support/e2e.js | 15 --- .../cypress/tests/.gitkeep | 0 .../cypress/tests/example.cy.js | 20 ---- .../language-configurator/mrs.developer.json | 9 -- .../locales/de/LC_MESSAGES/volto.po | 12 --- .../locales/en/LC_MESSAGES/volto.po | 12 --- .../locales/es/LC_MESSAGES/volto.po | 19 ---- .../locales/pt_BR/LC_MESSAGES/volto.po | 17 --- .../language-configurator/locales/volto.pot | 14 --- .../language-configurator/news/.gitkeep | 0 .../language-configurator/towncrier.toml | 33 ------ 20 files changed, 523 deletions(-) delete mode 100644 packages/language-configurator/.github/workflows/acceptance.yml delete mode 100644 packages/language-configurator/.github/workflows/changelog.yml delete mode 100644 packages/language-configurator/.github/workflows/code.yml delete mode 100644 packages/language-configurator/.github/workflows/i18n.yml delete mode 100644 packages/language-configurator/.github/workflows/storybook.yml delete mode 100644 packages/language-configurator/.github/workflows/unit.yml delete mode 100644 packages/language-configurator/cypress.config.js delete mode 100644 packages/language-configurator/cypress/.gitkeep delete mode 100644 packages/language-configurator/cypress/support/commands.js delete mode 100644 packages/language-configurator/cypress/support/e2e.js delete mode 100644 packages/language-configurator/cypress/tests/.gitkeep delete mode 100644 packages/language-configurator/cypress/tests/example.cy.js delete mode 100644 packages/language-configurator/mrs.developer.json delete mode 100644 packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po delete mode 100644 packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po delete mode 100644 packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po delete mode 100644 packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po delete mode 100644 packages/language-configurator/packages/language-configurator/locales/volto.pot delete mode 100644 packages/language-configurator/packages/language-configurator/news/.gitkeep delete mode 100644 packages/language-configurator/packages/language-configurator/towncrier.toml diff --git a/packages/language-configurator/.github/workflows/acceptance.yml b/packages/language-configurator/.github/workflows/acceptance.yml deleted file mode 100644 index 574f1ae6..00000000 --- a/packages/language-configurator/.github/workflows/acceptance.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Acceptance tests -on: - push: - paths: - - "*.js" - - "*.json" - - "*.yaml" - - "cypress/**" - - "packages/**" - - ".github/workflows/acceptance.yml" - -env: - NODE_VERSION: 20.x - CYPRESS_RETRIES: 2 - -jobs: - - acceptance: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Cache Cypress Binary - id: cache-cypress-binary - uses: actions/cache@v4 - with: - path: ~/.cache/Cypress - key: binary-${{ env.NODE_VERSION }}-${{ hashFiles('pnpm-lock.yaml') }} - - - name: Install dependencies - run: make install - - - name: Install Cypress if not in cache - if: steps.cache-cypress-binary.outputs.cache-hit != 'true' - working-directory: core/packages/volto - run: make cypress-install - - - uses: JarvusInnovations/background-action@v1 - name: Start Servers - with: - run: | - make ci-acceptance-backend-start & - make acceptance-frontend-prod-start & - # your step-level and job-level environment variables are available to your commands as-is - # npm install will count towards the wait-for timeout - # whenever possible, move unrelated scripts to a different step - # to background multiple processes: add & to the end of the command - - wait-on: | - http-get://localhost:55001/plone - http://localhost:3000 - # IMPORTANT: to use environment variables in wait-on, you must use this form: ${{ env.VAR }} - # See wait-on section below for all resource types and prefixes - - tail: true # true = stderr,stdout - # This will allow you to monitor the progress live - - log-output-resume: stderr - # Eliminates previosuly output stderr log entries from post-run output - - wait-for: 10m - - log-output: stderr,stdout # same as true - - log-output-if: failure - - - run: make ci-acceptance-test - - # Upload Cypress screenshots - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: cypress-screenshots-acceptance - path: acceptance/cypress/screenshots - - # Upload Cypress videos - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: cypress-videos-acceptance - path: acceptance/cypress/videos diff --git a/packages/language-configurator/.github/workflows/changelog.yml b/packages/language-configurator/.github/workflows/changelog.yml deleted file mode 100644 index 62bdddd5..00000000 --- a/packages/language-configurator/.github/workflows/changelog.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Changelog check -on: - pull_request: - types: [assigned, opened, synchronize, reopened, labeled, unlabeled] - branches: - - main - -env: - NODE_VERSION: 20.x - ADDON_NAME: language-configurator - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - # Fetch all history - fetch-depth: '0' - - - name: Install pipx - run: pip install towncrier - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: | - make install - - - 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 ${BASE_BRANCH} - towncrier check --dir packages/${ADDON_NAME} - env: - BASE_BRANCH: ${{ github.base_ref }} - if: github.event_name == 'pull_request' diff --git a/packages/language-configurator/.github/workflows/code.yml b/packages/language-configurator/.github/workflows/code.yml deleted file mode 100644 index 79f96f43..00000000 --- a/packages/language-configurator/.github/workflows/code.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Code analysis checks -on: - push: - paths: - - "*.js" - - "*.json" - - "*.yaml" - - "packages/**" - - ".github/workflows/code.yml" - -env: - NODE_VERSION: 20.x - -jobs: - codeanalysis: - runs-on: ubuntu-latest - - steps: - - name: Main checkout - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: make install - - - name: Linting - run: make lint diff --git a/packages/language-configurator/.github/workflows/i18n.yml b/packages/language-configurator/.github/workflows/i18n.yml deleted file mode 100644 index c8e463b1..00000000 --- a/packages/language-configurator/.github/workflows/i18n.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: i18n -on: - push: - paths: - - "*.js" - - "*.json" - - "*.yaml" - - "packages/**" - - ".github/workflows/i18n.yml" - -env: - NODE_VERSION: 20.x - -jobs: - unit: - runs-on: ubuntu-latest - - steps: - - name: Main checkout - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: make install - - - name: test i18n command - run: make i18n diff --git a/packages/language-configurator/.github/workflows/storybook.yml b/packages/language-configurator/.github/workflows/storybook.yml deleted file mode 100644 index 32a8b60f..00000000 --- a/packages/language-configurator/.github/workflows/storybook.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Storybook -on: - push: - paths: - - "*.js" - - "*.json" - - "*.yaml" - - "packages/**" - - ".github/workflows/storybook.yml" - -env: - NODE_VERSION: 20.x - -permissions: - contents: write - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: make install - - - name: Generate Storybook - run: | - make storybook-build - - - name: Deploy to GitHub pages - uses: JamesIves/github-pages-deploy-action@v4 - if: ${{ github.ref == 'refs/heads/main' }} - with: - branch: gh-pages - folder: .storybook-build diff --git a/packages/language-configurator/.github/workflows/unit.yml b/packages/language-configurator/.github/workflows/unit.yml deleted file mode 100644 index 0de7eba1..00000000 --- a/packages/language-configurator/.github/workflows/unit.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Unit Tests -on: - push: - paths: - - "*.js" - - "*.json" - - "*.yaml" - - "packages/**" - - ".github/workflows/unit.yml" - -env: - NODE_VERSION: 20.x - -jobs: - unit: - runs-on: ubuntu-latest - - steps: - - name: Main checkout - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Enable corepack - run: corepack enable - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: make install - - - name: Unit tests - run: make test-ci diff --git a/packages/language-configurator/cypress.config.js b/packages/language-configurator/cypress.config.js deleted file mode 100644 index dba4b580..00000000 --- a/packages/language-configurator/cypress.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { defineConfig } = require('cypress'); - -module.exports = defineConfig({ - viewportWidth: 1280, - viewportHeight: 1280, - retries: { - runMode: 3, - }, - e2e: { - baseUrl: 'http://localhost:3000', - specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}', - }, -}); diff --git a/packages/language-configurator/cypress/.gitkeep b/packages/language-configurator/cypress/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/language-configurator/cypress/support/commands.js b/packages/language-configurator/cypress/support/commands.js deleted file mode 100644 index 6a44064f..00000000 --- a/packages/language-configurator/cypress/support/commands.js +++ /dev/null @@ -1 +0,0 @@ -import '@plone/volto/cypress/add-commands'; diff --git a/packages/language-configurator/cypress/support/e2e.js b/packages/language-configurator/cypress/support/e2e.js deleted file mode 100644 index 4ff23d6d..00000000 --- a/packages/language-configurator/cypress/support/e2e.js +++ /dev/null @@ -1,15 +0,0 @@ -import 'cypress-axe'; -import 'cypress-file-upload'; -import './commands'; -import 'cypress-axe'; -import { setup, teardown } from '@plone/volto/cypress/support/reset-fixture'; - -beforeEach(function () { - cy.log('Setting up API fixture'); - setup(); -}); - -afterEach(function () { - cy.log('Tearing down API fixture'); - teardown(); -}); diff --git a/packages/language-configurator/cypress/tests/.gitkeep b/packages/language-configurator/cypress/tests/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/language-configurator/cypress/tests/example.cy.js b/packages/language-configurator/cypress/tests/example.cy.js deleted file mode 100644 index f3a1fadf..00000000 --- a/packages/language-configurator/cypress/tests/example.cy.js +++ /dev/null @@ -1,20 +0,0 @@ -context('Example Acceptance Tests', () => { - describe('Visit a page', () => { - beforeEach(() => { - // Given a logged in editor - cy.viewport('macbook-16'); - cy.createContent({ - contentType: 'Document', - contentId: 'document', - contentTitle: 'Test document', - }); - cy.autologin(); - }); - - it('As editor I can add edit a Page', function () { - cy.visit('/document'); - cy.navigate('/document/edit'); - cy.get('#toolbar-save').click(); - }); - }); -}); diff --git a/packages/language-configurator/mrs.developer.json b/packages/language-configurator/mrs.developer.json deleted file mode 100644 index 87bf82a5..00000000 --- a/packages/language-configurator/mrs.developer.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "core": { - "output": "./", - "package": "@plone/volto", - "url": "git@github.com:plone/volto.git", - "https": "https://github.com/plone/volto.git", - "tag": "18.0.0-alpha.35" - } -} diff --git a/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po deleted file mode 100644 index 796f3cf1..00000000 --- a/packages/language-configurator/packages/language-configurator/locales/de/LC_MESSAGES/volto.po +++ /dev/null @@ -1,12 +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: de\n" -"Language-Team: \n" -"Content-Type: \n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" diff --git a/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po deleted file mode 100644 index 38c369ef..00000000 --- a/packages/language-configurator/packages/language-configurator/locales/en/LC_MESSAGES/volto.po +++ /dev/null @@ -1,12 +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: en\n" -"Language-Team: \n" -"Content-Type: \n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" diff --git a/packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/es/LC_MESSAGES/volto.po deleted file mode 100644 index 7288b050..00000000 --- a/packages/language-configurator/packages/language-configurator/locales/es/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: 2023-05-09 11:45-0400\n" -"PO-Revision-Date: 2023-05-10 11:34-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" -"Preferred-Encodings: utf-8\n" -"MIME-Version: 1.0\n" -"Language-Code: es\n" -"Language-Name: Español\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" -"X-Generator: Poedit 2.2.1\n" diff --git a/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po b/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po deleted file mode 100644 index e01604d6..00000000 --- a/packages/language-configurator/packages/language-configurator/locales/pt_BR/LC_MESSAGES/volto.po +++ /dev/null @@ -1,17 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Plone\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-06T16:01:32.969Z\n" -"PO-Revision-Date: \n" -"Last-Translator: Plone i18n \n" -"Language: \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: pt_BR\n" -"Language-Name: Português do Brasil\n" -"Preferred-Encodings: utf-8\n" -"Domain: volto\n" diff --git a/packages/language-configurator/packages/language-configurator/locales/volto.pot b/packages/language-configurator/packages/language-configurator/locales/volto.pot deleted file mode 100644 index ea538b12..00000000 --- a/packages/language-configurator/packages/language-configurator/locales/volto.pot +++ /dev/null @@ -1,14 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-03-22T12:43:34.158Z\n" -"Last-Translator: Plone i18n \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: en\n" -"Language-Name: English\n" -"Preferred-Encodings: utf-8\n" -"Domain: volto\n" diff --git a/packages/language-configurator/packages/language-configurator/news/.gitkeep b/packages/language-configurator/packages/language-configurator/news/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/language-configurator/packages/language-configurator/towncrier.toml b/packages/language-configurator/packages/language-configurator/towncrier.toml deleted file mode 100644 index 4d41a433..00000000 --- a/packages/language-configurator/packages/language-configurator/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/collective/language-configurator/issue/{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 From 5ed5e569dd6b67f075386d3546332ab7aa3e9cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 19:29:51 +0200 Subject: [PATCH 186/226] Remove language-configurator completely --- packages/language-configurator/.eslintrc.js | 46 ---- packages/language-configurator/.gitignore | 12 -- packages/language-configurator/.npmignore | 16 -- packages/language-configurator/.npmrc | 6 - .../.pre-commit-config.yaml | 27 --- .../language-configurator/.prettierignore | 3 - packages/language-configurator/.prettierrc | 12 -- .../language-configurator/.storybook/main.js | 188 ---------------- .../.storybook/preview.jsx | 26 --- packages/language-configurator/.stylelintrc | 32 --- packages/language-configurator/Makefile | 138 ------------ packages/language-configurator/README.md | 203 ------------------ .../jest-addon.config.js | 17 -- packages/language-configurator/package.json | 44 ---- .../packages/language-configurator/.gitignore | 3 - .../language-configurator/.release-it.json | 25 --- .../language-configurator/CHANGELOG.md | 9 - .../language-configurator/babel.config.js | 17 -- .../language-configurator/package.json | 39 ---- .../language-configurator/public/.gitkeep | 0 .../src/components/.gitkeep | 0 .../language-configurator/src/index.js | 5 - .../language-configurator/tsconfig.json | 29 --- .../language-configurator/pnpm-workspace.yaml | 4 - .../language-configurator/volto.config.js | 7 - 25 files changed, 908 deletions(-) delete mode 100644 packages/language-configurator/.eslintrc.js delete mode 100644 packages/language-configurator/.gitignore delete mode 100644 packages/language-configurator/.npmignore delete mode 100644 packages/language-configurator/.npmrc delete mode 100644 packages/language-configurator/.pre-commit-config.yaml delete mode 100644 packages/language-configurator/.prettierignore delete mode 100644 packages/language-configurator/.prettierrc delete mode 100644 packages/language-configurator/.storybook/main.js delete mode 100644 packages/language-configurator/.storybook/preview.jsx delete mode 100644 packages/language-configurator/.stylelintrc delete mode 100644 packages/language-configurator/Makefile delete mode 100644 packages/language-configurator/README.md delete mode 100644 packages/language-configurator/jest-addon.config.js delete mode 100644 packages/language-configurator/package.json delete mode 100644 packages/language-configurator/packages/language-configurator/.gitignore delete mode 100644 packages/language-configurator/packages/language-configurator/.release-it.json delete mode 100644 packages/language-configurator/packages/language-configurator/CHANGELOG.md delete mode 100644 packages/language-configurator/packages/language-configurator/babel.config.js delete mode 100644 packages/language-configurator/packages/language-configurator/package.json delete mode 100644 packages/language-configurator/packages/language-configurator/public/.gitkeep delete mode 100644 packages/language-configurator/packages/language-configurator/src/components/.gitkeep delete mode 100644 packages/language-configurator/packages/language-configurator/src/index.js delete mode 100644 packages/language-configurator/packages/language-configurator/tsconfig.json delete mode 100644 packages/language-configurator/pnpm-workspace.yaml delete mode 100644 packages/language-configurator/volto.config.js diff --git a/packages/language-configurator/.eslintrc.js b/packages/language-configurator/.eslintrc.js deleted file mode 100644 index 96916a14..00000000 --- a/packages/language-configurator/.eslintrc.js +++ /dev/null @@ -1,46 +0,0 @@ -const fs = require('fs'); -const projectRootPath = __dirname; -const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); - -let coreLocation; -if (fs.existsSync(`${projectRootPath}/core`)) - coreLocation = `${projectRootPath}/core`; -else if (fs.existsSync(`${projectRootPath}/../../core`)) - coreLocation = `${projectRootPath}/../../core`; - -const registry = new AddonConfigurationRegistry( - `${coreLocation}/packages/volto`, -); - -// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons -const addonAliases = Object.keys(registry.packages).map((o) => [ - o, - registry.packages[o].modulePath, -]); - -module.exports = { - extends: `${coreLocation}/packages/volto/.eslintrc`, - rules: { - 'import/no-unresolved': 1, - }, - settings: { - 'import/resolver': { - alias: { - map: [ - ['@plone/volto', `${coreLocation}/packages/volto/src`], - [ - '@plone/volto-slate', - `${coreLocation}/core/packages/volto-slate/src`, - ], - ['@plone/registry', `${coreLocation}/packages/registry/src`], - [ - 'language-configurator', - './packages/language-configurator/src', - ], - ...addonAliases, - ], - extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], - }, - }, - }, -}; diff --git a/packages/language-configurator/.gitignore b/packages/language-configurator/.gitignore deleted file mode 100644 index 0e623cec..00000000 --- a/packages/language-configurator/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -.*project -.settings/ -.vscode -*~ -acceptance/cypress/videos/ -acceptance/node_modules -.storybook-build -build -core -node_modules -results -yarn.lock diff --git a/packages/language-configurator/.npmignore b/packages/language-configurator/.npmignore deleted file mode 100644 index eb6fa944..00000000 --- a/packages/language-configurator/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -.vscode/ -.history -logs -*.log -npm-debug.log* -.DS_Store -*.swp -yarn-error.log - -node_modules -dockerfiles -acceptance -build -dist -yarn.lock -.storybook diff --git a/packages/language-configurator/.npmrc b/packages/language-configurator/.npmrc deleted file mode 100644 index 71c68438..00000000 --- a/packages/language-configurator/.npmrc +++ /dev/null @@ -1,6 +0,0 @@ -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=*prettier* -public-hoist-pattern[]=*stylelint* -public-hoist-pattern[]=*cypress* -public-hoist-pattern[]=*process* -public-hoist-pattern[]=*parcel* diff --git a/packages/language-configurator/.pre-commit-config.yaml b/packages/language-configurator/.pre-commit-config.yaml deleted file mode 100644 index 3c7d331c..00000000 --- a/packages/language-configurator/.pre-commit-config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -repos: - - repo: local - hooks: - - id: prettier - name: prettier - entry: pnpm exec prettier --write - language: system - files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - types: [file] - - id: eslint - name: eslint - entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" - language: system - files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - types: [file] - - id: stylelint - name: stylelint - entry: pnpm exec stylelint --fix - language: system - files: '^packages/.*/src/.*/?.*.(css|scss|less)$' - types: [file] - - id: i18n - name: i18n - entry: make ci-i18n - language: system - files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - types: [file] diff --git a/packages/language-configurator/.prettierignore b/packages/language-configurator/.prettierignore deleted file mode 100644 index 16f05c73..00000000 --- a/packages/language-configurator/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -.storybook -CHANGELOG.md -README.md diff --git a/packages/language-configurator/.prettierrc b/packages/language-configurator/.prettierrc deleted file mode 100644 index c56f6269..00000000 --- a/packages/language-configurator/.prettierrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "trailingComma": "all", - "singleQuote": true, - "overrides": [ - { - "files": "*.overrides", - "options": { - "parser": "less" - } - } - ] - } diff --git a/packages/language-configurator/.storybook/main.js b/packages/language-configurator/.storybook/main.js deleted file mode 100644 index 1b15d3b2..00000000 --- a/packages/language-configurator/.storybook/main.js +++ /dev/null @@ -1,188 +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('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 = { - stories: [ - '../packages/**/*.mdx', - '../packages/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-webpack5-compiler-babel', - ], - framework: { - name: '@storybook/react-webpack5', - options: { builder: { useSWC: true } }, - }, - typescript: { - check: false, - checkOptions: {}, - reactDocgen: 'react-docgen-typescript', - reactDocgenTypescriptOptions: { - compilerOptions: { - allowSyntheticDefaultImports: false, - esModuleInterop: false, - }, - propFilter: () => true, - }, - }, - 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/registry/src/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 }, - }, - }; - - // Add-ons have to be loaded with babel - const addonPaths = registry - .getAddons() - .map((addon) => fs.realpathSync(addon.modulePath)); - - resultConfig.module.rules[13].exclude = (input) => - // exclude every input from node_modules except from @plone/volto - /node_modules\/(?!(@plone\/volto)\/)/.test(input) && - // Storybook default exclusions - /storybook-config-entry\.js$/.test(input) && - /storybook-stories\.js$/.test(input) && - // If input is in an addon, DON'T exclude it - !addonPaths.some((p) => input.includes(p)); - - resultConfig.module.rules[13].include = [ - /preview\.jsx/, - ...resultConfig.module.rules[13].include, - ...addonPaths, - ]; - - 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; - }, -}; diff --git a/packages/language-configurator/.storybook/preview.jsx b/packages/language-configurator/.storybook/preview.jsx deleted file mode 100644 index 1d2ac84b..00000000 --- a/packages/language-configurator/.storybook/preview.jsx +++ /dev/null @@ -1,26 +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'; -import enMessages from '@root/../locales/en.json'; - -import '@root/theme'; - -export const parameters = { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -}; - -export const decorators = [ - (Story) => ( - - - - - - ), -]; diff --git a/packages/language-configurator/.stylelintrc b/packages/language-configurator/.stylelintrc deleted file mode 100644 index 09d4a276..00000000 --- a/packages/language-configurator/.stylelintrc +++ /dev/null @@ -1,32 +0,0 @@ -{ - "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, - "order/properties-alphabetical-order": null - } -} diff --git a/packages/language-configurator/Makefile b/packages/language-configurator/Makefile deleted file mode 100644 index 3551679f..00000000 --- a/packages/language-configurator/Makefile +++ /dev/null @@ -1,138 +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 - -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` - -GIT_FOLDER=$(CURRENT_DIR)/.git -PRE_COMMIT=pipx run --spec 'pre-commit==3.7.1' pre-commit - -PLONE_VERSION=6 -DOCKER_IMAGE=plone/server-dev:${PLONE_VERSION} -DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:${PLONE_VERSION} - -ADDON_NAME='language-configurator' - -.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 :)" - -# Dev Helpers - -.PHONY: install -install: ## Installs the add-on in a development environment - @echo "$(GREEN)Install$(RESET)" - if [ -d $(GIT_FOLDER) ]; then $(PRE_COMMIT) install; else echo "$(RED) Not installing pre-commit$(RESET)";fi - pnpm dlx mrs-developer missdev --no-config --fetch-https - pnpm i - pnpm build:deps - -.PHONY: start -start: ## Starts Volto, allowing reloading of the add-on during development - pnpm start - -.PHONY: build -build: ## Build a production bundle for distribution of the project with the add-on - pnpm build - -core/packages/registry/dist: core/packages/registry/src - pnpm --filter @plone/registry build - -core/packages/components/dist: core/packages/components/src - pnpm --filter @plone/components build - -.PHONY: build-deps -build-deps: core/packages/registry/dist core/packages/components/dist ## Build dependencies - -.PHONY: i18n -i18n: ## Sync i18n - pnpm --filter $(ADDON_NAME) i18n - -.PHONY: ci-i18n -ci-i18n: ## Check if i18n is not synced - pnpm --filter $(ADDON_NAME) i18n && git diff -G'^[^\"POT]' --exit-code - -.PHONY: format -format: ## Format codebase - pnpm prettier:fix - pnpm lint:fix - pnpm stylelint:fix - -.PHONY: lint -lint: ## Lint, or catch and remove problems, in code base - pnpm lint - pnpm prettier - pnpm stylelint --allow-empty-input - -.PHONY: release -release: ## Release the add-on on npmjs.org - pnpm release - -.PHONY: release-dry-run -release-dry-run: ## Dry-run the release of the add-on on npmjs.org - pnpm release - -.PHONY: test -test: ## Run unit tests - pnpm test - -.PHONY: test-ci -ci-test: ## Run unit tests in CI - CI=1 RAZZLE_JEST_CONFIG=$(CURRENT_DIR)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests - -.PHONY: backend-docker-start -backend-docker-start: ## Starts a Docker-based backend for development - @echo "$(GREEN)==> Start Docker-based Plone Backend$(RESET)" - docker run -it --rm --name=backend -p 8080:8080 -e SITE=Plone $(DOCKER_IMAGE) - -## Storybook -.PHONY: storybook-start -storybook-start: ## Start Storybook server on port 6006 - @echo "$(GREEN)==> Start Storybook$(RESET)" - pnpm run storybook - -.PHONY: storybook-build -storybook-build: ## Build Storybook - @echo "$(GREEN)==> Build Storybook$(RESET)" - mkdir -p $(CURRENT_DIR)/.storybook-build - pnpm run build-storybook -o $(CURRENT_DIR)/.storybook-build - -## Acceptance -.PHONY: acceptance-frontend-dev-start -acceptance-frontend-dev-start: ## Start acceptance frontend in development mode - RAZZLE_API_PATH=http://127.0.0.1:55001/plone pnpm start - -.PHONY: acceptance-frontend-prod-start -acceptance-frontend-prod-start: ## Start acceptance frontend in production mode - RAZZLE_API_PATH=http://127.0.0.1:55001/plone pnpm build && pnpm start:prod - -.PHONY: acceptance-backend-start -acceptance-backend-start: ## Start backend acceptance server - docker run -it --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE) - -.PHONY: ci-acceptance-backend-start -ci-acceptance-backend-start: ## Start backend acceptance server in headless mode for CI - docker run -i --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE) - -.PHONY: acceptance-test -acceptance-test: ## Start Cypress in interactive mode - pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx}' - -.PHONY: ci-acceptance-test -ci-acceptance-test: ## Run cypress tests in headless mode for CI - pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx}' diff --git a/packages/language-configurator/README.md b/packages/language-configurator/README.md deleted file mode 100644 index ab95c24e..00000000 --- a/packages/language-configurator/README.md +++ /dev/null @@ -1,203 +0,0 @@ -# Language configurator (language-configurator) - -A new add-on for Volto - -[![npm](https://img.shields.io/npm/v/language-configurator)](https://www.npmjs.com/package/language-configurator) -[![](https://img.shields.io/badge/-Storybook-ff4785?logo=Storybook&logoColor=white&style=flat-square)](https://collective.github.io/language-configurator/) -[![Code analysis checks](https://github.com/collective/language-configurator/actions/workflows/code.yml/badge.svg)](https://github.com/collective/language-configurator/actions/workflows/code.yml) -[![Unit tests](https://github.com/collective/language-configurator/actions/workflows/unit.yml/badge.svg)](https://github.com/collective/language-configurator/actions/workflows/unit.yml) - -## Features - - - -## Installation - -To install your project, you must choose the method appropriate to your version of Volto. - - -### Volto 17 and earlier - -Create a new Volto project (you can skip this step if you already have one): - -``` -npm install -g yo @plone/generator-volto -yo @plone/volto my-volto-project --addon language-configurator -cd my-volto-project -``` - -Add `language-configurator` to your package.json: - -```JSON -"addons": [ - "language-configurator" -], - -"dependencies": { - "language-configurator": "*" -} -``` - -Download and install the new add-on by running: - -``` -yarn install -``` - -Start volto with: - -``` -yarn start -``` - -### Volto 18 and later - -Add `language-configurator` to your `package.json`: - -```json -"dependencies": { - "language-configurator": "*" -} -``` - -Add `language-configurator` to your `volto.config.js`: - -```javascript -const addons = ['language-configurator']; -``` - -If this package provides a Volto theme, and you want to activate it, then add the following to your `volto.config.js`: - -```javascript -const theme = 'language-configurator'; -``` - -## Test installation - -Visit http://localhost:3000/ in a browser, login, and check the awesome new features. - - -## Development - -The development of this add-on is done in isolation using a new approach using pnpm workspaces and latest `mrs-developer` and other Volto core improvements. -For this reason, it only works with pnpm and Volto 18 (currently in alpha). - - -### Pre-requisites - -- [Node.js](https://6.docs.plone.org/install/create-project.html#node-js) -- [Make](https://6.docs.plone.org/install/create-project.html#make) -- [Docker](https://6.docs.plone.org/install/create-project.html#docker) - - -### Make convenience commands - -Run `make help` to list the available commands. - -```text -help Show this help -install Installs the add-on in a development environment -start Starts Volto, allowing reloading of the add-on during development -build Build a production bundle for distribution of the project with the add-on -i18n Sync i18n -ci-i18n Check if i18n is not synced -format Format codebase -lint Lint, or catch and remove problems, in code base -release Release the add-on on npmjs.org -release-dry-run Dry-run the release of the add-on on npmjs.org -test Run unit tests -ci-test Run unit tests in CI -backend-docker-start Starts a Docker-based backend for development -storybook-start Start Storybook server on port 6006 -storybook-build Build Storybook -acceptance-frontend-dev-start Start acceptance frontend in development mode -acceptance-frontend-prod-start Start acceptance frontend in production mode -acceptance-backend-start Start backend acceptance server -ci-acceptance-backend-start Start backend acceptance server in headless mode for CI -acceptance-test Start Cypress in interactive mode -ci-acceptance-test Run cypress tests in headless mode for CI -``` - -### Development environment set up - -Install package requirements. - -```shell -make install -``` - -### Start developing - -Start the backend. - -```shell -make backend-docker-start -``` - -In a separate terminal session, start the frontend. - -```shell -make start -``` - -### Lint code - -Run ESlint, Prettier, and Stylelint in analyze mode. - -```shell -make lint -``` - -### Format code - -Run ESlint, Prettier, and Stylelint in fix mode. - -```shell -make format -``` - -### i18n - -Extract the i18n messages to locales. - -```shell -make i18n -``` - -### Unit tests - -Run unit tests. - -```shell -make test -``` - -### Run Cypress tests - -Run each of these steps in separate terminal sessions. - -In the first session, start the frontend in development mode. - -```shell -make acceptance-frontend-dev-start -``` - -In the second session, start the backend acceptance server. - -```shell -make acceptance-backend-start -``` - -In the third session, start the Cypress interactive test runner. - -```shell -make acceptance-test -``` - -## License - -The project is licensed under the MIT license. - -## Credits and Acknowledgements 🙏 - -Crafted with care by **Generated using [Cookieplone (0.7.1)](https://github.com/plone/cookieplone) and [cookiecutter-plone (5546534)](https://github.com/plone/cookiecutter-plone/commit/5546534df628a86d1e720069e17006e0b8eb66a4) on 2024-06-24 19:10:42.029315**. A special thanks to all contributors and supporters! diff --git a/packages/language-configurator/jest-addon.config.js b/packages/language-configurator/jest-addon.config.js deleted file mode 100644 index 984c267e..00000000 --- a/packages/language-configurator/jest-addon.config.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - roots: ['../../../packages'], - testMatch: ['/../../../../**/?(*.)+(spec|test).[jt]s?(x)'], - collectCoverageFrom: [ - 'src/addons/**/src/**/*.{js,jsx,ts,tsx}', - '!src/**/*.d.ts', - ], - transformIgnorePatterns: ['node_modules/(?!(volto-slate|@plone/volto)/)'], - coverageThreshold: { - global: { - branches: 5, - functions: 5, - lines: 5, - statements: 5, - }, - }, -}; diff --git a/packages/language-configurator/package.json b/packages/language-configurator/package.json deleted file mode 100644 index 44e69221..00000000 --- a/packages/language-configurator/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "language-configurator-dev", - "version": "1.0.0-alpha.0", - "description": "A new add-on for Volto", - "author": "Plone Community", - "homepage": "https://github.com/collective/language-configurator", - "license": "MIT", - "keywords": [ - "volto-addon", - "volto", - "plone", - "react" - ], - "scripts": { - "preinstall": "npx only-allow pnpm", - "start": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto start", - "start:prod": "pnpm --filter @plone/volto start:prod", - "build": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build", - "build:deps": "pnpm --filter @plone/registry --filter @plone/components build", - "i18n": "pnpm --filter language-configurator i18n", - "test": "RAZZLE_JEST_CONFIG=$(pwd)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests", - "lint": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --max-warnings=0 'packages/**/src/**/*.{js,jsx,ts,tsx}'", - "lint:fix": "VOLTOCONFIG=$(pwd)/volto.config.js eslint --fix 'packages/**/src/**/*.{js,jsx,ts,tsx}'", - "prettier": "prettier --check 'packages/**/src/**/*.{js,jsx,ts,tsx}'", - "prettier:fix": "prettier --write 'packages/**/src/**/*.{js,jsx,ts,tsx}' ", - "stylelint": "stylelint 'packages/**/src/**/*.{css,scss,less}' --allow-empty-input", - "stylelint:fix": "stylelint 'packages/**/src/**/*.{css,scss,less}' --fix --allow-empty-input", - "dry-release": "pnpm --filter language-configurator dry-release", - "release": "pnpm --filter language-configurator release", - "release-major-alpha": "pnpm --filter language-configurator release-major-alpha", - "release-alpha": "pnpm --filter language-configurator release-alpha", - "storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto storybook dev -p 6006 -c $(pwd)/.storybook", - "build-storybook": "VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto build-storybook -c $(pwd)/.storybook" - }, - "dependencies": { - "@plone/volto": "workspace:*", - "@plone/registry": "workspace:*", - "language-configurator": "workspace:*" - }, - "devDependencies": { - "mrs-developer": "^2.2.0" - }, - "packageManager": "pnpm@9.1.1" -} diff --git a/packages/language-configurator/packages/language-configurator/.gitignore b/packages/language-configurator/packages/language-configurator/.gitignore deleted file mode 100644 index b462bc98..00000000 --- a/packages/language-configurator/packages/language-configurator/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -build -README.md diff --git a/packages/language-configurator/packages/language-configurator/.release-it.json b/packages/language-configurator/packages/language-configurator/.release-it.json deleted file mode 100644 index 9ef4f97b..00000000 --- a/packages/language-configurator/packages/language-configurator/.release-it.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "hooks": { - "after:bump": [ - "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", - "pipx run towncrier build --yes --version ${version}", - "cp ../../README.md ./ && cp CHANGELOG.md ../../CHANGELOG.md", - "python3 -c 'import json; data = json.load(open(\"../../package.json\")); data[\"version\"] = \"${version}\"; json.dump(data, open(\"../../package.json\", \"w\"), indent=2)'", - "git add ../../CHANGELOG.md ../../package.json" - ], - "after:release": "rm .changelog.draft README.md" - }, - "git": { - "changelog": "pipx run towncrier build --draft --yes --version 0.0.0", - "requireUpstream": false, - "requireCleanWorkingDir": false, - "commitMessage": "Release ${version}", - "tagName": "${version}", - "tagAnnotation": "Release ${version}" - }, - "github": { - "release": true, - "releaseName": "${version}", - "releaseNotes": "cat .changelog.draft" - } -} diff --git a/packages/language-configurator/packages/language-configurator/CHANGELOG.md b/packages/language-configurator/packages/language-configurator/CHANGELOG.md deleted file mode 100644 index e24f3a8e..00000000 --- a/packages/language-configurator/packages/language-configurator/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Changelog - - - - diff --git a/packages/language-configurator/packages/language-configurator/babel.config.js b/packages/language-configurator/packages/language-configurator/babel.config.js deleted file mode 100644 index 51bd52b5..00000000 --- a/packages/language-configurator/packages/language-configurator/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/packages/language-configurator/packages/language-configurator/package.json b/packages/language-configurator/packages/language-configurator/package.json deleted file mode 100644 index 39e7dbf2..00000000 --- a/packages/language-configurator/packages/language-configurator/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "language-configurator", - "version": "1.0.0-alpha.0", - "description": "A new add-on for Volto", - "main": "src/index.js", - "license": "MIT", - "keywords": [ - "volto-addon", - "volto", - "plone", - "react" - ], - "author": "Plone Community", - "homepage": "https://github.com/collective/language-configurator#readme", - "repository": { - "type": "git", - "url": "git@github.com:collective/language-configurator.git" - }, - "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" - }, - "dependencies": {}, - "peerDependencies": { - "react": "18.2.0", - "react-dom": "18.2.0", - "react-intl": "3.8.0" - }, - "devDependencies": { - "@plone/scripts": "^3.6.1", - "release-it": "^17.1.1" - } -} diff --git a/packages/language-configurator/packages/language-configurator/public/.gitkeep b/packages/language-configurator/packages/language-configurator/public/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/language-configurator/packages/language-configurator/src/components/.gitkeep b/packages/language-configurator/packages/language-configurator/src/components/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/language-configurator/packages/language-configurator/src/index.js b/packages/language-configurator/packages/language-configurator/src/index.js deleted file mode 100644 index cb042f0d..00000000 --- a/packages/language-configurator/packages/language-configurator/src/index.js +++ /dev/null @@ -1,5 +0,0 @@ -const applyConfig = (config) => { - return config; -}; - -export default applyConfig; diff --git a/packages/language-configurator/packages/language-configurator/tsconfig.json b/packages/language-configurator/packages/language-configurator/tsconfig.json deleted file mode 100644 index ddb0f542..00000000 --- a/packages/language-configurator/packages/language-configurator/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "skipLibCheck": true, - "target": "es2022", - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, - "verbatimModuleSyntax": true, - "module": "preserve", - "noEmit": true, - "lib": ["es2022", "dom", "dom.iterable"], - "jsx": "react-jsx", - "paths": { - "@plone/volto/*": ["../../core/packages/volto/src/*"] - } - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": [ - "node_modules", - "build", - "public", - "coverage", - "**/*.test.{js,jsx,ts,tsx}", - "**/*.spec.{js,jsx,ts,tsx}", - "**/*.stories.{js,jsx,ts,tsx}" - ] -} diff --git a/packages/language-configurator/pnpm-workspace.yaml b/packages/language-configurator/pnpm-workspace.yaml deleted file mode 100644 index f9c04858..00000000 --- a/packages/language-configurator/pnpm-workspace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - # all packages in direct subdirs of packages/ - - 'core/packages/*' - - 'packages/*' diff --git a/packages/language-configurator/volto.config.js b/packages/language-configurator/volto.config.js deleted file mode 100644 index 58a00562..00000000 --- a/packages/language-configurator/volto.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const addons = ['language-configurator']; -const theme = ''; - -module.exports = { - addons, - theme, -}; From d30cac7aca60a2e64a552740000c151f6a2ab0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Mon, 24 Jun 2024 19:49:05 +0200 Subject: [PATCH 187/226] Remove frontend dockerfiles --- dockerfiles/frontend/Dockerfile.acceptance | 19 ------------ .../Dockerfile.acceptance.multilingual | 19 ------------ dockerfiles/frontend/Dockerfile.ci | 30 ------------------- .../frontend/Dockerfile.ci.multilingual | 30 ------------------- dockerfiles/frontend/Dockerfile.dev | 21 ------------- 5 files changed, 119 deletions(-) delete mode 100644 dockerfiles/frontend/Dockerfile.acceptance delete mode 100644 dockerfiles/frontend/Dockerfile.acceptance.multilingual delete mode 100644 dockerfiles/frontend/Dockerfile.ci delete mode 100644 dockerfiles/frontend/Dockerfile.ci.multilingual delete mode 100644 dockerfiles/frontend/Dockerfile.dev diff --git a/dockerfiles/frontend/Dockerfile.acceptance b/dockerfiles/frontend/Dockerfile.acceptance deleted file mode 100644 index a3750f70..00000000 --- a/dockerfiles/frontend/Dockerfile.acceptance +++ /dev/null @@ -1,19 +0,0 @@ -# syntax=docker/dockerfile:1 -ARG VOLTO_VERSION -FROM plone/frontend-dev:${VOLTO_VERSION} - -ARG ADDON_NAME -ARG ADDON_PATH - -# Copy volto.config.js -COPY --chown=node:node volto.config.js* /app/ - -COPY --chown=node:node package.json /app/src/addons/${ADDON_PATH}/ - -# not isMultilingual -COPY --chown=node:node ./dockerfiles/frontend/config_monolingual.js /app/src/config.js - -RUN < Date: Tue, 25 Jun 2024 14:40:49 +0200 Subject: [PATCH 188/226] Fic Cypress tests --- cypress.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress.config.js b/cypress.config.js index dba4b580..c4eeb15d 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -7,7 +7,7 @@ module.exports = defineConfig({ runMode: 3, }, e2e: { - baseUrl: 'http://localhost:3000', + baseUrl: 'http://127.0.0.1:3000', specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}', }, }); From 87341e373c2856d4e53a83492d456552c5a93b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:14:55 +0200 Subject: [PATCH 189/226] Delete monolingual.basic.cy.js --- cypress/tests/monolingual.basic.cy.js | 43 --------------------------- 1 file changed, 43 deletions(-) delete mode 100644 cypress/tests/monolingual.basic.cy.js diff --git a/cypress/tests/monolingual.basic.cy.js b/cypress/tests/monolingual.basic.cy.js deleted file mode 100644 index 2fa0c728..00000000 --- a/cypress/tests/monolingual.basic.cy.js +++ /dev/null @@ -1,43 +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'); - }); - }); -}); From 9213ce3474f2700e57d35daa1d70bf0fa2bc83d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:15:28 +0200 Subject: [PATCH 190/226] Make acceptance tests --- Makefile | 194 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 51 deletions(-) diff --git a/Makefile b/Makefile index 292ce45d..530a3661 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -# Makefile volto-searchkit-block - +### Defensive settings for make: +# https://tech.davis-hansson.com/p/make/ SHELL:=bash .ONESHELL: -# .SHELLFLAGS:=-xeu -o pipefail -O inherit_errexit -c +.SHELLFLAGS:=-eu -o pipefail -c .SILENT: .DELETE_ON_ERROR: MAKEFLAGS+=--warn-undefined-variables @@ -12,6 +12,8 @@ include variables.mk 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` @@ -19,6 +21,10 @@ GREEN=`tput setaf 2` RESET=`tput sgr0` YELLOW=`tput setaf 3` +GIT_FOLDER=$(CURRENT_DIR)/.git +PRE_COMMIT=pipx run --spec 'pre-commit==3.7.1' pre-commit + + BACKEND_ADDONS='collective.elastic.plone ${KGS} $(TESTING_ADDONS)' DEV_COMPOSE=dockerfiles/docker-compose.yml ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml @@ -29,13 +35,87 @@ DOCKER_COMPOSE=${CMD} -p ${PROJECT_NAME} -f ${DEV_COMPOSE} ACCEPTANCE=${CMD} -p ${PROJECT_NAME}-acceptance -f ${ACCEPTANCE_COMPOSE} ACCEPTANCE_MULTILINGUAL=${CMD} -p ${PROJECT_NAME}-acceptance-multilingual -f ${ACCEPTANCE_COMPOSE} + .PHONY: all all: help .PHONY: help -help: ## Show this 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 :)" +# Dev Helpers + +.PHONY: install +install: ## Installs the add-on in a development environment + @echo "$(GREEN)Install$(RESET)" + if [ -d $(GIT_FOLDER) ]; then $(PRE_COMMIT) install; else echo "$(RED) Not installing pre-commit$(RESET)";fi + pnpm dlx mrs-developer missdev --no-config --fetch-https + pnpm i + pnpm build:deps + +.PHONY: start +start: ## Starts Volto, allowing reloading of the add-on during development + pnpm start + +.PHONY: build +build: ## Build a production bundle for distribution of the project with the add-on + pnpm build + +core/packages/registry/dist: core/packages/registry/src + pnpm --filter @plone/registry build + +core/packages/components/dist: core/packages/components/src + pnpm --filter @plone/components build + +.PHONY: build-deps +build-deps: core/packages/registry/dist core/packages/components/dist ## Build dependencies + +.PHONY: clean +clean: + rm -rf ./core + find ./ -name node_modules -exec rm -rf {} \; + find ./ -name build -exec rm -rf {} \; + find ./ -name cache -exec rm -rf {} \; + +.PHONY: i18n +i18n: ## Sync i18n + pnpm --filter $(ADDON_NAME) i18n + +.PHONY: ci-i18n +ci-i18n: ## Check if i18n is not synced + pnpm --filter $(ADDON_NAME) i18n && git diff -G'^[^\"POT]' --exit-code + +.PHONY: format +format: ## Format codebase + pnpm prettier:fix + pnpm lint:fix + pnpm stylelint:fix + +.PHONY: lint +lint: ## Lint, or catch and remove problems, in code base + pnpm lint + pnpm prettier + pnpm stylelint --allow-empty-input + +.PHONY: release +release: ## Release the add-on on npmjs.org + pnpm release + +.PHONY: release-dry-run +release-dry-run: ## Dry-run the release of the add-on on npmjs.org + pnpm release + +.PHONY: test +test: ## Run unit tests + pnpm test + +.PHONY: test-ci +ci-test: ## Run unit tests in CI + CI=1 RAZZLE_JEST_CONFIG=$(CURRENT_DIR)/jest-addon.config.js pnpm --filter @plone/volto test -- --passWithNoTests + + +# Development + .PHONY: build-backend build-backend: ## Build @echo "$(GREEN)==> Build Backend Container $(RESET)" @@ -89,50 +169,46 @@ opensearchandingest-up: ## Start containers for opensearch and ingest. `make ope ${DOCKER_COMPOSE} --profile opensearchandingest up -# Dev Helpers -.PHONY: i18n -i18n: ## Sync i18n - ${DOCKER_COMPOSE} run addon-dev i18n -.PHONY: format -format: ## Format codebase - ${DOCKER_COMPOSE} run addon-dev lint:fix - ${DOCKER_COMPOSE} run addon-dev prettier:fix - ${DOCKER_COMPOSE} run addon-dev stylelint:fix -.PHONY: lint -lint: ## Lint Codebase - ${DOCKER_COMPOSE} run addon-dev lint - ${DOCKER_COMPOSE} run addon-dev prettier - ${DOCKER_COMPOSE} run addon-dev stylelint +# Acceptance monolingual +.PHONY: acceptance-frontend-dev-start +acceptance-frontend-dev-start: ## Start acceptance frontend in development mode + SEARCHKITBLOCK_TESTING_LANGUAGESETTINGS=monolingual RAZZLE_API_PATH=http://localhost:55001/plone pnpm start -.PHONY: test -test: ## Run unit tests - ${DOCKER_COMPOSE} run addon-dev test --watchAll - -.PHONY: test-ci -test-ci: ## Run unit tests in CI - ${DOCKER_COMPOSE} run -e CI=1 addon-dev test +.PHONY: acceptance-frontend-prod-start +acceptance-frontend-prod-start: ## Start acceptance frontend in production mode + SEARCHKITBLOCK_TESTING_LANGUAGESETTINGS=monolingual RAZZLE_API_PATH=http://localhost:55001/plone pnpm build && pnpm start:prod - -# Acceptance -.PHONY: build-acceptance -build-acceptance: ## Install Cypress, build containers - (cd acceptance && yarn) +.PHONY: acceptance-build +acceptance-build: ## Install Cypress, build containers ${ACCEPTANCE} --profile dev build --no-cache -.PHONY: start-acceptance-containers -start-acceptance: ## Start acceptance server-containers +.PHONY: acceptance-start +acceptance-start: ## Start acceptance server-containers ${ACCEPTANCE} --profile dev up -d --force-recreate -.PHONY: test-acceptance -test-acceptance: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.monolingual.config.js) +# .PHONY: test-acceptance +# test-acceptance: ## Start Cypress +# (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.monolingual.config.js) + +# .PHONY: test-acceptance-headless +# # test-acceptance-headless: install-acceptance ## Run cypress tests in CI +# test-acceptance-headless: ## Run cypress tests in CI +# (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.monolingual.config.js) + +# DEBUG CYPRESS +.PHONY: acceptance-test-test +acceptance-test-test: ## Start Cypress in interactive mode + pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/example.*.{js,jsx,ts,tsx}' -.PHONY: test-acceptance-headless -# test-acceptance-headless: install-acceptance ## Run cypress tests in CI -test-acceptance-headless: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.monolingual.config.js) +.PHONY: acceptance-test +acceptance-test: ## Start Cypress in interactive mode + pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/monolingual.*.{js,jsx,ts,tsx}' + +.PHONY: ci-acceptance-test +ci-acceptance-test: ## Run cypress tests in headless mode for CI + pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/monolingual.*.{js,jsx,ts,tsx}' .PHONY: stop-test-acceptance-server stop-test-acceptance-server: ## Stop acceptance server @@ -144,22 +220,38 @@ status-test-acceptance-server: ## Status of Acceptance Server # Acceptance multilingual -.PHONY: build-acceptance-multilingual -build-acceptance-multilingual: ## multilingual – Install Cypress, build containers for multilingual site - (cd acceptance && yarn) +.PHONY: acceptance-frontend-dev-start-multilingual +acceptance-frontend-dev-start-multilingual: ## Start acceptance frontend in development mode + SEARCHKITBLOCK_TESTING_LANGUAGESETTINGS=multilingual RAZZLE_API_PATH=http://localhost:55001/plone pnpm start + +.PHONY: acceptance-frontend-prod-start-multilingual +acceptance-frontend-prod-start-multilingual: ## Start acceptance frontend in production mode + SEARCHKITBLOCK_TESTING_LANGUAGESETTINGS=multilingual RAZZLE_API_PATH=http://localhost:55001/plone pnpm build && pnpm start:prod + +.PHONY: acceptance-build-multilingual +acceptance-build-multilingual: ## multilingual – Install Cypress, build containers for multilingual site @echo ${PLONE_VERSION} @echo ${ACCEPTANCE_MULTILINGUAL} ${ACCEPTANCE_MULTILINGUAL} --profile multilingual build --no-cache -.PHONY: start-acceptance-containers-multilingual -start-acceptance-multilingual: ## multilingual – Start acceptance server-containers for multilingual site +.PHONY: acceptance-start-containers-multilingual +acceptance-start-multilingual: ## multilingual – Start acceptance server-containers for multilingual site ${ACCEPTANCE_MULTILINGUAL} --profile multilingual up -d --force-recreate -.PHONY: test-acceptance -test-acceptance-multilingual: ## Start Cypress - (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.multilingual.config.js) +# .PHONY: test-acceptance-multilingual +# test-acceptance-multilingual: ## Start Cypress +# (cd acceptance && ./node_modules/.bin/cypress open --config-file cypress.multilingual.config.js) + +# .PHONY: test-acceptance-headless-multilingual +# # test-acceptance-headless: install-acceptance ## Run cypress tests in CI +# test-acceptance-headless-multilingual: ## Run cypress tests in CI +# (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.multilingual.config.js) + +.PHONY: acceptance-test +acceptance-test-multilingual: ## Start Cypress in interactive mode + pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/multilingual.*.{js,jsx,ts,tsx}' + +.PHONY: ci-acceptance-test +ci-acceptance-test-multilingual: ## Run cypress tests in headless mode for CI + pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/multilingual.*.{js,jsx,ts,tsx}' -.PHONY: test-acceptance-headless -# test-acceptance-headless: install-acceptance ## Run cypress tests in CI -test-acceptance-headless-multilingual: ## Run cypress tests in CI - (cd acceptance && ./node_modules/.bin/cypress run --config-file cypress.multilingual.config.js) From c9f018dd79dbae44d25f2366a0976e2b2db62114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:15:54 +0200 Subject: [PATCH 191/226] Add dependencies --- packages/volto-searchkit-block/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/volto-searchkit-block/package.json b/packages/volto-searchkit-block/package.json index 3dd3daec..d181a791 100644 --- a/packages/volto-searchkit-block/package.json +++ b/packages/volto-searchkit-block/package.json @@ -26,7 +26,11 @@ "release-major-alpha": "release-it major --preRelease=alpha", "release-alpha": "release-it --preRelease=alpha" }, - "dependencies": {}, + "dependencies": { + "@eeacms/volto-matomo": "*", + "react-overridable": "^0.0.3", + "react-searchkit": "v2.2.0" + }, "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0", From e23b68660e414d61449e7f48811d8b8f91400642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:23:31 +0200 Subject: [PATCH 192/226] pre commit hook fails on pnpm not available --- .pre-commit-config.yaml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3c7d331c..f3cdada8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,24 @@ repos: - repo: local hooks: - - id: prettier - name: prettier - entry: pnpm exec prettier --write - language: system - files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - types: [file] - - id: eslint - name: eslint - entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" - language: system - files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - types: [file] - - id: stylelint - name: stylelint - entry: pnpm exec stylelint --fix - language: system - files: '^packages/.*/src/.*/?.*.(css|scss|less)$' - types: [file] + # - id: prettier + # name: prettier + # entry: pnpm exec prettier --write + # language: system + # files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + # types: [file] + # - id: eslint + # name: eslint + # entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" + # language: system + # files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + # types: [file] + # - id: stylelint + # name: stylelint + # entry: pnpm exec stylelint --fix + # language: system + # files: '^packages/.*/src/.*/?.*.(css|scss|less)$' + # types: [file] - id: i18n name: i18n entry: make ci-i18n From 9b2ee78b73873021364b10fc8fd1a5f4f816c8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:27:22 +0200 Subject: [PATCH 193/226] pre commit hook missing pnpm --- .pre-commit-config.yaml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f3cdada8..3c7d331c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,24 @@ repos: - repo: local hooks: - # - id: prettier - # name: prettier - # entry: pnpm exec prettier --write - # language: system - # files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - # types: [file] - # - id: eslint - # name: eslint - # entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" - # language: system - # files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' - # types: [file] - # - id: stylelint - # name: stylelint - # entry: pnpm exec stylelint --fix - # language: system - # files: '^packages/.*/src/.*/?.*.(css|scss|less)$' - # types: [file] + - id: prettier + name: prettier + entry: pnpm exec prettier --write + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: eslint + name: eslint + entry: bash -c "VOLTOCONFIG=$(pwd)/volto.config.js pnpm exec eslint --max-warnings=0 --fix" + language: system + files: '^packages/.*/src/.*/?.*.(js|jsx|ts|tsx)$' + types: [file] + - id: stylelint + name: stylelint + entry: pnpm exec stylelint --fix + language: system + files: '^packages/.*/src/.*/?.*.(css|scss|less)$' + types: [file] - id: i18n name: i18n entry: make ci-i18n From 7e24ccd953351a9c3a6d8bfd5129803cd356827a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20Su=CC=88ss?= Date: Tue, 25 Jun 2024 15:41:03 +0200 Subject: [PATCH 194/226] public stuff --- public/android-chrome-192x192.png | Bin 0 -> 8295 bytes public/android-chrome-512x512.png | Bin 0 -> 23153 bytes public/apple-touch-icon.png | Bin 0 -> 7973 bytes public/favicon-16x16.png | Bin 0 -> 1012 bytes public/favicon-32x32.png | Bin 0 -> 1618 bytes public/favicon.ico | Bin 0 -> 15086 bytes public/icon.svg | 13 ++++++++++++ public/index.html.spa | 34 ++++++++++++++++++++++++++++++ public/robots.txt | 2 ++ public/site.webmanifest | 19 +++++++++++++++++ 10 files changed, 68 insertions(+) create mode 100644 public/android-chrome-192x192.png create mode 100644 public/android-chrome-512x512.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/favicon.ico create mode 100644 public/icon.svg create mode 100644 public/index.html.spa create mode 100644 public/robots.txt create mode 100644 public/site.webmanifest diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..5a31bad458a0ade6db43c2e63f09dd4466c674a6 GIT binary patch literal 8295 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

*2lY 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 MK~4pgi!}867hxEky#N3J literal 0 HcmV?d00001 diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f0a5f8bc85194f67106dca45d5d8ae453d239897 GIT binary patch literal 7973 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 literal 0 HcmV?d00001 diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..bf6cf158a0ab234876f6ebac0b2502d00857efa0 GIT binary patch literal 1012 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>qbVOX^a ztbIR2D@;4kOpwV(!RCP3jX*OGfQ>o`vaxwT%tp9lfM)Jz=-vrd0~Q3@-nIwI1!@6u zoA!Wg2U-oX9ccb4h9;mQdpH`|sS!wby>NG96i^BqwUHwM(ghfjvcl-p|h>oW;X=JPUH)F&9yn%sr&7TGiiHv&cCo1 z6p@Tc-tI2ZC3~it06Clm9+AZi46KJhm{Gdw<7}WHdx@v7EBjL(Ax?QiwHN8@fkL-D zT^vIsF6;JJM>8gh9ASU`YgKBlqR!Px*;_Iuu5-~Aja~8cpZt-1+kR$0E1y^V%(6jy zzWo9g=Z73NuE|bE`1wNBCf%}IzCvDhd85y?T`Gq26x2Vl9?;mtGKWjnLizmNnUP^y zAG*00Xl0e|j7w*3TvoEydy93r!5Y1)+V!tzow+T+W4P<%fgjV#zh~GfEIIY#N6X1C zlAABeB&IcpbGa9_bGrydI$ztj>t@(m)d?vVYgX_5-Ow+3UGG}SH2Yj(WA~@m|LqG4 z`tf%&=cTCs`@2tV`~BNIUT5ij#_uk&5482HHG#fVEpd$~Nl7e8wMs5Z1yT$~21bUu zhDN%E79j?fR>p=_CYIU;237_Ja$D8QQ8eV{r(~v8;?|J=V*Y8M21$?&!TD(=<%vb9 z4CUqJdYO6I#mR{Use1WE>9gP2NC6dvSA|5B1SOU$6cpvBW#*(RlvEa^Dr6RvBr`Bn z%z6BYhodk|L*tbH=`)^BgBX~Vx%HB{g_VW9CyOu(E4Vb698O_Y-W;NE`o@(LN6s9P iIl_Lr!DE4!9>Xhf!IDo-rc;4dFnGH9xvX_rvpD@m|Ko9xMh!6nM$p9eP02tsF zG629%3IKy#0BA)35Je>o>sJ9__B4E5Bm)Wnq6NxkkOhBDSe(jDF}oQYJP(k@e~y$| zfSd<;ttPz|7>(1X*-ao@3$nBTCXo3VyQdx~O#}$ch501_lN^{d0pucp%*@YvNH8^n z3b0O4S|bWX5UlQ$2BNfJ_r+;F90X!Bp#?jIVze2|4r{>#zCz7ZZ)n{HfN0SD9N6uY z5{oDSmV-HVA*WAILr@glF%gu|g-wBqF+M7|gn6-hfJGq$4m)+jXb-R`Mmy4lSuj5I z!r7rW=Q65?Sd;_1HAQqTtVHf*lr~jMb!A%iZ}Iftrp~jPjYi{AuIF|5m5)jyLfODJ z_V|sV@ec!S2gC5r6(3(`&9nS@P-xbp&G?ZS5}$Xwis1M1O}dRsDe3+8(_jX$s)gK#e zJke1Xw^!cJxA+KKHH!@nqL*#C-CPD=W{a4cQUD<2ppV&5GyMVF#7kHq4E*!C^JjnQ zsnz+|0bss_MGuJd?M!4Rtch4}waDt`YL^O<`(fALT!~A|oqG-~D^NRYy2h4i{^~f} zQz7rJk^AkcO?fO|m2o#i=G9-a@_m=cy85zinYiA+%_aDk^x)@@UESVaQVS;43<>w6 z`Q|n?w>>rEgt>p=hN_KGy*+mw4tnd%wW*6T4ef@ECSJJ;aobiJHBMHwzsL<7iDr8p zE7E(nOmdtX#cuNL!du+5m3GOc!(2z5^yJ{4A7<|(r)rpv#5XZjH4iH%>WYcOdO%~Z z>(F-}v(nlRUFW=vt+2+DdjzEf7*Ase+G@EzBzp$TO+Ok6Hl_A_lmQ}p!eqpi6 z-fwh^Z;&RPnP1zNcdf1$7q)N6;Z}0P(x1+KAd-jESoj9cfZf~PB)T{$Q#$NzJ24DSk9BD$SeXMxoFslps|s?LUUZq_~85+5a1S9z4i}1_!hP zJ1Jfw<%tDADwTR9>=35#c_M*Fl6Y(J>s5|W6qOzsEs5YsNg{Dle8P4CNg@*oNC_ed zAAq!yr|;&@O5W(|+7Z^-#d;nAaEl2Ze1gqlo8SUloDGSNthf?~TdavBb>8b~Z|!Pb f*oyB&m}P4{z#!3D@@} literal 0 HcmV?d00001 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b9c0d0c0a6803fdd4e02d218a7bb47d79adfb94b GIT binary patch literal 15086 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 F{2x){hN%Dm literal 0 HcmV?d00001 diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 00000000..72640f02 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/index.html.spa b/public/index.html.spa new file mode 100644 index 00000000..27e655dc --- /dev/null +++ b/public/index.html.spa @@ -0,0 +1,34 @@ + + + + + + + + + React App + + + + +

+
+