Skip to content

Conversation

@oidacra
Copy link
Member

@oidacra oidacra commented Feb 5, 2026

Summary

Implements comprehensive character limit validation for Story Block fields with real-time feedback, enhanced error handling in the block editor component, and improved test coverage for validation scenarios.
This ensures content editors receive immediate feedback when exceeding configured character limits and prevents invalid content from being saved.

New Edit Content

CleanShot.2026-02-05.at.11.59.04.mp4

Old Edit Content

CleanShot.2026-02-06.at.12.55.18.mp4

Changes Made

Frontend (Angular/TypeScript)

Block Editor Component (core-web/libs/block-editor/src/lib/components/dot-block-editor/)

  • Enhanced error handling and validation (dot-block-editor.component.ts:157-196)

    • Added hasFieldError input to receive external validation state from parent components
    • Implemented charLimitError getter to expose character limit validation errors to the template
    • Added hasError getter combining external and internal error states for unified error styling
    • Implemented requiredError getter for displaying required field validation messages
    • Added updateCharLimitValidity() method that validates character count against charLimit and sets charLimitExceeded form control error
    • Implemented clearCharLimitError() method to properly remove char limit errors while preserving other validation errors
    • Enhanced editor lifecycle with char limit validation on create, update, and proper onTouched handling on blur events
  • Template improvements (dot-block-editor.component.html:26-54)

    • Added error styling with editor-wrapper--error class applied when validation errors exist
    • Created structured footer layout displaying both validation errors and character counts
    • Added localized error messages for required fields and character limit exceeded scenarios
    • Implemented responsive layout with error messages (flex: 7) and character counter (flex: 5)
  • Styling enhancements (dot-block-editor.component.scss:48-122)

    • Added .editor-wrapper--error class with red outline for visual error indication
    • Implemented .block-editor__footer with flexbox layout for error messages and counters
    • Styled error messages with red color palette and appropriate font sizing
  • Comprehensive test coverage (dot-block-editor.component.spec.ts:442-672)

    • Added 24 new test cases covering all validation scenarios
    • Tested hasFieldError input behavior and hasError getter combinations
    • Validated charLimitError getter with various error states
    • Tested requiredError with touched/untouched control states
    • Comprehensive onBlockEditorChange tests for character limit validation edge cases (exceeds limit, within limit, at limit, no limit, preserving other errors)

Edit Content Form Integration

  • Validator integration (core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts:526-573)

    • Implemented blockEditorRequiredValidator() for Story Block fields instead of generic Validators.required
    • Custom validator checks for actual text content, not just JSON structure presence
  • Custom validators (core-web/libs/edit-content/src/lib/utils/validators.ts:1-82)

    • New blockEditorRequiredValidator() function that validates Story Block content has meaningful text
    • Comprehensive test suite (validators.spec.ts) with 12 test cases covering empty content, whitespace-only, valid text, nested structures, and edge cases
  • Field wrapper enhancements (core-web/libs/edit-content/src/lib/fields/shared/base-wrapper-field.ts)

    • Added fieldHasError getter to expose form control validation state to child components
    • Enables proper error state propagation to block editor component
  • Template updates (dot-edit-content-block-editor.component.html:16)

    • Added hasFieldError binding to block editor component for external error state
    • Added hint text support in field label

Backend (Java)

Story Block Validation Utilities

  • Enhanced validation logic (dotCMS/src/main/java/com/dotcms/contenttype/util/StoryBlockUtil.java)

    • Added validateCharLimit() method that parses Story Block JSON and counts plain text characters
    • Recursive traversal of Story Block structure to extract all text content from nested nodes
    • Throws DotContentletValidationException with CHAR_LIMIT_EXCEEDED code when limit exceeded
    • Integrated validation into validateContent() method with proper null/empty checks
  • Comprehensive test coverage (dotCMS/src/integration-test/java/com/dotcms/contenttype/test/StoryBlockUtilTest.java)

    • Added 15+ new test cases covering character limit validation scenarios
    • Tests for nested content structures (paragraphs, headings, lists, tables)
    • Edge cases: empty content, whitespace-only, HTML entity handling, nested structures
    • Validation error code verification and error message formatting

