Expand my Community achievements bar.

SOLVED

Form Core Component submission using Custom Action Type

Avatar

Level 5

Hi All,

 

Building a Form component by using Form core components. Added Custom Action type in drop down "Custom Action Call " of Form Core container proxy. Now on selection of above custom action in Container component & while Form submission need to call Service/Servlet the back-end for logic execution how to achieve what changes are needed. Any pointers around this on how to & where changes are needed.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @rsl_lucky, using servlet will be much more complicated due to the way how form component has been design. But of course it is possible. It can be done like this:

You will need to create your own Form container component that will extend /apps/core/wcm/components/form/container/v2/container and do some modification in container.html, e.g.

<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~ Copyright 2016 Adobe
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
<form data-sly-use.container="com.adobe.cq.wcm.core.components.models.form.Container"
      data-sly-use.grid="com.day.cq.wcm.foundation.model.responsivegrid.ResponsiveGrid"
      method="${container.method}" action="${container.action @ addSelectors='formHandler'}" id="${container.id}" name="${container.name}"
      enctype="${container.enctype}"
      class="cmp-form ${grid.cssClass}">
    <div data-sly-test="${container.errorMessages}"
         data-sly-list.item="${container.errorMessages}"
         class="cmp-form-error">
        <p class="cmp-form-error__item">${item}</p>
    </div>
    <input type="hidden" name=":formstart" value="${resource.path}"/>
    <input type="hidden" name="_charset_" value="utf-8"/>
    <input data-sly-test="${container.redirect}" type="hidden" name=":redirect" value="${container.redirect @ extension='html'}"/>
    <sly data-sly-repeat.paragraph="${grid.paragraphs}"
         data-sly-resource="${paragraph.path @ resourceType=paragraph.resourceType, decorationTagName='div', cssClassName=paragraph.cssClass}"></sly>
    <sly data-sly-resource="${resource.path @ resourceType=container.resourceTypeForDropArea, appendPath='/*', decorationTagName='div', cssClassName='new section aem-Grid-newComponent'}"
         data-sly-test="${wcmmode.edit || wcmmode.preview}"></sly>
</form>

The change that has been done is formHandler selector has been added, so it will be included in link set inside action attribute.

Next we have servlet that will consume the request:

package com.mysite.core.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(service = Servlet.class, property = {
    "sling.servlet.methods=" + HttpConstants.METHOD_POST,
    "sling.servlet.resourceTypes=sling/servlet/default",
    "sling.servlet.selectors=formHandler",
    "sling.servlet.extensions=html"
})
public class MyFormServlet  extends SlingAllMethodsServlet {

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Handling form request via servlet");
    }
}

and the last thing, you need to disable com.adobe.cq.wcm.core.components.internal.servlets.CoreFormHandlingServlet OSGi component - you can sue component disbaler functinality form ACS Commons. The main reason for that is the fact that CoreFormHandlingServlet is servlet and filter so it is run in filter chain for every request.

I think that the previous solution is a bit better, because you do not need to change what you are getting out of the box. Of course running service from jsp is not a nice solution but I think as long you will keep your business logic in some java class/service this could be acceptable.

View solution in original post

8 Replies

Avatar

Community Advisor

Hi @rsl_lucky, please find below very simple implementation of custom action that is handled by OSGi service.

CRX structure:

sample-form-action-structure.png

Code for above structure:

 

<customAction
    jcr:primaryType="sling:Folder"
    jcr:title="Custom action"
    sling:resourceType="foundation/components/form/action">
    <cq:dialog
        jcr:primaryType="nt:unstructured"
        jcr:title="Form Container"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <granite:data
            jcr:primaryType="nt:unstructured"
            showhidetargetvalue="my-app/components/form/actions/customAction"
            usesRedirect="true"/>
    </cq:dialog>
</customAction>

 

post.POST.jsp

 

<%--
    Custom Action example

--%><%
%><%@include file="/libs/foundation/global.jsp"%><%
%><%@page session="false" %><%
%><%@page import="com.myapp.MySampleService"%><%
%><%

    final MySampleService mySampleService = sling.getService(MySampleService.class);
    mySampleService.handleFormRequest(slingRequest, slingResponse);

%>

 

