Hi All,
I am trying to inject one Tab in all my dialog dynamically using Filters. I have done something similar for classic dialog using some similar concept as described in this blog Programmatically adding tabs to CQ dialogs - WE ARE INSIDE Earlier this manipulation was easy as it was on the JSON.
I am very close to make it work, I am able to add the tab and also it is loading properly, only thing I am struggling with is tab does not load the value in dialog even though it is saved properly into JCR. I am using AEM 6.3 with SP1 along with CFP
Below is my high level technical approach
AEM makes call to this path which loads the dialog HTML, I am manipulating the HTML to insert the dialog related HTML in my filter
Also as AEM touch dialogs are now component so most of the execution starts from this JSP
/libs/cq/gui/components/authoring/dialog/dialog.jsp, below lines of code is where it loads the component and creates html for it.
ResourceWrapper hideOnEditWrapper = new FilteringResourceWrapper(resource.getChild("content"));
ResourceWrapper contentWrapper = new WCMFilteringResourceWrapper(hideOnEditWrapper, data, sling.getService(ExpressionResolver.class), slingRequest);
// Standard dialog
cmp.include(contentWrapper, new Tag(contentAttrs));
Below is my Filter code, I did tried adding service ranking hoping that whatever filter or service add value to rest of the dialog element will add it to my dialog fields also but did not got any luck, please suggest how I can load these values in dialog other possible hacky ways I can think of is to write JS listener to load data in my tab or do more nasty manipulation in java to add value also.
package com.mysite.platform.core.filters;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.engine.EngineConstants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysite.platform.core.beans.ComponentDialogFilterBean;
@Component(
service = { Filter.class },
immediate = true,
configurationPolicy = ConfigurationPolicy.REQUIRE,
property = { EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
"label=Component Dialog Filter", "description=Filter to add conditional configuration tab to a component dialog", "service.ranking=2147483647" })
public class ComponentDialogFilter implements Filter
{
private Logger LOG = LoggerFactory.getLogger(ComponentDialogFilter.class);
private ComponentDialogFilterBean componentDialogFilterBean;
//Default values of OSGI service configuration
private static String [] CC_EXCLUDE_PATHS = {"/content/experience-fragments"};
private static String CC_HEAD_HTML_TAG_VALUE = "<a class=\"coral-TabPanel-tab\" href=\"#\" data-toggle=\"tab\">cc</a>";
private static String CC_BODY_HTML_TAG_VALUE = "<section class=\"coral-TabPanel-pane coral-FixedColumn\"><div class=\"coral-FixedColumn-column\"><div><div class=\"coral-Form-fieldwrapper\"><label class=\"coral-Form-fieldlabel\">CC</label><input class=\"coral-Form-field\" type=\"text\" name=\"./cc\" data-foundation-validation=\"\" data-validation=\"\" is=\"coral-textfield\"></div></div></div></section>";
@Override
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
if (request instanceof SlingHttpServletRequest && response instanceof SlingHttpServletResponse)
{
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
LOG.debug("Request path processed *** " + slingRequest.getRequestURI());
if (slingRequest.getRequestURI().contains("/_cq_dialog.html/"))
{
final HttpServletResponse servletResponse = (HttpServletResponse) response;
final StringHttpServletResponseWrapper responseWrapper = new StringHttpServletResponseWrapper(
servletResponse);
String dialogHTML = getDialogHTML(slingRequest, responseWrapper);
response.setContentType("text/html");
response.getWriter().write(addTabtoDialog(dialogHTML));
} else
{
// Not a request related to dialog continue without any modification
chain.doFilter(request, response);
}
} else
{
// Not a SlingHttpServletRequest/Response, so ignore.
chain.doFilter(request, response);
}
}
/**
* Gets the dialog HTML.
*
* @param slingRequest The sling request.
* @param responseWrapper The response wrapper.
* @return The dialog HTML
* @throws IOException Error while adapting to a page.
* @throws ServletException Error while adapting to a page.
*/
private String getDialogHTML(final SlingHttpServletRequest slingRequest,
final StringHttpServletResponseWrapper responseWrapper) throws ServletException, IOException
{
slingRequest.getRequestDispatcher(slingRequest.getRequestURI()).include(slingRequest, responseWrapper);
return responseWrapper.toString();
}
/**
* Add a new tab item to dialog HTML.
*
* @param dialogHTML The dialog HTML.
* @return The merged JSON.
*/
private String addTabtoDialog(final String dialogHTML)
{
if (StringUtils.isNotEmpty(dialogHTML))
{
String newDialogHTML = dialogHTML;
newDialogHTML = insertHTMLTag(newDialogHTML, componentDialogFilterBean.getCcHeadHTMLTagValue(), "</nav>",
1);
newDialogHTML = insertHTMLTag(newDialogHTML, componentDialogFilterBean.getCcBodyHTMLTagValue(),
"</section>", 2);
LOG.trace("Final modified dialog output *** " + newDialogHTML);
return newDialogHTML;
}
// In case of failure return original dialog
return dialogHTML;
}
/**
* Inserts tag in the correct location.
*
* @param dialogHTML The dialog HTML.
* @param insertedHTML The inserted HTML.
* @param delimiterTag The delimiter tag.
* @param order The order.
*
* @return dialog output HTML.
*/
private String insertHTMLTag(final String dialogHTML, final String insertedHTML, final String delimiterTag,
int order)
{
StringBuilder output = new StringBuilder();
String beforeOutput = StringUtils.substringBeforeLast(dialogHTML, delimiterTag);
String afterOutput = StringUtils.substringAfterLast(dialogHTML, delimiterTag);
// If tag is inserted inside the delimiterTag
if (order == 1)
{
return output.append(beforeOutput).append(insertedHTML).append(delimiterTag).append(afterOutput).toString();
} else
{
return output.append(beforeOutput).append(delimiterTag).append(insertedHTML).append(afterOutput).toString();
}
}
/**
* Activate.
*
* @param properties The OSGi configuration properties.
*/
@Activate
protected final void activate(final Map<String, Object> properties)
{
// Initialize and keep the configuration in memory on start of component
setConfigProperties(properties);
LOG.debug("Component dialog filter is enabled ***");
}
/**
* Deactivate method.
*/
@Deactivate
public void deactivate()
{
this.componentDialogFilterBean = null;
LOG.debug("Component dialog filter is disabled ***");
}
/**
* Setting service configuration.
*
* @param properties
*/
private void setConfigProperties(Map<String, Object> properties)
{
this.componentDialogFilterBean = new ComponentDialogFilterBean();
this.componentDialogFilterBean.setEnabled(PropertiesUtil.toBoolean(properties.get("enabled"), true));
this.componentDialogFilterBean
.setExcludePaths(PropertiesUtil.toStringArray(properties.get("templatePath"), CC_EXCLUDE_PATHS));
this.componentDialogFilterBean.setCcHeadHTMLTagValue(
PropertiesUtil.toString(properties.get("ccHeadTagValue"), CC_HEAD_HTML_TAG_VALUE));
this.componentDialogFilterBean.setCcBodyHTMLTagValue(
PropertiesUtil.toString(properties.get("ccBodyTagValue"), CC_BODY_HTML_TAG_VALUE));
LOG.debug("ComponentDialogFilter: {}", this.componentDialogFilterBean);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
LOG.debug("Component dialog filter is initialized ***");
}
@Override
public void destroy()
{
LOG.debug("Component dialog filter is Destroyed *** ");
}
/**
* Response wrapper class.
*/
class StringHttpServletResponseWrapper extends HttpServletResponseWrapper
{
private final transient StringWriter stringWriter = new StringWriter();
/**
* Constructor.
*
* @param response The response.
*/
public StringHttpServletResponseWrapper(final HttpServletResponse response)
{
super(response);
}
/**
* Gets the writer.
*
* @return The print writer.
* @throws IOException Error while adapting to a page.
*/
@Override
public final PrintWriter getWriter() throws IOException
{
return new PrintWriter(stringWriter);
}
/**
* Gets the output stream.
*
* @return The output stream.
* @throws IOException Error while adapting to a page.
*/
@Override
public final ServletOutputStream getOutputStream() throws IOException
{
return super.getOutputStream();
}
/**
* Gets the string representation of response.
*
* @return The response string.
*/
public final String toString()
{
return stringWriter.toString();
}
}
}
Solved! Go to Solution.
Views
Replies
Total Likes
You can control show hide of tab under some path using rendercondition Render Conditions — Granite UI 1.0 documentation
Probably you can use feature flag or simple rendition for this.
more info - Sling Feature Flag in AEM
Views
Replies
Total Likes
Why are you trying to programmatically add a tab into dialog using code. Why not simply add it to the dialog?
Views
Replies
Total Likes
Yes, I agree with Scott.
You can create dialog manually or programmatically. If you have concern to add tab at multiple places then just write a utility to add tab nodes in component dialog using JCR API Or you can create tab dialog somewhere and include using all the places using include. Include — Granite UI 1.0 documentation this will help you in case of further modification you can change at one place and changes will be applied at all the places wherever dialog is used.
Views
Replies
Total Likes
I don't want to add it directly on the dialog as under some particular path this tab should not be included. I am aware of include options.
Views
Replies
Total Likes
I am not sure this is best practice. If you want to modify your dialog with code - you should be using Granite API.
Views
Replies
Total Likes
You can control show hide of tab under some path using rendercondition Render Conditions — Granite UI 1.0 documentation
Probably you can use feature flag or simple rendition for this.
more info - Sling Feature Flag in AEM
Views
Replies
Total Likes
Views
Likes
Replies