collective.fragmentsblock 1.0.0a2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- collective_fragmentsblock-1.0.0a2/.copier-answers.yml +14 -0
- collective_fragmentsblock-1.0.0a2/.editorconfig +21 -0
- collective_fragmentsblock-1.0.0a2/.github/workflows/ci.yml +114 -0
- collective_fragmentsblock-1.0.0a2/.gitignore +97 -0
- collective_fragmentsblock-1.0.0a2/.pre-commit-config.yaml +14 -0
- collective_fragmentsblock-1.0.0a2/CHANGELOG.md +73 -0
- collective_fragmentsblock-1.0.0a2/PKG-INFO +194 -0
- collective_fragmentsblock-1.0.0a2/README.md +160 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/.nvmrc +1 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/build-plugins/scope-wrap.ts +106 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/package.json +40 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/pnpm-lock.yaml +1666 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/pnpm-workspace.yaml +4 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/FragmentBlockEdit.tsx +39 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/FragmentBlockView.tsx +33 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/FragmentIcon.tsx +22 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/fragments.ts +192 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/index.tsx +55 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/schema.ts +102 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/styles.css +21 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/src/types.ts +17 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/test/fragment-block.test.tsx +361 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/test/stubs/plone-helpers.ts +20 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/test/stubs/plone-registry.ts +21 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/tsconfig.json +12 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/vite.config.ts +88 -0
- collective_fragmentsblock-1.0.0a2/bundle-src/vitest.config.ts +21 -0
- collective_fragmentsblock-1.0.0a2/docs/adr/0001-fragments-are-registered-static-markup.md +115 -0
- collective_fragmentsblock-1.0.0a2/docs/adr/0002-the-editor-fetches-the-fragment-list-from-the-server.md +83 -0
- collective_fragmentsblock-1.0.0a2/pyproject.toml +127 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/configure.zcml +8 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/services/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/services/configure.zcml +8 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/services/fragments/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/services/fragments/configure.zcml +13 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/api/services/fragments/get.py +19 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/configure.zcml +61 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/fragments.py +197 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/i18n.py +6 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/interfaces.py +35 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/README.rst +37 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/__init__.py +0 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/collective.fragmentsblock.pot +16 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/en/LC_MESSAGES/collective.fragmentsblock.po +15 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/update.py +68 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/locales/update.sh +12 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/meta.zcml +17 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/permissions.zcml +11 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/default/browserlayer.xml +7 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/default/catalog.xml +5 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/default/metadata.xml +8 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/default/registry.xml +20 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/default/rolemap.xml +7 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/uninstall/browserlayer.xml +7 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/uninstall/metadata.xml +4 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/profiles/uninstall/registry.xml +11 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/setuphandlers.py +22 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/static/fragment-block.css +16 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/static/fragment-block.js +243 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/static/fragment-block.js.map +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/testing.py +71 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/__init__.py +0 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/conftest.py +16 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/fixture.zcml +8 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/fragments/contact-box.html +4 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/fragments/not an id.html +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/fragments/seal.html +4 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_fragments.py +181 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_service_fragments.py +47 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_setup.py +116 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_upgrade_step_1001.py +47 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_view_fragment_block_view.py +120 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/tests/test_zcml.py +86 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/1001/.gitkeep +0 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/1001/metadata.txt +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/1001.zcml +31 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/configure.zcml +8 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/upgrades/v1001.py +14 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/views/__init__.py +1 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/views/configure.zcml +14 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/views/fragment_block_view.py +23 -0
- collective_fragmentsblock-1.0.0a2/src/collective/fragmentsblock/zcml.py +39 -0
- collective_fragmentsblock-1.0.0a2/uv.lock +6706 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Do not edit this file manually.
|
|
2
|
+
# This file is auto-generated by Copier.
|
|
3
|
+
# Changes here will be overwritten when you update the template.
|
|
4
|
+
_src_path: /home/dev/.copier-templates/plone-copier-templates/backend_addon
|
|
5
|
+
author_email: md@derico.de
|
|
6
|
+
author_name: Maik Derstappen
|
|
7
|
+
github_organization: collective
|
|
8
|
+
is_headless: false
|
|
9
|
+
package_description: 'Aurora fragment block: reusable content fragments referenced
|
|
10
|
+
live from central fragment documents'
|
|
11
|
+
package_name: collective.fragmentsblock
|
|
12
|
+
package_title: Collective Fragmentsblock
|
|
13
|
+
plone_version: '6.0'
|
|
14
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[*]
|
|
2
|
+
charset = utf-8
|
|
3
|
+
end_of_line = lf
|
|
4
|
+
indent_style = space
|
|
5
|
+
insert_final_newline = true
|
|
6
|
+
trim_trailing_whitespace = true
|
|
7
|
+
|
|
8
|
+
[*.py]
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.{cfg,ini,toml}]
|
|
12
|
+
indent_size = 4
|
|
13
|
+
|
|
14
|
+
[*.{xml,zcml,html,pt,zpt}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
|
|
17
|
+
[*.{css,js,json}]
|
|
18
|
+
indent_size = 2
|
|
19
|
+
|
|
20
|
+
[*.{yml,yaml}]
|
|
21
|
+
indent_size = 2
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
env:
|
|
16
|
+
BLICCA_TOKEN: ${{ secrets.BLICCA_TOKEN }}
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
# plone.blicca.auroraeditor is PRIVATE and not on PyPI: it is the editor
|
|
24
|
+
# this block mounts in and the source of the rendering API the package
|
|
25
|
+
# imports, so without it nothing installs and nothing is tested. A
|
|
26
|
+
# missing token is a red job on purpose — a green run over a package
|
|
27
|
+
# that could not be installed says nothing.
|
|
28
|
+
- name: Require the host checkout token
|
|
29
|
+
if: env.BLICCA_TOKEN == ''
|
|
30
|
+
run: |
|
|
31
|
+
echo "::error::BLICCA_TOKEN is not set. This package depends on"
|
|
32
|
+
echo "::error::plone.blicca.auroraeditor, which is private and not"
|
|
33
|
+
echo "::error::on PyPI, so the tests cannot run."
|
|
34
|
+
exit 1
|
|
35
|
+
|
|
36
|
+
# pyproject resolves the sources below from `../<name>`, so this
|
|
37
|
+
# repository checks itself out into a subdirectory and its siblings land
|
|
38
|
+
# beside it, exactly as they sit under the site's `sources/`.
|
|
39
|
+
- uses: actions/checkout@v5
|
|
40
|
+
with:
|
|
41
|
+
path: collective.fragmentsblock
|
|
42
|
+
|
|
43
|
+
- uses: actions/checkout@v5
|
|
44
|
+
with:
|
|
45
|
+
repository: derico-de/plone.blicca.auroraeditor
|
|
46
|
+
token: ${{ env.BLICCA_TOKEN }}
|
|
47
|
+
path: plone.blicca.auroraeditor
|
|
48
|
+
|
|
49
|
+
- name: Set up uv and Python ${{ matrix.python-version }}
|
|
50
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
51
|
+
with:
|
|
52
|
+
enable-cache: true
|
|
53
|
+
python-version: ${{ matrix.python-version }}
|
|
54
|
+
|
|
55
|
+
- name: Run tests
|
|
56
|
+
working-directory: collective.fragmentsblock
|
|
57
|
+
run: uv run --frozen --extra test pytest -v
|
|
58
|
+
|
|
59
|
+
lint:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v5
|
|
63
|
+
|
|
64
|
+
- name: Set up uv
|
|
65
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
66
|
+
with:
|
|
67
|
+
enable-cache: true
|
|
68
|
+
|
|
69
|
+
- name: Run ruff
|
|
70
|
+
run: uvx ruff@0.16.6 check .
|
|
71
|
+
|
|
72
|
+
bundle:
|
|
73
|
+
# The editor half is committed build output — there is no Node on the
|
|
74
|
+
# deploy host (block add-on contract §3.1) — so nothing rebuilds it at
|
|
75
|
+
# install time and it rots the moment someone edits bundle-src/src/ and
|
|
76
|
+
# forgets. The `git diff --exit-code` below is what makes committing an
|
|
77
|
+
# artifact safe rather than merely convenient.
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v5
|
|
81
|
+
|
|
82
|
+
# pnpm is pinned here rather than by a `packageManager` field in
|
|
83
|
+
# bundle-src/package.json: pnpm 12 answers that field by writing a
|
|
84
|
+
# `packageManagerDependencies` block into the lockfile — its own binary
|
|
85
|
+
# for every platform — and the lockfile is not the place for that.
|
|
86
|
+
- uses: pnpm/action-setup@v4
|
|
87
|
+
with:
|
|
88
|
+
version: 12.3.4
|
|
89
|
+
|
|
90
|
+
- name: Set up Node
|
|
91
|
+
uses: actions/setup-node@v4
|
|
92
|
+
with:
|
|
93
|
+
node-version-file: bundle-src/.nvmrc
|
|
94
|
+
cache: pnpm
|
|
95
|
+
cache-dependency-path: bundle-src/pnpm-lock.yaml
|
|
96
|
+
|
|
97
|
+
# `working-directory`, not `--dir`: the repository root has no
|
|
98
|
+
# package.json, and pnpm resolves the workspace from where it stands.
|
|
99
|
+
- name: Install
|
|
100
|
+
run: pnpm install --frozen-lockfile
|
|
101
|
+
working-directory: bundle-src
|
|
102
|
+
|
|
103
|
+
- name: Build
|
|
104
|
+
run: pnpm build
|
|
105
|
+
working-directory: bundle-src
|
|
106
|
+
|
|
107
|
+
# After the build: the harness reads the built artifact.
|
|
108
|
+
- name: Test
|
|
109
|
+
run: pnpm test
|
|
110
|
+
working-directory: bundle-src
|
|
111
|
+
|
|
112
|
+
# If this fails, someone edited bundle-src/src/ and did not rebuild.
|
|
113
|
+
- name: The committed bundle is the one this source builds
|
|
114
|
+
run: git diff --exit-code -- src/collective/fragmentsblock/static/
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
|
|
26
|
+
# PyInstaller
|
|
27
|
+
*.manifest
|
|
28
|
+
*.spec
|
|
29
|
+
|
|
30
|
+
# Installer logs
|
|
31
|
+
pip-log.txt
|
|
32
|
+
pip-delete-this-directory.txt
|
|
33
|
+
|
|
34
|
+
# Unit test / coverage reports
|
|
35
|
+
htmlcov/
|
|
36
|
+
.tox/
|
|
37
|
+
.nox/
|
|
38
|
+
.coverage
|
|
39
|
+
.coverage.*
|
|
40
|
+
.cache
|
|
41
|
+
nosetests.xml
|
|
42
|
+
coverage.xml
|
|
43
|
+
*.cover
|
|
44
|
+
*.py,cover
|
|
45
|
+
.hypothesis/
|
|
46
|
+
.pytest_cache/
|
|
47
|
+
|
|
48
|
+
# Type checkers / linters
|
|
49
|
+
.mypy_cache/
|
|
50
|
+
.pyre/
|
|
51
|
+
.pytype/
|
|
52
|
+
.ruff_cache/
|
|
53
|
+
|
|
54
|
+
# Translations (compiled catalogs are generated; .pot/.po are tracked)
|
|
55
|
+
*.mo
|
|
56
|
+
|
|
57
|
+
# Environments
|
|
58
|
+
.env
|
|
59
|
+
.venv
|
|
60
|
+
env/
|
|
61
|
+
venv/
|
|
62
|
+
ENV/
|
|
63
|
+
env.bak/
|
|
64
|
+
venv.bak/
|
|
65
|
+
|
|
66
|
+
# IDEs / editors
|
|
67
|
+
.idea/
|
|
68
|
+
.vscode/
|
|
69
|
+
*.swp
|
|
70
|
+
*.swo
|
|
71
|
+
*~
|
|
72
|
+
|
|
73
|
+
# Node / frontend tooling (themes, mockup patterns, svelte apps)
|
|
74
|
+
node_modules/
|
|
75
|
+
npm-debug.log*
|
|
76
|
+
yarn-debug.log*
|
|
77
|
+
yarn-error.log*
|
|
78
|
+
pnpm-debug.log*
|
|
79
|
+
.pnpm-store/
|
|
80
|
+
.parcel-cache/
|
|
81
|
+
.vite/
|
|
82
|
+
|
|
83
|
+
# Build outputs from frontend tooling (in addition to top-level dist/)
|
|
84
|
+
src/collective/fragmentsblock/**/dist/
|
|
85
|
+
src/collective/fragmentsblock/**/.cache/
|
|
86
|
+
|
|
87
|
+
# Plone / Zope runtime data
|
|
88
|
+
*.fs
|
|
89
|
+
*.fs.index
|
|
90
|
+
*.fs.lock
|
|
91
|
+
*.fs.tmp
|
|
92
|
+
blobs/
|
|
93
|
+
|
|
94
|
+
# Project specific
|
|
95
|
+
*.log
|
|
96
|
+
.DS_Store
|
|
97
|
+
Thumbs.db
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.5.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-yaml
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: check-merge-conflict
|
|
9
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
10
|
+
rev: v0.3.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: ruff
|
|
13
|
+
args: [--fix]
|
|
14
|
+
- id: ruff-format
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0a2 (2026-10-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
- Nothing changed yet.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 1.0.0a1 (2026-10-04)
|
|
10
|
+
|
|
11
|
+
- The editor needs nothing from a provider add-on any more. The fragment
|
|
12
|
+
block's `install()` fetches the new `@fragments` service and registers
|
|
13
|
+
every provider's fragments before the first render; the host awaits it
|
|
14
|
+
(`plone.blicca.auroraeditor` 1.0.0a3, block-api 1.2, declared on the
|
|
15
|
+
record by upgrade step 1001). A theme ships its `fragments/*.html` and
|
|
16
|
+
the `fragments:folder` line, no editor bundle, no record, no rebuild.
|
|
17
|
+
`IFragmentsProvider` gains `records()`; a fragment's title is its
|
|
18
|
+
first-line `<!-- title: … -->` comment, else derived from the file name
|
|
19
|
+
(ADR 0002).
|
|
20
|
+
|
|
21
|
+
- Add the `fragments:folder` ZCML directive. A provider add-on registers
|
|
22
|
+
its fragments folder with one line in `configure.zcml` and no Python
|
|
23
|
+
code. The folder is resolved relative to the package, checked at
|
|
24
|
+
startup, and registered under the package name unless `name` is given.
|
|
25
|
+
|
|
26
|
+
- Register the `INonInstallable` utility that hides the uninstall profile.
|
|
27
|
+
`HiddenProfiles` was defined in `setuphandlers.py` but never registered in
|
|
28
|
+
`configure.zcml`, so it hid nothing: harmless while this package has no
|
|
29
|
+
upgrade profile, and a profile offered as an installable add-on of its own
|
|
30
|
+
the moment one is scaffolded.
|
|
31
|
+
|
|
32
|
+
- A fragment block now **names itself with the fragment's title** while blocks
|
|
33
|
+
are dragged. The editor collapses the canvas to one row per block and reads
|
|
34
|
+
each row's identifying line off the rendered block — but a fragment is a
|
|
35
|
+
piece of a design and is often pure decoration with no text at all, so every
|
|
36
|
+
fragment row read "Fragment" against an empty line and two of them in one
|
|
37
|
+
page were indistinguishable exactly while being reordered. The view stamps
|
|
38
|
+
`data-block-summary` with the record's title (block add-on contract §1.6),
|
|
39
|
+
which is the same string the picker offers, so the row reads "Fragment —
|
|
40
|
+
Balkenlage (Trenner)". Editing affordance only: the attribute is inert on
|
|
41
|
+
the classic page, whose server-rendered markup does not carry it.
|
|
42
|
+
|
|
43
|
+
- The fragment block's settings form now carries **block width** and
|
|
44
|
+
**background** — the two placement controls, offered because a fragment
|
|
45
|
+
is a piece of a design rather than a whole one: a contact box wants the
|
|
46
|
+
narrow column, a divider the full bleed, and the same file may want both
|
|
47
|
+
on two different pages. Both are Aurora style fields, so the editor and
|
|
48
|
+
the classic renderer stamp the class and custom properties on the block
|
|
49
|
+
wrapper generically — no change to either view component or to
|
|
50
|
+
`@@aurora-block-fragment`. Width defaults to `default`, the width
|
|
51
|
+
fragment blocks already rendered at, so existing content is unchanged;
|
|
52
|
+
the background slots are read from the host's registered palette and the
|
|
53
|
+
field is omitted on a host that registers none.
|
|
54
|
+
|
|
55
|
+
- Initial release: the Aurora **fragment block** (`@type: fragment`) —
|
|
56
|
+
drop registered **design fragments** (static HTML files, typically cut
|
|
57
|
+
verbatim from a design mockup) into any Aurora-edited page. Aurora-first:
|
|
58
|
+
provider add-ons register `{id, title, html}` records into the shared
|
|
59
|
+
`@plone/registry` singleton from their install function
|
|
60
|
+
(`type: collective.fragmentsblock.fragment`); the block enumerates them
|
|
61
|
+
for its picker and renders the markup client-side, as-is, with optional
|
|
62
|
+
`${var}` substitution from the block's persisted `variables` mapping
|
|
63
|
+
(HTML-escaped, missing variables empty). Blicca classic pages render the
|
|
64
|
+
same file server-side via `@@aurora-block-fragment` and named
|
|
65
|
+
`IFragmentsProvider` utilities (`FragmentsFolder` reads
|
|
66
|
+
`<id>.html` files from a provider package directory) with identical
|
|
67
|
+
substitution semantics. Fail-soft placeholders everywhere; ships both
|
|
68
|
+
halves of the Blicca block add-on contract, with the committed editor
|
|
69
|
+
bundle built from `bundle-src/`
|
|
70
|
+
(`@plone-collective/aurora-fragment-block`). Variables render on both
|
|
71
|
+
surfaces but have no sidebar UI yet — see
|
|
72
|
+
`docs/adr/0001-fragments-are-registered-static-markup.md` for the design
|
|
73
|
+
and its deferred parts.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: collective.fragmentsblock
|
|
3
|
+
Version: 1.0.0a2
|
|
4
|
+
Summary: Aurora fragment block: static design fragments registered by add-ons, rendered as-is
|
|
5
|
+
Project-URL: Homepage, https://github.com/collective/collective.fragmentsblock
|
|
6
|
+
Project-URL: Documentation, https://github.com/collective/collective.fragmentsblock
|
|
7
|
+
Project-URL: Repository, https://github.com/collective/collective.fragmentsblock.git
|
|
8
|
+
Project-URL: Issues, https://github.com/collective/collective.fragmentsblock/issues
|
|
9
|
+
Author-email: Maik Derstappen <md@derico.de>
|
|
10
|
+
License: GPL-2.0-or-later
|
|
11
|
+
Keywords: CMS,Plone
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Web Environment
|
|
14
|
+
Classifier: Framework :: Plone
|
|
15
|
+
Classifier: Framework :: Plone :: 6.0
|
|
16
|
+
Classifier: Framework :: Plone :: Addon
|
|
17
|
+
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: plone-blicca-auroraeditor>=1.0.0a3
|
|
25
|
+
Requires-Dist: plone-restapi
|
|
26
|
+
Requires-Dist: plone>=6.0
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: plone-app-testing; extra == 'test'
|
|
29
|
+
Requires-Dist: plone-testing; extra == 'test'
|
|
30
|
+
Requires-Dist: pytest; extra == 'test'
|
|
31
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
32
|
+
Requires-Dist: pytest-plone>=0.5.0; extra == 'test'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# collective.fragmentsblock
|
|
36
|
+
|
|
37
|
+
A **Fragment** block for the Aurora editor in [Plone](https://plone.org) Blicca.
|
|
38
|
+
|
|
39
|
+
A fragment is a static piece of HTML, usually cut straight from the design
|
|
40
|
+
mockup: a contact box, a seal, a call-to-action banner, a partner-logo
|
|
41
|
+
strip. Your theme ships it as a file in a folder it registers with one line
|
|
42
|
+
of ZCML. Editors then pick it from a list and drop it into a page. The
|
|
43
|
+
markup is rendered exactly as it is in the file, both in the editor and on
|
|
44
|
+
published pages.
|
|
45
|
+
|
|
46
|
+
Editors never edit the markup. When the file changes, every page using the
|
|
47
|
+
fragment updates on the next deployment.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
Add `collective.fragmentsblock` to your project dependencies and install it
|
|
52
|
+
in the Plone add-ons control panel.
|
|
53
|
+
|
|
54
|
+
Requirements: Plone 6, Python 3.10 or newer, and `plone.blicca.auroraeditor`
|
|
55
|
+
1.0.0a3 or newer (block-api 1.2: the editor waits for this block to fetch its
|
|
56
|
+
fragments before it renders).
|
|
57
|
+
|
|
58
|
+
This package only provides the block. Fragments come from your own add-on,
|
|
59
|
+
typically the theme package. See the next section.
|
|
60
|
+
|
|
61
|
+
## Register a fragment
|
|
62
|
+
|
|
63
|
+
Fragments live in a folder of your add-on, one HTML file per fragment. The
|
|
64
|
+
server reads the files when it renders classic pages, and the editor fetches
|
|
65
|
+
the same files from the server when it opens. Nothing is built, bundled or
|
|
66
|
+
copied.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
src/my/theme/
|
|
70
|
+
├── fragments/
|
|
71
|
+
│ ├── contact-box.html
|
|
72
|
+
│ └── seal.html
|
|
73
|
+
└── configure.zcml
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The file name is the fragment id. Ids must match `^[A-Za-z0-9][A-Za-z0-9_-]*$`
|
|
77
|
+
and are unique across the whole site, so prefix them if more than one
|
|
78
|
+
add-on may provide fragments. A file whose name is not a valid id is skipped
|
|
79
|
+
with a warning.
|
|
80
|
+
|
|
81
|
+
Register the folder in your `configure.zcml`:
|
|
82
|
+
|
|
83
|
+
```xml
|
|
84
|
+
<!-- my/theme/configure.zcml -->
|
|
85
|
+
<configure
|
|
86
|
+
xmlns="http://namespaces.zope.org/zope"
|
|
87
|
+
xmlns:fragments="http://namespaces.plone.org/fragmentsblock">
|
|
88
|
+
|
|
89
|
+
<include package="collective.fragmentsblock" file="meta.zcml" />
|
|
90
|
+
|
|
91
|
+
<fragments:folder directory="fragments" />
|
|
92
|
+
|
|
93
|
+
</configure>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`directory` is relative to your package. The folder is registered under
|
|
97
|
+
your package name. Pass `name="..."` if you need another one. A folder that
|
|
98
|
+
does not exist is an error at startup, not an empty picker.
|
|
99
|
+
|
|
100
|
+
That is all. Restart Plone and the fragments appear in the picker.
|
|
101
|
+
|
|
102
|
+
### Titles
|
|
103
|
+
|
|
104
|
+
Editors pick a fragment by its title. Put it in a comment on the first line
|
|
105
|
+
of the file:
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<!-- title: Contact box -->
|
|
109
|
+
<aside class="contact-box">
|
|
110
|
+
...
|
|
111
|
+
</aside>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Without that comment the title is derived from the file name:
|
|
115
|
+
`contact-box.html` becomes "Contact box", `partner_logos.html` becomes
|
|
116
|
+
"Partner logos". The comment is part of the markup and is rendered with it;
|
|
117
|
+
browsers ignore it.
|
|
118
|
+
|
|
119
|
+
## Add a fragment to a page
|
|
120
|
+
|
|
121
|
+
1. Open the page in the Aurora editor.
|
|
122
|
+
2. Type `/` and choose **Fragment** from the slash menu.
|
|
123
|
+
3. In the block settings, pick the fragment from the **Fragment** list.
|
|
124
|
+
|
|
125
|
+
The fragment renders immediately. Two more settings control placement:
|
|
126
|
+
|
|
127
|
+
- **Block width**: `narrow`, `default`, `layout` or `full`. Defaults to
|
|
128
|
+
`default`.
|
|
129
|
+
- **Background**: a named colour slot from your theme's palette, such as
|
|
130
|
+
`Grey` or `Accent`. The field only appears if the theme registers a
|
|
131
|
+
palette.
|
|
132
|
+
|
|
133
|
+
Both paint the block wrapper around the fragment, not the fragment itself.
|
|
134
|
+
|
|
135
|
+
If a fragment's add-on gets uninstalled, the editor shows a note in place
|
|
136
|
+
of the block and the published page renders nothing there. Pages never
|
|
137
|
+
break.
|
|
138
|
+
|
|
139
|
+
## Good to know
|
|
140
|
+
|
|
141
|
+
- **Fragment HTML is trusted.** It ships with your add-on and is not
|
|
142
|
+
sanitised. A `<script>` in it runs on the published page.
|
|
143
|
+
- **Variables.** Markup may contain `${name}` tokens. They are filled from
|
|
144
|
+
the block's `variables` mapping, HTML-escaped, with missing names
|
|
145
|
+
rendered empty. There is no editor UI for this yet. The mapping can be
|
|
146
|
+
set through the REST API or an upgrade step. For now, if a fragment
|
|
147
|
+
needs different values on different pages, ship one fragment per
|
|
148
|
+
variant.
|
|
149
|
+
- **Wrapper.** Both the editor and the server wrap the markup in a
|
|
150
|
+
`<div class="block-fragment">`. Mockup HTML written for a grid or flex
|
|
151
|
+
parent needs to account for that.
|
|
152
|
+
- **The editor fetches `@fragments`.** When the editor opens, the fragment
|
|
153
|
+
block requests `<site>/@fragments` with the session cookie and registers
|
|
154
|
+
what comes back before the first render. The list is every provider's
|
|
155
|
+
`records()`; where two providers use the same id, the one whose name sorts
|
|
156
|
+
first wins, on both surfaces. If the request fails, the block stays
|
|
157
|
+
available with an empty picker and existing fragment blocks say so instead
|
|
158
|
+
of rendering nothing.
|
|
159
|
+
- **Other sources.** A folder of files is the stock provider. For anything
|
|
160
|
+
else, register a named utility that implements `IFragmentsProvider`:
|
|
161
|
+
`get(fragment_id)` returns the HTML or `None`, and `records()` lists every
|
|
162
|
+
fragment as `{"id", "title", "html"}`.
|
|
163
|
+
- **Registering from JavaScript still works.** An Aurora add-on may register
|
|
164
|
+
fragments itself, with `registerFragment(config, {id, title, html})` from
|
|
165
|
+
the npm package `@plone-collective/aurora-fragment-block` or with a bare
|
|
166
|
+
`config.registerUtility` of type `collective.fragmentsblock.fragment`. The
|
|
167
|
+
server then needs a matching provider, or the fragment renders only in the
|
|
168
|
+
editor.
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
The editor half lives in `bundle-src/` and is built into the Python
|
|
173
|
+
package's `static/` folder. The build output is committed.
|
|
174
|
+
|
|
175
|
+
```shell
|
|
176
|
+
cd bundle-src
|
|
177
|
+
pnpm install
|
|
178
|
+
pnpm build
|
|
179
|
+
pnpm test
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Python tests:
|
|
183
|
+
|
|
184
|
+
```shell
|
|
185
|
+
uv run --extra test pytest
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
GPLv2 for the Python package, MIT for the npm package.
|
|
191
|
+
|
|
192
|
+
## Author
|
|
193
|
+
|
|
194
|
+
Maik Derstappen, [derico.de](https://derico.de), <md@derico.de>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# collective.fragmentsblock
|
|
2
|
+
|
|
3
|
+
A **Fragment** block for the Aurora editor in [Plone](https://plone.org) Blicca.
|
|
4
|
+
|
|
5
|
+
A fragment is a static piece of HTML, usually cut straight from the design
|
|
6
|
+
mockup: a contact box, a seal, a call-to-action banner, a partner-logo
|
|
7
|
+
strip. Your theme ships it as a file in a folder it registers with one line
|
|
8
|
+
of ZCML. Editors then pick it from a list and drop it into a page. The
|
|
9
|
+
markup is rendered exactly as it is in the file, both in the editor and on
|
|
10
|
+
published pages.
|
|
11
|
+
|
|
12
|
+
Editors never edit the markup. When the file changes, every page using the
|
|
13
|
+
fragment updates on the next deployment.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Add `collective.fragmentsblock` to your project dependencies and install it
|
|
18
|
+
in the Plone add-ons control panel.
|
|
19
|
+
|
|
20
|
+
Requirements: Plone 6, Python 3.10 or newer, and `plone.blicca.auroraeditor`
|
|
21
|
+
1.0.0a3 or newer (block-api 1.2: the editor waits for this block to fetch its
|
|
22
|
+
fragments before it renders).
|
|
23
|
+
|
|
24
|
+
This package only provides the block. Fragments come from your own add-on,
|
|
25
|
+
typically the theme package. See the next section.
|
|
26
|
+
|
|
27
|
+
## Register a fragment
|
|
28
|
+
|
|
29
|
+
Fragments live in a folder of your add-on, one HTML file per fragment. The
|
|
30
|
+
server reads the files when it renders classic pages, and the editor fetches
|
|
31
|
+
the same files from the server when it opens. Nothing is built, bundled or
|
|
32
|
+
copied.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/my/theme/
|
|
36
|
+
├── fragments/
|
|
37
|
+
│ ├── contact-box.html
|
|
38
|
+
│ └── seal.html
|
|
39
|
+
└── configure.zcml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The file name is the fragment id. Ids must match `^[A-Za-z0-9][A-Za-z0-9_-]*$`
|
|
43
|
+
and are unique across the whole site, so prefix them if more than one
|
|
44
|
+
add-on may provide fragments. A file whose name is not a valid id is skipped
|
|
45
|
+
with a warning.
|
|
46
|
+
|
|
47
|
+
Register the folder in your `configure.zcml`:
|
|
48
|
+
|
|
49
|
+
```xml
|
|
50
|
+
<!-- my/theme/configure.zcml -->
|
|
51
|
+
<configure
|
|
52
|
+
xmlns="http://namespaces.zope.org/zope"
|
|
53
|
+
xmlns:fragments="http://namespaces.plone.org/fragmentsblock">
|
|
54
|
+
|
|
55
|
+
<include package="collective.fragmentsblock" file="meta.zcml" />
|
|
56
|
+
|
|
57
|
+
<fragments:folder directory="fragments" />
|
|
58
|
+
|
|
59
|
+
</configure>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`directory` is relative to your package. The folder is registered under
|
|
63
|
+
your package name. Pass `name="..."` if you need another one. A folder that
|
|
64
|
+
does not exist is an error at startup, not an empty picker.
|
|
65
|
+
|
|
66
|
+
That is all. Restart Plone and the fragments appear in the picker.
|
|
67
|
+
|
|
68
|
+
### Titles
|
|
69
|
+
|
|
70
|
+
Editors pick a fragment by its title. Put it in a comment on the first line
|
|
71
|
+
of the file:
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!-- title: Contact box -->
|
|
75
|
+
<aside class="contact-box">
|
|
76
|
+
...
|
|
77
|
+
</aside>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Without that comment the title is derived from the file name:
|
|
81
|
+
`contact-box.html` becomes "Contact box", `partner_logos.html` becomes
|
|
82
|
+
"Partner logos". The comment is part of the markup and is rendered with it;
|
|
83
|
+
browsers ignore it.
|
|
84
|
+
|
|
85
|
+
## Add a fragment to a page
|
|
86
|
+
|
|
87
|
+
1. Open the page in the Aurora editor.
|
|
88
|
+
2. Type `/` and choose **Fragment** from the slash menu.
|
|
89
|
+
3. In the block settings, pick the fragment from the **Fragment** list.
|
|
90
|
+
|
|
91
|
+
The fragment renders immediately. Two more settings control placement:
|
|
92
|
+
|
|
93
|
+
- **Block width**: `narrow`, `default`, `layout` or `full`. Defaults to
|
|
94
|
+
`default`.
|
|
95
|
+
- **Background**: a named colour slot from your theme's palette, such as
|
|
96
|
+
`Grey` or `Accent`. The field only appears if the theme registers a
|
|
97
|
+
palette.
|
|
98
|
+
|
|
99
|
+
Both paint the block wrapper around the fragment, not the fragment itself.
|
|
100
|
+
|
|
101
|
+
If a fragment's add-on gets uninstalled, the editor shows a note in place
|
|
102
|
+
of the block and the published page renders nothing there. Pages never
|
|
103
|
+
break.
|
|
104
|
+
|
|
105
|
+
## Good to know
|
|
106
|
+
|
|
107
|
+
- **Fragment HTML is trusted.** It ships with your add-on and is not
|
|
108
|
+
sanitised. A `<script>` in it runs on the published page.
|
|
109
|
+
- **Variables.** Markup may contain `${name}` tokens. They are filled from
|
|
110
|
+
the block's `variables` mapping, HTML-escaped, with missing names
|
|
111
|
+
rendered empty. There is no editor UI for this yet. The mapping can be
|
|
112
|
+
set through the REST API or an upgrade step. For now, if a fragment
|
|
113
|
+
needs different values on different pages, ship one fragment per
|
|
114
|
+
variant.
|
|
115
|
+
- **Wrapper.** Both the editor and the server wrap the markup in a
|
|
116
|
+
`<div class="block-fragment">`. Mockup HTML written for a grid or flex
|
|
117
|
+
parent needs to account for that.
|
|
118
|
+
- **The editor fetches `@fragments`.** When the editor opens, the fragment
|
|
119
|
+
block requests `<site>/@fragments` with the session cookie and registers
|
|
120
|
+
what comes back before the first render. The list is every provider's
|
|
121
|
+
`records()`; where two providers use the same id, the one whose name sorts
|
|
122
|
+
first wins, on both surfaces. If the request fails, the block stays
|
|
123
|
+
available with an empty picker and existing fragment blocks say so instead
|
|
124
|
+
of rendering nothing.
|
|
125
|
+
- **Other sources.** A folder of files is the stock provider. For anything
|
|
126
|
+
else, register a named utility that implements `IFragmentsProvider`:
|
|
127
|
+
`get(fragment_id)` returns the HTML or `None`, and `records()` lists every
|
|
128
|
+
fragment as `{"id", "title", "html"}`.
|
|
129
|
+
- **Registering from JavaScript still works.** An Aurora add-on may register
|
|
130
|
+
fragments itself, with `registerFragment(config, {id, title, html})` from
|
|
131
|
+
the npm package `@plone-collective/aurora-fragment-block` or with a bare
|
|
132
|
+
`config.registerUtility` of type `collective.fragmentsblock.fragment`. The
|
|
133
|
+
server then needs a matching provider, or the fragment renders only in the
|
|
134
|
+
editor.
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
The editor half lives in `bundle-src/` and is built into the Python
|
|
139
|
+
package's `static/` folder. The build output is committed.
|
|
140
|
+
|
|
141
|
+
```shell
|
|
142
|
+
cd bundle-src
|
|
143
|
+
pnpm install
|
|
144
|
+
pnpm build
|
|
145
|
+
pnpm test
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Python tests:
|
|
149
|
+
|
|
150
|
+
```shell
|
|
151
|
+
uv run --extra test pytest
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
GPLv2 for the Python package, MIT for the npm package.
|
|
157
|
+
|
|
158
|
+
## Author
|
|
159
|
+
|
|
160
|
+
Maik Derstappen, [derico.de](https://derico.de), <md@derico.de>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|