diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java index 5d7c410231..7e88f9a7a6 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java @@ -146,4 +146,7 @@ private FormConstants() { /** The resource type for date time input field v1 */ public static final String RT_FD_FORM_DATETIME_V1 = RT_FD_FORM_PREFIX + "datetime/v1/datetime"; + /** The resource type for Adobe Sign Block v1 */ + public static final String RT_FD_FORM_ADOBE_SIGN_BLOCK_V1 = RT_FD_FORM_PREFIX + "adobesignblock/v1/adobesignblock"; + } diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/AdobeSignBlockImpl.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/AdobeSignBlockImpl.java new file mode 100644 index 0000000000..0d34545833 --- /dev/null +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/AdobeSignBlockImpl.java @@ -0,0 +1,141 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2026 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +package com.adobe.cq.forms.core.components.internal.models.v1.form; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Exporter; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; +import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; + +import com.adobe.cq.export.json.ComponentExporter; +import com.adobe.cq.export.json.ExporterConstants; +import com.adobe.cq.forms.core.components.internal.form.FormConstants; +import com.adobe.cq.forms.core.components.models.form.Base; +import com.adobe.cq.forms.core.components.models.form.FieldType; +import com.adobe.cq.forms.core.components.models.form.Text; +import com.adobe.cq.forms.core.components.util.AbstractBaseImpl; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Sling Model for the Adobe Sign Block core component. + * + *

+ * Extends {@link AbstractBaseImpl} (same base as all other form field components) so it participates correctly in + * form-model serialisation and test casting. Implements the {@link Text} interface — replicating the rich-text value + * and fieldType behaviour from {@link TextImpl} — while inheriting: + *

+ * + */ +@Model( + adaptables = { SlingHttpServletRequest.class, Resource.class }, + adapters = { Text.class, Base.class, + ComponentExporter.class }, + resourceType = { FormConstants.RT_FD_FORM_ADOBE_SIGN_BLOCK_V1 }) +@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) +public class AdobeSignBlockImpl extends AbstractBaseImpl implements Text { + + private static final Pattern ADOBE_SIGN_TAG_PATTERN = Pattern.compile("\\{\\{[*]?([^:]*)_es_:"); + + /** + * The Adobe Sign merge-tag markup (e.g. {@code + * {{fieldName_es_:signer1:signature}}}), computed client-side by editDialog.js reusing + * integration-adobesign's own dialog plugin ({@code getDomContentFromDialog()} / + * {@code ADOBESIGN_UTILS.getDomContent()}) from the included Type/Name/Options/etc. field set, + * rather than reimplemented here. + */ + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) + private String adobeSignFieldTag; + + /** + * Combines the author's narrative {@code value} text - always authored as rich text, there is no + * plain-text mode for this field - with the client-computed {@link #adobeSignFieldTag}, so + * downstream DoR/agreement generation sees the same wire format it always has, regardless of how the + * field was authored. Never wrapped in an artificial {@code

} — the narrative can already contain + * its own block-level markup (lists, paragraphs), and nesting that inside a single forced {@code

} + * produces invalid HTML that corrupts the author-canvas DOM. + */ + @Override + public String getValue() { + String tag = StringUtils.defaultString(adobeSignFieldTag); + String markup = StringUtils.isNotBlank(value) ? value + " " + tag : tag; + return translate("value", markup); + } + + /** + * Always {@code true}: the Text field is always authored as rich text, and the rendered value + * always additionally contains the real (unescaped) placeholder markup above. + */ + @Override + public boolean isRichText() { + return true; + } + + /** + * Excluded from JSON; the rich-text content is surfaced via {@link #getValue()}. + */ + @Override + @JsonIgnore + public String getText() { + return getValue(); + } + + @Override + public String getFieldType() { + return FieldType.ADOBE_SIGN_BLOCK.getValue(); + } + + /** + * Returns the names of Adobe Sign text-tag fields embedded in this block's HTML content + * (e.g. {@code {{Signature1_es_:signer1:signature}}} yields {@code "Signature1"}). + * Returns {@code null} when no text tags are present so the property is omitted from JSON. + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + public String[] getAdobeSignFields() { + String blockValue = getValue(); + if (blockValue != null && !blockValue.isEmpty()) { + List fieldNames = new ArrayList<>(); + Set seen = new HashSet<>(); + Matcher matcher = ADOBE_SIGN_TAG_PATTERN.matcher(blockValue); + while (matcher.find()) { + String fieldName = matcher.group(1); + if (seen.add(fieldName)) { + fieldNames.add(fieldName); + } + } + if (!fieldNames.isEmpty()) { + return fieldNames.toArray(new String[0]); + } + } + return null; + } +} diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java index afe215afd1..c704c82db1 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java @@ -30,6 +30,7 @@ public enum FieldType { DROP_DOWN("drop-down"), RADIO_GROUP("radio-group"), PLAIN_TEXT("plain-text"), + ADOBE_SIGN_BLOCK("adobe-sign-block"), CHECKBOX("checkbox"), BUTTON("button"), PANEL("panel"), diff --git a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/.content.xml b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/.content.xml new file mode 100644 index 0000000000..8dddf859a6 --- /dev/null +++ b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/.content.xml @@ -0,0 +1,7 @@ + + diff --git a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/_cq_template.xml b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/_cq_template.xml new file mode 100644 index 0000000000..92d274b6c9 --- /dev/null +++ b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/adobesignblock/_cq_template.xml @@ -0,0 +1,8 @@ + + diff --git a/it/content/src/main/content/jcr_root/conf/core-components-it/settings/wcm/policies/.content.xml b/it/content/src/main/content/jcr_root/conf/core-components-it/settings/wcm/policies/.content.xml index f8a5146990..894d852ab6 100755 --- a/it/content/src/main/content/jcr_root/conf/core-components-it/settings/wcm/policies/.content.xml +++ b/it/content/src/main/content/jcr_root/conf/core-components-it/settings/wcm/policies/.content.xml @@ -12,7 +12,7 @@ jcr:primaryType="nt:unstructured" jcr:title="Core Components Examples - Root" sling:resourceType="wcm/core/components/policy/policy" - components="[/libs/wcm/foundation/components/responsivegrid,group:Core Components Examples,group:.core-wcm,group:.core-wcm-form,group:Core Components Examples - Adaptive Form,group:Core Components Examples - Forms And Communications Portal]"> + components="[/libs/wcm/foundation/components/responsivegrid,group:Core Components Examples,group:.core-wcm,group:.core-wcm-form,group:Core Components Examples - Adaptive Form,group:Core Components Examples - Forms And Communications Portal,group:Adobe Sign - Adaptive Form]"> + components="[group:Core Components Examples - Adaptive Form,group:Core Components Examples - Forms And Communications Portal,group:Adobe Sign - Adaptive Form]"> diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/adobesignblock/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/adobesignblock/.content.xml new file mode 100644 index 0000000000..d9e7865114 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/adobesignblock/.content.xml @@ -0,0 +1,7 @@ + +