feat(uve): Page Scanner — a11y and GEO report tools with private URL handling#35212
feat(uve): Page Scanner — a11y and GEO report tools with private URL handling#35212wezell merged 53 commits intouve-experimentfrom
Conversation
- Add DOT_PAGE_SCANNER_API_URL and DOT_PAGE_SCANNER_API_AUTH_TOKEN env vars with whitelist exemption for token key - Add FEATURE_FLAG_PAGE_SCANNER boolean feature flag - Add PageScannerResource proxy endpoints POST /api/v1/page-scanner/a11y/check and /geo/check with short-lived JWT injection and 503 guard when env vars are missing - Add DotPageScannerReportComponent modal (90vh p-dialog) with skeleton loading, a11y accordion grouped by violation code, and GEO report with doughnut chart + category breakdown + top issues - Refactor dot-page-tools-seo panel: add prominent Accessibility/GEO tiles at top (gated by flag), clamp existing tool descriptions to 2 lines - Wire up in DotEmaShellComponent with feature flag guard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tokens Replace Tailwind bg-* classes on styleClass with [style] bindings overriding --p-chip-background and --p-chip-color design tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ervice URL - Simplified HTML structure in dot-page-scanner-report.component.html for better readability. - Updated CLOUDFLARE_BASE URL in dot-page-scanner.service.ts to a new endpoint. - Added new i18n message keys for GEO score in Language.properties. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…child components - Extract DotPageScannerA11yReportComponent and DotPageScannerGeoReportComponent - Move shared types to models.ts and CHIP_STYLES to chip-styles.ts - Parent keeps dialog shell, loading/error state and HTTP orchestration - Fix chart colors: Chart.js requires hex values, not CSS variables - Add GEO score heading and description i18n keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all getXxx() template calls with computed() signals and pre-computed data properties to avoid re-evaluation on every change detection cycle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anner templates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ynamically in template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l styling to templates
…to template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ture - Changed line height in styles.scss to 1.4rem for better readability. - Updated CLOUDFLARE_BASE URL in dot-page-scanner.service.ts. - Added 'message' field to A11yGroup interface in models.ts for improved data handling. - Refactored dot-page-scanner-a11y-report.component.html for better layout and accessibility. - Enhanced dot-page-scanner-a11y-report.component.ts to include message descriptions in the report.
…blocks for element/selector Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…canner service Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… enum in page scanner report Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nent, remove tags from model Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… scanner button design Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l instructions Instead of sending localhost/private IPs to the scanner API and getting a 400, detect private URLs before making the HTTP call and immediately show the tunnel setup UI. Also adds the private URL error UI with 3-step cloudflared instructions and i18n keys. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tions Show a warning screen with two paragraphs (factual + safety note) and an outlined accept button before revealing the tunnel setup steps. Resets consent on dialog close. Also splits the consent message into two i18n keys for clarity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…omponent Add dot-page-scan-loading with a browser illustration and a scan line that animates up and down in a loop, shared between a11y and geo reports. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iption Consent message now clearly mentions both dotCMS and headless apps in one short sentence. Removed the duplicate description paragraph from the instructions screen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove hardcoded default for DOT_PAGE_SCANNER_API_AUTH_TOKEN so unconfigured installs correctly return 503. Remove the token from ConfigurationResource whitelist and drop BLACKLIST_EXEMPTIONS to prevent token exposure via /api/v1/configuration/config. API URL remains in the whitelist as it is safe to expose. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Import DotMessageService from @dotcms/data-access (not @dotcms/ui) - Store takeUntilDestroyed() as field to satisfy injection context requirement - Type scan$ as Observable union to resolve subscribe overload ambiguity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use standard dotCMS ResponseEntityView<ErrorEntity> for all error responses instead of plain strings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detect PAGE_SCANNER_NOT_CONFIGURED error code from the 503 response and show a dedicated UI telling the user to contact their administrator. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove client-side private URL pre-check so the not-configured error takes priority. Private URL detection now relies solely on the server error response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Map external API auth errors to 502 BAD_GATEWAY before returning to the browser. A raw 401 from the upstream service would be intercepted by the Angular HTTP interceptor and log the user out of dotCMS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detect PAGE_SCANNER_AUTH_FAILED error code and show a dedicated UI with key_off icon telling the user credentials are invalid and to contact their administrator. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace repeated icon+title+description divs with a reusable DotPageScannerMessageComponent that supports ng-content for optional extra content like buttons and warnings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…plate Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
...i/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.html
Show resolved
Hide resolved
.../ui/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.ts
Show resolved
Hide resolved
| margin-right: 2.25rem; | ||
| } | ||
| } | ||
|
|
||
| .p-dialog { | ||
| .p-dialog-header { | ||
| padding: spacing.$spacing-4; | ||
| border-bottom: 1px solid colors.$color-palette-gray-300; | ||
| padding: 1rem; | ||
| border-bottom: 1px solid var(--p-gray-300); | ||
|
|
||
| .p-dialog-header-icon { | ||
| opacity: 0; | ||
| } | ||
| } | ||
|
|
||
| .p-dialog-content { | ||
| padding: 0 spacing.$spacing-3; | ||
| padding: 0 0.75rem; | ||
| } |
…timization - Button title: 'GEO Routing' -> 'Generative Engine Optimization' - Button subtitle: 'Analyze geographic performance' -> 'Optimize content for AI-powered search engines' - Dialog title: 'GEO Report' -> 'Generative Engine Optimization (GEO) Report' - Score description: updated to name specific AI engines (Google AI Overviews, Perplexity, ChatGPT) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| long ttl = page.getCacheTTL() > 0 ? page.getCacheTTL() * 1000 : 60 * 60 * 24 * 7 * 1000; // 1 week | ||
|
|
||
| Logger.debug(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); | ||
| Logger.info(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); |
There was a problem hiding this comment.
Changing the logging level here can generate too much noise. Do you really need this?
There was a problem hiding this comment.
yeah, this is from a bad merge - I think I just turned this to debug
There was a problem hiding this comment.
Not ours — this change came in via a merge, we did not modify StaticPageCacheImpl.java.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java
Outdated
Show resolved
Hide resolved
Restored files that were dropped during a merge conflict resolution: - Task260403SetLz4CompressionOnTextColumns.java - Task260403SetPermissionReferenceUnlogged.java - FiltersTest.java (restored shouldResolvePageWithTrailingSlash test) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java
Outdated
Show resolved
Hide resolved
| long ttl = page.getCacheTTL() > 0 ? page.getCacheTTL() * 1000 : 60 * 60 * 24 * 7 * 1000; // 1 week | ||
|
|
||
| Logger.debug(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); | ||
| Logger.info(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); |
There was a problem hiding this comment.
yeah, this is from a bad merge - I think I just turned this to debug
dotCMS/src/main/java/com/dotmarketing/servlets/InitServlet.java
Outdated
Show resolved
Hide resolved
| long ttl = page.getCacheTTL() > 0 ? page.getCacheTTL() * 1000 : 60 * 60 * 24 * 7 * 1000; // 1 week | ||
|
|
||
| Logger.debug(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); | ||
| Logger.info(this.getClass(), () -> "PageCache Put: ttl:" + ttl + " key:" + cacheKey); |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| final HttpServletRequest request, | ||
| final HttpServletResponse response, | ||
| final Map<String, Object> body, | ||
| final String checkType) { |
There was a problem hiding this comment.
checktype should be an enum I think
There was a problem hiding this comment.
Fixed — added CheckType enum with a11y and geo values.
| public Response a11yCheck( | ||
| @Context final HttpServletRequest request, | ||
| @Context final HttpServletResponse response, | ||
| final Map<String, Object> body) { |
There was a problem hiding this comment.
Probably want a typed Form object here? Back at you Mr. Freddy Typescript!
There was a problem hiding this comment.
Fixed — replaced Map<String, Object> with typed PageScanCheckForm DTO.
Replace string checkType param with CheckType enum (a11y/geo) and replace Map<String, Object> request body with typed PageScanCheckForm. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What
Adds two new Page Scanner tools to the Universal Visual Editor: an Accessibility (a11y) report and a GEO routing report, accessible from the Page Tools panel.
Why
Editors working in UVE need actionable feedback on WCAG compliance and geographic content performance without leaving the editor. The scanner calls an external API, so the feature needs to handle cases where the page URL is not publicly reachable.
Key decisions
.local) are detected client-side before any API call, immediately surfacing a consent gate and cloudflared tunnel setup guide — covers both local dotCMS instances and local headless webapps connected via UVE.signalStatewith astatus: 'idle' | 'pending' | 'done'enum instead of boolean flags, per the Kent C. Dodds pattern.dot-page-scan-loadingcomponent with an animated browser illustration and scan line — shared between both report types.::ng-deepdialog overrides).Scope
dot-page-scanner-report,dot-page-scanner-a11y-report,dot-page-scanner-geo-report,dot-page-scan-loadingdot-page-scanner.serviceLanguage.properties