Goal
AEM's Revert to this Version, reverts the asset as a whole (both binary and metadata) to selected version, however if you want the binary to be reverted to a specific version but keep metadata current, try the following approach...
Demo | Package Install | Github
Solution
1) Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-revert-binary-not-metadata
2) Create node /apps/eaem-revert-binary-not-metadata/clientlib of type cq:ClientLibraryFolder, add String property categories with value cq.gui.coral.common.admin.timeline, String[] property dependencies with value lodash
3) Create file (nt:file) /apps/eaem-revert-binary-not-metadata/clientlib/js.txt, add
revert-binary-not-metadata.js
4) Create file (nt:file) /apps/eaem-revert-binary-not-metadata/clientlib/revert-binary-not-metadata.js, add the following code (logic injects a hidden field eaem-revert-toversion-file-only into the form posted and filter in next step reads it for taking a backup of metadata before its reverted to another version)
(function($, $document) {
var EAEM_REVERT_CSS = "eaem-revert-toversion-file-only",
REVERT_FILE_NOT_METADATA_TITLE = "Revert to this Version (File)",
REVERT_TO_VERSION_SEL = ".cq-common-admin-timeline-event-button";
$document.on("click", ".cq-common-admin-timeline-event", addRevertToThisVersionFile);
function addRevertToThisVersionFile(){
var $timeLineButton = $(this).find(REVERT_TO_VERSION_SEL);
if(!_.isEmpty($timeLineButton.next("." + EAEM_REVERT_CSS))){
return;
}
$(getButtonHtml()).insertAfter($timeLineButton).click(revertToVersion);
}
function getButtonHtml(){
return '<button is="coral-button" class="' + EAEM_REVERT_CSS + '" size="M" variant="secondary" style="width:100%; margin-top:.2rem">' +
'<coral-button-label>' + REVERT_FILE_NOT_METADATA_TITLE + '</coral-button-label>' +
'</button>'
}
function revertToVersion(event){
event.preventDefault();
var $form = $(this).closest("form"),
$otbRevert = $(this).prev(REVERT_TO_VERSION_SEL);
$form.append("<input type='hidden' name='" + EAEM_REVERT_CSS + "' value='true'/>");
$otbRevert.click();
}
})(jQuery, jQuery(document));
5) Add filter apps.experienceaem.assets.ExperienceAEMVersionFilter for taking backup of metadata before product's logic reverts both binary and metadata. Later, restore metadata from the backup, thereby keeping it current...
package apps.experienceaem.assets;
import com.day.cq.commons.jcr.JcrUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.Session;
import javax.servlet.*;
import java.io.IOException;
@Component(
service = Filter.class,
immediate = true,
name = "Experience AEM Restore file only (not metadata) Filter",
property = {
Constants.SERVICE_RANKING + ":Integer=-99",
"sling.filter.scope=COMPONENT",
"sling.filter.selectors=version"
}
)
public class ExperienceAEMVersionFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(ExperienceAEMVersionFilter.class);
private static String EAEM_REVERT_PARAM = "eaem-revert-toversion-file-only";
private static String METADATA_BACKUP = "metadata-backup";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest)request;
String assetPath = slingRequest.getParameter("path");
if(StringUtils.isEmpty(assetPath)){
chain.doFilter(slingRequest, response);
return;
}
String revertParam = slingRequest.getParameter(EAEM_REVERT_PARAM);
if(StringUtils.isEmpty(revertParam)){
chain.doFilter(slingRequest, response);
return;
}
ResourceResolver resolver = slingRequest.getResourceResolver();
Session session = resolver.adaptTo(Session.class);
Resource assetResource = resolver.getResource(assetPath);
if(assetResource == null){
chain.doFilter(slingRequest, response);
return;
}
String folderPath = assetPath.substring(0, assetPath.lastIndexOf("/"));
String backupName = assetResource.getName() + "-" + METADATA_BACKUP;
String backupPath = folderPath + "/" + backupName;
Resource metadata = assetResource.getChild("jcr:content/metadata");
if(metadata == null){
chain.doFilter(slingRequest, response);
return;
}
try{
if(session.itemExists(backupPath)){
session.removeItem(backupPath);
}
JcrUtil.copy(metadata.adaptTo(Node.class), resolver.getResource(folderPath).adaptTo(Node.class), backupName, true);
chain.doFilter(slingRequest, response);
String metadataPath = metadata.getPath();
session.removeItem(metadataPath);
session.move(backupPath, metadataPath);
session.save();
}catch(Exception e){
logger.error("Error taking metadata backup - " + assetPath , e);
}
}
@Override
public void destroy() {
}
}