Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6ebd2de
chore: ignore .worktrees directory
rismehta Mar 19, 2026
4e83d24
docs: add base, field and container JCR authoring schema YAML files
rismehta Mar 19, 2026
4547152
docs: add authoring schemas and update READMEs for date-type field co…
rismehta Mar 19, 2026
b56096d
docs: add authoring schemas and update READMEs for text-type field co…
rismehta Mar 19, 2026
615c57c
docs: add authoring schemas and update READMEs for choice field compo…
rismehta Mar 19, 2026
8973f2b
docs: fix copy-paste data-cmp-is error in numberinput README
rismehta Mar 19, 2026
176a272
docs: fix copy-paste errors in dropdown README
rismehta Mar 19, 2026
fd2ce13
docs: add authoring schemas and update READMEs for fileinput, image, …
rismehta Mar 19, 2026
0d95969
docs: add authoring schemas and update READMEs for fileinput, image, …
rismehta Mar 19, 2026
4a9c69e
docs: add authoring schemas and update READMEs for button, text, title
rismehta Mar 19, 2026
5a3dbf9
docs: add authoring schemas and update READMEs for fragment, captcha,…
rismehta Mar 19, 2026
38edde5
test: expand coverage for enableUncheckedValue absent coercion in Che…
rismehta Mar 19, 2026
a19b6a0
fix: address schema quality issues — deduplicate PanelImpl properties…
rismehta Mar 19, 2026
b65cc0b
feat: add JCR authoring schema validation tests and root discriminato…
rismehta Mar 19, 2026
266b01f
feat: add root discriminator schema and JCR authoring schema validati…
rismehta Mar 19, 2026
077cecf
test: add full FormContainer JCR authoring schema validation test
rismehta Mar 19, 2026
3689569
fix: correct schema discriminator, add missing fieldTypes, allow JCR …
rismehta Mar 19, 2026
d60ae2a
fix: make flattened test schemas strict + fix base schema additionalP…
rismehta Mar 19, 2026
7dab03c
fix(schema): document JCR default-value omission rules and fix TextIm…
rismehta Mar 19, 2026
11316e6
fix(schema): address PR review comments — schema sync, copyright, jac…
rismehta Mar 19, 2026
58602f0
refactor(schema): eliminate flattened JSON schemas — single source of…
rismehta Mar 19, 2026
daf3cae
feat(script): fetch JCR authoring schemas from GitHub at runtime
rismehta Mar 19, 2026
425ea42
feat(script): print GitHub schema source and SHA in header output; fi…
rismehta Mar 19, 2026
8b4c36c
fix(script): fix negative validation test to use invalid fieldType en…
rismehta Mar 19, 2026
516ba5b
feat(script): add argparse CLI dispatch for skill-ready individual op…
rismehta Mar 19, 2026
ad82466
fix(script): add --json flag to each argparse subparser for post-subc…
rismehta Mar 19, 2026
1298385
fix(script): emit JSON on early-return error paths in cmd_put and cmd…
rismehta Mar 19, 2026
769b876
docs(script): add comprehensive CLI usage docstring with env vars, su…
rismehta Mar 19, 2026
0150a24
feat(e2e): add JCR authoring schema Cypress validation test
rismehta Mar 19, 2026
f80a93a
chore(e2e): replace yarn.lock with package-lock.json (npm 7.5.4)
rismehta Mar 19, 2026
5d5f20d
docs(authoring-schema): add ADR-001 explaining JCR authoring schema v…
rismehta Mar 19, 2026
3eba34b
chore(af-core): apply code formatter (format-code profile)
rismehta Mar 19, 2026
3533a1e
feat(validate): support recursive tree validation in cmd_validate
rismehta Mar 19, 2026
ef0aeec
fix(build): fix RAT license check failures
rismehta Mar 19, 2026
9f0c000
fix(schema): resolve patternProperties ref scope drift via referencin…
rismehta Mar 19, 2026
f4eaa76
fix(cypress): fix validator circular ref crash + add failing assertio…
rismehta Mar 19, 2026
3403596
feat(content-api): add create-site subcommand and --content/--content…
rismehta Mar 19, 2026
aab8cbe
fix(e2e): scroll overlay into view before clicking hidden toolbar
rismehta Mar 19, 2026
3c2d6d9
fix(schema-cache): key cache on authoring-schema subtree SHA not comm…
rismehta Mar 20, 2026
50b2776
fix(schema): replace fd:viewType-based captcha discriminator with sin…
rismehta Mar 20, 2026
f6560fe
fix(schema): resolve all 148 sample form validation failures
rismehta Mar 20, 2026
fdaa9bb
fix(e2e): validate all sample forms instead of random 5
rismehta Mar 20, 2026
beabcc7
fix(authoring-schema): fix remaining schema violations across all sam…
rismehta Mar 20, 2026
57dba63
fix(e2e): simplify openEditableToolbar to always scroll+click+assert
rismehta Mar 22, 2026
a3c7518
fix(e2e): retry overlay click once when EditableToolbar stays hidden
rismehta Mar 22, 2026
4740e36
feat(scripts): create dam:Asset stub alongside cq:Page for Forms Mana…
rismehta Mar 23, 2026
b2edbc2
feat(authoring-schema): Content API validation with JCR authoring schema
rismehta Mar 23, 2026
02d34b0
fix(pom): correct maven-resources-plugin indentation in af-core pom
rismehta Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ ui.tests/test-module/**/*.cy.json

