diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f8ae1e5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ + +# EditorConfig Configurtaion file, for more details see: +# https://EditorConfig.org +# EditorConfig is a convention description, that could be interpreted +# by multiple editors to enforce common coding conventions for specific +# file types + +# top-most EditorConfig file: +# Will ignore other EditorConfig files in Home directory or upper tree level. +root = true + + +[*] # For All Files +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +# Set default charset +charset = utf-8 +# Indent style default +indent_style = space + +[*.{py,cfg,ini}] +# 4 space indentation +indent_size = 4 + +[*.{html,dtml,pt,zpt,xml,zcml,js,json,ts,less,scss,css,sass,yml,yaml}] +# 2 space indentation +indent_size = 2 + +[{Makefile,.gitmodules}] +# Tab indentation (no size specified, but view as 4 spaces) +indent_style = tab +indent_size = unset +tab_width = unset diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index b3aade7..7b9530a 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -12,6 +12,10 @@ on: env: NODE_VERSION: 24 +defaults: + run: + working-directory: ./frontend + jobs: acceptance: diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml new file mode 100644 index 0000000..c3ed3ea --- /dev/null +++ b/.github/workflows/backend.yml @@ -0,0 +1,97 @@ +name: Backend CI + +on: + workflow_call: + inputs: + base-tag: + required: true + type: string + image-name-prefix: + required: true + type: string + image-name-suffix: + required: true + type: string + python-version: + required: true + type: string + plone-version: + required: true + type: string + working-directory: + required: false + type: string + default: backend + +jobs: + + lint: + name: "Backend: Lint" + uses: plone/meta/.github/workflows/backend-lint.yml@2.x + with: + python-version: ${{ inputs.python-version }} + plone-version: ${{ inputs.plone-version }} + working-directory: ${{ inputs.working-directory }} + + test: + name: "Backend: Test" + uses: plone/meta/.github/workflows/backend-pytest.yml@2.x + strategy: + fail-fast: false + matrix: + python-version: ['3.11', '3.12', '3.13'] + plone-version: ['6.2-latest', '6.1-latest', '6.0-latest'] + with: + python-version: ${{ matrix.python-version }} + plone-version: ${{ matrix.plone-version }} + working-directory: ${{ inputs.working-directory }} + + coverage: + name: "Backend: Coverage" + uses: plone/meta/.github/workflows/backend-pytest-coverage.yml@2.x + with: + python-version: ${{ inputs.python-version }} + plone-version: ${{ inputs.plone-version }} + working-directory: ${{ inputs.working-directory }} + + release: + name: "Backend: Build and publish Container Image" + uses: plone/meta/.github/workflows/container-image-build-push.yml@2.x + needs: + - lint + - test + - coverage + permissions: + contents: read + packages: write + with: + base-tag: ${{ inputs.base-tag }} + image-name-prefix: ${{ inputs.image-name-prefix }} + image-name-suffix: ${{ inputs.image-name-suffix }} + working-directory: ${{ inputs.working-directory }} + build-args: | + PLONE_VERSION=${{ inputs.plone-version }} + push: ${{ github.event_name != 'pull_request' }} + secrets: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + report: + name: "Final report" + if: ${{ always() }} + runs-on: ubuntu-latest + needs: + - lint + - test + - coverage + - release + steps: + - name: Write report + run: | + echo '# Workflow Report' >> $GITHUB_STEP_SUMMARY + echo '| Job ID | Conclusion |' >> $GITHUB_STEP_SUMMARY + echo '| --- | --- |' >> $GITHUB_STEP_SUMMARY + echo '| lint | ${{ needs.lint.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| test | ${{ needs.test.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| coverage | ${{ needs.coverage.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| release | ${{ needs.release.result }} |' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 29b5ec7..c51af0a 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -1,4 +1,4 @@ -name: Changelog check +name: "Changelog" on: pull_request: types: [assigned, opened, synchronize, reopened, labeled, unlabeled] @@ -8,12 +8,68 @@ on: env: NODE_VERSION: 24 ADDON_NAME: volto-plate + BASE_BRANCH: main jobs: - build: + config: runs-on: ubuntu-latest + outputs: + backend: ${{ steps.filter.outputs.backend }} + frontend: ${{ steps.filter.outputs.frontend }} + repository: ${{ steps.filter.outputs.repository }} steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v5 + + - uses: dorny/paths-filter@v3.0.2 + id: filter + with: + filters: | + backend: + - 'backend/**' + repository: + - '.github/**' + - '.vscode/**' + - 'devops/**' + - 'docs/**' + - 'docker-compose.yml' + - 'README.md' + frontend: + - 'frontend/**' + + backend: + if: ${{ needs.config.outputs.backend == 'true' }} + runs-on: ubuntu-latest + needs: + - config + steps: + - uses: actions/checkout@v5 + with: + # Fetch all history + fetch-depth: '0' + + - name: Install pipx + run: pip install towncrier + + - name: Check for presence of a Change Log fragment (only pull requests) + if: github.event_name == 'pull_request' + 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 --compare-with origin/${{ env.BASE_BRANCH }} --config backend/pyproject.toml --dir backend/ + + frontend: + if: ${{ needs.config.outputs.frontend == 'true' }} + runs-on: ubuntu-latest + needs: + - config + defaults: + run: + working-directory: ./frontend + steps: + - uses: actions/checkout@v5 with: # Fetch all history fetch-depth: '0' @@ -21,7 +77,7 @@ jobs: - name: Install pipx run: pip install towncrier - - name: Use Node.js + - name: Use Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} @@ -43,16 +99,37 @@ jobs: ${{ runner.os }}-pnpm-store- - name: Install dependencies - run: | - make install + run: make install - name: Check for presence of a Change Log fragment (only pull requests) + if: github.event_name == 'pull_request' 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 }} + cd .. + towncrier check --compare-with origin/${{ env.BASE_BRANCH }} --config frontend/packages/${{ env.ADDON_NAME }}/towncrier.toml --dir frontend/packages/${{ env.ADDON_NAME }} + + repository: + if: ${{ needs.config.outputs.repository == 'true' }} + runs-on: ubuntu-latest + needs: + - config + steps: + - uses: actions/checkout@v5 + with: + # Fetch all history + fetch-depth: '0' + + - name: Install pipx + run: pip install towncrier + + - name: Check for presence of a Change Log fragment (only pull requests) if: github.event_name == 'pull_request' + 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 --compare-with origin/${{ env.BASE_BRANCH }} --config towncrier.toml --dir . diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml deleted file mode 100644 index 0ed3afe..0000000 --- a/.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: 24 - -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: npm i -g corepack@latest && 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/config.yml b/.github/workflows/config.yml new file mode 100644 index 0000000..231cd09 --- /dev/null +++ b/.github/workflows/config.yml @@ -0,0 +1,109 @@ +name: "Compute Config variables" + +on: + workflow_call: + inputs: + image-name-prefix: + required: false + type: string + default: "ghcr.io/kitconcept/collective-addon" + node-version: + required: false + type: string + default: "24.x" + python-version: + required: false + type: string + default: "3.13" + outputs: + acceptance: + description: "Flag reporting if we should run the acceptance jobs" + value: ${{ jobs.config.outputs.acceptance }} + backend: + description: "Flag reporting if we should run the backend jobs" + value: ${{ jobs.config.outputs.backend }} + devops: + description: "Flag reporting if we should run the devops jobs" + value: ${{ jobs.config.outputs.devops }} + docs: + description: "Flag reporting if we should run the docs jobs" + value: ${{ jobs.config.outputs.docs }} + frontend: + description: "Flag reporting if we should run the frontend jobs" + value: ${{ jobs.config.outputs.frontend }} + base-tag: + description: "Base tag to be used when creating container images" + value: ${{ jobs.config.outputs.base-tag }} + image-name-prefix: + description: "Image name prefix for container images" + value: ${{ inputs.image-name-prefix }} + node-version: + description: "Node version to be used" + value: ${{ inputs.node-version }} + python-version: + description: "Python version to be used" + value: ${{ inputs.python-version }} + plone-version: + description: "Plone version to be used" + value: ${{ jobs.config.outputs.plone-version }} + volto-version: + description: "Volto version to be used" + value: ${{ jobs.config.outputs.volto-version }} + +jobs: + config: + runs-on: ubuntu-latest + outputs: + acceptance: ${{ steps.filter.outputs.acceptance }} + backend: ${{ steps.filter.outputs.backend }} + devops: ${{ steps.filter.outputs.devops }} + docs: ${{ steps.filter.outputs.docs }} + frontend: ${{ steps.filter.outputs.frontend }} + base-tag: ${{ steps.vars.outputs.base-tag }} + plone-version: ${{ steps.vars.outputs.plone-version }} + volto-version: ${{ steps.vars.outputs.volto-version }} + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Compute several vars needed for the CI + id: vars + shell: bash + run: | + echo "base-tag=sha-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "plone-version=$(cat backend/version.txt)" >> $GITHUB_OUTPUT + python3 -c 'import json; data = json.load(open("./frontend/mrs.developer.json")); print("volto-version=" + (data["core"].get("tag") or "latest"))' >> $GITHUB_OUTPUT + + - uses: dorny/paths-filter@v3.0.2 + id: filter + with: + filters: | + acceptance: + - '.github/workflows/*' + - 'backend/**' + - 'frontend/**' + backend: + - 'backend/**' + - '.github/workflows/backend*' + devops: + - 'devops/**' + docs: + - '.readthedocs.yaml' + - 'docs/**' + - '.github/workflows/docs.yaml' + frontend: + - 'frontend/**' + - '.github/workflows/frontend*' + + - name: Test vars + run: | + echo "base-tag: ${{ steps.vars.outputs.base-tag }}" + echo 'plone-version: ${{ steps.vars.outputs.plone-version }}' + echo 'volto-version: ${{ steps.vars.outputs.volto-version }}' + echo 'event-name: ${{ github.event_name }}' + echo 'Paths - acceptance: ${{ steps.filter.outputs.acceptance }}' + echo 'Paths - backend: ${{ steps.filter.outputs.backend }}' + echo 'Paths - devops: ${{ steps.filter.outputs.devops }}' + echo 'Paths - docs: ${{ steps.filter.outputs.docs }}' + echo 'Paths - frontend: ${{ steps.filter.outputs.frontend }}' \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..104df08 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,22 @@ +name: Documentation + +on: + workflow_call: + inputs: + python-version: + required: true + type: string + working-directory: + required: false + type: string + default: docs + +jobs: + + build: + name: "Docs: Build" + uses: plone/meta/.github/workflows/docs-build.yml@2.x + with: + python-version: ${{ inputs.python-version }} + working-directory: ${{ inputs.working-directory }} + check-vale: true diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml new file mode 100644 index 0000000..449d05a --- /dev/null +++ b/.github/workflows/frontend.yml @@ -0,0 +1,107 @@ +name: Frontend CI + +on: + workflow_call: + inputs: + base-tag: + required: true + type: string + image-name-prefix: + required: true + type: string + image-name-suffix: + required: true + type: string + node-version: + required: true + type: string + volto-version: + required: true + type: string + storybook-deploy: + required: false + type: boolean + default: true + working-directory: + required: false + type: string + default: frontend + +jobs: + code-analysis: + name: "Frontend: Codeanalysis" + uses: plone/meta/.github/workflows/frontend-code.yml@2.x + with: + node-version: ${{ inputs.node-version }} + working-directory: ${{ inputs.working-directory }} + + i18n: + name: "Frontend: i18n" + uses: plone/meta/.github/workflows/frontend-i18n.yml@2.x + with: + node-version: ${{ inputs.node-version }} + working-directory: ${{ inputs.working-directory }} + + unit: + name: "Frontend: Unit tests" + uses: plone/meta/.github/workflows/frontend-unit.yml@2.x + with: + node-version: ${{ inputs.node-version }} + working-directory: ${{ inputs.working-directory }} + + storybook: + name: "Storybook" + uses: plone/meta/.github/workflows/frontend-storybook.yml@2.x + needs: + - code-analysis + - unit + - i18n + with: + node-version: ${{ needs.config.outputs.node-version }} + working-directory: ${{ inputs.working-directory }} + deploy: ${{ inputs.storybook-deploy }} + + release: + name: "Frontend: Build and publish container image" + uses: plone/meta/.github/workflows/container-image-build-push.yml@2.x + needs: + - code-analysis + - i18n + - unit + - storybook + permissions: + contents: read + packages: write + with: + base-tag: ${{ inputs.base-tag }} + image-name-prefix: ${{ inputs.image-name-prefix }} + image-name-suffix: ${{ inputs.image-name-suffix }} + working-directory: ${{ inputs.working-directory }} + build-args: | + VOLTO_VERSION=${{ inputs.volto-version }} + push: ${{ github.event_name != 'pull_request' }} + secrets: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + report: + name: "Final report" + if: ${{ always() }} + runs-on: ubuntu-latest + needs: + - code-analysis + - i18n + - unit + - storybook + - release + steps: + - name: Write report + run: | + echo '# Workflow Report' >> $GITHUB_STEP_SUMMARY + echo '| Job ID | Conclusion |' >> $GITHUB_STEP_SUMMARY + echo '| --- | --- |' >> $GITHUB_STEP_SUMMARY + echo '| code-analysis | ${{ needs.code-analysis.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| i18n | ${{ needs.i18n.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| unit | ${{ needs.unit.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| storybook | ${{ needs.storybook.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| release | ${{ needs.release.result }} |' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml deleted file mode 100644 index b82a511..0000000 --- a/.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: 24 - -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: npm i -g corepack@latest && 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/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3a9b33c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,77 @@ +name: CI + +on: + push: + paths: + - "backend/**" + - "devops/**" + - "frontend/**" + - ".github/workflows/*.yml" + - "*.md" + - "*.toml" + - "docker-compose*" + - "docs/**" + - .readthedocs.yaml + + workflow_dispatch: + +jobs: + config: + uses: ./.github/workflows/config.yml + + backend: + uses: ./.github/workflows/backend.yml + needs: + - config + with: + base-tag: ${{ needs.config.outputs.base-tag }} + image-name-prefix: ${{ needs.config.outputs.image-name-prefix }} + image-name-suffix: backend + python-version: ${{ needs.config.outputs.python-version }} + plone-version: ${{ needs.config.outputs.plone-version }} + if: ${{ needs.config.outputs.backend == 'true' }} + permissions: + contents: read + packages: write + docs: + uses: ./.github/workflows/docs.yml + needs: + - config + with: + python-version: ${{ needs.config.outputs.python-version }} + if: ${{ needs.config.outputs.docs == 'true' }} + + frontend: + uses: ./.github/workflows/frontend.yml + needs: + - config + with: + base-tag: ${{ needs.config.outputs.base-tag }} + image-name-prefix: ${{ needs.config.outputs.image-name-prefix }} + image-name-suffix: frontend + node-version: ${{ needs.config.outputs.node-version }} + volto-version: ${{ needs.config.outputs.volto-version }} + if: ${{ needs.config.outputs.frontend == 'true' }} + permissions: + contents: read + packages: write + + report: + name: "Final report" + if: ${{ always() }} + runs-on: ubuntu-latest + needs: + - config + - backend + - frontend + - docs + steps: + - name: Write report + run: | + echo '# Workflow Report' >> $GITHUB_STEP_SUMMARY + echo '| Job ID | Conclusion |' >> $GITHUB_STEP_SUMMARY + echo '| --- | --- |' >> $GITHUB_STEP_SUMMARY + echo '| config | ${{ needs.config.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| backend | ${{ needs.backend.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| frontend | ${{ needs.frontend.result }} |' >> $GITHUB_STEP_SUMMARY + echo '| docs | ${{ needs.docs.result }} |' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/rtd-pr-preview.yml b/.github/workflows/rtd-pr-preview.yml new file mode 100644 index 0000000..d5cc8b3 --- /dev/null +++ b/.github/workflows/rtd-pr-preview.yml @@ -0,0 +1,27 @@ +# .github/workflows/rtd-pr-preview.yml +name: readthedocs/actions +on: + pull_request_target: + types: + - opened + # Execute this action only on PRs that touch + # documentation files. + paths: + - "docs/docs/**" + - "docs/styles/**" + - docs/.readthedocs.yaml + - docs/.vale.ini + - docs/Makefile + - docs/uv.lock + +permissions: + pull-requests: write + +jobs: + documentation-links: + runs-on: ubuntu-latest + steps: + - uses: readthedocs/actions/preview@v1 + with: + project-slug: "plone-sphinx-theme" + single-version: "true" diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml deleted file mode 100644 index 6035ecc..0000000 --- a/.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: 24 - -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: npm i -g corepack@latest && 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 ci-test diff --git a/.gitignore b/.gitignore index e4c3d4b..9099385 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,5 @@ -.*project -.settings/ -.vscode -*~ -acceptance/cypress/videos/ -acceptance/node_modules -.storybook-build -build -/core +!.vscode node_modules -results -yarn.lock +/core /public - -/seven -!/seven/packages/plate +*.mo diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..c629b39 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,25 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.13" + commands: + # Cancel building pull requests when there aren't changes in the docs directory or YAML file. + # You can add any other files or directories that you'd like here as well, + # like your docs requirements file, or other files that will change your docs build. + # + # If there are no changes (git diff exits with 0) we force the command to return with 183. + # This is a special exit code on Read the Docs that will cancel the build immediately. + - | + if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- docs/docs/ docs/styles/ .readthedocs.yaml docs/.vale.ini docs/Makefile docs/uv.lock .github/workflows/docs.yml; + then + exit 183; + fi + - cd docs && make rtd-pr-preview diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b314591 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "stylelint.vscode-stylelint", + "esbenp.prettier-vscode", + "charliermarsh.ruff", + "ms-python.python", + "ExecutableBookProject.myst-highlight", + "ms-vscode-remote.remote-containers", + "redhat.vscode-yaml", + "editorconfig.editorconfig", + "github.vscode-github-actions", + "plone.plone-vs-utilities" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d10b955 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Plone Backend", + "type": "debugpy", + "request": "launch", + "python":"${command:python.interpreterPath}", + "program": "${workspaceFolder}/backend/.venv/bin/runwsgi", + "args": [ + "instance/etc/zope.ini" + ], + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/backend", + "justMyCode": false, + "subProcess": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ec3c7bd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,25 @@ +{ + "eslint.workingDirectories": ["./frontend"], + "stylelint.enable": true, + "css.validate": false, + "less.validate": false, + "scss.validate": false, + "[css][less][scss]": { + "editor.tabSize": 2, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.stylelint": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "ruff.organizeImports": true, + "python.terminal.activateEnvironment": true, + "python.testing.pytestArgs": [ + "/backendtests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "[markdown]": { + "editor.formatOnSave": false + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4430090..8b94ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changelog +# Change log - -## 1.0.0-alpha.0 (2026-02-03) diff --git a/Makefile b/Makefile index 9f6579f..54a986f 100644 --- a/Makefile +++ b/Makefile @@ -2,15 +2,22 @@ # https://tech.davis-hansson.com/p/make/ SHELL:=bash .ONESHELL: -.SHELLFLAGS:=-eu -o pipefail -c +.SHELLFLAGS:=-xeu -o pipefail -O inherit_errexit -c .SILENT: .DELETE_ON_ERROR: MAKEFLAGS+=--warn-undefined-variables MAKEFLAGS+=--no-builtin-rules CURRENT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +GIT_FOLDER=$(CURRENT_DIR)/.git + +REPOSITORY_SETTINGS := $(shell uvx repoplone settings dump) -# Recipe snippets for reuse +PROJECT_NAME=volto-plate +STACK_NAME=${PROJECT_NAME} + +VOLTO_VERSION := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.frontend.volto_version') +PLONE_VERSION := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.backend.base_package_version') # We like colors # From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects @@ -19,126 +26,220 @@ GREEN=`tput setaf 2` RESET=`tput sgr0` YELLOW=`tput setaf 3` -GIT_FOLDER=$(CURRENT_DIR)/.git - -PLONE_VERSION=6 -DOCKER_IMAGE=plone/server-dev:${PLONE_VERSION} -DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:${PLONE_VERSION} -API_PATH ?= http://127.0.0.1:55001/plone - -ADDON_NAME='@kitconcept/volto-plate' +.PHONY: all +all: install +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' .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: clean -clean: ## Clean environment - @echo "$(RED)==> Cleaning Volto core and node_modules$(RESET)" - rm -rf core node_modules - +help: ## This help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + + +.PHONY: debug-settings +debug-settings: ## Debug settings + @echo "Debug settings" + @echo "PROJECT_NAME: $(PROJECT_NAME)" + @echo "VOLTO_VERSION: $(VOLTO_VERSION)" + @echo "PLONE_VERSION: $(PLONE_VERSION)" + +########################################### +# Frontend +########################################### +.PHONY: frontend-install +frontend-install: ## Install React Frontend + $(MAKE) -C "./frontend/" install + +.PHONY: frontend-build +frontend-build: ## Build React Frontend + $(MAKE) -C "./frontend/" build + +.PHONY: frontend-start +frontend-start: ## Start React Frontend + $(MAKE) -C "./frontend/" start + +.PHONY: frontend-test +frontend-test: ## Test frontend codebase + @echo "Test frontend" + $(MAKE) -C "./frontend/" test + +########################################### +# Backend +########################################### +.PHONY: backend-install +backend-install: ## Create virtualenv and install Plone + $(MAKE) -C "./backend/" install + $(MAKE) backend-create-site + +.PHONY: backend-build +backend-build: ## Build Backend + $(MAKE) -C "./backend/" install + +.PHONY: backend-create-site +backend-create-site: ## Create a Plone site with default content + $(MAKE) -C "./backend/" create-site + +.PHONY: backend-update-example-content +backend-update-example-content: ## Export example content inside package + $(MAKE) -C "./backend/" update-example-content + +.PHONY: backend-start +backend-start: ## Start Plone Backend + $(MAKE) -C "./backend/" start + +.PHONY: backend-test +backend-test: ## Test backend codebase + @echo "Test backend" + $(MAKE) -C "./backend/" test + +########################################### +# Environment +########################################### .PHONY: install -install: ## Installs the add-on in a development environment - @echo "$(GREEN)Install$(RESET)" - pnpm dlx mrs-developer missdev --no-config --fetch-https - pnpm i - make 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: $(shell find core/packages/registry/src -type f) - pnpm --filter @plone/registry build - -core/packages/components/dist: $(shell find core/packages/components/src -type f) - 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 +install: ## Install + @echo "Install Backend & Frontend" + $(MAKE) backend-install + $(MAKE) frontend-install +.PHONY: clean +clean: ## Clean installation + @echo "Clean installation" + $(MAKE) -C "./backend/" clean + $(MAKE) -C "./frontend/" clean + +########################################### +# QA +########################################### .PHONY: format -format: ## Format codebase - pnpm prettier:fix - pnpm lint:fix - pnpm stylelint:fix +format: ## Format codebase + @echo "Format the codebase" + $(MAKE) -C "./backend/" format + $(MAKE) -C "./frontend/" format .PHONY: lint -lint: ## Lint, or catch and remove problems, in code base - pnpm lint - pnpm prettier - pnpm stylelint --allow-empty-input +lint: ## Format codebase + @echo "Lint the codebase" + $(MAKE) -C "./backend/" lint + $(MAKE) -C "./frontend/" lint -.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: check +check: format lint ## Lint and Format codebase +########################################### +# i18n +########################################### +.PHONY: i18n +i18n: ## Update locales + @echo "Update locales" + $(MAKE) -C "./backend/" i18n + $(MAKE) -C "./frontend/" i18n + +########################################### +# Testing +########################################### .PHONY: test -test: ## Run unit tests - pnpm test - -.PHONY: ci-test -ci-test: ## Run unit tests in CI - # Unit Tests need the i18n to be built - VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto i18n - CI=1 pnpm --filter @kitconcept/volto-plate exec vitest --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 --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 storybook-build -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=$(API_PATH) pnpm build && pnpm start:prod - +test: backend-test frontend-test ## Test codebase + +########################################### +# Container images +########################################### +.PHONY: build-images +build-images: ## Build container images + @echo "Build" + $(MAKE) -C "./backend/" build-image + $(MAKE) -C "./frontend/" build-image + +########################################### +# Local Stack +########################################### +.PHONY: stack-create-site +stack-create-site: ## Local Stack: Create a new site + @echo "Create a new site in the local Docker stack" + @echo "(Stack must not be running already.)" + VOLTO_VERSION=$(VOLTO_VERSION) PLONE_VERSION=$(PLONE_VERSION) docker compose -f docker-compose.yml run --build backend ./docker-entrypoint.sh create-site + +.PHONY: stack-start +stack-start: ## Local Stack: Start Services + @echo "Start local Docker stack" + VOLTO_VERSION=$(VOLTO_VERSION) PLONE_VERSION=$(PLONE_VERSION) docker compose -f docker-compose.yml up -d --build + @echo "Now visit: http://collective-addon.localhost" + +.PHONY: stack-status +stack-status: ## Local Stack: Check Status + @echo "Check the status of the local Docker stack" + @docker compose -f docker-compose.yml ps + +.PHONY: stack-stop +stack-stop: ## Local Stack: Stop Services + @echo "Stop local Docker stack" + @docker compose -f docker-compose.yml stop + +.PHONY: stack-rm +stack-rm: ## Local Stack: Remove Services and Volumes + @echo "Remove local Docker stack" + @docker compose -f docker-compose.yml down + @echo "Remove local volume data" + @docker volume rm $(PROJECT_NAME)_vol-site-data + +########################################### +# Acceptance +########################################### .PHONY: acceptance-backend-start -acceptance-backend-start: ## Start backend acceptance server - docker run -it --rm -p 55001:55001 $(DOCKER_IMAGE_ACCEPTANCE) +acceptance-backend-start: + @echo "Start acceptance backend" + $(MAKE) -C "./backend/" acceptance-backend-start -.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-frontend-dev-start +acceptance-frontend-dev-start: + @echo "Start acceptance frontend" + $(MAKE) -C "./frontend/" acceptance-frontend-dev-start .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}' +acceptance-test: + @echo "Start acceptance tests in interactive mode" + $(MAKE) -C "./frontend/" acceptance-test + +# Build Docker images +.PHONY: acceptance-frontend-image-build +acceptance-frontend-image-build: + @echo "Build acceptance frontend image" + @docker build frontend -t kitconcept/collective-addon-frontend:acceptance -f frontend/Dockerfile --build-arg VOLTO_VERSION=$(VOLTO_VERSION) + +.PHONY: acceptance-backend-image-build +acceptance-backend-image-build: + @echo "Build acceptance backend image" + @docker build backend -t kitconcept/collective-addon-backend:acceptance -f backend/Dockerfile.acceptance --build-arg PLONE_VERSION=$(PLONE_VERSION) + +.PHONY: acceptance-images-build +acceptance-images-build: ## Build Acceptance frontend/backend images + $(MAKE) acceptance-backend-image-build + $(MAKE) acceptance-frontend-image-build + +.PHONY: acceptance-frontend-container-start +acceptance-frontend-container-start: + @echo "Start acceptance frontend" + @docker run --rm -p 3000:3000 --name collective-addon-frontend-acceptance --link collective-addon-backend-acceptance:backend -e RAZZLE_API_PATH=http://localhost:55001/plone -e RAZZLE_INTERNAL_API_PATH=http://backend:55001/plone -d kitconcept/collective-addon-frontend:acceptance + +.PHONY: acceptance-backend-container-start +acceptance-backend-container-start: + @echo "Start acceptance backend" + @docker run --rm -p 55001:55001 --name collective-addon-backend-acceptance -d kitconcept/collective-addon-backend:acceptance + +.PHONY: acceptance-containers-start +acceptance-containers-start: ## Start Acceptance containers + $(MAKE) acceptance-backend-container-start + $(MAKE) acceptance-frontend-container-start + +.PHONY: acceptance-containers-stop +acceptance-containers-stop: ## Stop Acceptance containers + @echo "Stop acceptance containers" + @docker stop collective-addon-frontend-acceptance + @docker stop collective-addon-backend-acceptance .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}' --env API_PATH="$(API_PATH)" +ci-acceptance-test: + @echo "Run acceptance tests in CI mode" + $(MAKE) acceptance-containers-start + pnpm dlx wait-on --httpTimeout 20000 http-get://localhost:55001/plone http://localhost:3000 + $(MAKE) -C "./frontend/" ci-acceptance-test + $(MAKE) acceptance-containers-stop diff --git a/README.md b/README.md index 27ae9f9..de63c27 100644 --- a/README.md +++ b/README.md @@ -57,60 +57,26 @@ From the developer perspective, it provides: This package is only compatible with Volto 19 and later versions. -## Installation - -To install your project, you must choose the method appropriate to your version of Volto. - - -### Volto 18 and later - -Add `@kitconcept/volto-plate` to your `package.json`: - -```json -"dependencies": { - "@kitconcept/volto-plate": "*" -} -``` - -Add `@kitconcept/volto-plate` to your `volto.config.js`: - -```javascript -const addons = ['@kitconcept/volto-plate']; -``` - -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 = '@kitconcept/volto-plate'; -``` - -## 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). - +## Quick Start ๐Ÿ ### Prerequisites โœ… - An [operating system](https://6.docs.plone.org/install/create-project-cookieplone.html#prerequisites-for-installation) that runs all the requirements mentioned. +- [uv](https://6.docs.plone.org/install/create-project-cookieplone.html#uv) - [nvm](https://6.docs.plone.org/install/create-project-cookieplone.html#nvm) -- [Node.js and pnpm](https://6.docs.plone.org/install/create-project.html#node-js) 22 +- [Node.js and pnpm](https://6.docs.plone.org/install/create-project.html#node-js) 24 - [Make](https://6.docs.plone.org/install/create-project-cookieplone.html#make) - [Git](https://6.docs.plone.org/install/create-project-cookieplone.html#git) - [Docker](https://docs.docker.com/get-started/get-docker/) (optional) + ### Installation ๐Ÿ”ง 1. Clone this repository, then change your working directory. ```shell - git clone git@github.com:collective/volto-plate.git - cd volto-plate + git clone git@github.com:kitconcept/collective-addon.git + cd collective-addon ``` 2. Install this code base. @@ -120,116 +86,114 @@ For this reason, it only works with pnpm and Volto 18 (currently in alpha). ``` -### 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 acceptance tests in interactive mode -ci-acceptance-test Run acceptance tests in headless mode for CI -``` +### Fire Up the Servers ๐Ÿ”ฅ -### Development environment set up +1. Create a new Plone site on your first run. -Install package requirements. + ```shell + make backend-create-site + ``` -```shell -make install -``` +2. Start the backend at http://localhost:8080/. -### Start developing + ```shell + make backend-start + ``` -Start the backend. +3. In a new shell session, start the frontend at http://localhost:3000/. -```shell -make backend-docker-start -``` + ```shell + make frontend-start + ``` -In a separate terminal session, start the frontend. +Voila! Your Plone site should be live and kicking! ๐ŸŽ‰ -```shell -make start -``` +### Local Stack Deployment ๐Ÿ“ฆ + +Deploy a local Docker Compose environment that includes the following. -### Lint code +- Docker images for Backend and Frontend ๐Ÿ–ผ๏ธ +- A stack with a Traefik router and a PostgreSQL database ๐Ÿ—ƒ๏ธ +- Accessible at [http://collective-addon.localhost](http://collective-addon.localhost) ๐ŸŒ -Run ESlint, Prettier, and Stylelint in analyze mode. +Run the following commands in a shell session. ```shell -make lint +make stack-create-site +make stack-start ``` -### Format code +And... you're all set! Your Plone site is up and running locally! ๐Ÿš€ -Run ESlint, Prettier, and Stylelint in fix mode. +## Project structure ๐Ÿ—๏ธ -```shell -make format -``` +This monorepo consists of the following distinct sections: + +- **backend**: Houses the API and Plone installation, utilizing pip instead of buildout, and includes a policy package named kitconcept.plate. +- **frontend**: Contains the React (Volto) package. +- **devops**: Encompasses Docker stack, Ansible playbooks, and cache settings. +- **docs**: Scaffold for writing documentation for your project. + +### Why this structure? ๐Ÿค” -### i18n +- All necessary codebases to run the site are contained within the repository (excluding existing add-ons for Plone and React). +- Specific GitHub Workflows are triggered based on changes in each codebase (refer to .github/workflows). +- Simplifies the creation of Docker images for each codebase. +- Demonstrates Plone installation/setup without buildout. -Extract the i18n messages to locales. +## Code quality assurance ๐Ÿง + +To check your code against quality standards, run the following shell command. ```shell -make i18n +make check ``` -### Unit tests +### Format the codebase -Run unit tests. +To format and rewrite the code base, ensuring it adheres to quality standards, run the following shell command. ```shell -make test +make format ``` -### Run Acceptance tests +| Section | Tool | Description | Configuration | +| --- | --- | --- | --- | +| backend | Ruff | Python code formatting, imports sorting | [`backend/pyproject.toml`](./backend/pyproject.toml) | +| backend | `zpretty` | XML and ZCML formatting | -- | +| frontend | ESLint | Fixes most common frontend issues | [`frontend/.eslintrc.js`](.frontend/.eslintrc.js) | +| frontend | prettier | Format JS and Typescript code | [`frontend/.prettierrc`](.frontend/.prettierrc) | +| frontend | Stylelint | Format Styles (css, less, sass) | [`frontend/.stylelintrc`](.frontend/.stylelintrc) | -This project uses Playwright for acceptance testing. +Formatters can also be run within the `backend` or `frontend` folders. -Run each of these steps in separate terminal sessions. +### Linting the codebase +or `lint`: -In the first session, start the frontend in development mode. - -```shell -make acceptance-frontend-dev-start + ```shell +make lint ``` -In the second session, start the backend acceptance server. +| Section | Tool | Description | Configuration | +| --- | --- | --- | --- | +| backend | Ruff | Checks code formatting, imports sorting | [`backend/pyproject.toml`](./backend/pyproject.toml) | +| backend | Pyroma | Checks Python package metadata | -- | +| backend | check-python-versions | Checks Python version information | -- | +| backend | `zpretty` | Checks XML and ZCML formatting | -- | +| frontend | ESLint | Checks JS / Typescript lint | [`frontend/.eslintrc.js`](.frontend/.eslintrc.js) | +| frontend | prettier | Check JS / Typescript formatting | [`frontend/.prettierrc`](.frontend/.prettierrc) | +| frontend | Stylelint | Check Styles (css, less, sass) formatting | [`frontend/.stylelintrc`](.frontend/.stylelintrc) | -```shell -make acceptance-backend-start -``` +Linters can be run individually within the `backend` or `frontend` folders. + +## Internationalization ๐ŸŒ -In the third session, start the acceptance tests in interactive mode. +Generate translation files for Plone and Volto with ease: ```shell -make acceptance-test +make i18n ``` -## License - -The project is licensed under the MIT license. - ## Credits and acknowledgements ๐Ÿ™ -Generated using [Cookieplone (0.9.8)](https://github.com/plone/cookieplone) and [cookieplone-templates (1d2012a)](https://github.com/plone/cookieplone-templates/commit/1d2012a950e4374f91c3f93e9531fde44b330ab3) on 2025-09-23 09:45:10.835983. A special thanks to all contributors and supporters! +Generated using [Cookieplone (0.9.10)](https://github.com/plone/cookieplone) and [cookieplone-templates (2c54630)](https://github.com/plone/cookieplone-templates/commit/2c5463046f43a87e36d11a7edc2b4176b2d593aa) on 2026-02-13 10:21:42.975825. A special thanks to all contributors and supporters! diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..e9e94ff --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,14 @@ +.editorconfig +.gitattributes +bin +Dockerfile +Dockerfile.acceptance +include +instance +instance.yaml +lib +lib64 +Makefile +pyvenv.cfg +var +.venv diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 0000000..b3561a7 --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,35 @@ + +# EditorConfig Configuration file, for more details see: +# https://EditorConfig.org +# EditorConfig is a convention description, that could be interpreted +# by multiple editors to enforce common coding conventions for specific +# file types + +# top-most EditorConfig file: +# Will ignore other EditorConfig files in Home directory or upper tree level. +root = true + + +[*] # For All Files +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +# Set default charset +charset = utf-8 +# Indent style default +indent_style = space + +[*.{py,cfg,ini}] +# 4 space indentation +indent_size = 4 + +[*.{html,dtml,pt,zpt,xml,zcml,js,json,ts,less,scss,css,sass,yml,yaml}] +# 2 space indentation +indent_size = 2 + +[{Makefile,.gitmodules}] +# Tab indentation (no size specified, but view as 4 spaces) +indent_style = tab +indent_size = unset +tab_width = unset diff --git a/backend/.flake8 b/backend/.flake8 new file mode 100644 index 0000000..7ef4f64 --- /dev/null +++ b/backend/.flake8 @@ -0,0 +1,22 @@ +# Generated from: +# https://github.com/plone/meta/tree/master/config/default +# See the inline comments on how to expand/tweak this configuration file +[flake8] +doctests = 1 +ignore = + # black takes care of line length + E501, + # black takes care of where to break lines + W503, + # black takes care of spaces within slicing (list[:]) + E203, + # black takes care of spaces after commas + E231, + +## +# Add extra configuration options in .meta.toml: +# [flake8] +# extra_lines = """ +# _your own configuration lines_ +# """ +## diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..3bb87bf --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,45 @@ +# python related +*.egg-info +*.pyc +*.pyo + +# tools related +__pycache__/ +.coverage +.mypy_cache +.pytest_cache +.ruff_cache +/build/ +coverage.xml +dist/ +docs/_build +node_modules/ + +# venv / buildout related +.eggs/ +.installed.cfg +.mr.developer.cfg +.venv/ +bin/ +develop-eggs/ +eggs/ +etc/ +include/ +lib/ +lib64 +parts/ +pyvenv.cfg +var/ + +# mxdev +.installed.txt +.lock +/.mxdev_cache/ +/.make-sentinels/ +/*-mxdev.txt +/instance/ +/reports/ +/sources/ +/venv/ +constraints*.txt +requirements*.txt \ No newline at end of file diff --git a/backend/CHANGELOG.md b/backend/CHANGELOG.md new file mode 100644 index 0000000..3020bae --- /dev/null +++ b/backend/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + + + + diff --git a/backend/CONTRIBUTORS.md b/backend/CONTRIBUTORS.md new file mode 100644 index 0000000..baa64d1 --- /dev/null +++ b/backend/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +# Contributors + +- kitconcept, GmbH [info@kitconcept.com] diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..ec2d457 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,38 @@ +# syntax=docker/dockerfile:1 +ARG PLONE_VERSION=6.2.0a1 +FROM plone/server-builder:${PLONE_VERSION} AS builder + +WORKDIR /app + + +# Add local code +COPY scripts/ scripts/ +COPY . src + +# Install local requirements and pre-compile mo files +RUN < + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/backend/LICENSE.md b/backend/LICENSE.md new file mode 100644 index 0000000..5542f03 --- /dev/null +++ b/backend/LICENSE.md @@ -0,0 +1,15 @@ +kitconcept.plate Copyright 2026, kitconcept, GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, +MA 02111-1307 USA. diff --git a/backend/Makefile b/backend/Makefile new file mode 100644 index 0000000..35cc3d1 --- /dev/null +++ b/backend/Makefile @@ -0,0 +1,168 @@ +### Defensive settings for make: +# https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +.SHELLFLAGS:=-xeu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +# 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` + +# Python checks +UV?=uv + +# installed? +ifeq (, $(shell which $(UV) )) + $(error "UV=$(UV) not found in $(PATH)") +endif + +REPOSITORY_SETTINGS := $(shell uvx repoplone settings dump) +IMAGE_NAME_PREFIX := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.container_images_prefix') +IMAGE_TAG=latest +IMAGE_NAME=$(IMAGE_NAME_PREFIX)-backend:$(IMAGE_TAG) + +PLONE_SITE_ID=Plone +BACKEND_FOLDER=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +EXAMPLE_CONTENT_FOLDER := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.backend.code_path')/setuphandlers/examplecontent +PACKAGE_NAME := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.backend.name') + +VENV_FOLDER=$(BACKEND_FOLDER)/.venv +export VIRTUAL_ENV=$(VENV_FOLDER) +BIN_FOLDER=$(VENV_FOLDER)/bin + +# Environment variables to be exported +export PYTHONWARNINGS := ignore +export DOCKER_BUILDKIT := 1 +export PYTHON_VERSION := 3.11 +export PLONE_VERSION := $(shell echo '$(REPOSITORY_SETTINGS)' | jq -r '.backend.base_package_version') + +all: build + +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' +.PHONY: help +help: ## This help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + + +.PHONY: debug-settings +debug-settings: ## Debug settings + @echo "PLONE_VERSION: $(PLONE_VERSION)" + @echo "PACKAGE_NAME: $(PACKAGE_NAME)" + @echo "EXAMPLE_CONTENT_FOLDER: $(EXAMPLE_CONTENT_FOLDER)" + @echo "IMAGE_NAME: $(IMAGE_NAME)" + + +requirements-mxdev.txt: pyproject.toml mx.ini ## Generate constraints file + @echo "$(GREEN)==> Generate constraints file$(RESET)" + @echo '-c https://dist.plone.org/release/$(PLONE_VERSION)/constraints.txt' > requirements.txt + @uvx mxdev -c mx.ini + +$(VENV_FOLDER): requirements-mxdev.txt ## Install dependencies + @echo "$(GREEN)==> Install environment$(RESET)" +ifdef CI + @uv venv $(VENV_FOLDER) +else + @uv venv --python=$(PYTHON_VERSION) $(VENV_FOLDER) +endif + @uv pip install -r requirements-mxdev.txt + +.PHONY: sync +sync: $(VENV_FOLDER) ## Sync project dependencies + @echo "$(GREEN)==> Sync project dependencies$(RESET)" + @uv pip install -r requirements-mxdev.txt + +instance/etc/zope.ini instance/etc/zope.conf: instance.yaml ## Create instance configuration + @echo "$(GREEN)==> Create instance configuration$(RESET)" + @uvx cookiecutter -f --no-input -c 2.1.1 --config-file instance.yaml gh:plone/cookiecutter-zope-instance + +.PHONY: config +config: instance/etc/zope.ini + +.PHONY: install +install: $(VENV_FOLDER) config ## Install Plone and dependencies + +.PHONY: clean +clean: ## Clean installation and instance + @echo "$(RED)==> Cleaning environment and build$(RESET)" + @rm -rf $(VENV_FOLDER) pyvenv.cfg .installed.cfg instance/etc .venv .pytest_cache .ruff_cache constraints* requirements* + +.PHONY: remove-data +remove-data: ## Remove all content + @echo "$(RED)==> Removing all content$(RESET)" + rm -rf $(VENV_FOLDER) instance/var + +.PHONY: start +start: $(VENV_FOLDER) instance/etc/zope.ini ## Start a Plone instance on localhost:8080 + @$(BIN_FOLDER)/runwsgi instance/etc/zope.ini + +.PHONY: console +console: $(VENV_FOLDER) instance/etc/zope.ini ## Start a console into a Plone instance + @$(BIN_FOLDER)/zconsole debug instance/etc/zope.conf + +.PHONY: create-site +create-site: $(VENV_FOLDER) instance/etc/zope.ini ## Create a new site from scratch + @$(BIN_FOLDER)/zconsole run instance/etc/zope.conf ./scripts/create_site.py + +# Example Content +.PHONY: update-example-content +update-example-content: $(VENV_FOLDER) ## Export example content inside package + @echo "$(GREEN)==> Export example content into $(EXAMPLE_CONTENT_FOLDER) $(RESET)" + if [ -d $(EXAMPLE_CONTENT_FOLDER)/content ]; then rm -r $(EXAMPLE_CONTENT_FOLDER)/* ;fi + @$(BIN_FOLDER)/plone-exporter instance/etc/zope.conf $(PLONE_SITE_ID) $(EXAMPLE_CONTENT_FOLDER) + +# QA +.PHONY: lint +lint: ## Check and fix code base according to Plone standards + @echo "$(GREEN)==> Lint codebase$(RESET)" + @uvx ruff@latest check --fix --config $(BACKEND_FOLDER)/pyproject.toml + @uvx pyroma@latest -d . + @uvx check-python-versions@latest . + @uvx zpretty@latest --check src + +.PHONY: format +format: ## Check and fix code base according to Plone standards + @echo "$(GREEN)==> Format codebase$(RESET)" + @uvx ruff@latest check --select I --fix --config $(BACKEND_FOLDER)/pyproject.toml + @uvx ruff@latest format --config $(BACKEND_FOLDER)/pyproject.toml + @uvx zpretty@latest -i src + +# i18n +.PHONY: i18n +i18n: $(VENV_FOLDER) ## Update locales + @echo "$(GREEN)==> Updating locales$(RESET)" + @$(BIN_FOLDER)/python -m $(PACKAGE_NAME).locales + +# Tests +.PHONY: test +test: $(VENV_FOLDER) ## run tests + @$(BIN_FOLDER)/pytest + +.PHONY: test-coverage +test-coverage: $(VENV_FOLDER) ## run tests with coverage + @$(BIN_FOLDER)/pytest --cov=$(PACKAGE_NAME) --cov-report term-missing + +# Build Docker images +.PHONY: build-image +build-image: ## Build Docker Images + @docker build . -t $(IMAGE_NAME) -f Dockerfile --build-arg PLONE_VERSION=$(PLONE_VERSION) + +# Acceptance tests +.PHONY: acceptance-backend-start +acceptance-backend-start: ## Start backend acceptance server + ZSERVER_HOST=0.0.0.0 ZSERVER_PORT=55001 LISTEN_PORT=55001 APPLY_PROFILES="$(PACKAGE_NAME):default" CONFIGURE_PACKAGES="plone.restapi,plone.volto,plone.volto.cors,$(PACKAGE_NAME)" $(BIN_FOLDER)/robot-server plone.app.robotframework.testing.VOLTO_ROBOT_TESTING + +.PHONY: acceptance-image-build +acceptance-image-build: ## Build Docker Images + @docker build . -t $(IMAGE_NAME_PREFIX)-backend-acceptance:$(IMAGE_TAG) -f Dockerfile.acceptance --build-arg PLONE_VERSION=$(PLONE_VERSION) + +## Add bobtemplates features (check bobtemplates.plone's documentation to get the list of available features) +add: $(VENV_FOLDER) + @uvx plonecli add $(filter-out $@,$(MAKECMDGOALS)) diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..ead0c0b --- /dev/null +++ b/backend/README.md @@ -0,0 +1,87 @@ +# kitconcept.plate + +A new add-on for Plone that includes both backend and Volto frontend + +## Features + +TODO: List our awesome features + +## Installation + +Install kitconcept.plate with uv. + +```shell +uv add kitconcept.plate +``` + +Create the Plone site. + +```shell +make create-site +``` + +## Contribute + +- [Issue tracker](https://github.com/kitconcept/volto-plate/issues) +- [Source code](https://github.com/kitconcept/volto-plate/) + +### Prerequisites โœ… + +- An [operating system](https://6.docs.plone.org/install/create-project-cookieplone.html#prerequisites-for-installation) that runs all the requirements mentioned. +- [uv](https://6.docs.plone.org/install/create-project-cookieplone.html#uv) +- [Make](https://6.docs.plone.org/install/create-project-cookieplone.html#make) +- [Git](https://6.docs.plone.org/install/create-project-cookieplone.html#git) +- [Docker](https://docs.docker.com/get-started/get-docker/) (optional) + +### Installation ๐Ÿ”ง + +1. Clone this repository. + + ```shell + git clone git@github.com:kitconcept/volto-plate.git + cd volto-plate/backend + ``` + +2. Install this code base. + + ```shell + make install + ``` + + +### Add features using `plonecli` or `bobtemplates.plone` + +This package provides markers as strings (``) that are compatible with [`plonecli`](https://github.com/plone/plonecli) and [`bobtemplates.plone`](https://github.com/plone/bobtemplates.plone). +These markers act as hooks to add all kinds of features through subtemplates, including behaviors, control panels, upgrade steps, or other subtemplates from `bobtemplates.plone`. +`plonecli` is a command line client for `bobtemplates.plone`, adding autocompletion and other features. + +To add a feature as a subtemplate to your package, use the following command pattern. + +```shell +make add +``` + +For example, you can add a content type to your package with the following command. + +```shell +make add content_type +``` + +You can add a behavior with the following command. + +```shell +make add behavior +``` + +```{seealso} +You can check the list of available subtemplates in the [`bobtemplates.plone` `README.md` file](https://github.com/plone/bobtemplates.plone/?tab=readme-ov-file#provided-subtemplates). +See also the documentation of [Mockup and Patternslib](https://6.docs.plone.org/classic-ui/mockup.html) for how to build the UI toolkit for Classic UI. +``` + +## License + +The project is licensed under GPLv2. + +## Credits and acknowledgements ๐Ÿ™ + +Generated from the [`cookieplone-templates` template](https://github.com/plone/cookieplone-templates/tree/main/) on 2026-02-16 16:36:30.. A special thanks to all contributors and supporters! diff --git a/backend/bobtemplate.cfg b/backend/bobtemplate.cfg new file mode 100644 index 0000000..12e901a --- /dev/null +++ b/backend/bobtemplate.cfg @@ -0,0 +1,7 @@ +[main] +version = 6.0.13 +template = plone_addon +git_init = True +python = python3 +package.dottedname = kitconcept.plate +package.browserlayer = BrowserLayer diff --git a/backend/instance.yaml b/backend/instance.yaml new file mode 100644 index 0000000..bc05f08 --- /dev/null +++ b/backend/instance.yaml @@ -0,0 +1,5 @@ +# Configuration for https://github.com/plone/cookiecutter-zope-instance + +default_context: + initial_user_password: 'admin' + zcml_package_includes: 'kitconcept.plate' \ No newline at end of file diff --git a/backend/mx.ini b/backend/mx.ini new file mode 100644 index 0000000..70f2001 --- /dev/null +++ b/backend/mx.ini @@ -0,0 +1,17 @@ +; This is a mxdev configuration file +; it can be used to override versions of packages already defined in the +; constraints files and to add new packages from VCS like git. +; to learn more about mxdev visit https://pypi.org/project/mxdev/ + +[settings] +main-package = -e .[test] +; example how to override a package version +; version-overrides = +; example.package==2.1.0a2 + +; example section to use packages from git +; [example.contenttype] +; url = https://github.com/collective/example.contenttype.git +; pushurl = git@github.com:collective/example.contenttype.git +; extras = test +; branch = master diff --git a/backend/news/.changelog_template.jinja b/backend/news/.changelog_template.jinja new file mode 100644 index 0000000..0cf429a --- /dev/null +++ b/backend/news/.changelog_template.jinja @@ -0,0 +1,15 @@ +{% if sections[""] %} +{% for category, val in definitions.items() if category in sections[""] %} + +### {{ definitions[category]['name'] }} + +{% for text, values in sections[""][category].items() %} +- {{ text }} {{ values|join(', ') }} +{% endfor %} + +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} diff --git a/backend/news/.gitkeep b/backend/news/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/news/.gitkeep @@ -0,0 +1 @@ + diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..0b9aed7 --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,185 @@ +[project] +name = "kitconcept.plate" +dynamic = ["version"] +description = "A new add-on for Plone that includes both backend and Volto frontend" +readme = "README.md" +license = "GPL-2.0-only" +requires-python = ">=3.11" +authors = [ + { name = "kitconcept, GmbH", email = "info@kitconcept.com" }, +] +keywords = [ + "CMS", + "Plone", + "Python", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Web Environment", + "Framework :: Plone", + "Framework :: Plone :: 6.1", + "Framework :: Plone :: 6.2", + "Framework :: Plone :: Addon", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ + "Products.CMFPlone", + "plone.api", + "plone.restapi", + "plone.volto", +] + +[project.optional-dependencies] +test = [ + "horse-with-no-namespace", + "plone.app.testing", + "plone.restapi[test]", + "pytest", + "pytest-cov", + "pytest-plone>=1.0.0a2", +] + +[project.urls] +Homepage = "https://github.com/kitconcept/volto-plate" +PyPI = "https://pypi.org/project/kitconcept.plate" +Source = "https://github.com/kitconcept/volto-plate" +Tracker = "https://github.com/kitconcept/volto-plate/issues" + + +[project.entry-points."plone.autoinclude.plugin"] +target = "plone" + +[tool.uv] +managed = false + +[tool.hatch.version] +path = "src/kitconcept/plate/__init__.py" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build] +strict-naming = true + +[tool.hatch.build.targets.sdist] +exclude = [ + "/.github", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/kitconcept"] + +[tool.towncrier] +directory = "news/" +filename = "CHANGELOG.md" +start_string = "\n" +title_format = "## {version} ({project_date})" +template = "news/.changelog_template.jinja" +issue_format = "[#{issue}](https://github.com/kitconcept/volto-plate/issues/{issue})" +underlines = ["", "", ""] + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking changes:" +showcontent = true + +[[tool.towncrier.type]] +directory = "feature" +name = "New features:" +showcontent = true + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Bug fixes:" +showcontent = true + +[[tool.towncrier.type]] +directory = "internal" +name = "Internal:" +showcontent = true + +[[tool.towncrier.type]] +directory = "documentation" +name = "Documentation:" +showcontent = true + +[[tool.towncrier.type]] +directory = "tests" +name = "Tests" +showcontent = true + +[tool.ruff] +target-version = "py312" +line-length = 88 +fix = true +lint.select = [ + # flake8-2020 + "YTT", + # flake8-bandit + "S", + # flake8-bugbear + "B", + # flake8-builtins + "A", + # flake8-comprehensions + "C4", + # flake8-debugger + "T10", + # flake8-simplify + "SIM", + # mccabe + "C90", + # pycodestyle + "E", "W", + # pyflakes + "F", + # pygrep-hooks + "PGH", + # pyupgrade + "UP", + # ruff + "RUF", +] +lint.ignore = [ + # DoNotAssignLambda + "E731", +] + +[tool.ruff.lint.isort] +case-sensitive = false +no-sections = true +force-single-line = true +from-first = true +lines-after-imports = 2 +lines-between-types = 1 +order-by-type = false + +[tool.ruff.format] +preview = true + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["E501", "RUF001", "S101"] + +[tool.check-manifest] +ignore = [ + ".editorconfig", + ".flake8", + "dependabot.yml", + "mx.ini", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] + +[tool.coverage.run] +source_pkgs = ["kitconcept.plate", "tests"] +branch = true +parallel = true +omit = [ + "src/kitconcept/plate/locales/*.py", +] diff --git a/backend/scripts/create_site.py b/backend/scripts/create_site.py new file mode 100644 index 0000000..f8e6d8d --- /dev/null +++ b/backend/scripts/create_site.py @@ -0,0 +1,72 @@ +from AccessControl.SecurityManagement import newSecurityManager +from kitconcept.plate.interfaces import IBrowserLayer +from Products.CMFPlone.factory import _DEFAULT_PROFILE +from Products.CMFPlone.factory import addPloneSite +from Products.GenericSetup.tool import SetupTool +from Testing.makerequest import makerequest +from zope.interface import directlyProvidedBy +from zope.interface import directlyProvides + +import os +import transaction + + +truthy = frozenset(("t", "true", "y", "yes", "on", "1")) + + +def asbool(s): + """Return the boolean value ``True`` if the case-lowered value of string + input ``s`` is a :term:`truthy string`. If ``s`` is already one of the + boolean values ``True`` or ``False``, return it.""" + if s is None: + return False + if isinstance(s, bool): + return s + s = str(s).strip() + return s.lower() in truthy + + +DELETE_EXISTING = asbool(os.getenv("DELETE_EXISTING")) +EXAMPLE_CONTENT = asbool(os.getenv("EXAMPLE_CONTENT", "1")) + +app = makerequest(globals()["app"]) + +request = app.REQUEST + +ifaces = [IBrowserLayer] +for iface in directlyProvidedBy(request): + ifaces.append(iface) + +directlyProvides(request, *ifaces) + +admin = app.acl_users.getUserById("admin") +admin = admin.__of__(app.acl_users) +newSecurityManager(None, admin) + +site_id = "Plone" +payload = { + "title": "kitconcept.plate", + "profile_id": _DEFAULT_PROFILE, + "distribution_name": "volto", + "setup_content": False, + "default_language": "en", + "portal_timezone": "UTC", +} + +if site_id in app.objectIds() and DELETE_EXISTING: + app.manage_delObjects([site_id]) + transaction.commit() + app._p_jar.sync() + +if site_id not in app.objectIds(): + site = addPloneSite(app, site_id, **payload) + transaction.commit() + + portal_setup: SetupTool = site.portal_setup + portal_setup.runAllImportStepsFromProfile("profile-kitconcept.plate:default") + transaction.commit() + + if EXAMPLE_CONTENT: + portal_setup.runAllImportStepsFromProfile("profile-kitconcept.plate:initial") + transaction.commit() + app._p_jar.sync() diff --git a/backend/src/kitconcept/plate/__init__.py b/backend/src/kitconcept/plate/__init__.py new file mode 100644 index 0000000..eb47ce7 --- /dev/null +++ b/backend/src/kitconcept/plate/__init__.py @@ -0,0 +1,14 @@ +"""Init and utils.""" + +from zope.i18nmessageid import MessageFactory + +import logging + + +__version__ = "1.0.0a0" + +PACKAGE_NAME = "kitconcept.plate" + +_ = MessageFactory(PACKAGE_NAME) + +logger = logging.getLogger(PACKAGE_NAME) diff --git a/backend/src/kitconcept/plate/configure.zcml b/backend/src/kitconcept/plate/configure.zcml new file mode 100644 index 0000000..03f901d --- /dev/null +++ b/backend/src/kitconcept/plate/configure.zcml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/volto-plate/news/.gitkeep b/backend/src/kitconcept/plate/content/__init__.py similarity index 100% rename from packages/volto-plate/news/.gitkeep rename to backend/src/kitconcept/plate/content/__init__.py diff --git a/backend/src/kitconcept/plate/content/meeting_notes.py b/backend/src/kitconcept/plate/content/meeting_notes.py new file mode 100644 index 0000000..7b1f8b7 --- /dev/null +++ b/backend/src/kitconcept/plate/content/meeting_notes.py @@ -0,0 +1,8 @@ +from kitconcept.plate import _ +from plone.app.textfield import RichText +from plone.supermodel import model +from zope import schema + + +class IMeetingNotes(model.Schema): + """Schema for meeting notes.""" diff --git a/packages/volto-plate/public/.gitkeep b/backend/src/kitconcept/plate/controlpanels/__init__.py similarity index 100% rename from packages/volto-plate/public/.gitkeep rename to backend/src/kitconcept/plate/controlpanels/__init__.py diff --git a/backend/src/kitconcept/plate/controlpanels/configure.zcml b/backend/src/kitconcept/plate/controlpanels/configure.zcml new file mode 100644 index 0000000..532f703 --- /dev/null +++ b/backend/src/kitconcept/plate/controlpanels/configure.zcml @@ -0,0 +1,10 @@ + + + + + diff --git a/backend/src/kitconcept/plate/dependencies.zcml b/backend/src/kitconcept/plate/dependencies.zcml new file mode 100644 index 0000000..b8d35ae --- /dev/null +++ b/backend/src/kitconcept/plate/dependencies.zcml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/volto-plate/src/components/.gitkeep b/backend/src/kitconcept/plate/indexers/__init__.py similarity index 100% rename from packages/volto-plate/src/components/.gitkeep rename to backend/src/kitconcept/plate/indexers/__init__.py diff --git a/backend/src/kitconcept/plate/indexers/configure.zcml b/backend/src/kitconcept/plate/indexers/configure.zcml new file mode 100644 index 0000000..ee4ad7c --- /dev/null +++ b/backend/src/kitconcept/plate/indexers/configure.zcml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/backend/src/kitconcept/plate/interfaces.py b/backend/src/kitconcept/plate/interfaces.py new file mode 100644 index 0000000..7e2f288 --- /dev/null +++ b/backend/src/kitconcept/plate/interfaces.py @@ -0,0 +1,7 @@ +"""Module where all interfaces, events and exceptions live.""" + +from zope.publisher.interfaces.browser import IDefaultBrowserLayer + + +class IBrowserLayer(IDefaultBrowserLayer): + """Marker interface that defines a browser layer.""" diff --git a/backend/src/kitconcept/plate/locales/__init__.py b/backend/src/kitconcept/plate/locales/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/locales/__main__.py b/backend/src/kitconcept/plate/locales/__main__.py new file mode 100644 index 0000000..a5677b6 --- /dev/null +++ b/backend/src/kitconcept/plate/locales/__main__.py @@ -0,0 +1,69 @@ +"""Update locales.""" + +from pathlib import Path + +import logging +import re +import subprocess + + +logger = logging.getLogger("i18n") +logger.setLevel(logging.DEBUG) + + +PATTERN = r"^[a-z]{2}.*" + +locale_path = Path(__file__).parent.resolve() +target_path = locale_path.parent.resolve() +domains = [path.name[:-4] for path in locale_path.glob("*.pot")] + +i18ndude = "uvx i18ndude" + +# ignore node_modules files resulting in errors +excludes = '"*.html *json-schema*.xml"' + + +def locale_folder_setup(domain: str): + languages = [path for path in locale_path.glob("*") if path.is_dir()] + for lang_folder in languages: + lc_messages_path = lang_folder / "LC_MESSAGES" + lang = lang_folder.name + if lc_messages_path.exists(): + continue + elif re.match(PATTERN, lang): + lc_messages_path.mkdir() + cmd = ( + f"msginit --locale={lang} " + f"--input={locale_path}/{domain}.pot " + f"--output={locale_path}/{lang}/LC_MESSAGES/{domain}.po" + ) + subprocess.call(cmd, shell=True) # noQA: S602 + + +def _rebuild(domain: str): + cmd = ( + f"{i18ndude} rebuild-pot --pot {locale_path}/{domain}.pot " + f"--exclude {excludes} " + f"--create {domain} {target_path}" + ) + subprocess.call(cmd, shell=True) # noQA: S602 + + +def _sync(domain: str): + cmd = ( + f"{i18ndude} sync --pot {locale_path}/{domain}.pot " + f"{locale_path}/*/LC_MESSAGES/{domain}.po" + ) + subprocess.call(cmd, shell=True) # noQA: S602 + + +def main(): + for domain in domains: + logger.info(f"Updating translations for {domain}") + locale_folder_setup(domain) + _rebuild(domain) + _sync(domain) + + +if __name__ == "__main__": + main() diff --git a/backend/src/kitconcept/plate/locales/de/LC_MESSAGES/kitconcept.plate.po b/backend/src/kitconcept/plate/locales/de/LC_MESSAGES/kitconcept.plate.po new file mode 100644 index 0000000..411e298 --- /dev/null +++ b/backend/src/kitconcept/plate/locales/de/LC_MESSAGES/kitconcept.plate.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: de\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: DOMAIN\n" diff --git a/backend/src/kitconcept/plate/locales/en/LC_MESSAGES/kitconcept.plate.po b/backend/src/kitconcept/plate/locales/en/LC_MESSAGES/kitconcept.plate.po new file mode 100644 index 0000000..0cadc3f --- /dev/null +++ b/backend/src/kitconcept/plate/locales/en/LC_MESSAGES/kitconcept.plate.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: en\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: DOMAIN\n" diff --git a/backend/src/kitconcept/plate/locales/es/LC_MESSAGES/kitconcept.plate.po b/backend/src/kitconcept/plate/locales/es/LC_MESSAGES/kitconcept.plate.po new file mode 100644 index 0000000..e39c307 --- /dev/null +++ b/backend/src/kitconcept/plate/locales/es/LC_MESSAGES/kitconcept.plate.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: es\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: DOMAIN\n" diff --git a/backend/src/kitconcept/plate/locales/it/LC_MESSAGES/kitconcept.plate.po b/backend/src/kitconcept/plate/locales/it/LC_MESSAGES/kitconcept.plate.po new file mode 100644 index 0000000..c4b010d --- /dev/null +++ b/backend/src/kitconcept/plate/locales/it/LC_MESSAGES/kitconcept.plate.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: it\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: DOMAIN\n" diff --git a/backend/src/kitconcept/plate/locales/kitconcept.plate.pot b/backend/src/kitconcept/plate/locales/kitconcept.plate.pot new file mode 100644 index 0000000..e169b78 --- /dev/null +++ b/backend/src/kitconcept/plate/locales/kitconcept.plate.pot @@ -0,0 +1,18 @@ +#--- PLEASE EDIT THE LINES BELOW CORRECTLY --- +#SOME DESCRIPTIVE TITLE. +#FIRST AUTHOR , YEAR. +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: en\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: kitconcept.plate\n" diff --git a/backend/src/kitconcept/plate/locales/pt_BR/LC_MESSAGES/kitconcept.plate.po b/backend/src/kitconcept/plate/locales/pt_BR/LC_MESSAGES/kitconcept.plate.po new file mode 100644 index 0000000..6abbf1a --- /dev/null +++ b/backend/src/kitconcept/plate/locales/pt_BR/LC_MESSAGES/kitconcept.plate.po @@ -0,0 +1,15 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2022-05-25 17:12+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"Language-Code: pt_BR\n" +"Language-Name: English\n" +"Preferred-Encodings: utf-8 latin1\n" +"Domain: DOMAIN\n" diff --git a/backend/src/kitconcept/plate/permissions.zcml b/backend/src/kitconcept/plate/permissions.zcml new file mode 100644 index 0000000..e2bdd95 --- /dev/null +++ b/backend/src/kitconcept/plate/permissions.zcml @@ -0,0 +1,5 @@ + + + + + diff --git a/backend/src/kitconcept/plate/profiles.zcml b/backend/src/kitconcept/plate/profiles.zcml new file mode 100644 index 0000000..96e3040 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles.zcml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/browserlayer.xml b/backend/src/kitconcept/plate/profiles/default/browserlayer.xml new file mode 100644 index 0000000..a09316a --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/browserlayer.xml @@ -0,0 +1,6 @@ + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/catalog.xml b/backend/src/kitconcept/plate/profiles/default/catalog.xml new file mode 100644 index 0000000..9558132 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/catalog.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/controlpanel.xml b/backend/src/kitconcept/plate/profiles/default/controlpanel.xml new file mode 100644 index 0000000..61d4fdf --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/controlpanel.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/diff_tool.xml b/backend/src/kitconcept/plate/profiles/default/diff_tool.xml new file mode 100644 index 0000000..6a1c5f4 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/diff_tool.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/metadata.xml b/backend/src/kitconcept/plate/profiles/default/metadata.xml new file mode 100644 index 0000000..eec40a3 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/metadata.xml @@ -0,0 +1,7 @@ + + + 1000 + + profile-plone.volto:default + + diff --git a/backend/src/kitconcept/plate/profiles/default/registry/.gitkeep b/backend/src/kitconcept/plate/profiles/default/registry/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/profiles/default/registry/main.xml b/backend/src/kitconcept/plate/profiles/default/registry/main.xml new file mode 100644 index 0000000..09a87ee --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/registry/main.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/repositorytool.xml b/backend/src/kitconcept/plate/profiles/default/repositorytool.xml new file mode 100644 index 0000000..4f674d6 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/repositorytool.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/rolemap.xml b/backend/src/kitconcept/plate/profiles/default/rolemap.xml new file mode 100644 index 0000000..a803517 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/rolemap.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/types.xml b/backend/src/kitconcept/plate/profiles/default/types.xml new file mode 100644 index 0000000..c26d3db --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/types.xml @@ -0,0 +1,8 @@ + + + + diff --git a/backend/src/kitconcept/plate/profiles/default/types/.gitkeep b/backend/src/kitconcept/plate/profiles/default/types/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/profiles/default/types/MeetingNotes.xml b/backend/src/kitconcept/plate/profiles/default/types/MeetingNotes.xml new file mode 100644 index 0000000..c8f5e88 --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/default/types/MeetingNotes.xml @@ -0,0 +1,85 @@ + + + Meeting Notes + Structured notes for meetings. + + False + MeetingNotes + string:contenttype/document + + + + True + True + + cmf.AddPortalContent + plone.dexterity.content.Item + kitconcept.plate.content.meeting_notes.IMeetingNotes + + + + + + + + + + + + + + + string:${folder_url}/++add++MeetingNotes + view + False + view + + + + + + + + + + + + + + + + diff --git a/backend/src/kitconcept/plate/profiles/initial/metadata.xml b/backend/src/kitconcept/plate/profiles/initial/metadata.xml new file mode 100644 index 0000000..f768b5a --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/initial/metadata.xml @@ -0,0 +1,4 @@ + + + 1000 + diff --git a/backend/src/kitconcept/plate/profiles/uninstall/browserlayer.xml b/backend/src/kitconcept/plate/profiles/uninstall/browserlayer.xml new file mode 100644 index 0000000..64abefb --- /dev/null +++ b/backend/src/kitconcept/plate/profiles/uninstall/browserlayer.xml @@ -0,0 +1,6 @@ + + + + diff --git a/backend/src/kitconcept/plate/serializers/__init__.py b/backend/src/kitconcept/plate/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/serializers/configure.zcml b/backend/src/kitconcept/plate/serializers/configure.zcml new file mode 100644 index 0000000..bf452e4 --- /dev/null +++ b/backend/src/kitconcept/plate/serializers/configure.zcml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/backend/src/kitconcept/plate/serializers/summary.py b/backend/src/kitconcept/plate/serializers/summary.py new file mode 100644 index 0000000..4e0605f --- /dev/null +++ b/backend/src/kitconcept/plate/serializers/summary.py @@ -0,0 +1,10 @@ +from plone.restapi.interfaces import IJSONSummarySerializerMetadata +from zope.interface import implementer + + +@implementer(IJSONSummarySerializerMetadata) +class JSONSummarySerializerMetadata: + """Additional metadata to be exposed on listings.""" + + def default_metadata_fields(self): + return {"image_field", "image_scales", "effective", "Subject"} diff --git a/backend/src/kitconcept/plate/setuphandlers/__init__.py b/backend/src/kitconcept/plate/setuphandlers/__init__.py new file mode 100644 index 0000000..f3d203f --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/__init__.py @@ -0,0 +1,17 @@ +from plone.base.interfaces.installable import INonInstallable +from zope.interface import implementer + + +@implementer(INonInstallable) +class HiddenProfiles: + def getNonInstallableProfiles(self): + """Hide uninstall profile from site-creation and quickinstaller.""" + return [ + "kitconcept.plate:uninstall", + ] + + def getNonInstallableProducts(self): + """Hide the upgrades package from site-creation and quickinstaller.""" + return [ + "kitconcept.plate.upgrades", + ] diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/.gitkeep b/backend/src/kitconcept/plate/setuphandlers/examplecontent/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/__metadata__.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/__metadata__.json new file mode 100644 index 0000000..7281b50 --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/__metadata__.json @@ -0,0 +1,40 @@ +{ + "__version__": "1.0.0", + "_blob_files_": [ + "a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png" + ], + "_data_files_": [ + "plone_site_root/data.json", + "a720393b3c0240e5bd27c43fcd2cfd1e/data.json", + "a58ccead718140c1baa98d43595fc3e6/data.json" + ], + "default_page": {}, + "local_roles": { + "a58ccead718140c1baa98d43595fc3e6": { + "local_roles": { + "admin": [ + "Owner" + ] + } + }, + "a720393b3c0240e5bd27c43fcd2cfd1e": { + "local_roles": { + "admin": [ + "Owner" + ] + } + }, + "plone_site_root": { + "local_roles": { + "admin": [ + "Owner" + ] + } + } + }, + "ordering": { + "a58ccead718140c1baa98d43595fc3e6": 0, + "a720393b3c0240e5bd27c43fcd2cfd1e": 42 + }, + "relations": [] +} \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/data.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/data.json new file mode 100644 index 0000000..08dbccf --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/data.json @@ -0,0 +1,56 @@ +{ + "@id": "/images/plone-foundation.png", + "@type": "Image", + "UID": "a58ccead718140c1baa98d43595fc3e6", + "allow_discussion": false, + "contributors": [], + "created": "2024-05-27T03:23:37+00:00", + "creators": [ + "admin" + ], + "description": "", + "effective": null, + "exclude_from_nav": false, + "expires": null, + "exportimport.constrains": {}, + "exportimport.conversation": [], + "exportimport.versions": {}, + "id": "plone-foundation.png", + "image": { + "blob_path": "a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png", + "content-type": "image/png", + "filename": "plone-foundation.png", + "height": 439, + "size": 50737, + "width": 2000 + }, + "is_folderish": false, + "language": "##DEFAULT##", + "layout": "image_view", + "lock": {}, + "modified": "2024-05-27T03:24:00+00:00", + "parent": { + "@id": "/images", + "@type": "Document", + "Subject": [], + "UID": "a720393b3c0240e5bd27c43fcd2cfd1e", + "description": "Site images", + "effective": "1969-12-31T03:00:00+00:00", + "image_field": null, + "image_scales": {}, + "review_state": "private", + "title": "Images", + "type_title": "Page" + }, + "review_state": null, + "rights": "", + "subjects": [ + "Plone" + ], + "title": "Plone Foundation Logo", + "type_title": "Image", + "version": "current", + "workflow_history": {}, + "working_copy": null, + "working_copy_of": null +} \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png new file mode 100644 index 0000000..9516c4c Binary files /dev/null and b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png differ diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a720393b3c0240e5bd27c43fcd2cfd1e/data.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a720393b3c0240e5bd27c43fcd2cfd1e/data.json new file mode 100644 index 0000000..0a4788e --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/a720393b3c0240e5bd27c43fcd2cfd1e/data.json @@ -0,0 +1,98 @@ +{ + "@id": "/images", + "@type": "Document", + "UID": "a720393b3c0240e5bd27c43fcd2cfd1e", + "allow_discussion": false, + "blocks": { + "30f271c5-dadf-4f47-987f-6ce417922c6b": { + "@type": "title" + }, + "ecfe183b-1d44-48f9-8eac-5de33884c7f5": { + "@type": "listing", + "headlineTag": "h2", + "querystring": { + "query": [ + { + "i": "portal_type", + "o": "plone.app.querystring.operation.selection.any", + "v": [ + "Image" + ] + }, + { + "i": "path", + "o": "plone.app.querystring.operation.string.absolutePath", + "v": "/images" + } + ], + "sort_on": "getObjPositionInParent", + "sort_order": "ascending", + "sort_order_boolean": false + }, + "variation": "imageGallery" + } + }, + "blocks_layout": { + "items": [ + "30f271c5-dadf-4f47-987f-6ce417922c6b", + "ecfe183b-1d44-48f9-8eac-5de33884c7f5" + ] + }, + "contributors": [], + "created": "2024-05-27T03:23:26+00:00", + "creators": [ + "admin" + ], + "description": "Site images", + "effective": null, + "exclude_from_nav": true, + "expires": null, + "exportimport.constrains": {}, + "exportimport.conversation": [], + "exportimport.versions": {}, + "id": "images", + "is_folderish": true, + "language": "##DEFAULT##", + "layout": "document_view", + "lock": { + "locked": false, + "stealable": true + }, + "modified": "2024-05-27T03:26:27+00:00", + "parent": { + "@id": "/", + "@type": "Plone Site", + "UID": "plone_site_root", + "description": "", + "title": "Project Title", + "type_title": "Plone Site" + }, + "preview_caption": null, + "preview_image": null, + "review_state": "published", + "rights": "", + "subjects": [], + "title": "Images", + "type_title": "Page", + "version": "current", + "workflow_history": { + "simple_publication_workflow": [ + { + "action": null, + "actor": "admin", + "comments": "", + "review_state": "private", + "time": "2024-05-27T03:23:26+00:00" + }, + { + "action": "publish", + "actor": "admin", + "comments": "", + "review_state": "published", + "time": "2024-05-27T03:50:30+00:00" + } + ] + }, + "working_copy": null, + "working_copy_of": null +} diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/plone_site_root/data.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/plone_site_root/data.json new file mode 100644 index 0000000..d0097e7 --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/content/plone_site_root/data.json @@ -0,0 +1,267 @@ +{ + "@id": "/Plone", + "@type": "Plone Site", + "UID": "plone_site_root", + "allow_discussion": null, + "blocks": { + "47c92ffe-c201-493c-97c9-a2d4e0beab69": { + "@type": "slate", + "plaintext": "Plone is a powerful content management system built on a rock-solid application stack written in the Python and JavaScript programming languages.", + "value": [ + { + "children": [ + { + "text": "Plone is a powerful content management system built on a rock-solid application stack written in the " + }, + { + "children": [ + { + "text": "Python" + } + ], + "type": "em" + }, + { + "text": " and " + }, + { + "children": [ + { + "text": "JavaScript" + } + ], + "type": "em" + }, + { + "text": " programming languages." + } + ], + "type": "p" + } + ] + }, + "5f31e9e4-173f-4ec1-ab48-64e9e2789b76": { + "@type": "title" + }, + "68357972-bd25-47e2-a098-c7932a923182": { + "@type": "image", + "alt": "Plone Foundation Logo", + "href": [ + { + "@id": "https://plone.org/foundation", + "title": "plone.org/foundation" + } + ], + "image_field": "image", + "url": "resolveuid/a58ccead718140c1baa98d43595fc3e6" + }, + "ae41d44d-c4b1-426c-acf6-2e28cb9a97a0": { + "@type": "slate", + "plaintext": "Find out more about Plone", + "value": [ + { + "children": [ + { + "text": "Find out more about Plone" + } + ], + "type": "h3" + } + ] + }, + "d9707a27-2565-4d7d-9daa-6c61d60ce8fd": { + "@type": "slate", + "plaintext": " The features of Plone Plone Documentation Plone Training Plone Community Forum Add-ons for Plone (backend) Add-ons for Volto (frontend) ", + "value": [ + { + "children": [ + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "The features of Plone" + } + ], + "data": { + "url": "https://plone.org/why-plone/features" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + }, + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "Plone Documentation" + } + ], + "data": { + "url": "https://docs.plone.org/" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + }, + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "Plone Training" + } + ], + "data": { + "url": "https://training.plone.org/" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + }, + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "Plone Community Forum" + } + ], + "data": { + "url": "https://community.plone.org/" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + }, + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "Add-ons for Plone (backend)" + } + ], + "data": { + "url": "https://github.com/collective/awesome-plone#readme" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + }, + { + "children": [ + { + "text": "" + }, + { + "children": [ + { + "text": "Add-ons for Volto (frontend)" + } + ], + "data": { + "url": "https://github.com/collective/awesome-volto#readme" + }, + "type": "link" + }, + { + "text": "" + } + ], + "type": "li" + } + ], + "type": "ul" + } + ] + }, + "e5ace182-3746-472c-ad81-696425a2b161": { + "@type": "slate", + "plaintext": "Welcome to your new Plone site!", + "value": [ + { + "children": [ + { + "text": "Welcome to your new Plone site!" + } + ], + "type": "h2" + } + ] + } + }, + "blocks_layout": { + "items": [ + "5f31e9e4-173f-4ec1-ab48-64e9e2789b76", + "e5ace182-3746-472c-ad81-696425a2b161", + "ae41d44d-c4b1-426c-acf6-2e28cb9a97a0", + "47c92ffe-c201-493c-97c9-a2d4e0beab69", + "d9707a27-2565-4d7d-9daa-6c61d60ce8fd", + "68357972-bd25-47e2-a098-c7932a923182" + ] + }, + "contributors": [], + "creators": [ + "admin" + ], + "description": "", + "effective": null, + "exclude_from_nav": false, + "expires": null, + "exportimport.constrains": {}, + "exportimport.conversation": [], + "exportimport.versions": {}, + "id": "Plone", + "is_folderish": true, + "items_total": 0, + "language": "##DEFAULT##", + "lock": { + "locked": false, + "stealable": true + }, + "parent": {}, + "review_state": null, + "rights": "", + "subjects": [], + "table_of_contents": null, + "title": "Project Title", + "type_title": "Plone Site", + "workflow_history": {} +} \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/principals.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/principals.json new file mode 100644 index 0000000..3bc1f3e --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/principals.json @@ -0,0 +1,4 @@ +{ + "groups": [], + "members": [] +} diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/redirects.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/redirects.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/redirects.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/relations.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/relations.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/relations.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/examplecontent/translations.json b/backend/src/kitconcept/plate/setuphandlers/examplecontent/translations.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/examplecontent/translations.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/backend/src/kitconcept/plate/setuphandlers/initial.py b/backend/src/kitconcept/plate/setuphandlers/initial.py new file mode 100644 index 0000000..03dfce8 --- /dev/null +++ b/backend/src/kitconcept/plate/setuphandlers/initial.py @@ -0,0 +1,16 @@ +from kitconcept.plate import logger +from pathlib import Path +from plone import api +from plone.exportimport import importers +from Products.GenericSetup.tool import SetupTool + + +EXAMPLE_CONTENT_FOLDER = Path(__file__).parent / "examplecontent" + + +def create_example_content(portal_setup: SetupTool): + """Import content available at the examplecontent folder.""" + portal = api.portal.get() + importer = importers.get_importer(portal) + for line in importer.import_site(EXAMPLE_CONTENT_FOLDER): + logger.info(line) diff --git a/backend/src/kitconcept/plate/testing.py b/backend/src/kitconcept/plate/testing.py new file mode 100644 index 0000000..9acbdfd --- /dev/null +++ b/backend/src/kitconcept/plate/testing.py @@ -0,0 +1,49 @@ +from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE +from plone.app.robotframework.testing import REMOTE_LIBRARY_BUNDLE_FIXTURE +from plone.app.testing import applyProfile +from plone.app.testing import FunctionalTesting +from plone.app.testing import IntegrationTesting +from plone.app.testing import PloneSandboxLayer +from plone.testing.zope import WSGI_SERVER_FIXTURE + +import kitconcept.plate + + +class Layer(PloneSandboxLayer): + defaultBases = (PLONE_APP_CONTENTTYPES_FIXTURE,) + + def setUpZope(self, app, configurationContext): + # Load any other ZCML that is required for your tests. + # The z3c.autoinclude feature is disabled in the Plone fixture base + # layer. + import plone.restapi + + self.loadZCML(package=plone.restapi) + self.loadZCML(package=kitconcept.plate) + + def setUpPloneSite(self, portal): + applyProfile(portal, "kitconcept.plate:default") + + +FIXTURE = Layer() + +INTEGRATION_TESTING = IntegrationTesting( + bases=(FIXTURE,), + name="Kitconcept.PlateLayer:IntegrationTesting", +) + + +FUNCTIONAL_TESTING = FunctionalTesting( + bases=(FIXTURE, WSGI_SERVER_FIXTURE), + name="Kitconcept.PlateLayer:FunctionalTesting", +) + + +ACCEPTANCE_TESTING = FunctionalTesting( + bases=( + FIXTURE, + REMOTE_LIBRARY_BUNDLE_FIXTURE, + WSGI_SERVER_FIXTURE, + ), + name="Kitconcept.PlateLayer:AcceptanceTesting", +) diff --git a/backend/src/kitconcept/plate/upgrades/__init__.py b/backend/src/kitconcept/plate/upgrades/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/upgrades/configure.zcml b/backend/src/kitconcept/plate/upgrades/configure.zcml new file mode 100644 index 0000000..0f43743 --- /dev/null +++ b/backend/src/kitconcept/plate/upgrades/configure.zcml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/backend/src/kitconcept/plate/vocabularies/__init__.py b/backend/src/kitconcept/plate/vocabularies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kitconcept/plate/vocabularies/configure.zcml b/backend/src/kitconcept/plate/vocabularies/configure.zcml new file mode 100644 index 0000000..e2bdd95 --- /dev/null +++ b/backend/src/kitconcept/plate/vocabularies/configure.zcml @@ -0,0 +1,5 @@ + + + + + diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py new file mode 100644 index 0000000..8e59715 --- /dev/null +++ b/backend/tests/conftest.py @@ -0,0 +1,16 @@ +from kitconcept.plate.testing import ACCEPTANCE_TESTING +from kitconcept.plate.testing import FUNCTIONAL_TESTING +from kitconcept.plate.testing import INTEGRATION_TESTING +from pytest_plone import fixtures_factory + + +pytest_plugins = ["pytest_plone"] + + +globals().update( + fixtures_factory(( + (ACCEPTANCE_TESTING, "acceptance"), + (FUNCTIONAL_TESTING, "functional"), + (INTEGRATION_TESTING, "integration"), + )) +) diff --git a/backend/tests/setup/test_setup_install.py b/backend/tests/setup/test_setup_install.py new file mode 100644 index 0000000..a276803 --- /dev/null +++ b/backend/tests/setup/test_setup_install.py @@ -0,0 +1,17 @@ +from kitconcept.plate import PACKAGE_NAME + + +class TestSetupInstall: + def test_addon_installed(self, installer): + """Test if kitconcept.plate is installed.""" + assert installer.is_product_installed(PACKAGE_NAME) is True + + def test_browserlayer(self, browser_layers): + """Test that IBrowserLayer is registered.""" + from kitconcept.plate.interfaces import IBrowserLayer + + assert IBrowserLayer in browser_layers + + def test_latest_version(self, profile_last_version): + """Test latest version of default profile.""" + assert profile_last_version(f"{PACKAGE_NAME}:default") == "1000" diff --git a/backend/tests/setup/test_setup_uninstall.py b/backend/tests/setup/test_setup_uninstall.py new file mode 100644 index 0000000..1b6bca2 --- /dev/null +++ b/backend/tests/setup/test_setup_uninstall.py @@ -0,0 +1,19 @@ +from kitconcept.plate import PACKAGE_NAME + +import pytest + + +class TestSetupUninstall: + @pytest.fixture(autouse=True) + def uninstalled(self, installer): + installer.uninstall_product(PACKAGE_NAME) + + def test_addon_uninstalled(self, installer): + """Test if kitconcept.plate is uninstalled.""" + assert installer.is_product_installed(PACKAGE_NAME) is False + + def test_browserlayer_not_registered(self, browser_layers): + """Test that IBrowserLayer is not registered.""" + from kitconcept.plate.interfaces import IBrowserLayer + + assert IBrowserLayer not in browser_layers diff --git a/backend/version.txt b/backend/version.txt new file mode 100644 index 0000000..81b00d8 --- /dev/null +++ b/backend/version.txt @@ -0,0 +1 @@ +6.2.0a1 diff --git a/dependabot.yml b/dependabot.yml new file mode 100644 index 0000000..df4d15b --- /dev/null +++ b/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..183d58e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,100 @@ +--- +name: collective-addon + +services: + traefik: + image: traefik:v2.11 + + ports: + - 80:80 + + labels: + - traefik.enable=true + - traefik.constraint-label=public + + # GENERIC MIDDLEWARES + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + + command: + - --providers.docker + - --providers.docker.constraints=Label(`traefik.constraint-label`, `public`) + - --providers.docker.exposedbydefault=false + - --entrypoints.http.address=:80 + - --accesslog + - --log + - --api + + frontend: + build: + context: ./frontend + args: + - VOLTO_VERSION=${VOLTO_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone + depends_on: + - backend + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-frontend.loadbalancer.server.port=3000 + # Routers + ## / + - traefik.http.routers.rt-frontend.rule=Host(`collective-addon.localhost`) + - traefik.http.routers.rt-frontend.entrypoints=http + - traefik.http.routers.rt-frontend.service=svc-frontend + - traefik.http.routers.rt-frontend.middlewares=gzip + + backend: + build: + context: ./backend + args: + - PLONE_VERSION=${PLONE_VERSION} + environment: + ZEO_ADDRESS: "zeo:8100" + depends_on: + - zeo + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Services + - traefik.http.services.svc-backend.loadbalancer.server.port=8080 + + # Middlewares + ## VHM rewrite /++api++/ + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/http/collective-addon.localhost/Plone/++api++/VirtualHostRoot$$1" + + ## VHM rewrite /ClassicUI/ + - "traefik.http.middlewares.mw-backend-vhm-classic.replacepathregex.regex=^/ClassicUI($$|/.*)" + - "traefik.http.middlewares.mw-backend-vhm-classic.replacepathregex.replacement=/VirtualHostBase/http/collective-addon.localhost/Plone/VirtualHostRoot/_vh_ClassicUI$$1" + + ## Basic Authentication + ### Note: all dollar signs in the hash need to be doubled for escaping. + ### To create user:password pair, it's possible to use this command: + ### echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g + ### Defaults to admin:admin + - traefik.http.middlewares.mw-backend-auth.basicauth.headerField=X-Auth-ClassicUI + - traefik.http.middlewares.mw-backend-auth.basicauth.users=admin:$$apr1$$uZPT5Fgu$$AmlIdamxT5ipBvPlsdfD70 + # Routers + - traefik.http.routers.rt-backend-api.rule=Host(`collective-addon.localhost`) && (PathPrefix(`/++api++`)) + - traefik.http.routers.rt-backend-api.entrypoints=http + - traefik.http.routers.rt-backend-api.service=svc-backend + - traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api + ## /ClassicUI + - traefik.http.routers.rt-backend-classic.rule=Host(`collective-addon.localhost`) && PathPrefix(`/ClassicUI`) + - traefik.http.routers.rt-backend-classic.entrypoints=http + - traefik.http.routers.rt-backend-classic.service=svc-backend + - traefik.http.routers.rt-backend-classic.middlewares=gzip,mw-backend-auth,mw-backend-vhm-classic + + zeo: + image: plone/plone-zeo:6.0.0 + volumes: + - vol-site-data:/data + +volumes: + vol-site-data: {} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..8c348ac --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,7 @@ +# Python related +*.egg-info +.venv + +# Documentation builds +_build +styles/Microsoft diff --git a/docs/.vale.ini b/docs/.vale.ini new file mode 100644 index 0000000..f89759a --- /dev/null +++ b/docs/.vale.ini @@ -0,0 +1,12 @@ +StylesPath = styles + +MinAlertLevel = suggestion + +Vocab = Base,Plone + +Packages = Microsoft + +[*] +BasedOnStyles = Vale, Microsoft +Microsoft.Contractions = suggestion +Microsoft.Units = suggestion diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..0a9dc4f --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,138 @@ +# Makefile for Sphinx documentation +.DEFAULT_GOAL = help +SHELL = bash + +# You can set these variables from the command line. +SPHINXOPTS ?= +PAPER ?= + +# Internal variables. +GREEN=`tput setaf 2` +RESET=`tput sgr0` +SPHINXBUILD = "$(realpath .venv/bin/sphinx-build)" +DOCS_DIR = ./docs/ +BUILDDIR = ./_build +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(DOCS_DIR) +VALEFILES := $(shell find $(DOCS_DIR) -type f -name "*.md" -print) +VALEOPTS ?= +PYTHONVERSION = >=3.11,<3.14 + +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' +.PHONY: help +help: # This help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + + +# environment management +.venv/bin/python: ## Create Python virtual environment and install package requirements + @uv sync + +.PHONY: install +install: .venv/bin/python ## Sync package requirements + + +.PHONY: init +init: clean clean-python .venv/bin/python ## Clean docs build directory and Python, and initialize Python virtual environment + +.PHONY: clean +clean: ## Clean docs build directory + cd $(DOCS_DIR) && rm -rf $(BUILDDIR)/ + +.PHONY: clean-python +clean-python: clean + rm -rf .venv/ + rm -rf *.egg-info +# /environment management + + +# documentation builders +.PHONY: html +html: .venv/bin/python ## Build html + @uv run sphinx-build -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: livehtml +livehtml: .venv/bin/python ## Rebuild Sphinx documentation on changes, with live-reload in the browser + @uv run sphinx-autobuild \ + --ignore "*.swp" \ + --port 8050 \ + -b html $(DOCS_DIR) "$(BUILDDIR)/html" $(SPHINXOPTS) $(O) + +.PHONY: dirhtml +dirhtml: .venv/bin/python + @uv run sphinx-build -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: .venv/bin/python + @uv run sphinx-build -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: text +text: .venv/bin/python + @uv run sphinx-build -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: changes +changes: .venv/bin/python + @uv run sphinx-build -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: rtd-prepare +rtd-prepare: ## Prepare environment on Read the Docs + asdf plugin add uv + asdf install uv latest + asdf global uv latest + +.PHONY: rtd-pr-preview ## Build pull request previews on Read the Docs +rtd-pr-preview: rtd-prepare .venv/bin/python ## Build pull request preview on Read the Docs + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/ + +.PHONY: build +build: html ## Build documentation in HTML format + +# /documentation builders + + +# test +.PHONY: linkcheck +linkcheck: .venv/bin/python ## Run linkcheck + @uv run sphinx-build -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/ ." + +.PHONY: linkcheckbroken +linkcheckbroken: .venv/bin/python ## Run linkcheck and show only broken links + @uv run sphinx-build -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck | GREP_COLORS='0;31' grep -wi "broken\|redirect" --color=always | GREP_COLORS='0;31' grep -vi "https://github.com/plone/volto/issues/" --color=always && if test $$? = 0; then exit 1; fi || test $$? = 1 + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/ ." + +.PHONY: doctest +doctest: .venv/bin/python ## Test snippets in the documentation + @uv run sphinx-build -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: vale +vale: .venv/bin/python ## Run Vale style, grammar, and spell checks + @uv run vale sync + @uv run vale --no-wrap $(VALEOPTS) $(VALEFILES) + @echo "Vale is finished; look for any errors in the above output." + @echo + +.PHONY: test +test: clean vale linkcheckbroken doctest ## Clean docs build, then run vale, linkcheckbroken, and doctest + +.PHONY: all +all: clean vale linkcheckbroken html ## Clean docs build, then run linkcheckbroken, and build html +# /test diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..0cc0025 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,124 @@ +# Collective Addon + +Documentation for Collective Addon. +A new add-on for Plone that includes both backend and Volto frontend + +This project provides a Sphinx-based documentation environment for your Plone project, powered by the [Plone Sphinx Theme](https://github.com/plone/plone-sphinx-theme). +It's generated using the `documentation_starter` template from [Cookieplone](https://github.com/plone/cookieplone). + + +## Prerequisites + +- [uv](https://docs.astral.sh/uv/) is the recommended tool for managing Python versions and project dependencies. + +To install uv, use the following command, or visit the [uv installation page](https://docs.astral.sh/uv/getting-started/installation/) for alternative methods. + +```shell +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + + +## Build documentation + +To build the HTML documentation, issue the following command. + +```shell +make html +``` + +To build the HTML documentation and view a live preview while editing your documentation, issue the following command. + +```shell +make livehtml +``` + +To check for broken links in your documentation, issue the following command. + +```shell +make linkcheckbroken +``` + +To check spelling, grammar, and style in your documentation, issue the following command. +You should pay attention to errors and warnings, and suggestions may get noisy. + +```shell +make vale +``` + +To delete the `docs` build directory and Python virtual environment, and reinitialize Python virtual environment, issue the following command. +This is useful to force reinstall dependencies and purge cached files in Sphinx builds. + +```shell +make init +``` + +For more `make` commands, issue the following command. + +```shell +make help +``` + + +## Customize the Collective Addon documentation + +This section provides how-to guidance to customize your documentation. + +The file `docs/conf.py` controls the configuration of your documentation. +It has extensive comments for each part, often with links to the authoritative documentation for extensions and configuration. + +The following sections describe customization not addressed in `docs/conf.py`. + + +### Manage dependencies + +You can configure which dependencies or requirements you want to use in your documentation using uv. +Requirements are stored in the `dev` dependency group in the `pyproject.toml` file. + +To add a requirement, use the following command. + +```shell +uv add --dev my-requirement +``` + +To remove a requirement, use the following command. + +```shell +uv remove --dev my-requirement +``` + +See also uv's documentation [Development dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/#development-dependencies). + +After installing a dependency, you might need to add it to your documentation's configuration file, `conf.py`, under the `extensions` key. + + +### Replace static files + +You might want to replace the default static files, located in `docs/_static`, `logo.svg` and `favicon.ico`. +Plone Sphinx Theme is configured to use these files when rendering documentation to HTML. + +If you rename `logo.svg`, you must update `conf.py`, under the `html_logo`, `ogp_image`, and `latex_logo` keys. + + +## Read the Docs + +Now that you've built your documentation, how do you publish it? +And how do you get collaborators to easily review your changes to your documentation? + +Thankfully, [Read the Docs](https://about.readthedocs.com/) provides both hosting of documentation and pull request previews. +For public repositories, this service is free. +However, the Plone Foundation donates to Read the Docs for their unwavering support to open source software, and encourages you to do the same. + +Your documentation scaffold is partially configured to use Read the Docs. +In several files, search for the string `MY_READTHEDOCS_PROJECT_SLUG`. +You'll need to replace that string with the slug that Read the Docs creates for you when you import your project. + +For complete documentation of this process, see the Plone 6 Documentation [Pull request preview builds](https://6.docs.plone.org/contributing/documentation/admins.html#pull-request-preview-builds). + +See also Read the Docs documentation: + +- [Pull request previews](https://docs.readthedocs.com/platform/stable/pull-requests.html) +- [Build process overview](https://docs.readthedocs.com/platform/stable/builds.html) + +## Credits and acknowledgements ๐Ÿ™ + +This was generated by the [cookieplone-templates documentation_starter template](https://github.com/plone/cookieplone-templates/tree/main/documentation_starter) on 2026-02-13 09:23:37. A special thanks to all contributors and supporters! diff --git a/docs/docs/_static/favicon.ico b/docs/docs/_static/favicon.ico new file mode 100644 index 0000000..4904846 Binary files /dev/null and b/docs/docs/_static/favicon.ico differ diff --git a/docs/docs/_static/logo.svg b/docs/docs/_static/logo.svg new file mode 100644 index 0000000..b597336 --- /dev/null +++ b/docs/docs/_static/logo.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/docs/_templates/404.html b/docs/docs/_templates/404.html new file mode 100644 index 0000000..782c30d --- /dev/null +++ b/docs/docs/_templates/404.html @@ -0,0 +1,20 @@ +{% extends "!page.html" %} + +{%- block htmltitle %} +{{ title|striptags|e }}{%- for parent in parents %} โ€“ {{ parent.title }}{%- endfor %}{{ titlesuffix }} +{%- endblock %} + +{% block body %} +
+

