Expand my Community achievements bar.

SOLVED

Inject a dialog tab using Filter in AEM Touch dialog

Avatar

Level 2

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

http://localhost:4502/mnt/override/apps/web/platform/components/content/productgrids/_cq_dialog.html...

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();

        }

    }

}

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

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



Arun Patidar

View solution in original post

5 Replies

Avatar

Level 10

Why are you trying to programmatically add a tab into dialog using code. Why not simply add it to the dialog?

Avatar

Community Advisor

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.



Arun Patidar

Avatar

Level 2

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.

Avatar

Level 10

I am not sure this is best practice. If you want to modify your dialog with code - you should be using Granite API.

Avatar

Correct answer by
Community Advisor

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



Arun Patidar