# Agent working artifacts
docs/superpowers/
.worktrees/
38 changes: 38 additions & 0 deletions bundles/af-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,38 @@
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-authoring-schemas-from-docs</id>
<phase>generate-test-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<!--
Single source of truth: docs/authoring-schema/ YAML files.
Copied here at build time so tests can load them from the classpath.
Do NOT manually edit YAML files in src/test/resources/authoring-schema/ —
edit docs/authoring-schema/ instead and rebuild.
-->
<outputDirectory>${project.build.testOutputDirectory}/authoring-schema</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../../docs/authoring-schema</directory>
<includes>
<include>**/*.yaml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down Expand Up @@ -587,6 +619,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@
import org.apache.commons.lang3.StringUtils;

public enum Heading {
H1("h1"),
H2("h2"),
H3("h3"),
H4("h4"),
H5("h5"),
H6("h6");
H1("h1"), H2("h2"), H3("h3"), H4("h4"), H5("h5"), H6("h6");

private String element;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ public class ComponentDataImpl implements FormComponentData {
/**
* Creates a new ComponentDataImpl instance.
*
* Note: This constructor stores references to FormComponent and Resource objects.
* These objects are designed to be immutable and shared across the system.
* The FormComponent interface provides read-only access to form component data,
* and the Resource interface represents an immutable JCR resource.
* Note: This constructor stores references to FormComponent and Resource objects. These objects are designed to be
* immutable and shared across the system. The FormComponent interface provides read-only access to form component
* data, and the Resource interface represents an immutable JCR resource.
*
* @param component The form component (immutable, read-only interface)
* @param resource The JCR resource (immutable, read-only interface)
* @param component
* The form component (immutable, read-only interface)
* @param resource
* The JCR resource (immutable, read-only interface)
*/
@SuppressFBWarnings(
value = "EI_EXPOSE_REP2",
Expand Down Expand Up @@ -161,9 +162,7 @@ public String getFieldType() {
@Nullable
public final String getJson() {
try {
return String.format("{\"%s\":%s}",
this.getId(),
new ObjectMapper().writeValueAsString(this));
return String.format("{\"%s\":%s}", this.getId(), new ObjectMapper().writeValueAsString(this));
} catch (JsonProcessingException e) {
LOGGER.error("Unable to generate dataLayer JSON string", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@
package com.adobe.cq.forms.core.components.internal.form;

/**
* Feature toggle identifiers for form components. When a toggle is enabled, the corresponding
* behavior is active; when disabled or when the toggle service is absent, legacy behavior is used.
* Feature toggle identifiers for form components. When a toggle is enabled, the corresponding behavior is active; when
* disabled or when the toggle service is absent, legacy behavior is used.
* <p>
* Toggles are driven by system properties. Code checks only the system property; the property is
* set/unset by the Granite Toggle API when you wire a toggle to it via OSGi. Setup:
* Toggles are driven by system properties. Code checks only the system property; the property is set/unset by the
* Granite Toggle API when you wire a toggle to it via OSGi. Setup:
* <ol>
* <li><strong>Enable the toggle</strong>: In the Web Console (configMgr), find "Adobe Granite
* Dynamic Toggle Provider" and add the toggle ID (e.g. {@link #FT_FRAGMENT_MERGE_CONTAINER_RULES_EVENTS})
* to the <em>Enabled Toggles</em> list. You can verify the toggle is active via the web console.</li>
* <li><strong>Enable the toggle</strong>: In the Web Console (configMgr), find "Adobe Granite Dynamic Toggle Provider"
* and add the toggle ID (e.g. {@link #FT_FRAGMENT_MERGE_CONTAINER_RULES_EVENTS}) to the <em>Enabled Toggles</em> list.
* You can verify the toggle is active via the web console.</li>
* <li><strong>Wire toggle to a system property</strong>: Add an OSGi factory configuration for
* {@code com.adobe.granite.toggle.monitor.systemproperty} (the ToggleMonitorSystemPropertyFactory).
* Use the same name for toggle and system property. Example JSON config:
* {@code com.adobe.granite.toggle.monitor.systemproperty} (the ToggleMonitorSystemPropertyFactory). Use the same name
* for toggle and system property. Example JSON config:
*
* <pre>
* {@code
Expand All @@ -39,49 +39,50 @@
* }
* </pre>
*
* The segment after {@code ~} is a unique instance name. When the toggle is enabled, the factory
* sets the system property; when disabled, it unsets it.</li>
* <li><strong>In code</strong>: Use {@link com.adobe.cq.forms.core.components.util.ComponentUtils#isToggleEnabled(String)}
* with the toggle ID; it reads the system property (value {@code "true"} means enabled).</li>
* The segment after {@code ~} is a unique instance name. When the toggle is enabled, the factory sets the system
* property; when disabled, it unsets it.</li>
* <li><strong>In code</strong>: Use
* {@link com.adobe.cq.forms.core.components.util.ComponentUtils#isToggleEnabled(String)} with the toggle ID; it reads
* the system property (value {@code "true"} means enabled).</li>
* </ol>
* You can also enable a toggle for local or override use by setting the JVM system property
* directly (e.g. {@code -DFT_FORMS-24087=true}) without the OSGi config.
* You can also enable a toggle for local or override use by setting the JVM system property directly (e.g.
* {@code -DFT_FORMS-24087=true}) without the OSGi config.
*/
public final class FeatureToggleConstants {

private FeatureToggleConstants() {}

/**
* When enabled, fragment container rules and events are merged with the fragment placeholder
* (panel) rules and events: for rules the panel overrides the container for the same key; for
* events panel handlers run first then fragment handlers are appended. When disabled, only the
* panel rules and events are used; fragment container rules/events are not merged.
* When enabled, fragment container rules and events are merged with the fragment placeholder (panel) rules and
* events: for rules the panel overrides the container for the same key; for events panel handlers run first then
* fragment handlers are appended. When disabled, only the panel rules and events are used; fragment container
* rules/events are not merged.
* <p>
* System property: same name ({@code FT_FORMS-24087}); set to {@code "true"} to enable when
* using {@code ToggleMonitorSystemPropertyFactory} or for direct -D JVM override.
* System property: same name ({@code FT_FORMS-24087}); set to {@code "true"} to enable when using
* {@code ToggleMonitorSystemPropertyFactory} or for direct -D JVM override.
*/
public static final String FT_FRAGMENT_MERGE_CONTAINER_RULES_EVENTS = "FT_FORMS-24087";

/**
* When enabled, the default {@code custom:setProperty} event handler ({@code "$event.payload"})
* is no longer injected into the model JSON by the server. The af2-web-runtime provides this
* default at the client side, reducing JSON payload size for every form component.
* When enabled, the default {@code custom:setProperty} event handler ({@code "$event.payload"}) is no longer
* injected into the model JSON by the server. The af2-web-runtime provides this default at the client side,
* reducing JSON payload size for every form component.
* <p>
* <strong>Prerequisite:</strong> the af2-web-runtime must include the corresponding fallback
* in {@code Scriptable.getCompiledEvent} before this toggle is enabled.
* <strong>Prerequisite:</strong> the af2-web-runtime must include the corresponding fallback in
* {@code Scriptable.getCompiledEvent} before this toggle is enabled.
* <p>
* System property: same name ({@code FT_FORMS-24343}); set to {@code "true"} to enable.
*/
public static final String FT_SKIP_DEFAULT_SET_PROPERTY_EVENT = "FT_FORMS-24343";

/**
* When enabled, container components (panels, wizards, fragments, form containers) export their
* children as a flat {@code "items"} JSON array instead of the Sling Model Exporter's default
* {@code ":items"} map + {@code ":itemsOrder"} array. This reduces JSON payload size by
* eliminating the redundant resource-name keys and the separate ordering array.
* When enabled, container components (panels, wizards, fragments, form containers) export their children as a flat
* {@code "items"} JSON array instead of the Sling Model Exporter's default {@code ":items"} map +
* {@code ":itemsOrder"} array. This reduces JSON payload size by eliminating the redundant resource-name keys and
* the separate ordering array.
* <p>
* <strong>Prerequisite:</strong> the af2-web-runtime already supports the {@code items} array
* format natively via {@code Container._initialize()}.
* <strong>Prerequisite:</strong> the af2-web-runtime already supports the {@code items} array format natively via
* {@code Container._initialize()}.
* <p>
* System property: same name ({@code FT_FORMS-24358}); set to {@code "true"} to enable.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@

import com.adobe.cq.forms.core.components.models.form.FormClientLibManager;

@Model(
adaptables = { SlingHttpServletRequest.class },
adapters = FormClientLibManager.class)
@Model(adaptables = { SlingHttpServletRequest.class }, adapters = FormClientLibManager.class)
public class FormClientLibManagerImpl implements FormClientLibManager {

@SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@

import com.adobe.cq.forms.core.components.models.form.FormConfigurationProvider;

@Model(
adaptables = { Resource.class },
adapters = FormConfigurationProvider.class)
@Model(adaptables = { Resource.class }, adapters = FormConfigurationProvider.class)
public class FormConfigurationProviderImpl implements FormConfigurationProvider {

private static final String CUSTOM_FUNCTION_CONFIG_BUCKET_NAME = "settings/cloudconfigs";
Expand All @@ -51,8 +49,8 @@ public class FormConfigurationProviderImpl implements FormConfigurationProvider
public String getCustomFunctionModuleUrl() {
String customFunctionUrl = "";
if (resource != null && configurationResourceResolver != null) {
Resource configResource = configurationResourceResolver.getResource(resource, CUSTOM_FUNCTION_CONFIG_BUCKET_NAME,
CUSTOM_FUNCTION_CONFIG_NAME);
Resource configResource = configurationResourceResolver.getResource(resource,
CUSTOM_FUNCTION_CONFIG_BUCKET_NAME, CUSTOM_FUNCTION_CONFIG_NAME);
if (configResource != null) {
Resource jcrResource = configResource.getChild(JcrConstants.JCR_CONTENT);
if (jcrResource != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ private FormConstants() {
/** The resource type for fragment v1 */
public static final String RT_FD_FORM_FRAGMENT_V1 = RT_FD_FORM_PREFIX + "fragment/v1/fragment";

public static final String RT_FD_FRAGMENT_CONTAINER_V1 = RT_FD_FORM_PREFIX + "fragmentcontainer/v1/fragmentcontainer";
public static final String RT_FD_FRAGMENT_CONTAINER_V1 = RT_FD_FORM_PREFIX
+ "fragmentcontainer/v1/fragmentcontainer";

/** The resource type for terms and conditions v1 */
public static final String RT_FD_FORM_TERMS_AND_CONDITIONS_V1 = RT_FD_FORM_PREFIX + "termsandconditions/v1/termsandconditions";
public static final String RT_FD_FORM_TERMS_AND_CONDITIONS_V1 = RT_FD_FORM_PREFIX
+ "termsandconditions/v1/termsandconditions";

public static final String FORM_FIELD_TYPE = "form";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
adapters = FormStructureParser.class)
@Model(adaptables = { SlingHttpServletRequest.class, Resource.class }, adapters = FormStructureParser.class)
public class FormStructureParserImpl implements FormStructureParser {
private static final Logger logger = LoggerFactory.getLogger(FormStructureParserImpl.class);
@SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
Expand Down Expand Up @@ -78,7 +76,8 @@ public String getThemeEditorThemeRef() {
}
// get client library from theme content path
if (StringUtils.isNotBlank(themeContentPath)) {
Resource themeResource = resource.getResourceResolver().getResource(themeContentPath + ThemeConstants.RELATIVE_PATH_METADATA);
Resource themeResource = resource.getResourceResolver()
.getResource(themeContentPath + ThemeConstants.RELATIVE_PATH_METADATA);
if (themeResource != null) {
ValueMap themeProps = themeResource.getValueMap();
themeClientLibRef = themeProps.get(ThemeConstants.PROPERTY_CLIENTLIB_CATEGORY, "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@

import com.adobe.cq.forms.core.components.models.form.HtlUtil;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
adapters = HtlUtil.class)
@Model(adaptables = { SlingHttpServletRequest.class, Resource.class }, adapters = HtlUtil.class)
public class HtlUtilImpl implements HtlUtil {
@SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
@Nullable
private SlingHttpServletRequest request;

public Boolean isEdgeDeliveryRequest() {
if (request != null) {
Object isEdgeDelivery = request.getAttribute("com.adobe.aem.wcm.franklin.internal.servlets.FranklinDeliveryServlet");
Object isEdgeDelivery = request
.getAttribute("com.adobe.aem.wcm.franklin.internal.servlets.FranklinDeliveryServlet");
return isEdgeDelivery != null && isEdgeDelivery.equals(Boolean.TRUE);
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,27 @@ private ReservedProperties() {
public static final String PN_IS_TITLE_RICH_TEXT = "isTitleRichText";
public static final String PN_ORIENTATION = "orientation";
public static final String PN_TYPE_MESSAGE = "typeMessage";
public static final String PN_REQUIRED_MESSAGE = "mandatoryMessage"; // reusing the same property name as in foundation
public static final String PN_REQUIRED_MESSAGE = "mandatoryMessage"; // reusing the same property name as in
// foundation
public static final String PN_MINIMUM_MESSAGE = "minimumMessage";
public static final String PN_MAXIMUM_MESSAGE = "maximumMessage";
public static final String PN_MINLENGTH_MESSAGE = "minLengthMessage";
public static final String PN_MAXLENGTH_MESSAGE = "maxLengthMessage";
public static final String PN_MAX_FILE_SIZE_MESSAGE = "maxFileSizeMessage"; // for fileInput min, max number of files, maximum file size
public static final String PN_MAX_FILE_SIZE_MESSAGE = "maxFileSizeMessage"; // for fileInput min, max number of
// files, maximum file size
// and accept of file type messages
public static final String PN_ACCEPT_MESSAGE = "acceptMessage";
public static final String PN_STEP_MESSAGE = "stepMessage";
public static final String PN_FORMAT_MESSAGE = "formatMessage";
public static final String PN_PATTERN = "pattern";
public static final String PN_PATTERN_MESSAGE = "validatePictureClauseMessage"; // reusing the same property name as in foundation
public static final String PN_PATTERN_MESSAGE = "validatePictureClauseMessage"; // reusing the same property name as
// in foundation
public static final String PN_MINITEMS_MESSAGE = "minItemsMessage";
public static final String PN_MAXITEMS_MESSAGE = "maxItemsMessage";
public static final String PN_UNIQUE_ITEMS_MESSAGE = "uniqueItemsMessage";
public static final String PN_ENFORCE_ENUM_MESSAGE = "enforceEnumMessage";
public static final String PN_VALIDATION_EXPRESSION_MESSAGE = "validateExpMessage"; // reusing the same property name as in foundation
public static final String PN_VALIDATION_EXPRESSION_MESSAGE = "validateExpMessage"; // reusing the same property
// name as in foundation
public static final String PN_MULTISELECT = "multiSelect";
public static final String PN_MULTISELECTION = "multiSelection";
public static final String PN_ENABLE_UNCHECKED_VALUE = "enableUncheckedValue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
adapters = { Button.class, ComponentExporter.class },
resourceType = { FormConstants.RT_FD_FORM_BUTTON_V1, FormConstants.RT_FD_FORM_SUBMIT_BUTTON_V1,
FormConstants.RT_FD_FORM_RESET_BUTTON_V1 })
adapters = { Button.class,
ComponentExporter.class },
resourceType = { FormConstants.RT_FD_FORM_BUTTON_V1,
FormConstants.RT_FD_FORM_SUBMIT_BUTTON_V1, FormConstants.RT_FD_FORM_RESET_BUTTON_V1 })
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class ButtonImpl extends AbstractBaseImpl implements Button {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ public Integer getMaxItems() {

@Override
public Type getType() {
return super.getType(); // check box group always has type array // we always return boolean[], string[] or number[]
return super.getType(); // check box group always has type array // we always return boolean[], string[] or
// number[]
}

@Override
Expand Down
Loading
Loading