Set crx:replicate Deny at Root
Publish Actions Unavailable
Publish to Preview via Workflow
1) Add a Restriction Pattern core\src\main\java\apps\experienceaem\sites\core\acls\EAEMEmbargoTypeRestriction.java
package apps.experienceaem.sites.core.acls;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
public class EAEMEmbargoTypeRestriction implements RestrictionPattern {
private final String restrictedValue;
public static final String RESTRICTION_TYPE_EMBARGO = "embargoedContent";
EAEMEmbargoTypeRestriction(String restrictedValue) {
this.restrictedValue = restrictedValue;
}
public boolean matches(Tree tree, PropertyState propertyState) {
Tree child = tree.getChild(JcrConstants.JCR_CONTENT);
if(!child.hasProperty(RESTRICTION_TYPE_EMBARGO)){
return false;
}
String value = child.getProperty(RESTRICTION_TYPE_EMBARGO).getValue(Type.STRING);
return restrictedValue.equalsIgnoreCase(value);
}
public boolean matches(String path) {
return false;
}
public boolean matches() {
return false;
}
}
2) Add the Restriction Provider core\src\main\java\apps\experienceaem\sites\core\acls\EAEMPublishRestrictionProvider.java
package apps.experienceaem.sites.core.acls;
import com.google.common.collect.ImmutableMap;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.security.authorization.restriction.*;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component(
service = RestrictionProvider.class
)
public class EAEMPublishRestrictionProvider extends AbstractRestrictionProvider {
private static final Logger log = LoggerFactory.getLogger(EAEMPublishRestrictionProvider.class);
public EAEMPublishRestrictionProvider() {
super(supportedRestrictions());
}
private static Map<String, RestrictionDefinition> supportedRestrictions() {
RestrictionDefinition embargoRes = new RestrictionDefinitionImpl(EAEMEmbargoTypeRestriction.RESTRICTION_TYPE_EMBARGO,
Type.STRING, false);
return ImmutableMap.of(embargoRes.getName(), embargoRes);
}
@Override
public RestrictionPattern getPattern(String oakPath, Tree tree) {
if (oakPath == null) {
return RestrictionPattern.EMPTY;
} else {
List<RestrictionPattern> patterns = new ArrayList(1);
PropertyState embargoProperty = tree.getProperty(EAEMEmbargoTypeRestriction.RESTRICTION_TYPE_EMBARGO);
if (embargoProperty != null) {
patterns.add(new EAEMEmbargoTypeRestriction(embargoProperty.getValue(Type.STRING)));
}
return CompositePattern.create(patterns);
}
}
@Override
public RestrictionPattern getPattern(String oakPath, Set<Restriction> restrictions) {
if (oakPath == null || restrictions.isEmpty()) {
return RestrictionPattern.EMPTY;
} else {
List<RestrictionPattern> patterns = new ArrayList(2);
for (Restriction r : restrictions) {
String name = r.getDefinition().getName();
if (EAEMEmbargoTypeRestriction.RESTRICTION_TYPE_EMBARGO.equals(name)) {
patterns.add(new EAEMEmbargoTypeRestriction(r.getProperty().getValue(Type.STRING)));
} else {
log.debug("EAEMPublishRestrictionProvider : Ignoring unsupported restriction " + name);
}
}
return CompositePattern.create(patterns);
}
}
}
3) Workflow process step for Publishing the Page to Preview only using service user eaem-embargo-service eaem-page-replicate-restriction\core\src\main\java\apps\experienceaem\sites\core\acls\EmbargoReplicationToPreview.java
package apps.experienceaem.sites.core.acls;
import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.exec.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
import com.day.cq.replication.*;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
import java.util.HashMap;
import java.util.Map;
@Component(
service = WorkflowProcess.class,
property = { "process.label=Experience AEM Embargo Replicate to Preview Process Step" })
public class EmbargoReplicationToPreview implements WorkflowProcess {
private static final Logger log = LoggerFactory.getLogger(EmbargoReplicationToPreview.class.getName());
private static final String PREVIEW_AGENT = "preview";
@Reference
ResourceResolverFactory resourceResolverFactory;
@Reference
Replicator replicator;
public void execute(final WorkItem workItem, final WorkflowSession workflowSession, final MetaDataMap args)
throws WorkflowException {
String pagePath = getPayloadPath(workItem.getWorkflowData());
try{
ResourceResolver serviceResolver = getEmbargoServiceResolver();
ReplicationOptions options = new ReplicationOptions();
options.setFilter(new AgentFilter() {
@Override
public boolean isIncluded(Agent agent) {
return agent.getId().equals(PREVIEW_AGENT);
}
});
replicator.replicate(serviceResolver.adaptTo(Session.class), ReplicationActionType.ACTIVATE, pagePath, options);
ReplicationStatus repStatus = serviceResolver.getResource(pagePath).adaptTo(ReplicationStatus.class);
ReplicationStatus previewStatus = repStatus.getStatusForAgent(PREVIEW_AGENT);
log.info("Page Replication status of {} is {} done by {} ", pagePath,
previewStatus.getLastReplicationAction().getName(), previewStatus.getLastPublishedBy());
}catch(Exception e){
log.error("Error while publish to preview {} ", pagePath, e);
}
}
private String getPayloadPath(WorkflowData wfData) {
String payloadPath = null;
if (wfData.getPayloadType().equals("JCR_PATH")) {
payloadPath = (String)wfData.getPayload();
}
return payloadPath;
}
private ResourceResolver getEmbargoServiceResolver() throws Exception{
Map<String, Object> SERVICE_MAP = new HashMap<>();
SERVICE_MAP.put(ResourceResolverFactory.SUBSERVICE, "eaem-embargo-service");
return resourceResolverFactory.getServiceResourceResolver(SERVICE_MAP);
}
}
4) Create the service user eaem-embargo-service and give necessary replicate permissions in ui.config\src\main\content\jcr_root\apps\eaem-page-replicate-restriction\osgiconfig\config\org.apache.sling.jcr.repoinit.RepositoryInitializer~eaem.cfg.json
{
"scripts": [
"create path (sling:OrderedFolder) /content/dam/eaem-page-replicate-restriction",
"create path (nt:unstructured) /content/dam/eaem-page-replicate-restriction/jcr:content",
"set properties on /content/dam/eaem-page-replicate-restriction/jcr:content\n set cq:conf{String} to /conf/eaem-page-replicate-restriction\n set jcr:title{String} to \"AEM Page Replicate Restriction Provider\"\nend",
"create service user eaem-embargo-service with path system/experience-aem",
"set ACL for eaem-embargo-service \n allow crx:replicate on /content \n allow jcr:read, rep:write on / \n end"
]
}
5) Service user to bundle mapping in ui.config\src\main\content\jcr_root\apps\eaem-page-replicate-restriction\osgiconfig\config\org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-eaem.cfg.json
{
"user.mapping": [
"eaem-page-replicate-restriction.core:eaem-embargo-service=[eaem-embargo-service]"
]
}
6) Workflow model config in ui.content\src\main\content\jcr_root\conf\global\settings\workflow\models\embargo-publish-to-preview\.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:designPath="/libs/settings/wcm/designs/default"
cq:lastModified="{Date}2025-07-30T17:25:27.518-05:00"
cq:lastModifiedBy="admin"
cq:template="/libs/cq/workflow/templates/model"
jcr:primaryType="cq:PageContent"
jcr:title="Embargo Service Publish To Preview"
sling:resourceType="cq/workflow/components/pages/model"
lastSynced="{Date}2025-07-30T17:25:32.864-05:00">
<flow
jcr:primaryType="nt:unstructured"
sling:resourceType="foundation/components/parsys">
<process
jcr:created="{Date}2025-07-30T17:25:04.730-05:00"
jcr:createdBy="admin"
jcr:description="Publish to Preview using Service User"
jcr:lastModified="{Date}2025-07-30T17:25:27.514-05:00"
jcr:lastModifiedBy="admin"
jcr:primaryType="nt:unstructured"
jcr:title="Publish to Preview using Service User"
sling:resourceType="cq/workflow/components/model/process">
<metaData
jcr:primaryType="nt:unstructured"
PROCESS="apps.experienceaem.sites.core.acls.EmbargoReplicationToPreview"
PROCESS_AUTO_ADVANCE="true"/>
</process>
</flow>
</jcr:content>
</jcr:root>
7) Page dialog extension to add tab for Embargo properties in ui.apps\src\main\content\jcr_root\apps\eaem-page-replicate-restriction\components\page\_cq_dialog\.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured">
<content jcr:primaryType="nt:unstructured">
<items jcr:primaryType="nt:unstructured">
<tabs jcr:primaryType="nt:unstructured">
<items jcr:primaryType="nt:unstructured">
<eaem
cq:showOnCreate="{Boolean}true"
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM"
sling:orderBefore="socialmedia"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<section
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM"
sling:resourceType="granite/ui/components/coral/foundation/form/fieldset">
<items jcr:primaryType="nt:unstructured">
<embargo
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
checked="{Boolean}false"
fieldDescription="If checked, page is embargoed and only available in preview"
name="./embargoedContent"
text="Is Embargoed Page?"
value="true"/>
</items>
</section>
</items>
</column>
</items>
</eaem>
</items>
</tabs>
</items>
</content>
</jcr:root>
No comments:
Post a Comment