@eeacms/volto-cca-policy 0.3.124 → 0.3.125
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.
- package/CHANGELOG.md +38 -0
- package/artifacts/BLOCKS.md +167 -0
- package/artifacts/link-integrity-block-fields-report.md +132 -0
- package/artifacts/link-integrity-blocks-report.md +52 -0
- package/artifacts/link-integrity-workflow/understanding-link-integrity.md +143 -0
- package/artifacts/link-integrity-workflow/volto-block-link-analysis.md +63 -0
- package/artifacts/link-integrity-workflow/volto-block-link-discovery.md +60 -0
- package/artifacts/test-fixes/test-fixes-specification.md +267 -0
- package/jest-addon.config.js +1 -0
- package/jest-node-crypto-mock.js +3 -0
- package/package.json +1 -1
- package/src/components/index.js +1 -0
- package/src/components/manage/Blocks/ReadMore/ReadMoreView.test.jsx +13 -0
- package/src/components/manage/Workflow/WorkflowLinkIntegrityModal.jsx +192 -0
- package/src/components/manage/Workflow/WorkflowLinkIntegrityModal.test.jsx +149 -0
- package/src/components/theme/Views/ArchivedVersionListing.test.jsx +14 -18
- package/src/components/theme/Views/CcaEventView.test.jsx +12 -3
- package/src/components/theme/Views/DatabaseItemView.test.jsx +2 -2
- package/src/components/theme/Widgets/GeolocationWidget.jsx +1 -1
- package/src/customizations/volto/components/manage/Workflow/README.md +23 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +359 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,44 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [0.3.125](https://github.com/eea/volto-cca-policy/compare/0.3.124...0.3.125) - 18 May 2026
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: add activity indicator during workflow transition [Tiberiu Ichim - [`869cf77`](https://github.com/eea/volto-cca-policy/commit/869cf7733968fc55739f0ad4e94b2d5ffb9a7bbc)]
|
|
12
|
+
|
|
13
|
+
#### :bug: Bug Fixes
|
|
14
|
+
|
|
15
|
+
- fix(eslint): silence no-console warning in ReadMoreView test [Tiberiu Ichim - [`751dfdb`](https://github.com/eea/volto-cca-policy/commit/751dfdb089bc3ebe8099c990147848338d2146e6)]
|
|
16
|
+
- fix(tests): fix 3 failing test suites [Tiberiu Ichim - [`9956429`](https://github.com/eea/volto-cca-policy/commit/995642903bca5750bed270671881a9172476d242)]
|
|
17
|
+
- fix: make workflow link integrity check robust to API errors and URL variations [Tiberiu Ichim - [`07260f2`](https://github.com/eea/volto-cca-policy/commit/07260f2ae89786ba49736d53b1650d4e6332eafb)]
|
|
18
|
+
- fix: stale link integrity data and transition execution robustness [Tiberiu Ichim - [`704faf8`](https://github.com/eea/volto-cca-policy/commit/704faf8bf7aeb6ad4614715c3c95b5599ce13a57)]
|
|
19
|
+
- fix: race condition in link integrity check during workflow transition [Tiberiu Ichim - [`a2f3deb`](https://github.com/eea/volto-cca-policy/commit/a2f3deb4e1b83c094138f46ec8da1573c7939694)]
|
|
20
|
+
|
|
21
|
+
#### :house: Internal changes
|
|
22
|
+
|
|
23
|
+
- style: Automated code fix [eea-jenkins - [`a7207bf`](https://github.com/eea/volto-cca-policy/commit/a7207bf7b41da19a845af247c5628aa00a870099)]
|
|
24
|
+
- style: Automated code fix [eea-jenkins - [`eff3551`](https://github.com/eea/volto-cca-policy/commit/eff35514016705cc6103945beb298f9b0c0a4903)]
|
|
25
|
+
|
|
26
|
+
#### :house: Documentation changes
|
|
27
|
+
|
|
28
|
+
- docs: add test fixes specification for 4 failing test suites [Tiberiu Ichim - [`c00a536`](https://github.com/eea/volto-cca-policy/commit/c00a5368f06a5d28d8d455cf75b3b8cabb947155)]
|
|
29
|
+
- docs: finalize detailed guide on block discovery and link extraction [Tiberiu Ichim - [`253d576`](https://github.com/eea/volto-cca-policy/commit/253d576176e6a76f860a8371a598d82a49323404)]
|
|
30
|
+
- docs: finalize technical details on block nesting and link extraction [Tiberiu Ichim - [`78dcd18`](https://github.com/eea/volto-cca-policy/commit/78dcd18435eb192a0c380c11fe70d83d44b35545)]
|
|
31
|
+
- docs: add technical documentation on how Volto discovers links in blocks [Tiberiu Ichim - [`89fff64`](https://github.com/eea/volto-cca-policy/commit/89fff648e9dda10a6b6145ac13fff7b14c2c330e)]
|
|
32
|
+
- docs: document activity indicators and UX improvements [Tiberiu Ichim - [`e7bdb88`](https://github.com/eea/volto-cca-policy/commit/e7bdb888237eb8a3181b11520e1002c60a310aa5)]
|
|
33
|
+
- docs: document race condition prevention logic [Tiberiu Ichim - [`5ce00b2`](https://github.com/eea/volto-cca-policy/commit/5ce00b255c6835132c50d74ce08af9a6ddb53262)]
|
|
34
|
+
- docs: add user story for testing link integrity warning [Tiberiu Ichim - [`5026dcb`](https://github.com/eea/volto-cca-policy/commit/5026dcb039a5d2b603b51da33e999ba416e9e26e)]
|
|
35
|
+
|
|
36
|
+
#### :hammer_and_wrench: Others
|
|
37
|
+
|
|
38
|
+
- Add artifacts [Tiberiu Ichim - [`37cccf1`](https://github.com/eea/volto-cca-policy/commit/37cccf1d3f1d926c64c7d192b071c7cd0061cb0a)]
|
|
39
|
+
- Add blocks overview [Tiberiu Ichim - [`1aa6e8b`](https://github.com/eea/volto-cca-policy/commit/1aa6e8b780f4f7a00641872ef7a99e2223cd1feb)]
|
|
40
|
+
- test: add tests for WorkflowLinkIntegrityModal [Tiberiu Ichim - [`53e1dd1`](https://github.com/eea/volto-cca-policy/commit/53e1dd1ef924352f1b8328819cd727633b47658b)]
|
|
41
|
+
- test: fix console warnings in test suite [Tiberiu Ichim - [`a3d49b4`](https://github.com/eea/volto-cca-policy/commit/a3d49b40d63a452546322b9f0e170a965f2dc3fc)]
|
|
42
|
+
- Add spec [Tiberiu Ichim - [`2034b80`](https://github.com/eea/volto-cca-policy/commit/2034b80f470344a6c20524b04364f1ae9f770a6d)]
|
|
43
|
+
- Add README for shadowed Workflow component [Tiberiu Ichim - [`5dac2e7`](https://github.com/eea/volto-cca-policy/commit/5dac2e7a7941c4e47afa229fb65c4b08db8606b8)]
|
|
44
|
+
- Add implementation artifacts for link integrity warning [Tiberiu Ichim - [`84e2aa4`](https://github.com/eea/volto-cca-policy/commit/84e2aa4529dbd02a05bc148a9900f7cc4a25c54d)]
|
|
7
45
|
### [0.3.124](https://github.com/eea/volto-cca-policy/compare/0.3.123...0.3.124) - 12 May 2026
|
|
8
46
|
|
|
9
47
|
#### :bug: Bug Fixes
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Custom Blocks in `volto-cca-policy`
|
|
2
|
+
|
|
3
|
+
This addon registers **20 custom blocks** and extends **2 existing blocks** (Listing, Tabs Block) with new variations.
|
|
4
|
+
All blocks are installed via `src/components/manage/Blocks/index.js`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Custom Blocks
|
|
9
|
+
|
|
10
|
+
### AST Navigation
|
|
11
|
+
- **Block ID:** `astNavigation`
|
|
12
|
+
- **Group:** `site`
|
|
13
|
+
- **Restricted to:** Mission pages (via `blockAvailableInMission`)
|
|
14
|
+
- **What it does:** Renders a visual navigation map (logo map) for the *Adaptation Support Tool (AST)*. Supports two image types (`ast` and `uast`). Editors configure 6 linked items that map to clickable regions on the logo image. Clicking a region navigates to the corresponding content page.
|
|
15
|
+
|
|
16
|
+
### C3S Indicators Glossary
|
|
17
|
+
- **Block ID:** `c3SIndicatorsGlossaryBlock`
|
|
18
|
+
- **Group:** `site`
|
|
19
|
+
- **Restricted to:** Mission pages
|
|
20
|
+
- **What it does:** Displays a glossary table of C3S (Copernicus Climate Change Service) indicator terms. The content is provided by a server-side `@components` view (`c3s_indicators_glossary_table`) and rendered as raw HTML.
|
|
21
|
+
|
|
22
|
+
### C3S Indicators Listing
|
|
23
|
+
- **Block ID:** `c3SIndicatorListingBlock`
|
|
24
|
+
- **Group:** `site`
|
|
25
|
+
- **Restricted to:** Mission pages
|
|
26
|
+
- **What it does:** Renders a description and a linked list of C3S indicators. Content (description text + item list with URLs) is provided by the server-side `@components` view `c3s_indicators_listing`.
|
|
27
|
+
|
|
28
|
+
### C3S Indicators Overview
|
|
29
|
+
- **Block ID:** `c3SIndicatorsOverviewBlock`
|
|
30
|
+
- **Group:** `site`
|
|
31
|
+
- **Restricted to:** Mission pages
|
|
32
|
+
- **What it does:** Displays an HTML description/overview of C3S indicators, fetched from the server-side `@components` view `c3s_indicators_overview`.
|
|
33
|
+
|
|
34
|
+
### Case Study Explorer
|
|
35
|
+
- **Block ID:** `caseStudyExplorer`
|
|
36
|
+
- **Group:** `site`
|
|
37
|
+
- **Restricted to:** Mission pages
|
|
38
|
+
- **What it does:** An interactive map-based explorer for climate adaptation case studies. Loads case study data from an ArcGIS JSON endpoint (`@@case-studies-map.arcgis.json`). Provides filter controls (sectors, impacts, etc.) and an interactive map with clickable features. Supports URL-based filter parameters.
|
|
39
|
+
|
|
40
|
+
### Collection Statistics
|
|
41
|
+
- **Block ID:** `collectionStats`
|
|
42
|
+
- **Group:** `site`
|
|
43
|
+
- **What it does:** Displays aggregated statistics for a content collection. Shows two groups of stats with icons and counts:
|
|
44
|
+
- **Health impacts:** Climate-sensitive diseases, Heat, Wildfires, Droughts and floods, Air pollution and aero-allergens
|
|
45
|
+
- **Content types:** Adaptation options, Case studies, Guidance, Indicators, Information portals, Organisations, Publications, Research projects, Tools, Videos
|
|
46
|
+
- Stats are computed from query stats via `getQueryStats` and can link to filtered search results.
|
|
47
|
+
|
|
48
|
+
### Content Links
|
|
49
|
+
- **Block ID:** `contentLinks`
|
|
50
|
+
- **Group:** `site`
|
|
51
|
+
- **Variations:**
|
|
52
|
+
- `default` — Simple list of linked content items
|
|
53
|
+
- `navigationList` — Navigation-style list with active-state highlighting based on current URL
|
|
54
|
+
- `dropdown` — Dropdown selector for linking to content items (with configurable placeholder text)
|
|
55
|
+
- **What it does:** Displays a set of manually selected content items as links. Useful for sidebars, navigation menus, or quick-access link groups.
|
|
56
|
+
|
|
57
|
+
### Country Map Heat Index
|
|
58
|
+
- **Block ID:** `countryMapHeatIndex`
|
|
59
|
+
- **Group:** `site`
|
|
60
|
+
- **Restricted to:** Mission pages
|
|
61
|
+
- **What it does:** An interactive OpenLayers map of European countries colored by heat index data. Fetches metadata from `@@countries-heat-index-json`. Countries are highlighted with tooltips and are clickable, navigating to the respective country page. Includes a filter control and supports lazy loading via visibility sensor.
|
|
62
|
+
|
|
63
|
+
### Country Map Observatory
|
|
64
|
+
- **Block ID:** `countryMapObservatory`
|
|
65
|
+
- **Group:** `site`
|
|
66
|
+
- **Restricted to:** Mission pages
|
|
67
|
+
- **What it does:** An interactive OpenLayers map for the Health Observatory section. Renders EU countries with metadata from `@@countries-metadata-extract-2025`. Supports thematic map modes (e.g., "National adaptation policy"), tooltips, and click-to-navigate interactions. Uses WMS tile sources and lazy loading.
|
|
68
|
+
|
|
69
|
+
### Country Map Profile
|
|
70
|
+
- **Block ID:** `countryMapProfile`
|
|
71
|
+
- **Group:** `site`
|
|
72
|
+
- **Restricted to:** Mission pages
|
|
73
|
+
- **What it does:** An interactive OpenLayers map for country profiles. Renders EU countries with metadata from `@@countries-metadata-extract-2025` (including flag images). Supports thematic map modes, tooltips, click-to-navigate, and a filter control. Uses WMS tile sources and lazy loading.
|
|
74
|
+
|
|
75
|
+
### Country Profile Detail
|
|
76
|
+
- **Block ID:** `countryProfileDetail`
|
|
77
|
+
- **Group:** `site`
|
|
78
|
+
- **What it does:** Renders the detailed country profile content provided by a server-side `@components` view (`countryprofile.html`). Displays tabbed sections with accordions, including a language disclaimer for non-English content and optional top messages/accordions.
|
|
79
|
+
|
|
80
|
+
### Data Connected Embed Block
|
|
81
|
+
- **Block ID:** `data_connected_embed` (extends `@eeacms/volto-datablocks`)
|
|
82
|
+
- **What it does:** Customizes the data-connected embed block from `volto-datablocks` to inject the current page language (`lang`) as an additional query parameter. This ensures embedded data visualizations respect the page's language context. Wraps the base `ViewEmbedBlock` with Redux connectivity and `injectIntl`.
|
|
83
|
+
|
|
84
|
+
### ECDE Indicators
|
|
85
|
+
- **Block ID:** `ecdeIndicators`
|
|
86
|
+
- **Group:** `site`
|
|
87
|
+
- **Restricted to:** Mission pages
|
|
88
|
+
- **What it does:** Embeds European Climate Data Explorer (ECDE) indicator visualizations from the Copernicus Climate Data Store (CDS). Loads region data from an EEA ArcGIS service and renders interactive CDS toolbox apps (e.g., growing degree days, mean temperature) for selected European NUTS regions.
|
|
89
|
+
|
|
90
|
+
### Filter AceContent
|
|
91
|
+
- **Block ID:** `filterAceContent`
|
|
92
|
+
- **Group:** `site`
|
|
93
|
+
- **Restricted to:** Mission pages
|
|
94
|
+
- **Variations:**
|
|
95
|
+
- `simpleListing` — Listing view (default)
|
|
96
|
+
- `simpleCards` — Card grid view
|
|
97
|
+
- **What it does:** Provides a faceted search/filter interface for ACE (Adaptation Content Exchange) content. Supports filtering by bio-regions, countries, climate impacts, sectors, element types, funding programmes, macro-regions, key types, and free-text search. Uses a custom select widget with styled dropdowns.
|
|
98
|
+
|
|
99
|
+
### Flourish Visualization
|
|
100
|
+
- **Block ID:** `FlourishEmbedBlock`
|
|
101
|
+
- **Group:** `site`
|
|
102
|
+
- **What it does:** Embeds interactive data visualizations from [Flourish.studio](https://flourish.studio). Editors paste a Flourish embed code, and the block extracts the visualization URL, wraps it in an iframe with privacy protection (lazy loading via consent gate), and displays it at a fixed height of 980px.
|
|
103
|
+
|
|
104
|
+
### RAST (Related And Sub-Tree) Block
|
|
105
|
+
- **Block ID:** `rastBlock`
|
|
106
|
+
- **Group:** `site`
|
|
107
|
+
- **Restricted to:** Mission pages
|
|
108
|
+
- **What it does:** Renders a contextual navigation block showing related content and sub-items from a configurable root path. Supports skipping specific items and optionally showing subfolders. Uses `ContextNavigation` and `RASTAccordion` components to display a hierarchical accordion-style navigation tree.
|
|
109
|
+
|
|
110
|
+
### Read More
|
|
111
|
+
- **Block ID:** `readMoreBlock`
|
|
112
|
+
- **Group:** `site`
|
|
113
|
+
- **What it does:** Creates a collapsible "read more" section. Wraps all preceding sibling blocks in a height-constrained container with an expand/collapse button. Configurable properties include open/closed labels, button position, and initial height.
|
|
114
|
+
|
|
115
|
+
### Redirection Block
|
|
116
|
+
- **Block ID:** `redirectBlock`
|
|
117
|
+
- **Group:** `site`
|
|
118
|
+
- **What it does:** Automatically redirects anonymous users to a configured target URL. Logged-in users (editors) see a discreet notice showing the redirect target instead of being redirected, so they can still edit the page.
|
|
119
|
+
|
|
120
|
+
### Relevant AceContent
|
|
121
|
+
- **Block ID:** `relevantAceContent`
|
|
122
|
+
- **Group:** `site`
|
|
123
|
+
- **Restricted to:** Mission pages
|
|
124
|
+
- **What it does:** Displays a list of relevant ACE (Adaptation Content Exchange) items. Supports two modes: manually selected items and dynamically searched results (by element type, sector, search type, special tags, or search text). Can combine both modes. Renders as a simple linked list.
|
|
125
|
+
|
|
126
|
+
### Search AceContent
|
|
127
|
+
- **Block ID:** `searchAceContent`
|
|
128
|
+
- **Group:** `site`
|
|
129
|
+
- **Restricted to:** Mission pages
|
|
130
|
+
- **What it does:** Displays search results from the ACE content search API. Shows results as a linked list with translated labels and counts. Includes a "Share your information" button linking to the share page.
|
|
131
|
+
|
|
132
|
+
### Tabs Block — Spotlight Variation
|
|
133
|
+
- **Block ID:** `tabs_block` (extends `@eeacms/volto-tabs-block`)
|
|
134
|
+
- **Variation added:** `spotlight`
|
|
135
|
+
- **What it does:** Adds a "Spotlight" variation to the standard tabs block. Renders tab content with a spotlight-style layout featuring configurable icons or images (with position and size options), simple markdown support, and scroll-to-target behavior.
|
|
136
|
+
|
|
137
|
+
### Trans Region Select
|
|
138
|
+
- **Block ID:** `transRegionSelect`
|
|
139
|
+
- **Group:** `site`
|
|
140
|
+
- **Restricted to:** Mission pages
|
|
141
|
+
- **What it does:** Renders a dropdown selector for transnational regions. Fetches region data from the content's `@components` view (`transnationalregion`) and provides a dropdown with links to each region's page, plus a default "Other regions" option.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Extended Standard Blocks
|
|
146
|
+
|
|
147
|
+
### Listing (standard Volto block — extended)
|
|
148
|
+
The standard Volto `listing` block is extended with **6 new variations**:
|
|
149
|
+
|
|
150
|
+
| Variation | Description |
|
|
151
|
+
|-----------|-------------|
|
|
152
|
+
| `dropdown` | Renders listed items as a dropdown selector with a configurable placeholder text |
|
|
153
|
+
| `organisationCards` | Displays items as a 4-column card grid with logo/image and description (for organisations) |
|
|
154
|
+
| `indicatorCards` | Displays C3S/Lancet Countdown indicators as styled cards with title and description |
|
|
155
|
+
| `eventCards` | Displays events as cards with date info, contact details, event URL, and EEA branding |
|
|
156
|
+
| `eventAccordion` | Displays events in an accordion layout with formatted date ranges and "read more" expand |
|
|
157
|
+
| `simpleListing` | Plain text list of linked items (minimal styling) |
|
|
158
|
+
| `simpleCards` | Generic 4-column card grid with image/logo and title |
|
|
159
|
+
|
|
160
|
+
Additionally, the listing block's `noResultsComponent` is overridden to render nothing (instead of showing "No results" text).
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Shared Utilities
|
|
165
|
+
|
|
166
|
+
- **`withResponsiveContainer.js`** — HOC that wraps map blocks in a responsive container (used by Country Map blocks)
|
|
167
|
+
- **`withVisibilitySensor.jsx`** — HOC that defers map initialization until the block is visible in the viewport (lazy loading)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Link Integrity — Block Field Coverage Report
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-05-18
|
|
4
|
+
**Scope:** All blocks registered by `volto-cca-policy`, analyzed against the link integrity retrievers available in `plone.restapi` and `eea.climateadapt`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Retrievers in Play
|
|
9
|
+
|
|
10
|
+
Three retrievers scan block data when content is saved:
|
|
11
|
+
|
|
12
|
+
| Retriever | Source | Order | Fields Scanned | Scope |
|
|
13
|
+
|---|---|---|---|---|
|
|
14
|
+
| `GenericBlockLinksRetriever` | `plone.restapi` | 1 | `url`, `href`, `preview_image` (top-level only) | All blocks (`block_type = None`) |
|
|
15
|
+
| `TextBlockLinksRetriever` | `plone.restapi` | 100 | DraftJS `entityMap` LINK entities (`href`, `url`) | `block_type = "text"` |
|
|
16
|
+
| `SlateBlockLinksRetriever` | `plone.restapi` | 100 | Slate nodes: `handle_a` → `data.link.internal.internal_link[0]["@id"]`, `handle_link` → `data.url` | `block_type = "slate"` |
|
|
17
|
+
| `CCAObjectListLinksRetriever` | `eea.climateadapt` | 10 | `items[*].source`, `items[*].href`, `items[*].url`, top-level `linkTo`, top-level `image` | All blocks (`block_type = None`) |
|
|
18
|
+
|
|
19
|
+
**Key constraint:** `GenericBlockLinksRetriever` only checks **top-level** fields named exactly `url`, `href`, or `preview_image`. It does NOT recurse into lists, dicts, or nested structures. Any link stored in a non-standard field name or nested inside a list requires the `CCAObjectListLinksRetriever` (or a new custom retriever).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Block-by-Block Analysis
|
|
24
|
+
|
|
25
|
+
### 1. Blocks WITH internal link fields — FULLY COVERED
|
|
26
|
+
|
|
27
|
+
These blocks use standard top-level field names (`href`, `url`) that `GenericBlockLinksRetriever` catches, or their nested `items` are covered by `CCAObjectListLinksRetriever`.
|
|
28
|
+
|
|
29
|
+
| Block | Field(s) | Widget | Covered By | Notes |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| **RedirectBlock** | `href` | `object_browser` | `GenericBlockLinksRetriever` | Standard `href` at top level |
|
|
32
|
+
| **CountryMapObservatory** | `href` | `object_browser` | `GenericBlockLinksRetriever` | Standard `href` at top level |
|
|
33
|
+
| **CollectionStatistics** | `href` | `object_browser` | `GenericBlockLinksRetriever` | Standard `href` at top level |
|
|
34
|
+
| **ASTNavigation** | `href` (top-level) | `object_browser` | `GenericBlockLinksRetriever` | Main entry page link |
|
|
35
|
+
| **ASTNavigation** | `items[*].href` | `object_browser` (in `object_list`) | `CCAObjectListLinksRetriever` | Nav items — `items` list with `href` field |
|
|
36
|
+
| **ContentLinks** | `items[*].source` | `object_browser` (in `object_list`) | `CCAObjectListLinksRetriever` | `source` in items is explicitly handled |
|
|
37
|
+
| **RelevantAceContent** | `items[*].source` | `object_browser` (in `object_list`) | `CCAObjectListLinksRetriever` | `source` in items is explicitly handled |
|
|
38
|
+
|
|
39
|
+
### 2. Blocks WITH internal link fields — NOT COVERED (GAP)
|
|
40
|
+
|
|
41
|
+
These blocks have fields that store internal links (via `object_browser`, path strings, or similar) but the field name or structure is **not** recognized by any existing retriever.
|
|
42
|
+
|
|
43
|
+
| Block | Field(s) | Widget / Type | Why Not Covered | Risk |
|
|
44
|
+
|---|---|---|---|---|
|
|
45
|
+
| **Listing** (standard, extended) | `linkHref` | `object_browser` | Field name is `linkHref` — not in `GenericBlockLinksRetriever` (`url`, `href`, `preview_image`) and not in `CCAObjectListLinksRetriever` (`linkTo`, `image`). The CCA retriever checks `linkTo` but Volto core uses `linkHref`. | **Medium** — The "Link to..." / "More..." button link is silently missed. |
|
|
46
|
+
| **RASTBlock** | `root_path` | plain `string` (path) | Stores a plain path string (e.g., `/en/knowledge-and-data/...`), NOT a `resolveuid/` URL. The retrievers only match strings containing `resolveuid`. | **Low** — Editors type paths manually; no object_browser. Moving the target folder breaks the link with no warning. |
|
|
47
|
+
|
|
48
|
+
### 3. Blocks WITHOUT internal link fields — NO ACTION NEEDED
|
|
49
|
+
|
|
50
|
+
These blocks either have no link-bearing fields, fetch content dynamically from the server, or use server-side `@components` views.
|
|
51
|
+
|
|
52
|
+
| Block | Rationale |
|
|
53
|
+
|---|---|
|
|
54
|
+
| **C3SIndicatorsGlossaryBlock** | Empty schema. Content fetched from `@components` view `c3s_indicators_glossary_table`. |
|
|
55
|
+
| **C3SIndicatorsListingBlock** | Empty schema. Content fetched from `@components` view `c3s_indicators_listing`. |
|
|
56
|
+
| **C3SIndicatorsOverviewBlock** | Empty schema (category field commented out). Content fetched from `@components` view `c3s_indicators_overview`. |
|
|
57
|
+
| **CaseStudyExplorer** | No schema file. Loads data from ArcGIS JSON endpoint (`@@case-studies-map.arcgis.json`). No content links stored in block data. |
|
|
58
|
+
| **CountryMapHeatIndex** | No schema file. Renders country map from static GeoJSON + metadata endpoint. No content links in block data. |
|
|
59
|
+
| **CountryMapProfile** | No schema file. Same pattern as CountryMapHeatIndex. No content links in block data. |
|
|
60
|
+
| **CountryProfileDetail** | No schema file. Renders HTML from `@components` view `countryprofile`. |
|
|
61
|
+
| **ECDEIndicators** | No schema file. Embeds CDS toolbox apps by region code. No content links in block data. |
|
|
62
|
+
| **FilterAceContent** | Schema has only filter parameters (vocabularies, search text, sort). No `object_browser` or link fields. Results are fetched dynamically. |
|
|
63
|
+
| **SearchAceContent** | Same as FilterAceContent — only filter/search parameters. No content links stored. |
|
|
64
|
+
| **FlourishEmbedBlock** | `embed_code` is a `textarea` with raw HTML/JS. Not parsed by any retriever. Links (if any) are external Flourish URLs. |
|
|
65
|
+
| **ReadMore** | Behavioral block only (labels, height, position). No links. |
|
|
66
|
+
| **DataConnectedEmbedBlock** | Extends `@eeacms/volto-datablocks`. Only adds a `languageParam` string field. The base block's URL fields (if any) are handled by the base package's own mechanisms. |
|
|
67
|
+
| **TransRegionSelect** | `region` is a vocabulary choice (not a link). Renders links dynamically from `@components` view. |
|
|
68
|
+
| **TabsBlock (Spotlight)** | Container block. Links in child blocks are discovered recursively by `visit_blocks`. The spotlight variation's `image` field (in tab settings) is covered by `CCAObjectListLinksRetriever` (top-level `image`). |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Summary of Gaps
|
|
73
|
+
|
|
74
|
+
### Gap 1: Listing block — `linkHref` field
|
|
75
|
+
|
|
76
|
+
**Problem:** The standard Volto listing block stores its "More..." link in a field named `linkHref`. The `GenericBlockLinksRetriever` only scans `url`, `href`, `preview_image`. The `CCAObjectListLinksRetriever` scans `linkTo` and `image` — neither matches `linkHref`.
|
|
77
|
+
|
|
78
|
+
**Impact:** If an editor configures a listing block with a "Link to..." destination, and that destination is moved or made private, the link integrity system will NOT report this listing block as a breacher.
|
|
79
|
+
|
|
80
|
+
**Remediation options:**
|
|
81
|
+
1. **Extend `CCAObjectListLinksRetriever`** to also scan `linkHref` (add it to the top-level fields list alongside `linkTo` and `image`). This is the simplest fix — one line change.
|
|
82
|
+
2. **Register a dedicated `ListingBlockLinksRetriever`** adapted to `block_type = "listing"`. More explicit but more code.
|
|
83
|
+
|
|
84
|
+
**Recommendation:** Option 1 — add `linkHref` to the top-level fields in `CCAObjectListLinksRetriever`.
|
|
85
|
+
|
|
86
|
+
### Gap 2: RASTBlock — `root_path` field
|
|
87
|
+
|
|
88
|
+
**Problem:** `root_path` stores a plain content path (e.g., `/en/knowledge-and-data/rast`) as a string. It is NOT stored as `resolveuid/<UID>`, so even if the field name were recognized, `get_urls_from_value` would not match it (it checks for `resolveuid` in the string).
|
|
89
|
+
|
|
90
|
+
**Impact:** If the root folder is moved, the RAST block silently points to a 404. No link integrity warning.
|
|
91
|
+
|
|
92
|
+
**Remediation options:**
|
|
93
|
+
1. **Change the widget** to `object_browser` and store as `resolveuid/` — then add `root_path` to a retriever. This is a schema change with migration implications.
|
|
94
|
+
2. **Accept the risk** — the field is typed manually by editors and is inherently fragile regardless of link integrity.
|
|
95
|
+
3. **Add a custom deserializer** that converts the path to `resolveuid/` on save, then add `root_path` to a retriever.
|
|
96
|
+
|
|
97
|
+
**Recommendation:** Option 2 (accept risk) for now — the field is a single path string used by a small number of blocks, and converting it to `object_browser` would be a larger UX change. Document the limitation.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Coverage Matrix
|
|
102
|
+
|
|
103
|
+
| Block | Link Fields | Generic Retriever | CCA Retriever | Gap? |
|
|
104
|
+
|---|---|---|---|---|
|
|
105
|
+
| RedirectBlock | `href` | ✅ | — | No |
|
|
106
|
+
| CountryMapObservatory | `href` | ✅ | — | No |
|
|
107
|
+
| CollectionStatistics | `href` | ✅ | — | No |
|
|
108
|
+
| ASTNavigation | `href` (top) | ✅ | — | No |
|
|
109
|
+
| ASTNavigation | `items[*].href` | ❌ | ✅ | No |
|
|
110
|
+
| ContentLinks | `items[*].source` | ❌ | ✅ | No |
|
|
111
|
+
| RelevantAceContent | `items[*].source` | ❌ | ✅ | No |
|
|
112
|
+
| **Listing** | `linkHref` | ❌ | ❌ | **Yes** |
|
|
113
|
+
| RASTBlock | `root_path` | ❌ | ❌ | Yes (low risk) |
|
|
114
|
+
| FlourishEmbedBlock | `embed_code` | ❌ | ❌ | N/A (external) |
|
|
115
|
+
| All others | (none) | — | — | No |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Notes on plone.restapi / plone.volto Checkouts
|
|
120
|
+
|
|
121
|
+
- **plone.restapi** is checked out at `backend/sources/plone.restapi/`. It provides `GenericBlockLinksRetriever` (fields: `url`, `href`, `preview_image`), `TextBlockLinksRetriever` (DraftJS), and `SlateBlockLinksRetriever`.
|
|
122
|
+
- **plone.volto** is NOT checked out in `backend/sources/`. It is installed as a packaged dependency in the backend venv. No custom link integrity retrievers were found in the EEA policy package (`eea.volto.policy`) — it only provides serialization/deserialization transformers, not link retrievers.
|
|
123
|
+
- The `CCAObjectListLinksRetriever` in `eea.climateadapt` is the **only** custom link integrity retriever in the project. It runs at order 10 (before Generic at order 1, but both return independent lists that are unioned).
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Recommendations
|
|
128
|
+
|
|
129
|
+
1. **Fix the Listing gap immediately** — add `linkHref` to `CCAObjectListLinksRetriever`'s top-level field scan (one-line change in `blocks_linkintegrity.py`).
|
|
130
|
+
2. **Consider adding `image` to Generic coverage analysis** — `CCAObjectListLinksRetriever` already covers `image`, but if a future block uses `image` without items, it would only be caught by CCA's retriever.
|
|
131
|
+
3. **Document the RASTBlock limitation** in the block's developer notes or BLOCKS.md.
|
|
132
|
+
4. **Future blocks:** Always name link fields `href` or `url` at the top level, or `items[*].href` / `items[*].source` for nested lists, to benefit from existing retrievers.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Volto Block Link Integrity Analysis Report - volto-cca-policy
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Identify blocks within the `volto-cca-policy` add-on that contain internal links in fields or structures not covered by the standard Plone/Volto link integrity discovery mechanisms.
|
|
5
|
+
|
|
6
|
+
## Standard Coverage Recap
|
|
7
|
+
|
|
8
|
+
The following mechanisms are provided by core packages:
|
|
9
|
+
|
|
10
|
+
1. **`plone.restapi` (Generic Retriever)**: Automatically extracts links from top-level fields: `url`, `href`, `preview_image`.
|
|
11
|
+
2. **`plone.restapi` (Visitors)**: Recursively visits sub-blocks stored in `blocks` or `data.blocks`.
|
|
12
|
+
3. **`plone.volto` (Visitors/Retrievers)**: Recursively visits sub-blocks stored in `columns`, `hrefList`, and `slides`.
|
|
13
|
+
4. **`eea.climateadapt` (Backend Custom Retriever)**: Specifically covers `items[*].source`, `items[*].href`, `items[*].url`, and top-level `linkTo` and `image`.
|
|
14
|
+
|
|
15
|
+
## Analysis of volto-cca-policy Blocks
|
|
16
|
+
|
|
17
|
+
| Block (@type) | Field Location | Covered? | Reason / Mechanism |
|
|
18
|
+
| :--- | :--- | :--- | :--- |
|
|
19
|
+
| `ContentLinks` | `items[*].source` | **Yes** | `CCAObjectListLinksRetriever` (Backend) |
|
|
20
|
+
| `RelevantAceContent` | `items[*].source` | **Yes** | `CCAObjectListLinksRetriever` (Backend) |
|
|
21
|
+
| `ASTNavigation` | `items[*].href` | **Yes** | `CCAObjectListLinksRetriever` (Backend) |
|
|
22
|
+
| `Listing` (Shadowed) | `linkTo` | **Yes** | `CCAObjectListLinksRetriever` (Backend) |
|
|
23
|
+
| `RedirectBlock` | `href` | **Yes** | `GenericBlockLinksRetriever` (Core) |
|
|
24
|
+
| `CountryMapObservatory`| `href` | **Yes** | `GenericBlockLinksRetriever` (Core) |
|
|
25
|
+
| `CollectionStatistics` | `href` | **Yes** | `GenericBlockLinksRetriever` (Core) |
|
|
26
|
+
| `TabsBlock` (Spotlight)| `tabs[*].blocks` | **NO** | `tabs` key is not visited by core visitors. |
|
|
27
|
+
| `TabsBlock` (Spotlight)| `tabs[*].image` | **NO** | `tabs` structure is not scanned for link fields. |
|
|
28
|
+
| `FlourishEmbedBlock` | `embed_code` | **NO** | Raw HTML/JS (requires parsing). |
|
|
29
|
+
| `CaseStudyExplorer` | `adaptation_options_links` | **NO** | Raw HTML (often dynamic source). |
|
|
30
|
+
| `RASTBlock` | `root_path` | **NO** | Hardcoded string path (not a UID). |
|
|
31
|
+
|
|
32
|
+
## Detailed Findings
|
|
33
|
+
|
|
34
|
+
### 1. TabsBlock (Spotlight Variation)
|
|
35
|
+
The `TabsBlock` in `volto-cca-policy` uses a custom `tabs` key to store a dictionary of tab data. Each tab can contain its own sub-blocks (`blocks` key) and a promotional `image`.
|
|
36
|
+
* **Sub-blocks Visibility**: Since neither `plone.restapi` nor `plone.volto` includes `tabs` in their `IBlockVisitor` implementations, the backend link integrity system is "blind" to any blocks placed inside a spotlight tab.
|
|
37
|
+
* **Image Visibility**: The `image` field within a tab is nested inside the `tabs` object list/map, which is not scanned by the generic or CCA-specific retrievers.
|
|
38
|
+
|
|
39
|
+
### 2. FlourishEmbedBlock
|
|
40
|
+
This block stores raw HTML or Javascript snippets for embedding Flourish charts. Internal links within these snippets are not tracked because they are not stored as structured data or `resolveuid/` patterns.
|
|
41
|
+
|
|
42
|
+
### 3. RASTBlock
|
|
43
|
+
Uses a `root_path` field which stores an absolute path as a string (e.g., `/en/knowledge-and-data/...`). Link integrity tracking depends on `resolveuid/` patterns and `zc.relation` objects. Paths are not automatically tracked or updated if the target moves.
|
|
44
|
+
|
|
45
|
+
## Recommendations
|
|
46
|
+
|
|
47
|
+
1. **Expand Backend Retriever**: Update `CCAObjectListLinksRetriever` (or add a new one) to handle the `tabs` structure:
|
|
48
|
+
* Visit each item in the `tabs` dictionary.
|
|
49
|
+
* Extract links from the `image` field if present.
|
|
50
|
+
* (Optional) If `tabs` items contain their own blocks, a custom `IBlockVisitor` should be registered to allow core recursion to reach them.
|
|
51
|
+
2. **Refactor RASTBlock**: If possible, change `root_path` to use a proper object browser widget and store a UID.
|
|
52
|
+
3. **Documentation**: Advise developers to stick to `blocks`, `columns`, or `slides` for sub-block storage, or `items` for list-based links to benefit from existing retrievers.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Understanding Link Integrity in Plone and Volto
|
|
2
|
+
|
|
3
|
+
## Use Case
|
|
4
|
+
A user is editing a content item and decides to change its workflow state to "Private". Before this action is finalized, the system should check if other content items on the website are linking to this item. If such links exist, the user should be informed that these links will effectively "break" for public users (leading to an unauthorized screen).
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
Identify a programmatic way (ideally a REST API endpoint) to retrieve a list of content items that link to a specific object.
|
|
8
|
+
|
|
9
|
+
## Research Findings
|
|
10
|
+
|
|
11
|
+
### 1. `plone.app.linkintegrity`
|
|
12
|
+
- **Mechanism**: Tracks internal links using the `zc.relation` catalog with the relationship name `isReferencing`.
|
|
13
|
+
- **Logic**: Extracts links from Dexterity `RichText` fields and Volto blocks (via `plone.volto`).
|
|
14
|
+
- **Querying**: Provides utility functions in `plone.app.linkintegrity.utils`, notably `getIncomingLinks(obj)`.
|
|
15
|
+
|
|
16
|
+
### 2. `plone.restapi`
|
|
17
|
+
- **Existing Endpoints**:
|
|
18
|
+
- **`/@linkintegrity?uids=<UID>`**: Returns "breaches" (items linking to the specified UIDs). This is the same logic used by Plone's delete confirmation screen.
|
|
19
|
+
- **`/@relations?target=<UID>&relation=isReferencing`**: A more generic endpoint to query the relation catalog. Omitting the `relation` parameter returns all types of incoming relations (including `relatedItems`).
|
|
20
|
+
- **Recommendation**: For the "warn before private" use case, `/@linkintegrity` is highly suitable because it returns structured data specifically designed for notifying users about broken links. However, `/@relations` is better if a simple list of *any* linking item is needed.
|
|
21
|
+
|
|
22
|
+
### 3. `plone.volto` / Volto Frontend
|
|
23
|
+
- **Backend**: Ensures that internal links within Volto blocks (columns, teasers, etc.) are correctly indexed by `plone.app.linkintegrity`.
|
|
24
|
+
- **Frontend Logic**:
|
|
25
|
+
- Volto has a `linkIntegrityCheck` action in `@plone/volto/actions` that calls the `/@linkintegrity` endpoint.
|
|
26
|
+
- The results are stored in the `linkIntegrity` reducer.
|
|
27
|
+
- The `ContentsDeleteModal` component in Volto core is the canonical example of how to display these breaches.
|
|
28
|
+
- **Workflow Integration**: The state transition dropdown is managed by the `Workflow` component (`@plone/volto/components/manage/Workflow/Workflow`).
|
|
29
|
+
|
|
30
|
+
## Recommended Solution
|
|
31
|
+
|
|
32
|
+
To implement the "warn before private" feature:
|
|
33
|
+
|
|
34
|
+
1. **Backend Endpoint**: Use `GET /@linkintegrity?uids=<UID>`.
|
|
35
|
+
2. **Frontend Integration (Shadowing)**:
|
|
36
|
+
- Shadow the core `Workflow` component by copying it to `frontend/src/addons/volto-cca-policy/src/customizations/volto/components/manage/Workflow/Workflow.jsx`.
|
|
37
|
+
- Intercept the `onChange` handler in the shadowed component.
|
|
38
|
+
- If the target state is "Private" (or similar), trigger the `linkIntegrityCheck`.
|
|
39
|
+
- Show a confirmation modal (similar to `ContentsDeleteModal`) if breaches are found.
|
|
40
|
+
|
|
41
|
+
## Volto Implementation Details
|
|
42
|
+
|
|
43
|
+
### Shadowing Path
|
|
44
|
+
```
|
|
45
|
+
frontend/src/addons/volto-cca-policy/src/customizations/volto/components/manage/Workflow/Workflow.jsx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Logic Hook
|
|
49
|
+
In the shadowed `Workflow.jsx`, modify the `transition` function:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const transition = (selectedOption) => {
|
|
53
|
+
const isPrivateTransition = ['private', 'reject', 'retract'].includes(selectedOption.value) ||
|
|
54
|
+
selectedOption.url.endsWith('/reject') ||
|
|
55
|
+
selectedOption.url.endsWith('/retract');
|
|
56
|
+
|
|
57
|
+
if (isPrivateTransition) {
|
|
58
|
+
setPendingOption(selectedOption);
|
|
59
|
+
dispatch(linkIntegrityCheck([content.UID]));
|
|
60
|
+
setShowWarningModal(true);
|
|
61
|
+
} else {
|
|
62
|
+
executeTransition(selectedOption);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Components to reuse
|
|
68
|
+
- `Confirm` from `semantic-ui-react` for the modal wrapper.
|
|
69
|
+
* **Existing Actions**: `linkIntegrityCheck` from `@plone/volto/actions`.
|
|
70
|
+
* **Existing Reducers**: `state.linkIntegrity.result` to access the breaches.
|
|
71
|
+
* **UI Reference**: `ContentsDeleteModal.jsx` in Volto core for the table rendering logic of broken links.
|
|
72
|
+
|
|
73
|
+
## Final Implementation
|
|
74
|
+
|
|
75
|
+
The feature was implemented in the `volto-cca-policy` add-on on a dedicated branch.
|
|
76
|
+
|
|
77
|
+
### Git Branch
|
|
78
|
+
- **Branch Name**: `link-integrity-workflow`
|
|
79
|
+
|
|
80
|
+
### Files Created/Modified
|
|
81
|
+
1. **Shadowed Component**: `src/customizations/volto/components/manage/Workflow/Workflow.jsx`
|
|
82
|
+
- Shadowed from `@plone/volto/components/manage/Workflow/Workflow.jsx`.
|
|
83
|
+
- Modified to intercept state changes to `private`, `reject`, and `retract`.
|
|
84
|
+
- Integrated with `linkIntegrityCheck` action and `WorkflowLinkIntegrityModal`.
|
|
85
|
+
2. **New Component**: `src/components/manage/Workflow/WorkflowLinkIntegrityModal.jsx`
|
|
86
|
+
- A custom confirmation modal that displays link integrity breaches.
|
|
87
|
+
- Uses `semantic-ui-react` for the UI (Confirm, Table, Loader).
|
|
88
|
+
- Accesses `state.linkIntegrity.result` to render the list of affected content.
|
|
89
|
+
3. **Components Index**: `src/components/index.js`
|
|
90
|
+
- Exported `WorkflowLinkIntegrityModal` for use in the shadowed `Workflow` component.
|
|
91
|
+
|
|
92
|
+
### Logic Summary
|
|
93
|
+
- **Trigger**: When the user selects a "Private" transition in the workflow dropdown.
|
|
94
|
+
- **Verification**: The `linkIntegrityCheck` action is dispatched for the current object's UID.
|
|
95
|
+
- **Race Condition Prevention**: The system explicitly waits for the `linkIntegrity.loaded` state to be true before deciding whether to auto-proceed or show the modal. This prevents transitions from executing prematurely due to stale or initial empty state.
|
|
96
|
+
- **Activity Indicators (UX)**:
|
|
97
|
+
- During the link integrity check, a `Dimmer` and `Loader` are shown within the `WorkflowLinkIntegrityModal` with the message "Checking references...".
|
|
98
|
+
- During the actual workflow transition execution, a `Dimmer` and `Loader` are shown over the state selection dropdown to indicate that the transition is in progress.
|
|
99
|
+
- **Interaction**:
|
|
100
|
+
- If no incoming links exist (`breaches.length === 0`), the transition proceeds automatically once the check is loaded.
|
|
101
|
+
- If incoming links are found, the `WorkflowLinkIntegrityModal` displays the list of referencing pages.
|
|
102
|
+
- The user can either "Cancel" the transition or select "Change state anyway" to proceed.
|
|
103
|
+
|
|
104
|
+
## User Story: Testing the Link Integrity Warning
|
|
105
|
+
|
|
106
|
+
### Description
|
|
107
|
+
As a Content Editor, I want to be warned when I am about to make a page private if other pages are linking to it, so that I can avoid creating broken links for visitors.
|
|
108
|
+
|
|
109
|
+
### Acceptance Criteria
|
|
110
|
+
1. **Positive Case (Breaches Found)**:
|
|
111
|
+
- Given a "Target Page" that is published.
|
|
112
|
+
- Given a "Source Page" that contains a link to "Target Page".
|
|
113
|
+
- When I go to "Target Page" and select "Make Private" (or "Reject" / "Retract") from the state dropdown.
|
|
114
|
+
- Then I should see a "Checking references..." loading indicator.
|
|
115
|
+
- Then I should see a warning modal titled "Warning: Potential broken links".
|
|
116
|
+
- Then the modal should list "Source Page" as an item that will have a broken link.
|
|
117
|
+
- When I click "Cancel", the modal should close and the page should remain "Published".
|
|
118
|
+
- When I click "Change state anyway", the modal should close and the page should transition to "Private".
|
|
119
|
+
|
|
120
|
+
2. **Negative Case (No Breaches)**:
|
|
121
|
+
- Given a "Target Page" that is published and has NO incoming links.
|
|
122
|
+
- When I select "Make Private" from the state dropdown.
|
|
123
|
+
- Then I should see a brief "Checking references..." indicator.
|
|
124
|
+
- Then the page should transition to "Private" immediately without showing a warning modal.
|
|
125
|
+
|
|
126
|
+
### Test Procedure
|
|
127
|
+
1. **Setup Content**:
|
|
128
|
+
- Create a page named "Linked Page" and Publish it.
|
|
129
|
+
- Create another page named "Referencing Page".
|
|
130
|
+
- In "Referencing Page", add a Text block and insert an internal link to "Linked Page". Publish "Referencing Page".
|
|
131
|
+
2. **Verify Warning**:
|
|
132
|
+
- Navigate to "Linked Page".
|
|
133
|
+
- Open the state dropdown in the toolbar.
|
|
134
|
+
- Select "Make Private".
|
|
135
|
+
- **Observe**: The "Checking references..." dimmer should appear briefly.
|
|
136
|
+
- **Verify**: The warning modal should appear listing "Referencing Page".
|
|
137
|
+
3. **Test Cancellation**:
|
|
138
|
+
- Click "Cancel" in the modal.
|
|
139
|
+
- **Verify**: The page is still in "Published" state.
|
|
140
|
+
4. **Test Confirmation**:
|
|
141
|
+
- Select "Make Private" again.
|
|
142
|
+
- Click "Change state anyway" in the modal.
|
|
143
|
+
- **Verify**: The page state changes to "Private" and a success toast appears.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Volto Block Link Discovery Analysis - volto-cca-policy
|
|
2
|
+
|
|
3
|
+
This document analyzes the custom blocks defined in the `volto-cca-policy` add-on to identify which fields contain internal links and whether they are covered by the standard Plone/Volto link integrity discovery mechanism.
|
|
4
|
+
|
|
5
|
+
## Standard Discovery Mechanism Recap
|
|
6
|
+
|
|
7
|
+
As documented in `volto-block-link-discovery.md`, the `GenericBlockLinksRetriever` automatically extracts internal links from the following top-level fields:
|
|
8
|
+
- `url`
|
|
9
|
+
- `href`
|
|
10
|
+
- `preview_image`
|
|
11
|
+
|
|
12
|
+
Any field not named exactly like one of these, or any link nested within a list or dictionary (unless it's a `blocks` container), requires a specialized retriever on the backend.
|
|
13
|
+
|
|
14
|
+
## Analysis of volto-cca-policy Blocks
|
|
15
|
+
|
|
16
|
+
### 1. Blocks Covered by Generic Retriever
|
|
17
|
+
The following blocks use standard field names at the top level and should be automatically tracked.
|
|
18
|
+
|
|
19
|
+
| Block | Field | Description |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `RedirectBlock` | `href` | The target object for the redirection. |
|
|
22
|
+
| `CountryMapObservatory` | `href` | The parent location of all country profiles. |
|
|
23
|
+
| `CollectionStatistics` | `href` | The destination listing page. |
|
|
24
|
+
| `ASTNavigation` | `href` | The main entry page to AST/UAST (top-level). |
|
|
25
|
+
|
|
26
|
+
### 2. Blocks NOT Covered (Custom Field Names)
|
|
27
|
+
The following blocks use field names that are not recognized by the generic retriever.
|
|
28
|
+
|
|
29
|
+
| Block | Field | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `Listing` (Shadowed) | `linkTo` | Standard Volto listing block uses `linkTo` for the "More..." link. |
|
|
32
|
+
|
|
33
|
+
### 3. Blocks NOT Covered (Nested Links)
|
|
34
|
+
The following blocks store links within an `object_list`. The generic retriever does not scan inside these lists.
|
|
35
|
+
|
|
36
|
+
| Block | Field Path | Description |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `ContentLinks` | `items[*].source` | A list of hand-picked content items. |
|
|
39
|
+
| `RelevantAceContent` | `items[*].source` | A list of hand-picked content items (assigned items). |
|
|
40
|
+
| `ASTNavigation` | `items[*].href` | Individual navigation steps in the AST map. |
|
|
41
|
+
|
|
42
|
+
### 4. Special Cases
|
|
43
|
+
|
|
44
|
+
#### `FlourishEmbedBlock`
|
|
45
|
+
- **Field**: `embed_code` (Textarea)
|
|
46
|
+
- **Status**: Not covered.
|
|
47
|
+
- **Reason**: Contains raw HTML/JS snippets. Discovery would require parsing the HTML for URLs, which is not performed by the standard block retrievers.
|
|
48
|
+
|
|
49
|
+
#### `TabsBlock` (Spotlight Variation)
|
|
50
|
+
- **Status**: Covered (indirectly).
|
|
51
|
+
- **Reason**: This is a container block. The `NestedBlocksVisitor` handles the recursion into its child blocks, so any links within those children will be discovered normally.
|
|
52
|
+
|
|
53
|
+
#### `ReadMore`
|
|
54
|
+
- **Status**: No Links.
|
|
55
|
+
- **Reason**: This is a behavioral block that manipulates the DOM of its siblings on the frontend. It does not store links in its own block data.
|
|
56
|
+
|
|
57
|
+
## Recommendations for Link Integrity
|
|
58
|
+
|
|
59
|
+
To ensure full coverage for `volto-cca-policy` content, the following actions are recommended:
|
|
60
|
+
|
|
61
|
+
1. **Backend Adapters**: Register custom `IBlockFieldLinkIntegrityRetriever` adapters for `ContentLinks`, `RelevantAceContent`, and `ASTNavigation` to extract links from their `items` lists.
|
|
62
|
+
2. **Field Mapping**: If possible, refactor `ContentLinks` and `RelevantAceContent` to use `href` or `url` instead of `source` for individual items, although a custom retriever is still needed for the list structure.
|
|
63
|
+
3. **Core Coverage**: Verify if `plone.restapi` or `plone.volto` provides a specialized retriever for the `listing` block's `linkTo` field. If not, one should be added.
|