OSGi service:

 

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.osgi.service.component.annotations.Component;


@Component(service = MySampleService.class)
public class MySampleService {

    public void handleFormRequest(final SlingHttpServletRequest request, final SlingHttpServletResponse response) {
        System.out.println("Custom form action");
        // place for your code
    }
}

 

In general post.POST.jsp is handling the request, from this place you can run any logic you like.

Avatar

Level 5

Hi lukaszm 

Implemented the same code for testing the approach but service was not getting called. Let me try check the issue at service end and is their any other approach to implement to call servlet.

Avatar

Community Advisor

Hi @rsl_lucky, I have checked it one more time and it is working fine on my AEM 6.5.11 - service implementation is called. Do you see any errors in the log, also did you checked stdout.log - this is where the output from above sample code will go.

Avatar

Level 5

Hi lukaszm, 

Yes could find the updates in log file now, issue with my Service which got resolved. In case to call Servlet in above case instead of Service how can we ? Also any thought's how can we apply styles to each field as they are form core components.

Avatar

Correct answer by
Community Advisor

Hi @rsl_lucky, using servlet will be much more complicated due to the way how form component has been design. But of course it is possible. It can be done like this:

You will need to create your own Form container component that will extend /apps/core/wcm/components/form/container/v2/container and do some modification in container.html, e.g.

<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~ Copyright 2016 Adobe
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
<form data-sly-use.container="com.adobe.cq.wcm.core.components.models.form.Container"
      data-sly-use.grid="com.day.cq.wcm.foundation.model.responsivegrid.ResponsiveGrid"
      method="${container.method}" action="${container.action @ addSelectors='formHandler'}" id="${container.id}" name="${container.name}"
      enctype="${container.enctype}"
      class="cmp-form ${grid.cssClass}">
    <div data-sly-test="${container.errorMessages}"
         data-sly-list.item="${container.errorMessages}"
         class="cmp-form-error">
        <p class="cmp-form-error__item">${item}</p>
    </div>
    <input type="hidden" name=":formstart" value="${resource.path}"/>
    <input type="hidden" name="_charset_" value="utf-8"/>
    <input data-sly-test="${container.redirect}" type="hidden" name=":redirect" value="${container.redirect @ extension='html'}"/>
    <sly data-sly-repeat.paragraph="${grid.paragraphs}"
         data-sly-resource="${paragraph.path @ resourceType=paragraph.resourceType, decorationTagName='div', cssClassName=paragraph.cssClass}"></sly>
    <sly data-sly-resource="${resource.path @ resourceType=container.resourceTypeForDropArea, appendPath='/*', decorationTagName='div', cssClassName='new section aem-Grid-newComponent'}"
         data-sly-test="${wcmmode.edit || wcmmode.preview}"></sly>
</form>

The change that has been done is formHandler selector has been added, so it will be included in link set inside action attribute.

Next we have servlet that will consume the request:

package com.mysite.core.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(service = Servlet.class, property = {
    "sling.servlet.methods=" + HttpConstants.METHOD_POST,
    "sling.servlet.resourceTypes=sling/servlet/default",
    "sling.servlet.selectors=formHandler",
    "sling.servlet.extensions=html"
})
public class MyFormServlet  extends SlingAllMethodsServlet {

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Handling form request via servlet");
    }
}

and the last thing, you need to disable com.adobe.cq.wcm.core.components.internal.servlets.CoreFormHandlingServlet OSGi component - you can sue component disbaler functinality form ACS Commons. The main reason for that is the fact that CoreFormHandlingServlet is servlet and filter so it is run in filter chain for every request.

I think that the previous solution is a bit better, because you do not need to change what you are getting out of the box. Of course running service from jsp is not a nice solution but I think as long you will keep your business logic in some java class/service this could be acceptable.

Avatar

Level 5

Ok lukaszm , will try this approach of servlet & let know if any issues, thanks for the info.

Avatar

Level 3

Hi @rsl_lucky, I am facing the same issue can you please explain me how to create custom action type in core form container.I want to pass my servlet URL in that custom action type.

 

Thank you in advance

Avatar

Level 2

Is this approach viable for the ‘Mail’ action in Core Form Container?


Can you send an email using handleFromRequest?