Exception Handling Enhancements

  • Extended validation exception (dotCMS/src/main/java/com/dotmarketing/business/DotContentletValidationException.java:61-88)

    • Added CHAR_LIMIT_EXCEEDED validation code with field name and character limit parameters
    • Added STORYBLOCK_INVALID_JSON code for malformed Story Block content
    • Enhanced error message formatting with localized message support
  • New exception utilities (dotCMS/src/main/java/com/dotcms/exception/ExceptionUtil.java:1-14)

    • Added getValidationException() helper method for creating validation exceptions with proper error codes
    • Centralized validation exception creation pattern

API Integration

  • ESContentletAPIImpl (dotCMS/src/main/java/com/dotcms/business/ESContentletAPIImpl.java)

    • Integrated Story Block validation into contentlet save flow
    • Calls StoryBlockUtil.validateContent() for all Story Block fields with character limits
  • ContentletAjax (dotCMS/src/main/java/com/dotcms/rest/api/v1/portlets/contentlet/ajax/ContentletAjax.java)

    • Added Story Block validation to AJAX content save endpoint
    • Proper exception handling and error response formatting

Language Properties

  • New localization keys (dotCMS/src/main/webapp/WEB-INF/messages/Language.properties)
    • Added dot.edit.content.form.field.charLimitExceeded message for frontend error display

Tests

Frontend Tests

  • Block editor validation tests (dot-block-editor.component.spec.ts)

    • 24 new test cases for error handling, validation states, and character limit enforcement
    • Tests for input properties, getters, error state combinations, and validation edge cases
  • Custom validator tests (validators.spec.ts)

    • 12 test cases for blockEditorRequiredValidator() covering empty content, whitespace, valid text, nested structures

Backend Tests

  • Story Block validation tests (StoryBlockValidationTest.java)

    • Enhanced with 15+ new test scenarios for character limit validation
    • Tests for nested content structures, edge cases, error codes, and message formatting
  • StoryBlockUtil tests (StoryBlockUtilTest.java)

    • Extended with comprehensive character counting tests
    • Tests for recursive text extraction from complex Story Block structures

Configuration & Documentation

  • TypeScript unit testing agent (.cursor/agents/typescript-unit-testing.md)

    • New agent guide for AI assistants creating Angular/TypeScript tests
    • Documents Spectator patterns, data-testid conventions, and testing best practices
  • MCP server configuration (core-web/.mcp.json, core-web/.gemini/settings.json)

    • Added Nx MCP server configuration for improved AI tooling integration
  • Documentation formatting (core-web/CLAUDE.md, core-web/AGENTS.md)

    • Converted bullet point lists to markdown format for better readability

Technical Details

Character Limit Validation Flow

  1. Frontend Real-time Validation:

    • Block editor monitors character count via TipTap's character counter extension
    • On every update event, updateCharLimitValidity() compares count against configured charLimit
    • If exceeded, sets charLimitExceeded error on form control and marks as touched
    • Template reactively displays error message and applies error styling
    • Form submission is blocked by Angular form validation
  2. Backend Server-side Validation:

    • When content is saved, StoryBlockUtil.validateContent() is called
    • Recursively traverses Story Block JSON structure extracting all text nodes
    • Counts plain text characters (excluding HTML/JSON markup)
    • Throws DotContentletValidationException with CHAR_LIMIT_EXCEEDED code if limit exceeded
    • Returns proper error response to frontend with localized message