Page not found

+

We could not find the page you requested.

+

You can search.

+
+ + +{% endblock %} diff --git a/docs/docs/concepts/index.md b/docs/docs/concepts/index.md new file mode 100644 index 0000000..c339bc2 --- /dev/null +++ b/docs/docs/concepts/index.md @@ -0,0 +1,20 @@ +--- +myst: + html_meta: + "description": "Collective Addon concepts" + "property=og:description": "Collective Addon concepts" + "property=og:title": "Collective Addon concepts" + "keywords": "Plone, Collective Addon, concepts" +--- + +# Concepts + +This part of the documentation contains conceptual guides, including design defense and explanation of concepts for deeper study. +The Diรกtaxis framework also calls this class of documentation _explanation_. + +> Explanation is a discursive treatment of a subject, that permits reflection. +> Explanation is understanding-oriented. + +```{seealso} +https://diataxis.fr/explanation/ +``` diff --git a/docs/docs/conf.py b/docs/docs/conf.py new file mode 100644 index 0000000..7af06ec --- /dev/null +++ b/docs/docs/conf.py @@ -0,0 +1,368 @@ +# Configuration file for the Sphinx documentation builder. +# Collective Addon build configuration file + + +# -- Path setup -------------------------------------------------------------- + +from datetime import datetime + +from packaging.version import Version +from plone_sphinx_theme import __version__ + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath("../src/plone_sphinx_theme")) + +# -- Project information ----------------------------------------------------- + +project = "Collective Addon" +author = "Plone Foundation" +trademark_name = "kitconcept" +now = datetime.now() +year = str(now.year) +copyright = year + + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = __version__ +# The short X.Y version. +version = "v" + (Version(str(release)).base_version) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = "" +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = "%B %d, %Y" + + +# -- General configuration ---------------------------------------------------- + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# Add any Sphinx extension module names here, as strings. +# They can be extensions coming with Sphinx (named "sphinx.ext.*") +# or your custom ones. +extensions = [ + "myst_parser", + "notfound.extension", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", # plone.api + "sphinx.ext.doctest", # plone.api + "sphinx.ext.graphviz", + "sphinx.ext.ifconfig", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.viewcode", # plone.api + "sphinx_copybutton", + "sphinx_design", + "sphinx_examples", + "sphinx_reredirects", + "sphinx_sitemap", + "sphinx_tippy", + "sphinxcontrib.httpdomain", # plone.restapi + "sphinxcontrib.httpexample", # plone.restapi + "sphinxcontrib.mermaid", + "sphinxcontrib.video", + "sphinxcontrib.youtube", + "sphinxext.opengraph", +] + +# If true, the Docutils Smart Quotes transform, originally based on SmartyPants +# (limited to English) and currently applying to many languages, will be used +# to convert quotes and dashes to typographically correct entities. +smartquotes = False + +# Options for the linkcheck builder +# Ignore localhost +linkcheck_ignore = [ + # Ignore local and example URLs + r"http://0.0.0.0", + r"http://127.0.0.1", + r"http://localhost", + # Ignore file downloads + r"^/_static/", + # Ignore pages that require authentication + r"https://github.com/kitconcept/collectiveaddon/issues/new", # requires auth + # Ignore github.com pages with anchors + r"https://github.com/.*#.*", + # Ignore other specific anchors +] +linkcheck_allowed_redirects = { # TODO: Confirm usage of linkcheck_allowed_redirects + # All HTTP redirections from the source URI to the canonical URI will be treated as "working". +} +linkcheck_anchors = True +linkcheck_timeout = 5 +linkcheck_retries = 1 + +# The suffix of source filenames. +source_suffix = { + ".md": "markdown", + ".rst": "restructuredtext", +} + +# The master toctree document. +master_doc = "index" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +suppress_warnings = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "plone_sphinx_theme" # This can be configured +html_logo = "_static/logo.svg" +html_favicon = "_static/favicon.ico" +# The default value includes icon-links, so override it with that one omitted, and add it to html_theme_options[footer_content_items]. +html_sidebars = { + "**": [ + "navbar-logo", + "search-button-field", + "sbt-sidebar-nav", + ] +} +html_theme_options = { + "article_header_start": ["toggle-primary-sidebar"], + # "extra_footer": """

