Hello Fellow AEM'ers
I am actually looking for approaches to customize the side rail panel in page authoring mode so that I do not want to populate the images from a particular folder in DAM.
For example in below image, you can see all the images in highlighted screen. I would like to restrict that by default for a particular folder in DAM. I know we can use Filter path, but authors will everytime have to go and select their appropriate folder. Really appreciate any help or advice here.
Solved! Go to Solution.
Views
Replies
Total Likes
@samr2b1 There is no other way to achieve your goal without customization. AEM OOTB is not providing any option to configure the default path, either specific or dedicated endpoint you could use. Of course it can be done many ways, my first post shows probably the easiest customization. Here is an alternative, backend only solution that is using using Sling Filter.
package com.example.filters; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.engine.EngineConstants; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import java.io.IOException; @Component(service = Filter.class, property = { EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST, }) public class AssetsFinderFilter implements Filter { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; if (slingRequest.getRequestPathInfo().getResourcePath().startsWith("/bin/wcm/contentfinder/asset/view")) { filterChain.doFilter(new AssetsFinderSlingHttpServletRequest(slingRequest), response); } else { filterChain.doFilter(request, response); } } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
package com.example.filters; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.request.RequestPathInfo; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class AssetsFinderSlingHttpServletRequest extends SlingHttpServletRequestWrapper { protected RequestPathInfo requestPathInfo; public AssetsFinderSlingHttpServletRequest(SlingHttpServletRequest wrappedRequest) { super(wrappedRequest); WrappedRequestPathInfo wrappedRequestPathInfo = createWrappedRequestPathInfo(); RequestPathInfo wrappedRequestInfo = (RequestPathInfo) Proxy.newProxyInstance( RequestPathInfo.class.getClassLoader(), new Class[] { RequestPathInfo.class}, wrappedRequestPathInfo); requestPathInfo = wrappedRequestInfo; } public WrappedRequestPathInfo createWrappedRequestPathInfo() { return new WrappedRequestPathInfo(); } @Override public RequestPathInfo getRequestPathInfo() { return requestPathInfo; } class WrappedRequestPathInfo implements InvocationHandler { private final static String DEFAULT_SUFFIX = "/content/dam/we-retail"; private RequestPathInfo getOriginal() { return getSlingRequest().getRequestPathInfo(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); switch (methodName) { case "getResourcePath": return getResourcePath(); case "getExtension": return getExtension(); case "getSelectorString": return getSelectorString(); case "getSelectors": return getSelectors(); case "getSuffix": return getSuffix(); case "getSuffixResource": return getSuffixResource(); default: throw new UnsupportedOperationException("REQUESTPATHINFOWRAPPER >> NO IMPLEMENTATION FOR " + methodName); } } public String getResourcePath() { return getOriginal().getResourcePath(); } public String getExtension() { return getOriginal().getExtension(); } public String getSelectorString() { return getOriginal().getSelectorString(); } public String[] getSelectors() { return getOriginal().getSelectors(); } public String getSuffix() { return StringUtils.defaultIfBlank(getOriginal().getSuffix(), DEFAULT_SUFFIX); } public Resource getSuffixResource() { return getOriginal().getSuffixResource(); } } }
Summarizing, if you do not want to write custom code, then the only OOTB option is that authors will always set path manually. In other case customization is needed, you can choose which customization way will be the best from your perspective.
Hi @samr2b1,
You should be able to achieve your goal by overlaying /libs/cq/gui/components/authoring/editors/clientlibs/core/js/assetController/image/ImageAssetPanel.js this file contains method that builds query that is sent to /bin/wcm/contentfinder/asset/view.html which returns list of assets that are later visible in side panel base on given criteria e.g. path to specific folder to DAM that can be set by author. Below steps has been done on AEM 6.5.12, but should be applicable for other AEM versions as well.
As a result ImageAssetPanel.js will look like this:
(function ($, ns, channel, window, undefined) { /** * Asset Controller for the extended image type * * All assets that are related to the notion of image * * @memberOf Granite.author.ui.assetFinder * @inner * @alias imageAssetController * @ignore * @type {Granite.author.ui.assetFinder~AssetController} */ var self = {}, name = 'Images'; // make the loadAssets function more flexible self.searchRoot = '/content/dam/we-retail'; // open assets in the admin view (to edit properties) self.viewInAdminRoot = '/assetdetails.html{+item}'; var searchPath = self.searchRoot, imageServlet = '/bin/wcm/contentfinder/asset/view.html', itemResourceType = 'cq/gui/components/authoring/assetfinder/asset'; /** Pre asset type switch hook */ self.setUp = function () {}; /** Post asset type switch hook */ self.tearDown = function () {}; /** * Loads extended image type resources * * @param query {String} search query * @param lowerLimit {Number} lower bound for paging * @param upperLimit {Number} upper bound for paging * @returns {jQuery.Promise} */ self.loadAssets = function (query, lowerLimit, upperLimit) { var param = { '_dc': new Date().getTime(), // cache killer 'query': query.concat("order:\"-jcr:content/jcr:lastModified\" "), // sort by jcr:content/jcr:lastModified property 'mimeType': 'image,application/x-ImageSet,application/x-SpinSet,application/x-MixedMediaSet,application/x-CarouselSet', 'itemResourceType': itemResourceType, // single item rendering (cards) 'limit': lowerLimit + ".." + upperLimit, '_charset_': 'utf-8' }; return $.ajax({ type: 'GET', dataType: 'html', url: Granite.HTTP.externalize(imageServlet) + searchPath, data: param }); }; /** * Set URL to image servlet * @param {String} imgServlet - URL to image servlet */ self.setServlet = function (imgServlet) { imageServlet = imgServlet; }; self.setSearchPath = function (spath) { searchPath = spath || searchPath; }; self.setItemResourceType = function (rt) { itemResourceType = rt; }; self.resetSearchPath = function () { searchPath = self.searchRoot; }; // register as a asset tab ns.ui.assetFinder.register(name, self); }(jQuery, Granite.author, jQuery(document), this));
Optional step:
You may also want to change path that can be set by author, to use the same location.
To do **bleep** you have overaly /libs/wcm/core/content/editor/jcr:content/sidepanels/edit/items/tabs/items/assetsTab/items/filterPanel/items/search/items/searchpanel/items/imagepath, and change rootPath propoerty value.
Useful links:
In case you would like to set default DAM path dynamically, e.g. differetn path to different group of users or different, then most likely you will need prived those dynamic data e.g. via servlet to ImageAssetPanel.js
Thank you Lukasz for your detailed explanation. This is really helpful. Other than this, is there a way I can achieve this without doing further customizations on the siderail? My siderail is already heavily customized and I am looking to see if I can achieve this with any OOTB feature without doing much of code change.
@samr2b1 There is no other way to achieve your goal without customization. AEM OOTB is not providing any option to configure the default path, either specific or dedicated endpoint you could use. Of course it can be done many ways, my first post shows probably the easiest customization. Here is an alternative, backend only solution that is using using Sling Filter.
package com.example.filters; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.engine.EngineConstants; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import java.io.IOException; @Component(service = Filter.class, property = { EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST, }) public class AssetsFinderFilter implements Filter { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; if (slingRequest.getRequestPathInfo().getResourcePath().startsWith("/bin/wcm/contentfinder/asset/view")) { filterChain.doFilter(new AssetsFinderSlingHttpServletRequest(slingRequest), response); } else { filterChain.doFilter(request, response); } } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
package com.example.filters; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.request.RequestPathInfo; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class AssetsFinderSlingHttpServletRequest extends SlingHttpServletRequestWrapper { protected RequestPathInfo requestPathInfo; public AssetsFinderSlingHttpServletRequest(SlingHttpServletRequest wrappedRequest) { super(wrappedRequest); WrappedRequestPathInfo wrappedRequestPathInfo = createWrappedRequestPathInfo(); RequestPathInfo wrappedRequestInfo = (RequestPathInfo) Proxy.newProxyInstance( RequestPathInfo.class.getClassLoader(), new Class[] { RequestPathInfo.class}, wrappedRequestPathInfo); requestPathInfo = wrappedRequestInfo; } public WrappedRequestPathInfo createWrappedRequestPathInfo() { return new WrappedRequestPathInfo(); } @Override public RequestPathInfo getRequestPathInfo() { return requestPathInfo; } class WrappedRequestPathInfo implements InvocationHandler { private final static String DEFAULT_SUFFIX = "/content/dam/we-retail"; private RequestPathInfo getOriginal() { return getSlingRequest().getRequestPathInfo(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); switch (methodName) { case "getResourcePath": return getResourcePath(); case "getExtension": return getExtension(); case "getSelectorString": return getSelectorString(); case "getSelectors": return getSelectors(); case "getSuffix": return getSuffix(); case "getSuffixResource": return getSuffixResource(); default: throw new UnsupportedOperationException("REQUESTPATHINFOWRAPPER >> NO IMPLEMENTATION FOR " + methodName); } } public String getResourcePath() { return getOriginal().getResourcePath(); } public String getExtension() { return getOriginal().getExtension(); } public String getSelectorString() { return getOriginal().getSelectorString(); } public String[] getSelectors() { return getOriginal().getSelectors(); } public String getSuffix() { return StringUtils.defaultIfBlank(getOriginal().getSuffix(), DEFAULT_SUFFIX); } public Resource getSuffixResource() { return getOriginal().getSuffixResource(); } } }
Summarizing, if you do not want to write custom code, then the only OOTB option is that authors will always set path manually. In other case customization is needed, you can choose which customization way will be the best from your perspective.