Error Handling Architecture

  • Unified Error State: hasError getter combines external validation (hasFieldError) with internal char limit validation
  • Error Preservation: clearCharLimitError() properly removes char limit errors while preserving other validation errors (required, regex, etc.)
  • Form Control Integration: Implements proper ControlValueAccessor pattern with onTouched() called on blur, not on every change
  • Visual Feedback: Error styling (red border) applied immediately when validation fails

Key Design Decisions

  • Custom Required Validator: Story Block fields need special handling because empty Story Block JSON ({"type":"doc","content":[]}) is technically a value, but not meaningful content
  • Character Counting: Backend counts plain text only (no markup), matching user expectation and editor character counter display
  • Error Code System: Used structured error codes (CHAR_LIMIT_EXCEEDED) instead of string messages for better localization and client-side handling
  • Reactive Error Display: Leveraged Angular signals and getters for efficient reactive error display without manual change detection

Breaking Changes

None

Testing

  • Unit tests added/updated (36+ new test cases across frontend and backend)
  • Integration tests added/updated (Story Block validation integration tests)
  • Manual testing performed (tested in edit content form with various character limits)
  • E2E tests added/updated (not applicable for this internal validation feature)

Related Issues

Closes #34304

Additional Notes

  • Character limit validation is enforced both client-side (for immediate feedback) and server-side (for security)
  • The validation logic extracts plain text from Story Block JSON, matching the character counter display
  • Error messages are fully localized using the existing message key system
  • The implementation follows dotCMS patterns: UtilMethods.isSet() for null checks, Logger for logging, and proper exception hierarchy
  • Frontend uses modern Angular syntax (@if, input(), data-testid) as per dotCMS frontend standards

This PR fixes: #34304

This update introduces character limit validation for Story Block fields in the content management system. The validation checks if the character count exceeds a specified limit and logs warnings accordingly. Additionally, it enhances the  to handle character limit errors, ensuring that relevant error messages are generated when limits are exceeded.

Key changes include:
- New method in  to extract character count from Story Block JSON.
- Updates to  to validate character limits during content checks.
- Modifications to  and  to manage character limit errors.
- Added tests to ensure proper validation behavior for Story Block fields.

This feature improves content integrity by enforcing character limits on Story Block fields, enhancing user experience and data consistency.
This update introduces improved error handling and validation for the DotBlockEditor component. Key changes include:

- Added  input to manage external error states.
- Implemented  and  getters to provide detailed error feedback in the UI.
- Enhanced the template to display error messages for character limits and required fields.
- Introduced styling for error states in the editor wrapper.
- Updated tests to cover new validation scenarios and ensure robust error handling.

