Created
March 19, 2019 17:15
-
-
Save ahmed-musallam/1d1e9eeec9ef6db647a59f2739041983 to your computer and use it in GitHub Desktop.
An ACS MCP that helps in updating one component property in bulk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mycom.core.mcp; | |
import com.adobe.acs.commons.fam.ActionManager; | |
import com.adobe.acs.commons.mcp.ProcessDefinition; | |
import com.adobe.acs.commons.mcp.ProcessInstance; | |
import com.adobe.acs.commons.mcp.form.CheckboxComponent; | |
import com.adobe.acs.commons.mcp.form.FormField; | |
import com.adobe.acs.commons.mcp.form.PathfieldComponent; | |
import com.adobe.acs.commons.mcp.model.GenericReport; | |
import java.io.Serializable; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.EnumMap; | |
import java.util.List; | |
import javax.jcr.RepositoryException; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.sling.api.resource.LoginException; | |
import org.apache.sling.api.resource.ModifiableValueMap; | |
import org.apache.sling.api.resource.PersistenceException; | |
import org.apache.sling.api.resource.Resource; | |
import org.apache.sling.api.resource.ResourceResolver; | |
public class ComponentPropertyUpdater extends ProcessDefinition implements Serializable { | |
private static final long serialVersionUID = 7526476295622773464L; | |
@FormField( | |
name = "Starting Path", | |
description = "Starting Path", | |
hint = "/var/eventing", | |
component = PathfieldComponent.FolderSelectComponent.class, | |
options = {"base=/content"}) | |
public String startingPath; | |
@FormField( | |
name = "Component sling:ResourceType", | |
description = "The component sling:ResourceType you want to change as is in content", | |
hint = "myproject/path/to/component") | |
public String type; | |
@FormField( | |
name = "Property to change", | |
description = "The property you want to change", | |
hint = "someProperty") | |
public String property; | |
@FormField( | |
name = "nNew value", | |
description = "The new value to be set on the property", | |
hint = "some value") | |
public String propertyNewValue; | |
@FormField( | |
name = "Multi-valued property?", | |
description = | |
"If checked, the value will be saved as multivalued and will be treated as a CSV", | |
component = CheckboxComponent.class) | |
private boolean isMultiValued = true; | |
@Override | |
public void init() {} | |
@Override | |
public void buildProcess(ProcessInstance processInstance, ResourceResolver resourceResolver) | |
throws LoginException, RepositoryException { | |
processInstance.defineAction("Component Property Updater", resourceResolver, this::doSomething); | |
processInstance.getInfo().setDescription(startingPath); | |
} | |
private void doSomething(ActionManager manager) { | |
ComponentVisitor visitor = | |
new ComponentVisitor( | |
resource -> { | |
if (resource.getResourceType().equals(type)) { | |
manager.deferredWithResolver( | |
rr -> { | |
String path = resource.getPath(); | |
updateComponent(rr, path); | |
}); | |
} | |
}); | |
manager.deferredWithResolver( | |
rr -> { | |
Resource res = rr.getResource(startingPath); | |
if (res != null) { | |
visitor.accept(res); | |
} | |
}); | |
} | |
private void updateComponent(ResourceResolver rr, String componentPath) throws Exception { | |
Resource resource = rr.getResource(componentPath); | |
if (resource == null) { | |
throw new IllegalStateException("Could not get resource at path: " + componentPath); | |
} | |
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class); | |
if (map == null) { | |
throw new PersistenceException("resource at " + resource.getPath() + " is not modifiable."); | |
} else { | |
String old = map.get(property, String.class); | |
if (isMultiValued) { | |
// we have to remove the property and commit the resolver | |
// else we get an error: "Can not assign multiple values to single valued property" | |
if (map.containsKey(property)) { | |
map.remove(property); | |
rr.commit(); | |
rr.refresh(); | |
} | |
map.put(property, StringUtils.split(propertyNewValue, ",")); | |
} else { | |
map.put(property, propertyNewValue); | |
} | |
rr.commit(); | |
trackActivity(resource.getPath(), old); | |
} | |
} | |
@SuppressWarnings("squid:S00115") | |
public enum ReportColumns { | |
component_path, | |
property_changed, | |
old_value, | |
new_value | |
} | |
private transient List<EnumMap<ComponentPropertyUpdater.ReportColumns, Object>> reportRows; | |
private synchronized EnumMap<ReportColumns, Object> trackActivity(String path, String oldValue) { | |
if (reportRows == null) { | |
reportRows = Collections.synchronizedList(new ArrayList<>()); | |
} | |
EnumMap<ReportColumns, Object> reportRow = new EnumMap<>(ReportColumns.class); | |
reportRow.put(ReportColumns.component_path, path); | |
reportRow.put(ReportColumns.property_changed, property); | |
reportRow.put(ReportColumns.old_value, oldValue); | |
reportRow.put(ReportColumns.new_value, propertyNewValue); | |
reportRows.add(reportRow); | |
return reportRow; | |
} | |
@Override | |
public synchronized void storeReport( | |
ProcessInstance processInstance, ResourceResolver resourceResolver) | |
throws RepositoryException, PersistenceException { | |
GenericReport report = new GenericReport(); | |
report.setRows(reportRows, ReportColumns.class); | |
report.persist(resourceResolver, processInstance.getPath() + "/jcr:content/report"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mycom.core.mcp; | |
import com.adobe.acs.commons.mcp.AdminOnlyProcessDefinitionFactory; | |
import com.adobe.acs.commons.mcp.ProcessDefinitionFactory; | |
import org.osgi.service.component.annotations.Component; | |
@Component( | |
immediate = true, | |
property = {"label=Factory of Component Property Updater"}, | |
service = ProcessDefinitionFactory.class) | |
public class ComponentPropertyUpdaterFactory extends | |
AdminOnlyProcessDefinitionFactory<ComponentPropertyUpdater> { | |
@Override | |
public String getName() { | |
return "Component Property Updater"; | |
} | |
@Override | |
public ComponentPropertyUpdater createProcessDefinitionInstance() { | |
return new ComponentPropertyUpdater(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mycom.core.mcp; | |
import com.adobe.acs.commons.util.visitors.ContentVisitor; | |
import com.adobe.acs.commons.util.visitors.ResourceRunnable; | |
import com.day.cq.wcm.api.NameConstants; | |
import org.apache.jackrabbit.JcrConstants; | |
import org.apache.sling.jcr.resource.api.JcrResourceConstants; | |
/** The same as ACS's ContentVisitor, but accepts nt:unstructured nodes to traverse components. */ | |
public class ComponentVisitor extends ContentVisitor { | |
private static final String NT_PAGE_CONETNT = "cq:PageContent"; | |
private static final String[] CONTAINER_TYPES = { | |
JcrResourceConstants.NT_SLING_FOLDER, | |
JcrResourceConstants.NT_SLING_ORDERED_FOLDER, | |
JcrConstants.NT_FOLDER | |
}; | |
private static final String[] CONTENT_TYPES = { | |
NameConstants.NT_PAGE, NT_PAGE_CONETNT, JcrConstants.NT_UNSTRUCTURED | |
}; | |
public ComponentVisitor(ResourceRunnable runnable) { | |
super(runnable, CONTAINER_TYPES, CONTENT_TYPES); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment