diff --git a/it/apps/src/main/content/META-INF/vault/filter.xml b/it/apps/src/main/content/META-INF/vault/filter.xml
index a9c91e0f86..2f5660e047 100644
--- a/it/apps/src/main/content/META-INF/vault/filter.xml
+++ b/it/apps/src/main/content/META-INF/vault/filter.xml
@@ -2,5 +2,6 @@
+
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/.content.xml b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/.content.xml
new file mode 100644
index 0000000000..6d97bc0e2f
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/.content.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/custom-linkrewriter/.content.xml b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/custom-linkrewriter/.content.xml
new file mode 100644
index 0000000000..5af7a1c4d0
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/config/rewriter/custom-linkrewriter/.content.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/.content.xml b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/.content.xml
new file mode 100644
index 0000000000..f4c23add8e
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/README.md b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/README.md
new file mode 100644
index 0000000000..bb05396426
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/README.md
@@ -0,0 +1 @@
+## This component should be deployed in the Sites' instance where we need to embed the form
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/_cq_dialog/.content.xml b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/_cq_dialog/.content.xml
new file mode 100644
index 0000000000..512e5acdda
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/_cq_dialog/.content.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/formsembed.html b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/formsembed.html
new file mode 100644
index 0000000000..151ad4223c
--- /dev/null
+++ b/it/apps/src/main/content/jcr_root/apps/forms-core-components-it/sites/formsembed/formsembed.html
@@ -0,0 +1,57 @@
+
diff --git a/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.cq.forms.core.components.it.service.rewriter.CustomRunModeConfiguration.cfg.json b/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.cq.forms.core.components.it.service.rewriter.CustomRunModeConfiguration.cfg.json
new file mode 100644
index 0000000000..19be39d8ff
--- /dev/null
+++ b/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.cq.forms.core.components.it.service.rewriter.CustomRunModeConfiguration.cfg.json
@@ -0,0 +1,3 @@
+{
+ "runmode.info": "publish"
+}
diff --git a/it/core/pom.xml b/it/core/pom.xml
index 3ef2cf1334..88c9879226 100644
--- a/it/core/pom.xml
+++ b/it/core/pom.xml
@@ -161,7 +161,7 @@ Import-Package: javax.annotation;version=0.0.0,*
com.adobe.aem
core-forms-components-af-core
- 3.0.70
+ ${project.version}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbed.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbed.java
new file mode 100644
index 0000000000..bb751dc19b
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbed.java
@@ -0,0 +1,10 @@
+package com.adobe.cq.forms.core.components.it.models.embed;
+
+import com.adobe.cq.wcm.core.components.models.Component;
+import org.osgi.annotation.versioning.ConsumerType;
+
+@ConsumerType
+public interface FormsEmbed extends Component {
+
+ String getFormsUrl();
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbedImpl.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbedImpl.java
new file mode 100644
index 0000000000..9a0d0628e2
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/embed/FormsEmbedImpl.java
@@ -0,0 +1,35 @@
+package com.adobe.cq.forms.core.components.it.models.embed;
+
+import com.adobe.cq.export.json.ComponentExporter;
+import com.adobe.cq.export.json.ExporterConstants;
+import com.drew.lang.annotations.Nullable;
+import org.apache.sling.api.SlingHttpServletRequest;
+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;
+
+@Model(
+ adaptables = SlingHttpServletRequest.class,
+ adapters = {FormsEmbed.class, ComponentExporter.class},
+ resourceType = FormsEmbedImpl.RESOURCE_TYPE
+)
+@Exporter(
+ name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
+ extensions = ExporterConstants.SLING_MODEL_EXTENSION
+)
+public class FormsEmbedImpl implements FormsEmbed {
+
+ // TODO replace app name
+ public static final String RESOURCE_TYPE = "forms-core-component-it/components/formsembed";
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
+ @Nullable
+ protected String formsUrl;
+
+
+ @Override
+ public String getFormsUrl() {
+ return formsUrl;
+ }
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/CustomFormContainer.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/CustomFormContainer.java
new file mode 100644
index 0000000000..e4ca0cc94c
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/CustomFormContainer.java
@@ -0,0 +1,421 @@
+package com.adobe.cq.forms.core.components.it.models.formcontainer;
+
+import com.adobe.aemds.guide.common.GuideContainer;
+import com.adobe.aemds.guide.service.CoreComponentCustomPropertiesProvider;
+import com.adobe.aemds.guide.service.GuideSchemaType;
+import com.adobe.aemds.guide.utils.GuideConstants;
+import com.adobe.aemds.guide.utils.GuideUtils;
+import com.adobe.aemds.guide.utils.GuideWCMUtils;
+import com.adobe.cq.export.json.ComponentExporter;
+import com.adobe.cq.export.json.ContainerExporter;
+import com.adobe.cq.export.json.ExporterConstants;
+import com.adobe.cq.forms.core.components.internal.form.FormConstants;
+import com.adobe.cq.forms.core.components.it.service.rewriter.CustomRunModeConfiguration;
+import com.adobe.cq.forms.core.components.models.form.AutoSaveConfiguration;
+import com.adobe.cq.forms.core.components.models.form.Container;
+import com.adobe.cq.forms.core.components.models.form.FieldType;
+import com.adobe.cq.forms.core.components.models.form.FormClientLibManager;
+import com.adobe.cq.forms.core.components.models.form.FormContainer;
+import com.adobe.cq.forms.core.components.models.form.FormMetaData;
+import com.adobe.cq.forms.core.components.models.form.ThankYouOption;
+import com.adobe.cq.forms.core.components.util.AbstractContainerImpl;
+import com.adobe.cq.forms.core.components.util.ComponentUtils;
+import com.day.cq.commons.LanguageUtil;
+import com.day.cq.commons.jcr.JcrConstants;
+import com.day.cq.wcm.api.Page;
+import com.day.cq.wcm.api.PageManager;
+import com.drew.lang.annotations.Nullable;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import javax.annotation.PostConstruct;
+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.Default;
+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.OSGiService;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+import org.apache.sling.models.annotations.injectorspecific.SlingObject;
+import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Model(
+ adaptables = { SlingHttpServletRequest.class, Resource.class },
+ adapters = { FormContainer.class, ContainerExporter.class, ComponentExporter.class },
+ resourceType = { CustomFormContainer.RESOURCE_TYPE })
+@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
+public class CustomFormContainer extends AbstractContainerImpl implements FormContainer {
+ protected static final String RESOURCE_TYPE = "/components/adaptiveForm/formcontainer";
+
+ private static final Logger logger = LoggerFactory.getLogger(CustomFormContainer.class);
+ private static final String DOR_TYPE = "dorType";
+ private static final String DOR_TEMPLATE_REF = "dorTemplateRef";
+ private static final String DOR_TEMPLATE_TYPE = "dorTemplateType";
+ private static final String FD_SCHEMA_TYPE = "fd:schemaType";
+ private static final String FD_SCHEMA_REF = "fd:schemaRef";
+ private static final String FD_IS_HAMBURGER_MENU_ENABLED = "fd:isHamburgerMenuEnabled";
+ public static final String FD_FORM_DATA_ENABLED = "fd:formDataEnabled";
+ public static final String FD_ROLE_ATTRIBUTE = "fd:roleAttribute";
+ private static final String FD_CUSTOM_FUNCTIONS_URL = "fd:customFunctionsUrl";
+ private static final String FD_DATA_URL = "fd:dataUrl";
+
+ @OSGiService(injectionStrategy = InjectionStrategy.OPTIONAL)
+ private CoreComponentCustomPropertiesProvider coreComponentCustomPropertiesProvider;
+
+ @SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
+ @Nullable
+ private SlingHttpServletRequest request;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_THANK_YOU_MSG_V2)
+ @Nullable
+ private String thankYouMessage;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_THANK_YOU_OPTION)
+ @Nullable
+ private String thankYouOption;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_CLIENTLIB_REF)
+ @Nullable
+ private String clientLibRef;
+
+ @ValueMapValue(name = FD_IS_HAMBURGER_MENU_ENABLED, injectionStrategy = InjectionStrategy.OPTIONAL)
+ private Boolean isHamburgerMenuEnabled = false;
+
+ protected String contextPath = StringUtils.EMPTY;
+ private boolean formDataEnabled = false;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_TITLE)
+ @Nullable
+ private String title;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_REDIRECT)
+ @Nullable
+ private String redirect;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_PREFILL_SERVICE)
+ @Nullable
+ private String prefillService;
+
+ @ValueMapValue(name = FD_ROLE_ATTRIBUTE, injectionStrategy = InjectionStrategy.OPTIONAL)
+ @Nullable
+ private String roleAttribute;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_DATA)
+ @Nullable
+ private String data;
+
+ @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_SPEC_VERSION)
+ @Default(values = DEFAULT_FORMS_SPEC_VERSION)
+ private String specVersion;
+
+ @Self(injectionStrategy = InjectionStrategy.OPTIONAL)
+ private AutoSaveConfiguration autoSaveConfig;
+
+ @OSGiService
+ private CustomRunModeConfiguration runModeConfigService;
+
+ @Override
+ public String getFieldType() {
+ return super.getFieldType(FieldType.FORM);
+ }
+
+ private static final String PREFIX_PATH = "/custom";
+
+ @PostConstruct
+ protected void initFormContainerModel() {
+ if (request != null) {
+ contextPath = request.getContextPath();
+ request.setAttribute("formContainerPath", this.getPath());
+
+ Page currentPage = getCurrentPage();
+ if (currentPage != null) {
+ PageManager pageManager = currentPage.getPageManager();
+ Page resourcePage = pageManager.getContainingPage(resource);
+ if (resourcePage != null && !StringUtils.equals(currentPage.getPath(), resourcePage.getPath())) {
+ request.setAttribute(FormConstants.REQ_ATTR_REFERENCED_PATH, resourcePage.getPath());
+ }
+ }
+ FormClientLibManager formClientLibManager = request.adaptTo(FormClientLibManager.class);
+ if (formClientLibManager != null && clientLibRef != null) {
+ formClientLibManager.addClientLibRef(clientLibRef);
+ }
+ }
+ }
+
+ @Override
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+
+ @JsonIgnore
+ public String getContextPath() {
+ return contextPath != null ? contextPath : StringUtils.EMPTY;
+ }
+
+ @Override
+ @Nullable
+ @JsonIgnore
+ public String getThankYouMessage() {
+ return translate("thankYouMessage", thankYouMessage);
+ }
+
+ @Override
+ @Nullable
+ @JsonIgnore
+ public ThankYouOption getThankYouOption() {
+ return ThankYouOption.fromString(thankYouOption);
+ }
+
+ @Override
+ public String getAdaptiveFormVersion() {
+ return specVersion;
+ }
+
+ @Override
+ @Nullable
+ public String getClientLibRef() {
+ return clientLibRef;
+ }
+
+ @Override
+ @Nullable
+ public String getSchemaRef() {
+ return GuideContainer.from(resource).getSchemaRef();
+ }
+
+ @Override
+ @Nullable
+ public GuideSchemaType getSchemaType() {
+ return GuideContainer.from(resource).getSchema();
+ }
+
+ @Override
+ public FormMetaData getMetaData() {
+ return new FormMetaData() {
+ @Override
+ public String getVersion() {
+ return FormMetaData.super.getVersion();
+ }
+ };
+ }
+
+ @Override
+ @Nullable
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ @Nullable
+ public String getFormData() {
+ return data;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getEncodedCurrentPagePath() {
+ if (getCurrentPage() != null) {
+ return getId();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getId() {
+ String parentPagePath = getParentPagePath();
+ if (GuideWCMUtils.isForms(parentPagePath)) {
+ return ComponentUtils.getEncodedPath(parentPagePath);
+ } else {
+ // handling use-case when AF is used in iframe mode inside embed form component
+ if (request != null && request.getAttribute("formRenderingInsideEmbedContainer") != null) {
+ return ComponentUtils.getEncodedPath(StringUtils.replace(getPath(), "/" + JcrConstants.JCR_CONTENT + "/"
+ + GuideConstants.GUIDE_CONTAINER_NODE_NAME, ""));
+ }
+ return ComponentUtils.getEncodedPath(getPath());
+ }
+ }
+
+ @JsonIgnore
+ @Nullable
+ public String getRedirectUrl() {
+ String redirectURL = GuideUtils.getRedirectUrl(redirect, getPath());
+ // Only do this if redirect configured to relative URL, that is, page hosted on same AEM
+ if (StringUtils.isNotEmpty(redirectURL) && redirectURL.startsWith("/")) {
+ redirectURL = getContextPath() + redirectURL;
+ }
+ return redirectURL;
+ }
+
+ @JsonIgnore
+ @Nullable
+ public String getPrefillService() {
+ return prefillService;
+ }
+
+ public Boolean getIsHamburgerMenuEnabled() {
+ return isHamburgerMenuEnabled;
+ }
+
+ @Override
+ public String getRoleAttribute() {
+ return roleAttribute;
+ }
+
+ @Override
+ public String getAction() {
+ String action;
+ if ("publish".equals(runModeConfigService.getRunMode())) {
+ action = getContextPath() + PREFIX_PATH + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/submit/" + getId();
+ } else {
+ action = getContextPath() + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/submit/" + getId();
+ }
+ return action;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getDataUrl() {
+ String dataUrl;
+ if ("publish".equals(runModeConfigService.getRunMode())) {
+ dataUrl = getContextPath() + PREFIX_PATH + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/data/" + getId();
+ } else {
+ dataUrl = getContextPath() + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/data/" + getId();
+ }
+ return dataUrl;
+ }
+
+ @Override
+ public String getLang() {
+ // todo: uncomment once forms sdk is released
+ if (request != null) {
+ return GuideUtils.getAcceptLang(request);
+ } else {
+ return FormContainer.super.getLang();
+ }
+ }
+
+ @Override
+ public String getContainingPageLang() {
+ // todo: right now it is copy of aem form because app is part of far, and af-apps is not pare of far
+ if (request != null) {
+ Page currentPage = getCurrentPage();
+ if (!GuideWCMUtils.isForms(currentPage.getPath())) {
+ String pagePath = currentPage.getPath(), pageLocaleRoot = LanguageUtil.getLanguageRoot(pagePath), locale = "";
+ if (StringUtils.isNotBlank(pageLocaleRoot)) {
+ int localeStartIndex = StringUtils.lastIndexOf(pageLocaleRoot, '/');
+ locale = StringUtils.substring(pageLocaleRoot, localeStartIndex + 1);
+ }
+ return locale;
+ } else {
+ return FormContainer.super.getContainingPageLang();
+ }
+ } else {
+ return FormContainer.super.getContainingPageLang();
+ }
+ }
+
+ @Override
+ public String getLanguageDirection() {
+ return GuideUtils.getLanguageDirection(getLang());
+ }
+
+ @Override
+ public Map getProperties() {
+ Map properties = new LinkedHashMap<>();
+ if (coreComponentCustomPropertiesProvider != null) {
+ Map customProperties = coreComponentCustomPropertiesProvider.getProperties();
+ if (customProperties != null) {
+ properties.putAll(customProperties);
+ }
+ }
+ properties.putAll(super.getProperties());
+ if (getSchemaType() != null) {
+ properties.put(FD_SCHEMA_TYPE, getSchemaType());
+ }
+ if (StringUtils.isNotBlank(getSchemaRef())) {
+ properties.put(FD_SCHEMA_REF, getSchemaRef());
+ }
+ properties.put(FD_IS_HAMBURGER_MENU_ENABLED, getIsHamburgerMenuEnabled());
+ // adding a custom property to know if form data is enabled
+ // this is done so that an extra API call from the client can be avoided
+ if (StringUtils.isNotBlank(getPrefillService()) ||
+ (request != null && StringUtils.isNotBlank(request.getParameter(GuideConstants.AF_DATA_REF)))) {
+ formDataEnabled = true;
+ }
+ properties.put(FD_ROLE_ATTRIBUTE, getRoleAttribute());
+ properties.put(FD_FORM_DATA_ENABLED, formDataEnabled);
+ properties.put(ReservedProperties.FD_AUTO_SAVE_PROPERTY_WRAPPER, this.autoSaveConfig);
+ properties.put(FD_CUSTOM_FUNCTIONS_URL, getCustomFunctionUrl());
+ properties.put(FD_DATA_URL, getDataUrl());
+
+ return properties;
+ }
+
+ @Override
+ @JsonIgnore
+ public Map getDorProperties() {
+ Map customDorProperties = new LinkedHashMap<>();
+ if (dorType != null) {
+ customDorProperties.put(DOR_TYPE, dorType);
+ }
+ if (dorTemplateRef != null) {
+ customDorProperties.put(DOR_TEMPLATE_REF, dorTemplateRef);
+ }
+ if (dorTemplateType != null) {
+ customDorProperties.put(DOR_TEMPLATE_TYPE, dorTemplateType);
+ }
+ return customDorProperties;
+ }
+
+ @JsonIgnore
+ @Override
+ public void visit(Consumer callback) throws Exception {
+ traverseChild(this, callback);
+ }
+
+ private void traverseChild(Container container, Consumer callback) throws Exception {
+ for (ComponentExporter component : container.getItems()) {
+ callback.accept(component);
+
+ if (component instanceof Container) {
+ traverseChild((Container) component, callback);
+ }
+ }
+ }
+
+ @Override
+ @JsonIgnore
+ public String getParentPagePath() {
+ if (resource != null) {
+ PageManager pm = resource.getResourceResolver().adaptTo(PageManager.class);
+ if (pm != null) {
+ Page page = pm.getContainingPage(resource);
+ return page != null ? page.getPath() : StringUtils.EMPTY;
+ }
+ }
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ public String getName() {
+ return FormContainer.super.getName();
+ }
+
+ @Override
+ public String getCustomFunctionUrl() {
+ String customFunctionUrl;
+ if ("publish".equals(runModeConfigService.getRunMode())) {
+ customFunctionUrl = getContextPath() + PREFIX_PATH + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/customfunctions/" + getId();
+ } else {
+ customFunctionUrl = getContextPath() + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/customfunctions/" + getId();
+ }
+ return customFunctionUrl;
+ }
+
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/ReservedProperties.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/ReservedProperties.java
new file mode 100644
index 0000000000..13f7d5ae21
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/models/formcontainer/ReservedProperties.java
@@ -0,0 +1,172 @@
+package com.adobe.cq.forms.core.components.it.models.formcontainer;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.adobe.cq.forms.core.components.models.form.FormContainer;
+import com.adobe.cq.wcm.core.components.models.Component;
+import com.adobe.cq.wcm.core.components.models.Title;
+
+public final class ReservedProperties {
+ private ReservedProperties() {
+ // NOOP
+ }
+
+ private static final Logger logger = LoggerFactory.getLogger(ReservedProperties.class);
+
+ public static final String PN_ID = Component.PN_ID;
+ public static final String PN_VIEWTYPE = "fd:viewType";
+ public static final String PN_FIELDTYPE = "fieldType";
+ public static final String PN_DATAREF = "dataRef";
+ public static final String PN_NAME = "name";
+ public static final String PN_VALUE = "value";
+ public static final String PN_ICON = "icon";
+ public static final String PN_DATA = "data";
+ public static final String PN_SIZE = "size";
+ public static final String PN_VISIBLE = "visible";
+ public static final String PN_UNBOUND_FORM_ELEMENT = "unboundFormElement";
+ public static final String PN_DOR_EXCLUSION = "dorExclusion";
+ public static final String PN_DOR_COLSPAN = "dorColspan";
+ public static final String PN_DOR_BINDREF = "dorBindRef";
+ public static final String PN_DOR_EXCLUDE_TITLE = "dorExcludeTitle";
+ public static final String PN_DOR_EXCLUDE_DESC = "dorExcludeDescription";
+ public static final String PN_DOR_NUM_COLS = "dorNumCols";
+ public static final String PN_DOR_LAYOUT_TYPE = "dorLayoutType";
+ public static final String PN_DESCRIPTION = "description";
+ public static final String PN_TOOLTIP = "tooltip";
+ public static final String PN_DOR_TEMPLATE_TYPE = "fd:formType";
+ public static final String PN_TOOLTIP_VISIBLE = "tooltipVisible";
+ public static final String PN_TYPE = "type";
+ public static final String PN_DOR_TEMPLATE_REF = "dorTemplateRef";
+ public static final String PN_DOR_TYPE = "dorType";
+ public static final String PN_VALIDATION_EXPRESSION = "validationExpression";
+ public static final String PN_REQUIRED = "required";
+ public static final String PN_AUTOCOMPLETE = "autocomplete";
+ public static final String PN_ASSIST_PRIORITY = "assistPriority";
+ public static final String PN_CUSTOM = "custom";
+ public static final String PN_ENABLED = "enabled";
+ public static final String PN_REPEATABLE = "repeatable";
+ public static final String PN_MIN_OCCUR = "minOccur";
+ public static final String PN_MAX_OCCUR = "maxOccur";
+ public static final String PN_MIN_ITEMS = "minItems";
+ public static final String PN_MAX_ITEMS = "maxItems";
+ public static final String PN_LANG = "lang";
+ public static final String PN_LANG_DISPLAY_VALUE = "langDisplayValue";
+ public static final String PN_PLACEHOLDER = "placeholder";
+ public static final String PN_READ_ONLY = "readOnly";
+ public static final String PN_DEFAULT_VALUE = "default";
+ public static final String PN_FORMAT = "format";
+ public static final String PN_DISPLAY_FORMAT = "displayFormat";
+ public static final String PN_EDIT_FORMAT = "editFormat";
+ public static final String PN_DISPLAY_VALUE_EXPRESSION = "displayValueExpression";
+ public static final String PN_DATA_FORMAT = "dataFormat";
+ public static final String PN_MIN_LENGTH = "minLength";
+ public static final String PN_MAX_LENGTH = "maxLength";
+ public static final String PN_MINIMUM_DATE = "minimumDate";
+ public static final String PN_MAXIMUM_DATE = "maximumDate";
+ public static final String PN_MAXIMUM = "maximum";
+ public static final String PN_MINIMUM = "minimum";
+ public static final String PN_EXCLUSIVE_MINIMUM = "exclusiveMinimum";
+ public static final String PN_EXCLUSIVE_MAXIMUM = "exclusiveMaximum";
+ public static final String PN_EXCLUDE_MINIMUM = "excludeMinimum";
+ public static final String PN_EXCLUDE_MAXIMUM = "excludeMaximum";
+ public static final String PN_EXCLUDE_MINIMUM_CHECK = "excludeMinimumCheck";
+ public static final String PN_EXCLUDE_MAXIMUM_CHECK = "excludeMaximumCheck";
+ public static final String PN_ENFORCE_ENUM = "enforceEnum";
+ public static final String PN_ENUM = "enum";
+ public static final String PN_ENUM_NAMES = "enumNames";
+ public static final String PN_TITLE = "title";
+ public static final String PN_HIDE_TITLE = "hideTitle";
+ 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_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
+ // 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_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_MULTISELECT = "multiSelect";
+ public static final String PN_MULTISELECTION = "multiSelection";
+ public static final String PN_ENABLE_UNCHECKED_VALUE = "enableUncheckedValue";
+ public static final String PN_CHECKED_VALUE = "checkedValue";
+ public static final String PN_UNCHECKED_VALUE = "uncheckedValue";
+ public static final String PN_MAX_FILE_SIZE = "maxFileSize";
+ public static final String PN_FILE_ACCEPT = "accept";
+ public static final String PN_BUTTON_TEXT = "buttonText";
+ public static final String PN_WRAP_DATA = "wrapData";
+ public static final String PN_FRAGMENT_PATH = "fragmentPath";
+ public static final String PN_BUTTON_TYPE = "buttonType";
+ public static final String PN_THANK_YOU_MSG_V1 = "thankyouMessage";
+ public static final String PN_THANK_YOU_MSG_V2 = "thankYouMessage";
+ public static final String PN_THANK_YOU_OPTION = "thankYouOption";
+ public static final String PN_RUNTIME_DOCUMENT_PATH = FormContainer.PN_RUNTIME_DOCUMENT_PATH;
+ public static final String PN_CLOUD_SERVICE_PATH = "cloudServicePath";
+ public static final String PN_RECAPTCHA_CLOUD_SERVICE_PATH = "rcCloudServicePath";
+ public static final String PN_RECAPTCHA_SIZE = "recaptchaSize";
+ public static final String PN_BREAK_BEFORE_TEXT = "breakBeforeText";
+ public static final String PN_BREAK_AFTER_TEXT = "breakAfterText";
+ public static final String PN_OVERFLOW_TEXT = "overflowText";
+ public static final String PN_ALT_TEXT = "altText";
+ public static final String PN_IMAGE_SRC = "imageSrc";
+ public static final String PN_FILE_REF = "fileReference";
+ public static final String PN_SHOW_APPROVAL_OPTION = "showApprovalOption";
+ public static final String PN_SHOW_LINK = "showLink";
+ public static final String PN_SHOW_AS_POPUP = "showAsPopup";
+ public static final String PN_TEXT_IS_RICH = "textIsRich";
+ public static final String PN_MULTILINE = "multiLine";
+ public static final String PN_DESIGN_DEFAULT_TYPE = Title.PN_DESIGN_DEFAULT_TYPE;
+ public static final String PN_TITLE_LINK_DISABLED = Title.PN_TITLE_LINK_DISABLED;
+ public static final String PN_DRAG_DROP_TEXT = "dragDropText";
+ public static final String PN_DRAG_DROP_TEXT_V3 = "fd:dragDropText";
+ public static final String PN_CLIENTLIB_REF = "clientLibRef";
+ public static final String PN_REDIRECT = "redirect";
+ public static final String PN_PREFILL_SERVICE = "prefillService";
+ public static final String PN_SPEC_VERSION = "specVersion";
+ public static final String PN_RICH_TEXT = "richText";
+ public static final String PN_OPTIONS_RICH_TEXT = "areOptionsRichText";
+ public static final String PN_EXCLUDE_FROM_DOR = "excludeFromDor";
+ public static final String PN_MANDATORY = "mandatory";
+ public static final String PN_HTML_ELEMENT_TYPE_V2 = "fd:htmlelementType";
+ public static final String FD_AUTO_SAVE_PROPERTY_WRAPPER = "fd:autoSave";
+ public static final String FD_ENABLE_AUTO_SAVE = "fd:enableAutoSave";
+ public static final String FD_AUTO_SAVE_STRATEGY_TYPE = "fd:autoSaveStrategyType";
+ public static final String FD_AUTO_SAVE_INTERVAL = "fd:autoSaveInterval";
+ private static final Set reservedProperties = aggregateReservedProperties();
+
+ private static Set aggregateReservedProperties() {
+ Set reservedProperties = new HashSet<>();
+ Field[] fields = ReservedProperties.class.getDeclaredFields();
+
+ for (Field field : fields) {
+ if (field.getType().equals(String.class)) {
+ try {
+ reservedProperties.add((String) field.get(null));
+ } catch (IllegalAccessException e) {
+ logger.error("[AF] Error while accessing field: {}", field.getName(), e);
+ }
+ }
+ }
+
+ return reservedProperties;
+ }
+
+ public static Set getReservedProperties() {
+ return reservedProperties;
+ }
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomCloudRewriterTransformer.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomCloudRewriterTransformer.java
new file mode 100644
index 0000000000..46fadaaa24
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomCloudRewriterTransformer.java
@@ -0,0 +1,102 @@
+package com.adobe.cq.forms.core.components.it.service.rewriter;
+
+import org.apache.sling.rewriter.DefaultTransformer;
+import org.apache.sling.rewriter.TransformerFactory;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@Component(
+ service = TransformerFactory.class,
+ property = {
+ "pipeline.type=" + CustomCloudRewriterTransformer.TYPE,
+ }
+)
+public class CustomCloudRewriterTransformer implements TransformerFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(CustomCloudRewriterTransformer.class);
+
+ public static final String TYPE = "custom-linkrewriter";
+
+ public static final String PATH_PREFIX = "custom";
+
+
+ @Reference
+ private CustomRunModeConfiguration customRunModeConfiguration;
+
+ @Override
+ public TransformerImpl createTransformer() {
+ String runmode = customRunModeConfiguration.getRunMode();
+ log.info("current runmode = " + runmode);
+ boolean isPublish = "publish".equals(runmode);
+ return new TransformerImpl(isPublish);
+ }
+
+
+ protected static class TransformerImpl extends DefaultTransformer {
+
+ private final boolean isPublish;
+
+ public TransformerImpl(boolean isPublish) {
+ super();
+ this.isPublish = isPublish;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ AttributesImpl mutableAttributes =
+ attributes instanceof AttributesImpl ? (AttributesImpl) attributes
+ : new AttributesImpl(attributes);
+ if (isPublish) {
+ if ("link".equals(localName)) {
+ replaceLinkPaths(mutableAttributes);
+ } else if ("script".equals(localName) || "img".equals(localName)) {
+ replaceAttributePaths(mutableAttributes, "src");
+ } else if ("form".equals(localName)) {
+ replaceAttributePaths(mutableAttributes, "data-cmp-path");
+ } else if ("div".equals(localName)) {
+ replaceAttributePaths(mutableAttributes, "data-cmp-adaptiveformcontainer-path");
+ }
+ }
+
+ super.startElement(uri, localName, qName,
+ mutableAttributes);
+ }
+
+ private void replaceAttributePaths(AttributesImpl attributes, String attributeName) {
+ if (attributes.getIndex(attributeName) >= 0) {
+ int index = attributes.getIndex(attributeName);
+ String value = attributes.getValue(index);
+ setAttribute(attributes, index, addPathPrefix(value));
+ }
+ }
+
+ private void replaceLinkPaths(AttributesImpl attributes) {
+ if (attributes.getIndex("href") >= 0 && attributes.getIndex("rel") >= 0) {
+ String rel = attributes.getValue("rel");
+ if ("preload stylesheet".equals(rel) || "stylesheet".equals(rel)) {
+ int index = attributes.getIndex("href");
+ String value = attributes.getValue(index);
+ setAttribute(attributes, index, addPathPrefix(value));
+ }
+ }
+ }
+
+ private String addPathPrefix(String url) {
+ return "/" + PATH_PREFIX + url;
+ }
+
+ private void setAttribute(AttributesImpl attributes, int index, String value) {
+ String attrUri = attributes.getURI(index);
+ String attrLocalName = attributes.getLocalName(index);
+ String attrQName = attributes.getQName(index);
+ String attrType = attributes.getType(index);
+ attributes.setAttribute(index, attrUri, attrLocalName, attrQName, attrType, value);
+ }
+ }
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunMode.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunMode.java
new file mode 100644
index 0000000000..a5137d99f1
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunMode.java
@@ -0,0 +1,11 @@
+package com.adobe.cq.forms.core.components.it.service.rewriter;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@ObjectClassDefinition(name = "Runmode OSGI Configuration", description = "Configuration for Identifying Run Mode")
+public @interface CustomRunMode {
+
+ @AttributeDefinition(name = "Runmode Information", description = "Runmode Info")
+ String runmode_info() default "default";
+}
diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunModeConfiguration.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunModeConfiguration.java
new file mode 100644
index 0000000000..98bdf28543
--- /dev/null
+++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/rewriter/CustomRunModeConfiguration.java
@@ -0,0 +1,25 @@
+package com.adobe.cq.forms.core.components.it.service.rewriter;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+
+@Component(
+ service = CustomRunModeConfiguration.class
+)
+public class CustomRunModeConfiguration {
+
+ private CustomRunMode customRunMode;
+
+ @Activate
+ public void activate(CustomRunMode customRunMode) {
+ this.customRunMode = customRunMode;
+ }
+
+ public String getRunMode() {
+ if (customRunMode != null) {
+ return customRunMode.runmode_info();
+ } else {
+ return "default";
+ }
+ }
+}