These enhancements improve user experience by providing clear feedback on content validation, ensuring data integrity in the editing process.
@oidacra oidacra self-assigned this Feb 6, 2026
@oidacra oidacra changed the title 34304 block editor fix feat: implement Story Block character limit validation and enhance error handling (#34304) Feb 6, 2026
@oidacra oidacra marked this pull request as ready for review February 6, 2026 17:51
@nicobytes nicobytes requested a review from Copilot February 6, 2026 17:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements character-limit validation for Story Block / Block Editor fields across the Angular edit-content UI and backend contentlet validation flow, aiming to prevent saving content that exceeds configured limits and to improve validation UX and error reporting.

Changes:

  • Frontend: adds real-time char limit + required validation UI/logic to the block editor, integrates a custom “required” validator for Block Editor fields, and propagates field error state to the editor.
  • Backend: adds a CHAR_LIMIT validation code/path to DotContentletValidationException, maps it to localized error messages, and enforces char limit during validateContentlet.
  • Tests & tooling/docs: expands integration/unit test coverage and adds AI tooling configuration/docs updates.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
dotcms-integration/src/test/java/com/dotcms/contenttype/test/StoryBlockUtilTest.java Adds unit tests for StoryBlockUtil.getCharCount and refines test docs.
dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockValidationTest.java Adds integration tests for Story Block charLimit behavior during checkin.
dotCMS/src/main/webapp/WEB-INF/messages/Language.properties Adds localized message for char limit exceeded.
dotCMS/src/main/java/com/dotmarketing/util/importer/ImportLineValidationCodes.java Adds CHAR_LIMIT_EXCEEDED validation code.
dotCMS/src/main/java/com/dotmarketing/portlets/fileassets/business/FileAssetValidationException.java Extends constructor support to carry char limit metadata.
dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/business/DotContentletValidationException.java Adds char limit validation key, tracking map, builder support, and helpers.
dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/ajax/ContentletAjax.java Surfaces char limit validation errors in AJAX save responses.
dotCMS/src/main/java/com/dotcms/exception/ExceptionUtil.java Maps char limit validation errors into structured ValidationErrors.
dotCMS/src/main/java/com/dotcms/contenttype/util/StoryBlockUtil.java Adds getCharCount helper for extracting TipTap attrs.charCount.
dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java Enforces Story Block charLimit during validateContentlet.
core-web/libs/edit-content/src/lib/utils/validators.ts Adds custom blockEditorRequiredValidator() and JSON traversal helpers.
core-web/libs/edit-content/src/lib/utils/validators.spec.ts Adds unit tests for the new block editor required validator.
core-web/libs/edit-content/src/lib/fields/shared/base-wrapper-field.ts Adjusts required detection to prefer field definition over validator inference.
core-web/libs/edit-content/src/lib/fields/dot-edit-content-block-editor/dot-edit-content-block-editor.component.html Passes hasFieldError into the block editor and moves hint to label.
core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts Uses custom required validator for Block Editor fields.
core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.ts Adds error-state inputs/getters and sets/clears charLimitExceeded form errors.
core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.html Shows required/char-limit messages and applies error styling.
core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.scss Adds error outline + footer layout styling.
core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.spec.ts Adds unit tests for new error/validation behavior.
core-web/CLAUDE.md Markdown formatting tweaks.
core-web/AGENTS.md Markdown formatting tweaks.
core-web/.mcp.json Adds Nx MCP server configuration.
core-web/.gemini/settings.json Adds Gemini MCP server configuration.
.cursor/agents/typescript-unit-testing.md Adds TypeScript unit testing agent guide.

This commit refines the DotBlockEditor component by updating the HTML structure and CSS styles for error messages. Key changes include:

- Replaced the footer error container with a more flexible layout using PrimeFlex classes.
- Updated error message styling to enhance visibility and consistency.
- Removed redundant CSS rules related to the footer, streamlining the component's styles.

These adjustments improve the user interface and ensure better alignment with the overall design standards.
@semgrep-code-dotcms-test
Copy link

Semgrep found 1 ssc-4fd3a3fc-acff-4277-9d88-60469f5a4fa5 finding:

  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-block-editor/dot-edit-content-block-editor.component.ts

Risk: Affected versions of @angular/compiler and @angular/core are vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'). Angular's template compiler fails to classify the href and xlink:href attributes on SVG <script> elements as Resource URL contexts. This allows an attacker to bind a malicious data: URI or external script via [attr.href] or [attr.xlink:href], resulting in arbitrary JavaScript execution (XSS) in the victim's browser.

Fix: Upgrade this library to at least version 20.3.16 at core/core-web/yarn.lock:512.

Reference(s): GHSA-jrmj-c5cx-3cw6, CVE-2026-22610

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

@oidacra oidacra enabled auto-merge February 6, 2026 19:23
@oidacra oidacra added this pull request to the merge queue Feb 9, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 9, 2026
@erickgonzalez erickgonzalez added this pull request to the merge queue Feb 9, 2026
Merged via the queue into main with commit 0eaf174 Feb 9, 2026
39 checks passed
@erickgonzalez erickgonzalez deleted the 34304-block-editor-fix branch February 9, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[DEFECT] Block Editor Character Limit Not Enforced on Save

4 participants