Example `extra_footer` content. License info. Trademark info and usage.

+ #

Pull request previews by Read the Docs.

""", + "footer_content_items": [ + "author", + "copyright", + "last-updated", + "extra-footer", + "icon-links", + ], + # Uncomment for a page-wide footer + # "footer_end": ["version.html"], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/kitconcept/collectiveaddon", + "icon": "fa-brands fa-square-github", + "type": "fontawesome", + "attributes": { + "target": "_blank", + "rel": "noopener me", + "class": "nav-link custom-fancy-css", + }, + }, + # { + # "name": "Mastodon", + # "url": "https://MY_MASTODON_SERVER/@MY_MASTODON_USER", + # "icon": "fa-brands fa-mastodon", + # "type": "fontawesome", + # "attributes": { + # "target": "_blank", + # "rel": "noopener me", + # "class": "nav-link custom-fancy-css", + # }, + # }, + ], + "logo": { + "text": "Collective Addon", + }, + "navigation_with_keys": True, + "path_to_docs": "docs/docs", + "repository_branch": "main", + "repository_url": "https://github.com/kitconcept/collectiveaddon", + "search_bar_text": "Search", + "show_toc_level": 2, + "use_edit_page_button": True, + "use_issues_button": True, + "use_repository_button": True, +} +# suggest edit link +# remark: is mandatory in "edit_page_url_template" +# html_context = { +# "edit_page_url_template": "https://github.com/kitconcept/collectiveaddon/edit/main/docs/", +# } + +# Announce that we have an opensearch plugin +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch +html_use_opensearch = "https://MY_READTHEDOCS_PROJECT_SLUG.readthedocs.io" + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = f"{project} v{release}" + +# If false, no index is generated. +html_use_index = True + +# html_css_files = ["custom.css", ("print.css", {"media": "print"})] +# html_js_files = [] + +html_extra_path = [ + "robots.txt", +] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [ + "_static", +] + + +# -- Options for autodoc ---------------------------------------------------- + +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration +# Automatically extract typehints when specified and place them in +# descriptions of the relevant function/method. +# autodoc_typehints = "description" + +# Don't show class signature with the class' name. +autodoc_class_signature = "separated" + + +# -- Options for sphinx_sitemap to html ----------------------------- + +# Used by sphinx_sitemap to generate a sitemap +html_baseurl = "https://MY_READTHEDOCS_PROJECT_SLUG.readthedocs.io/" +# https://sphinx-sitemap.readthedocs.io/en/latest/advanced-configuration.html#customizing-the-url-scheme +sitemap_url_scheme = "{link}" +sitemap_filename = "sitemap-custom.xml" + +# -- Options for myST markdown conversion to html ----------------------------- + +# For more information see: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html +myst_enable_extensions = [ + "attrs_block", # Support parsing of block attributes. + "attrs_inline", # Support parsing of inline attributes. + "colon_fence", # You can also use ::: delimiters to denote code fences, instead of ```. + "deflist", # Support definition lists. https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#definition-lists + "html_image", # For inline images. See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#html-images + "linkify", # Identify "bare" web URLs and add hyperlinks. + "strikethrough", # See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#syntax-strikethrough + "substitution", # Use Jinja2 for substitutions. https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#substitutions-with-jinja2 +] + +myst_substitutions = {} + +# -- Intersphinx configuration ---------------------------------- + +# This extension can generate automatic links to the documentation of objects +# in other projects. Usage is simple: whenever Sphinx encounters a +# cross-reference that has no matching target in the current documentation set, +# it looks for targets in the documentation sets configured in +# intersphinx_mapping. A reference like :py:class:`zipfile.ZipFile` can then +# linkto the Python documentation for the ZipFile class, without you having to +# specify where it is located exactly. +# +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), + "plone": ("https://6.docs.plone.org/", None), +} + + +# -- GraphViz configuration ---------------------------------- +graphviz_output_format = "svg" + + +# -- Mermaid configuration ---------------------------------- +mermaid_version = "11.2.0" + + +# -- OpenGraph configuration ---------------------------------- +ogp_site_url = "https://MY_READTHEDOCS_PROJECT_SLUG.readthedocs.io/" +ogp_description_length = 200 +ogp_image = "https://MY_READTHEDOCS_PROJECT_SLUG/_static/MY_LOGO.svg" +ogp_site_name = "Collective Addon Documentation" +ogp_type = "website" +ogp_custom_meta_tags = [ + '', +] + + +# -- sphinx.ext.todo ----------------------- +# See http://sphinx-doc.org/ext/todo.html#confval-todo_include_todos +todo_include_todos = True + + +# -- sphinx-notfound-page configuration ---------------------------------- +notfound_urls_prefix = "" +notfound_template = "404.html" + + +# -- sphinx-reredirects configuration ---------------------------------- +# https://documatt.com/sphinx-reredirects/usage.html +redirects = {} + + +# -- sphinx-tippy configuration ---------------------------------- +tippy_anchor_parent_selector = "article.bd-article" +tippy_enable_doitips = False +tippy_enable_wikitips = False +tippy_props = { + "interactive": True, + "placement": "auto-end", +} + + +# -- Options for HTML help output ------------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "Collective AddonDocumentation" + + +# -- Options for LaTeX output ------------------------------------------------- + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]) +latex_documents = [ + ( + "index", + "Collective AddonDocumentation.tex", + "Collective Addon Documentation", + "kitconcept community", + "manual", + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = "_static/logo.svg" + + +# -- Configuration for source_replacements extension ----------------------- +# An extension that allows replacements for code blocks that +# are not supported in `rst_epilog` or other substitutions. +# https://stackoverflow.com/a/56328457/2214933 +def source_replace(app, docname, source): + result = source[0] + for key in app.config.source_replacements: + result = result.replace(key, app.config.source_replacements[key]) + source[0] = result + + +# Dict of replacements. +source_replacements = { + "{SUPPORTED_PYTHON_VERSIONS}": "3.10, 3.11, 3.12, or 3.13", +} + + +# Finally, configure app attributes. +def setup(app): + app.add_config_value("source_replacements", {}, True) + app.connect("source-read", source_replace) + # For sphinx.ext.ifconfig + app.add_config_value("context", "documentation", "env") diff --git a/docs/docs/glossary.md b/docs/docs/glossary.md new file mode 100644 index 0000000..0076988 --- /dev/null +++ b/docs/docs/glossary.md @@ -0,0 +1,57 @@ +--- +myst: + html_meta: + "description": "Terms and definitions used throughout the Plone Sphinx Theme documentation." + "property=og:description": "Terms and definitions used throughout the Plone Sphinx Theme documentation." + "property=og:title": "Glossary" + "keywords": "Plone, documentation, glossary, term, definition" +--- + +This glossary provides example terms and definitions relevant to **Collective Addon**. +A new add-on for Plone that includes both backend and Volto frontend + +```{note} +This is an example glossary demonstrating MyST Markdownโ€™s `{glossary}` directive. You can adapt it for your projectโ€™s appendix by editing or replacing these entries with your own terms and definitions. +``` + +(glossary-label)= + +# Glossary + +```{glossary} +:sorted: true + +Plone + [Plone](https://plone.org/) is an open-source content management system that is used to create, edit, and manage digital content, like websites, intranets and custom solutions. + It comes with over 20 years of growth, optimisations, and refinements. + The result is a system trusted by governments, universities, businesses, and other organisations all over the world. + +add-on + An add-on in Plone extends its functionality. + It is code that is released as a package to make it easier to install. + + In Volto, an add-on is a JavaScript package. + + In Plone core, an add-on is a Python package. + + - [Plone core add-ons](https://github.com/collective/awesome-plone#readme) + - [Volto add-ons](https://github.com/collective/awesome-volto#readme) + - [Add-ons tagged with the trove classifier `Framework :: Plone` on PyPI](https://pypi.org/search/?c=Framework+%3A%3A+Plone) + +Plone Sphinx Theme +plone-sphinx-theme + [Plone Sphinx Theme](https://plone-sphinx-theme.readthedocs.io/) is a Sphinx theme for [Plone 6 Documentation](https://6.docs.plone.org/), [Plone Conference Training](https://training.plone.org/), and documentation of various Plone packages. + This scaffold uses Plone Sphinx Theme. + +Markedly Structured Text +MyST + [Markedly Structured Text (MyST)](https://myst-parser.readthedocs.io/en/latest/) is a rich and extensible flavor of Markdown, for authoring Plone Documentation. + The sample documentation in this scaffold is written in MyST. + +Sphinx + [Sphinx](https://www.sphinx-doc.org/en/master/) is a tool that makes it easy to create intelligent and beautiful documentation. + It was originally created for Python documentation, and it has excellent facilities for the documentation of software projects in a range of languages. + It can generate multiple output formats, including HTML and PDF, from a single source. + This scaffold uses Sphinx to generate documentation in HTML format. + +``` diff --git a/docs/docs/how-to-guides/index.md b/docs/docs/how-to-guides/index.md new file mode 100644 index 0000000..06163ad --- /dev/null +++ b/docs/docs/how-to-guides/index.md @@ -0,0 +1,30 @@ +--- +myst: + html_meta: + "description": "Collective Addon how-to guides" + "property=og:description": "Collective Addon how-to guides" + "property=og:title": "Collective Addon how-to guides" + "keywords": "Plone, Collective Addon, how-to, guides" +--- + +# How-to guides + +This part of the documentation contains how-to guides, including installation and usage. + +> How-to guides are directions that guide the reader through a problem or towards a result. +> How-to guides are goal-oriented. + +```{seealso} +https://diataxis.fr/how-to-guides/ +``` + + +## Authors + +- {doc}`plone:contributing/documentation/myst-reference` +- {doc}`plone:contributing/documentation/authors` + + +## Designers + +- [Contribute to Plone Sphinx Theme](https://plone-sphinx-theme.readthedocs.io/guides/contribute.html) diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 0000000..19c98f9 --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,60 @@ +--- +myst: + html_meta: + "description": "A new add-on for Plone that includes both backend and Volto frontend" + "property=og:description": "A new add-on for Plone that includes both backend and Volto frontend" + "property=og:title": "Collective Addon" + "keywords": "Collective Addon, documentation, A new add-on for Plone that includes both backend and Volto frontend" +--- + +# Collective Addon + +Welcome to the documentation for Collective Addon! +A new add-on for Plone that includes both backend and Volto frontend + +This scaffold provides a ready-to-use environment for creating comprehensive documentation for {term}`Plone` projects, based on {term}`Plone Sphinx Theme`. + +Built with Markedly Structured Text ({term}`MyST`), this environment supports rich formatting, directives, and extensions tailored for technical documentation. + +It's structured following the [Diรกtaxis](https://diataxis.fr/) documentation framework. + +```{toctree} +:caption: How to guides +:maxdepth: 2 +:hidden: true + +how-to-guides/index +``` + +```{toctree} +:caption: Reference +:maxdepth: 2 +:hidden: true + +reference/index +``` + +```{toctree} +:caption: Tutorials +:maxdepth: 2 +:hidden: true + +tutorials/index +``` + +```{toctree} +:caption: Concepts +:maxdepth: 2 +:hidden: true + +concepts/index +``` + +```{toctree} +:caption: Appendices +:maxdepth: 2 +:hidden: true + +glossary +genindex +``` diff --git a/docs/docs/reference/index.md b/docs/docs/reference/index.md new file mode 100644 index 0000000..54da61e --- /dev/null +++ b/docs/docs/reference/index.md @@ -0,0 +1,23 @@ +--- +myst: + html_meta: + "description": "Collective Addon Reference" + "property=og:description": "Collective Addon Reference" + "property=og:title": "Collective Addon Reference" + "keywords": "Plone, _Collective Addon,_ reference" +--- + +# Reference + +This part of the documentation contains reference material, including APIs, configuration values, and environment variables. + +> Reference guides are technical descriptions of the machinery and how to operate it. +> Reference material is information-oriented. + +```{seealso} +https://diataxis.fr/reference/ +``` + +## Configuration + +- {doc}`plone:contributing/documentation/themes-and-extensions` diff --git a/docs/docs/robots.txt b/docs/docs/robots.txt new file mode 100644 index 0000000..b68dd24 --- /dev/null +++ b/docs/docs/robots.txt @@ -0,0 +1,8 @@ +# Disallow all user agents from the following directories and files +User-agent: * +Disallow: /_sources/ +Disallow: /.doctrees/ +Disallow: /*.txt$ +Disallow: /.buildinfo$ +Disallow: /objects.inv$ +Sitemap: https://MY_READTHEDOCS_PROJECT_SLUG.readthedocs.io/sitemap-custom.xml diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md new file mode 100644 index 0000000..35f6b86 --- /dev/null +++ b/docs/docs/tutorials/index.md @@ -0,0 +1,19 @@ +--- +myst: + html_meta: + "description": "Collective Addon Tutorials" + "property=og:description": "Collective Addon Tutorials" + "property=og:title": "Collective Addon Tutorials" + "keywords": "Plone, Collective Addon, tutorials" +--- + +# Tutorials + +This part of the documentation contains tutorials. + +> A tutorial is an experience that takes place under the guidance of a tutor. +> A tutorial is always learning-oriented. + +```{seealso} +https://diataxis.fr/tutorials/ +``` diff --git a/docs/pyproject.toml b/docs/pyproject.toml new file mode 100644 index 0000000..40dacc6 --- /dev/null +++ b/docs/pyproject.toml @@ -0,0 +1,61 @@ +[project] +name = "Collective_Addon_Documentation" +description = "A new add-on for Plone that includes both backend and Volto frontend" +keywords = [ + "Collective Addon", + "documentation", + "Plone", + "Diรกtaxis", +] +version="1.0.0" +readme = "README.md" +requires-python = ">=3.10,<3.14" +license = { file = "LICENSE" } +maintainers = [ + { name = "Plone Foundation", email = "info@kitconcept.com" }, +] +authors = [ + { name = "Plone Foundation", email = "info@kitconcept.com" }, +] +classifiers = [ + "Development Status :: 1 - Planning", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] + +[dependency-groups] +dev = [ + "graphviz", + "linkify-it-py", + "myst-parser", + "plone-sphinx-theme>=1.4.1", # Use the latest stable version or a minimum + "setuptools", # required by sphinxcontrib.httpexample, remove when it migrates to importlib. See https://github.com/collective/sphinxcontrib-httpexample/issues/106 + "sphinx-autobuild", + "sphinx-copybutton", + "sphinx-design", + "sphinx-examples", + "sphinx-notfound-page", + "sphinx-reredirects", + "sphinx-sitemap", + "sphinx-tippy", + "sphinxcontrib-httpdomain", + "sphinxcontrib-httpexample", + "sphinxcontrib-mermaid", + "sphinxcontrib-video", + "sphinxcontrib-youtube", + "sphinxext-opengraph", + "vale", +] + +[project.urls] +Repository = "https://github.com/kitconcept/collectiveaddon" +Documentation = "https://MY_READTHEDOCS_PROJECT_SLUG.readthedocs.io/" + +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/docs/styles/config/vocabularies/Base/accept.txt b/docs/styles/config/vocabularies/Base/accept.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/styles/config/vocabularies/Base/reject.txt b/docs/styles/config/vocabularies/Base/reject.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/styles/config/vocabularies/Plone/accept.txt b/docs/styles/config/vocabularies/Plone/accept.txt new file mode 100644 index 0000000..cde1ced --- /dev/null +++ b/docs/styles/config/vocabularies/Plone/accept.txt @@ -0,0 +1,51 @@ +`plone.api` +`plone.restapi` +`plone.volto` +accessor +APIs +[Aa]sync +[Bb]ackend +backport(ed|ing) +Barceloneta +[Bb]oolean +bugfix +buildout +cacheable +CommonJS +doctest +folderish +fieldset +getter +JavaScript +[Jj]enkins +jQuery +libxslt +Mockup +npm +nvm +Pastanaga +PLIP(s) +Plone +pluggab(le|ility) +[Pp]ortlet +prerendered +programatically +[Qq]uerystring +Razzle +[Rr]enderer +RichText +Sass +Schuko +subfolder +[Tt]owncrier +transpile[dr]{0,1} +[Uu]ncomment +[Uu]nhide +unregister +UUID +uv +validator +[Vv]iewlet +Volto +Vue +Zope diff --git a/docs/styles/config/vocabularies/Plone/reject.txt b/docs/styles/config/vocabularies/Plone/reject.txt new file mode 100644 index 0000000..81510d5 --- /dev/null +++ b/docs/styles/config/vocabularies/Plone/reject.txt @@ -0,0 +1,3 @@ +[^.]js +NodeJS +[Pp]re-requisite diff --git a/.eslintrc.js b/frontend/.eslintrc.js similarity index 100% rename from .eslintrc.js rename to frontend/.eslintrc.js diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..e4c3d4b --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,16 @@ +.*project +.settings/ +.vscode +*~ +acceptance/cypress/videos/ +acceptance/node_modules +.storybook-build +build +/core +node_modules +results +yarn.lock +/public + +/seven +!/seven/packages/plate diff --git a/.npmignore b/frontend/.npmignore similarity index 100% rename from .npmignore rename to frontend/.npmignore diff --git a/.npmrc b/frontend/.npmrc similarity index 100% rename from .npmrc rename to frontend/.npmrc diff --git a/.pnpmfile.cjs b/frontend/.pnpmfile.cjs similarity index 100% rename from .pnpmfile.cjs rename to frontend/.pnpmfile.cjs diff --git a/.prettierignore b/frontend/.prettierignore similarity index 100% rename from .prettierignore rename to frontend/.prettierignore diff --git a/.prettierrc b/frontend/.prettierrc similarity index 100% rename from .prettierrc rename to frontend/.prettierrc diff --git a/.stylelintrc b/frontend/.stylelintrc similarity index 100% rename from .stylelintrc rename to frontend/.stylelintrc diff --git a/packages/volto-plate/CHANGELOG.md b/frontend/CHANGELOG.md similarity index 100% rename from packages/volto-plate/CHANGELOG.md rename to frontend/CHANGELOG.md diff --git a/frontend/Makefile b/frontend/Makefile new file mode 100644 index 0000000..9f6579f --- /dev/null +++ b/frontend/Makefile @@ -0,0 +1,144 @@ +### 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 + +PLONE_VERSION=6 +DOCKER_IMAGE=plone/server-dev:${PLONE_VERSION} +DOCKER_IMAGE_ACCEPTANCE=plone/server-acceptance:${PLONE_VERSION} +API_PATH ?= http://127.0.0.1:55001/plone + +ADDON_NAME='@kitconcept/volto-plate' + +.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: clean +clean: ## Clean environment + @echo "$(RED)==> Cleaning Volto core and node_modules$(RESET)" + rm -rf core node_modules + +.PHONY: install +install: ## Installs the add-on in a development environment + @echo "$(GREEN)Install$(RESET)" + pnpm dlx mrs-developer missdev --no-config --fetch-https + pnpm i + make 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: $(shell find core/packages/registry/src -type f) + pnpm --filter @plone/registry build + +core/packages/components/dist: $(shell find core/packages/components/src -type f) + 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: ci-test +ci-test: ## Run unit tests in CI + # Unit Tests need the i18n to be built + VOLTOCONFIG=$(pwd)/volto.config.js pnpm --filter @plone/volto i18n + CI=1 pnpm --filter @kitconcept/volto-plate exec vitest --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 --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 storybook-build -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=$(API_PATH) 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}' --env API_PATH="$(API_PATH)" diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..de63c27 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,199 @@ + + + kitconcept, GmbH + + +# Volto Plate.js support
(@kitconcept/volto-plate) + +
+ +An add-on that adds a [Plate.js](https://www.platejs.org/) support in Volto. + +[![npm](https://img.shields.io/npm/v/@kitconcept/volto-plate)](https://www.npmjs.com/package/@kitconcept/volto-plate) +[![Code analysis checks](https://github.com/kitconcept/volto-plate/actions/workflows/code.yml/badge.svg)](https://github.com/kitconcept/volto-plate/actions/workflows/code.yml) +[![Unit tests](https://github.com/kitconcept/volto-plate/actions/workflows/unit.yml/badge.svg)](https://github.com/kitconcept/volto-plate/actions/workflows/unit.yml) +[![Acceptance tests](https://github.com/kitconcept/volto-plate/actions/workflows/acceptance.yml/badge.svg)](https://github.com/kitconcept/volto-plate/actions/workflows/acceptance.yml) +
+ +> [!WARNING] +> This package is in early development and should be used with caution in production environments. +> It is subject to breaking changes and incomplete features. +> Please test thoroughly and report any issues you encounter. + +## Features + +This package provides support for the [Plate.js](https://www.platejs.org/) rich text editor in Volto. + +It provides "on-the-fly" conversion between Slate.js and Plate.js data models, allowing seamless integration of Plate.js-based blocks in Volto. +The conversion is only one-way: from Slate.js to Plate.js when loading data into the editor. Once in Plate.js, the data remains in Plate.js format. +In the future, we may considering adding a backend package providing scripts for batch converting back and forth between the two formats if needed. + +Plate.js uses the concept of "blocks" that collide with the Volto ones. From now on, we will refer to Volto blocks as "Volto blocks" and Plate.js blocks as "Plate blocks" to avoid confusion. + +From the user perspective, it provides: +- A total replacement of slate (text) default block using Plate.js (by default) using block model 3. +- A new `Plate` block type, which uses Plate.js as the rich text editor. +- Basic features using the inline floating toolbar, extending the default Volto one with experimental additions. +- Slash commands to insert both Plate blocks and Volto blocks. +- Split block in here slash command. +- Add new block slash command. +- Ability to insert a Volto image block as a Plate image block (inline). +- Ability to insert existing "normal" Volto blocks via the (+) button inside the Plate block. + +## Behavior notes + +It keeps in place the default Volto rich text block (slate-based) assumptions, so you can have multiple plate.js-based blocks in the same page. +However, pressing `ENTER` won't create a new block, instead, it will create a new paragraph inside the same block, as is standard behavior in rich text editors. +You can create new blocks using the block chooser as usual, and using the `/` slash command inside the `plate.js`-based block. + +## Developer features + +From the developer perspective, it provides: +- A Volto block adapter to reuse existing Volto blocks inside Plate-based editors (e.g. rich text). +- A Plate plugin to reuse the Volto Image block inside Plate-based editors. +- A Plate-based Text block implementation for Volto, replacing the default Slate-based one. + +## Compatibility + +This package is only compatible with Volto 19 and later versions. + +## Quick Start ๐Ÿ + +### Prerequisites โœ… + +- An [operating system](https://6.docs.plone.org/install/create-project-cookieplone.html#prerequisites-for-installation) that runs all the requirements mentioned. +- [uv](https://6.docs.plone.org/install/create-project-cookieplone.html#uv) +- [nvm](https://6.docs.plone.org/install/create-project-cookieplone.html#nvm) +- [Node.js and pnpm](https://6.docs.plone.org/install/create-project.html#node-js) 24 +- [Make](https://6.docs.plone.org/install/create-project-cookieplone.html#make) +- [Git](https://6.docs.plone.org/install/create-project-cookieplone.html#git) +- [Docker](https://docs.docker.com/get-started/get-docker/) (optional) + + +### Installation ๐Ÿ”ง + +1. Clone this repository, then change your working directory. + + ```shell + git clone git@github.com:kitconcept/collective-addon.git + cd collective-addon + ``` + +2. Install this code base. + + ```shell + make install + ``` + + +### Fire Up the Servers ๐Ÿ”ฅ + +1. Create a new Plone site on your first run. + + ```shell + make backend-create-site + ``` + +2. Start the backend at http://localhost:8080/. + + ```shell + make backend-start + ``` + +3. In a new shell session, start the frontend at http://localhost:3000/. + + ```shell + make frontend-start + ``` + +Voila! Your Plone site should be live and kicking! ๐ŸŽ‰ + +### Local Stack Deployment ๐Ÿ“ฆ + +Deploy a local Docker Compose environment that includes the following. + +- Docker images for Backend and Frontend ๐Ÿ–ผ๏ธ +- A stack with a Traefik router and a PostgreSQL database ๐Ÿ—ƒ๏ธ +- Accessible at [http://collective-addon.localhost](http://collective-addon.localhost) ๐ŸŒ + +Run the following commands in a shell session. + +```shell +make stack-create-site +make stack-start +``` + +And... you're all set! Your Plone site is up and running locally! ๐Ÿš€ + +## Project structure ๐Ÿ—๏ธ + +This monorepo consists of the following distinct sections: + +- **backend**: Houses the API and Plone installation, utilizing pip instead of buildout, and includes a policy package named kitconcept.plate. +- **frontend**: Contains the React (Volto) package. +- **devops**: Encompasses Docker stack, Ansible playbooks, and cache settings. +- **docs**: Scaffold for writing documentation for your project. + +### Why this structure? ๐Ÿค” + +- All necessary codebases to run the site are contained within the repository (excluding existing add-ons for Plone and React). +- Specific GitHub Workflows are triggered based on changes in each codebase (refer to .github/workflows). +- Simplifies the creation of Docker images for each codebase. +- Demonstrates Plone installation/setup without buildout. + +## Code quality assurance ๐Ÿง + +To check your code against quality standards, run the following shell command. + +```shell +make check +``` + +### Format the codebase + +To format and rewrite the code base, ensuring it adheres to quality standards, run the following shell command. + +```shell +make format +``` + +| Section | Tool | Description | Configuration | +| --- | --- | --- | --- | +| backend | Ruff | Python code formatting, imports sorting | [`backend/pyproject.toml`](./backend/pyproject.toml) | +| backend | `zpretty` | XML and ZCML formatting | -- | +| frontend | ESLint | Fixes most common frontend issues | [`frontend/.eslintrc.js`](.frontend/.eslintrc.js) | +| frontend | prettier | Format JS and Typescript code | [`frontend/.prettierrc`](.frontend/.prettierrc) | +| frontend | Stylelint | Format Styles (css, less, sass) | [`frontend/.stylelintrc`](.frontend/.stylelintrc) | + +Formatters can also be run within the `backend` or `frontend` folders. + +### Linting the codebase +or `lint`: + + ```shell +make lint +``` + +| Section | Tool | Description | Configuration | +| --- | --- | --- | --- | +| backend | Ruff | Checks code formatting, imports sorting | [`backend/pyproject.toml`](./backend/pyproject.toml) | +| backend | Pyroma | Checks Python package metadata | -- | +| backend | check-python-versions | Checks Python version information | -- | +| backend | `zpretty` | Checks XML and ZCML formatting | -- | +| frontend | ESLint | Checks JS / Typescript lint | [`frontend/.eslintrc.js`](.frontend/.eslintrc.js) | +| frontend | prettier | Check JS / Typescript formatting | [`frontend/.prettierrc`](.frontend/.prettierrc) | +| frontend | Stylelint | Check Styles (css, less, sass) formatting | [`frontend/.stylelintrc`](.frontend/.stylelintrc) | + +Linters can be run individually within the `backend` or `frontend` folders. + +## Internationalization ๐ŸŒ + +Generate translation files for Plone and Volto with ease: + +```shell +make i18n +``` + +## Credits and acknowledgements ๐Ÿ™ + +Generated using [Cookieplone (0.9.10)](https://github.com/plone/cookieplone) and [cookieplone-templates (2c54630)](https://github.com/plone/cookieplone-templates/commit/2c5463046f43a87e36d11a7edc2b4176b2d593aa) on 2026-02-13 10:21:42.975825. A special thanks to all contributors and supporters! diff --git a/acceptance/tests/content.ts b/frontend/acceptance/tests/content.ts similarity index 100% rename from acceptance/tests/content.ts rename to frontend/acceptance/tests/content.ts diff --git a/acceptance/tests/edit.test.ts b/frontend/acceptance/tests/edit.test.ts similarity index 100% rename from acceptance/tests/edit.test.ts rename to frontend/acceptance/tests/edit.test.ts diff --git a/acceptance/tests/login.ts b/frontend/acceptance/tests/login.ts similarity index 100% rename from acceptance/tests/login.ts rename to frontend/acceptance/tests/login.ts diff --git a/acceptance/tests/plate.ts b/frontend/acceptance/tests/plate.ts similarity index 100% rename from acceptance/tests/plate.ts rename to frontend/acceptance/tests/plate.ts diff --git a/acceptance/tests/reset-fixture.ts b/frontend/acceptance/tests/reset-fixture.ts similarity index 100% rename from acceptance/tests/reset-fixture.ts rename to frontend/acceptance/tests/reset-fixture.ts diff --git a/acceptance/tests/test.ts b/frontend/acceptance/tests/test.ts similarity index 100% rename from acceptance/tests/test.ts rename to frontend/acceptance/tests/test.ts diff --git a/mrs.developer.json b/frontend/mrs.developer.json similarity index 100% rename from mrs.developer.json rename to frontend/mrs.developer.json diff --git a/package.json b/frontend/package.json similarity index 100% rename from package.json rename to frontend/package.json diff --git a/packages/volto-plate/.gitignore b/frontend/packages/volto-plate/.gitignore similarity index 100% rename from packages/volto-plate/.gitignore rename to frontend/packages/volto-plate/.gitignore diff --git a/packages/volto-plate/.release-it.json b/frontend/packages/volto-plate/.release-it.json similarity index 100% rename from packages/volto-plate/.release-it.json rename to frontend/packages/volto-plate/.release-it.json diff --git a/frontend/packages/volto-plate/CHANGELOG.md b/frontend/packages/volto-plate/CHANGELOG.md new file mode 100644 index 0000000..4430090 --- /dev/null +++ b/frontend/packages/volto-plate/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + + + + + +## 1.0.0-alpha.0 (2026-02-03) diff --git a/packages/volto-plate/babel.config.js b/frontend/packages/volto-plate/babel.config.js similarity index 100% rename from packages/volto-plate/babel.config.js rename to frontend/packages/volto-plate/babel.config.js diff --git a/packages/volto-plate/locales/de/LC_MESSAGES/volto.po b/frontend/packages/volto-plate/locales/de/LC_MESSAGES/volto.po similarity index 100% rename from packages/volto-plate/locales/de/LC_MESSAGES/volto.po rename to frontend/packages/volto-plate/locales/de/LC_MESSAGES/volto.po diff --git a/packages/volto-plate/locales/en/LC_MESSAGES/volto.po b/frontend/packages/volto-plate/locales/en/LC_MESSAGES/volto.po similarity index 100% rename from packages/volto-plate/locales/en/LC_MESSAGES/volto.po rename to frontend/packages/volto-plate/locales/en/LC_MESSAGES/volto.po diff --git a/packages/volto-plate/locales/es/LC_MESSAGES/volto.po b/frontend/packages/volto-plate/locales/es/LC_MESSAGES/volto.po similarity index 100% rename from packages/volto-plate/locales/es/LC_MESSAGES/volto.po rename to frontend/packages/volto-plate/locales/es/LC_MESSAGES/volto.po diff --git a/packages/volto-plate/locales/pt_BR/LC_MESSAGES/volto.po b/frontend/packages/volto-plate/locales/pt_BR/LC_MESSAGES/volto.po similarity index 100% rename from packages/volto-plate/locales/pt_BR/LC_MESSAGES/volto.po rename to frontend/packages/volto-plate/locales/pt_BR/LC_MESSAGES/volto.po diff --git a/packages/volto-plate/locales/volto.pot b/frontend/packages/volto-plate/locales/volto.pot similarity index 100% rename from packages/volto-plate/locales/volto.pot rename to frontend/packages/volto-plate/locales/volto.pot diff --git a/frontend/packages/volto-plate/news/.gitkeep b/frontend/packages/volto-plate/news/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/volto-plate/package.json b/frontend/packages/volto-plate/package.json similarity index 100% rename from packages/volto-plate/package.json rename to frontend/packages/volto-plate/package.json diff --git a/frontend/packages/volto-plate/public/.gitkeep b/frontend/packages/volto-plate/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/packages/volto-plate/src/components/.gitkeep b/frontend/packages/volto-plate/src/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/volto-plate/src/components/blocks/Text/TextBlockEdit.tsx b/frontend/packages/volto-plate/src/components/blocks/Text/TextBlockEdit.tsx similarity index 100% rename from packages/volto-plate/src/components/blocks/Text/TextBlockEdit.tsx rename to frontend/packages/volto-plate/src/components/blocks/Text/TextBlockEdit.tsx diff --git a/packages/volto-plate/src/components/blocks/Text/TextBlockView.tsx b/frontend/packages/volto-plate/src/components/blocks/Text/TextBlockView.tsx similarity index 100% rename from packages/volto-plate/src/components/blocks/Text/TextBlockView.tsx rename to frontend/packages/volto-plate/src/components/blocks/Text/TextBlockView.tsx diff --git a/packages/volto-plate/src/components/blocks/Text/index.ts b/frontend/packages/volto-plate/src/components/blocks/Text/index.ts similarity index 100% rename from packages/volto-plate/src/components/blocks/Text/index.ts rename to frontend/packages/volto-plate/src/components/blocks/Text/index.ts diff --git a/packages/volto-plate/src/config/blocks.ts b/frontend/packages/volto-plate/src/config/blocks.ts similarity index 79% rename from packages/volto-plate/src/config/blocks.ts rename to frontend/packages/volto-plate/src/config/blocks.ts index 6484ce6..65810c9 100644 --- a/packages/volto-plate/src/config/blocks.ts +++ b/frontend/packages/volto-plate/src/config/blocks.ts @@ -2,11 +2,6 @@ import type { ConfigType } from '@plone/registry'; import TextBlockInfo from '../components/blocks/Text'; export default function install(config: ConfigType) { - config.blocks.blocksConfig.slate = { - ...config.blocks.blocksConfig.slate, - ...TextBlockInfo, - }; - config.blocks.blocksConfig.plate = { ...config.blocks.blocksConfig.slate, ...TextBlockInfo, @@ -15,5 +10,16 @@ export default function install(config: ConfigType) { }; delete config.blocks.blocksConfig.plate.blockModel; + config.blocks.initialBlocks.MeetingNotes = ['plate']; + + return config; +} + +export function asDefault(config: ConfigType) { + config.blocks.blocksConfig.slate = { + ...config.blocks.blocksConfig.slate, + ...TextBlockInfo, + }; + return config; } diff --git a/packages/volto-plate/src/config/settings.ts b/frontend/packages/volto-plate/src/config/settings.ts similarity index 100% rename from packages/volto-plate/src/config/settings.ts rename to frontend/packages/volto-plate/src/config/settings.ts diff --git a/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-base-kit.tsx b/frontend/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-base-kit.tsx similarity index 100% rename from packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-base-kit.tsx rename to frontend/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-base-kit.tsx diff --git a/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-kit.tsx b/frontend/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-kit.tsx similarity index 100% rename from packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-kit.tsx rename to frontend/packages/volto-plate/src/customizations/@plone/plate/components/editor/plugins/media-kit.tsx diff --git a/packages/volto-plate/src/hooks/use-stable-plate-value.ts b/frontend/packages/volto-plate/src/hooks/use-stable-plate-value.ts similarity index 100% rename from packages/volto-plate/src/hooks/use-stable-plate-value.ts rename to frontend/packages/volto-plate/src/hooks/use-stable-plate-value.ts diff --git a/packages/volto-plate/src/index.ts b/frontend/packages/volto-plate/src/index.ts similarity index 100% rename from packages/volto-plate/src/index.ts rename to frontend/packages/volto-plate/src/index.ts diff --git a/packages/volto-plate/src/plate/context/BlocksApiContext.tsx b/frontend/packages/volto-plate/src/plate/context/BlocksApiContext.tsx similarity index 100% rename from packages/volto-plate/src/plate/context/BlocksApiContext.tsx rename to frontend/packages/volto-plate/src/plate/context/BlocksApiContext.tsx diff --git a/packages/volto-plate/src/plate/plugins/volto-block-adapter.tsx b/frontend/packages/volto-plate/src/plate/plugins/volto-block-adapter.tsx similarity index 100% rename from packages/volto-plate/src/plate/plugins/volto-block-adapter.tsx rename to frontend/packages/volto-plate/src/plate/plugins/volto-block-adapter.tsx diff --git a/packages/volto-plate/src/plate/plugins/volto-image-block.tsx b/frontend/packages/volto-plate/src/plate/plugins/volto-image-block.tsx similarity index 100% rename from packages/volto-plate/src/plate/plugins/volto-image-block.tsx rename to frontend/packages/volto-plate/src/plate/plugins/volto-image-block.tsx diff --git a/packages/volto-plate/src/plate/plugins/volto-media-kit.tsx b/frontend/packages/volto-plate/src/plate/plugins/volto-media-kit.tsx similarity index 100% rename from packages/volto-plate/src/plate/plugins/volto-media-kit.tsx rename to frontend/packages/volto-plate/src/plate/plugins/volto-media-kit.tsx diff --git a/packages/volto-plate/src/stories/SlateAndPlate.stories.tsx b/frontend/packages/volto-plate/src/stories/SlateAndPlate.stories.tsx similarity index 100% rename from packages/volto-plate/src/stories/SlateAndPlate.stories.tsx rename to frontend/packages/volto-plate/src/stories/SlateAndPlate.stories.tsx diff --git a/packages/volto-plate/src/stories/stories.css b/frontend/packages/volto-plate/src/stories/stories.css similarity index 100% rename from packages/volto-plate/src/stories/stories.css rename to frontend/packages/volto-plate/src/stories/stories.css diff --git a/packages/volto-plate/src/theme/_main.scss b/frontend/packages/volto-plate/src/theme/_main.scss similarity index 100% rename from packages/volto-plate/src/theme/_main.scss rename to frontend/packages/volto-plate/src/theme/_main.scss diff --git a/packages/volto-plate/src/theme/_variables.scss b/frontend/packages/volto-plate/src/theme/_variables.scss similarity index 100% rename from packages/volto-plate/src/theme/_variables.scss rename to frontend/packages/volto-plate/src/theme/_variables.scss diff --git a/packages/volto-plate/towncrier.toml b/frontend/packages/volto-plate/towncrier.toml similarity index 100% rename from packages/volto-plate/towncrier.toml rename to frontend/packages/volto-plate/towncrier.toml diff --git a/packages/volto-plate/tsconfig.json b/frontend/packages/volto-plate/tsconfig.json similarity index 100% rename from packages/volto-plate/tsconfig.json rename to frontend/packages/volto-plate/tsconfig.json diff --git a/packages/volto-plate/volto-block-adapter.md b/frontend/packages/volto-plate/volto-block-adapter.md similarity index 100% rename from packages/volto-plate/volto-block-adapter.md rename to frontend/packages/volto-plate/volto-block-adapter.md diff --git a/playwright.config.ts b/frontend/playwright.config.ts similarity index 100% rename from playwright.config.ts rename to frontend/playwright.config.ts diff --git a/pnpm-lock.yaml b/frontend/pnpm-lock.yaml similarity index 100% rename from pnpm-lock.yaml rename to frontend/pnpm-lock.yaml diff --git a/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml similarity index 100% rename from pnpm-workspace.yaml rename to frontend/pnpm-workspace.yaml diff --git a/frontend/volto.config.js b/frontend/volto.config.js new file mode 100644 index 0000000..4e945d8 --- /dev/null +++ b/frontend/volto.config.js @@ -0,0 +1,10 @@ +const addons = [ + '@kitconcept/volto-light-theme', + '@kitconcept/volto-plate:asDefault', +]; +const theme = '@kitconcept/volto-light-theme'; + +module.exports = { + addons, + theme, +}; diff --git a/news/.changelog_template.jinja b/news/.changelog_template.jinja new file mode 100644 index 0000000..b35bff3 --- /dev/null +++ b/news/.changelog_template.jinja @@ -0,0 +1,15 @@ +{% if sections[""] %} +{% for category, val in definitions.items() if category in sections[""] %} + +### {{ definitions[category]['name'] }} + +{% for text, values in sections[""][category].items() %} +- {{ text }} {{ values|join(', ') }} +{% endfor %} + +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} \ No newline at end of file diff --git a/repository.toml b/repository.toml new file mode 100644 index 0000000..6345a5c --- /dev/null +++ b/repository.toml @@ -0,0 +1,32 @@ +[repository] +name = "volto-plate" +changelog = "CHANGELOG.md" +version = "version.txt" +version_format = "semver" +container_images_prefix = "ghcr.io/kitconcept/volto-plate" +compose = ["docker-compose.yml"] + +[repository.towncrier] +section = "Project" +settings = "towncrier.toml" + +[backend.package] +name = "kitconcept.plate" +path = "backend" +changelog = "backend/CHANGELOG.md" +towncrier_settings = "backend/pyproject.toml" +base_package = "Products.CMFPlone" +publish = true + +[frontend.package] +name = "volto-plate" +path = "frontend/packages/volto-plate" +changelog = "frontend/packages/volto-plate/CHANGELOG.md" +towncrier_settings = "frontend/packages/volto-plate/towncrier.toml" +base_package = "@plone/volto" +publish = true + +[cookieplone] +template = "monorepo_addon" +template_version = "2c5463046f43a87e36d11a7edc2b4176b2d593aa" +generated_date = "2026-02-13 09:23:34" diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 0000000..37b3270 --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,33 @@ +[tool.towncrier] +filename = "CHANGELOG.md" +directory = "news/" +title_format = "## {version} ({project_date})" +underlines = ["", "", ""] +template = "./news/.changelog_template.jinja" +start_string = "\n" +issue_format = "[#{issue}](https://github.com/kitconcept/collective-addon/pull/{issue})" + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking" +showcontent = true + +[[tool.towncrier.type]] +directory = "feature" +name = "Feature" +showcontent = true + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Bugfix" +showcontent = true + +[[tool.towncrier.type]] +directory = "internal" +name = "Internal" +showcontent = true + +[[tool.towncrier.type]] +directory = "documentation" +name = "Documentation" +showcontent = true \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..41432f0 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.0a0 diff --git a/volto.config.js b/volto.config.js deleted file mode 100644 index 0a43002..0000000 --- a/volto.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const addons = ['@kitconcept/volto-light-theme', '@kitconcept/volto-plate']; -const theme = '@kitconcept/volto-light-theme'; - -module.exports = { - addons, - theme, -};