Como crear un custom taglib en Liferay

Para usar reCAPTCHAv3

Publicado por Miguel Ángel Júlvez el 02 de noviembre de 2018

Un taglib es un conjuntos de etiquetas que podemos usar en los ficheros JSP, similares en apariencia a una etiqueta html o xml, a las que podemos añadir una funcionalidad específica normalmente repetitiva que hará nuestro código más legible.

En Liferay 7, como no podía ser de otra manera, podemos añadir taglibs creando un module osgi con una configuración especial que permitirá el uso de estos taglibs entre los distintos modules.

Por seguir el hilo del anterior post donde explicaba como usar reCAPTCHAv3 en Liferay 7.1, vamos a crear en este post un taglib que simplificará el código que tenemos que poner en nuestros .jsp para usar esta nueva tecnología de Google.

Crear un custom taglib

Lo que debemos hacer es crear un nuevo module osgi (yo lo he llamado recaptcha-taglib) y dentro de él vamos a hacer varias cosas:

Crear el package con las clases java necesarias

Aquí vamos a definir el componente osgi y la configuración del Tag. En la configuración del Tag es donde indicamos los parámetros de entrada, en este caso un solo atributo action, y el .jsp a renderizar a la hora de mostrarse en pantalla

@Component(immediate = true)
public class ServletContextUtil {

	public static final ServletContext getServletContext() {
		return _instance._getServletContext();
	}

	@Activate
	protected void activate() {
		_instance = this;
	}

	@Deactivate
	protected void deactivate() {
		_instance = null;
	}

	@Reference(
		target = "(osgi.web.symbolicname=com.miguelangeljulvez.recaptcha.taglib)",
		unbind = "-"
	)
	protected void setServletContext(ServletContext servletContext) {
		_servletContext = servletContext;
	}

	private ServletContext _getServletContext() {
		return _servletContext;
	}

	private static ServletContextUtil _instance;

	private ServletContext _servletContext;

}
   public class CaptchaTag extends IncludeTag {

	@Override
	public void setPageContext(PageContext pageContext) {
		super.setPageContext(pageContext);

		setServletContext(ServletContextUtil.getServletContext());
	}

	public void setAction(String action) {
		_action = action;
	}

	@Override
	protected void cleanUp() {
		super.cleanUp();

		_action = null;
	}

	@Override
	protected String getPage() {
		return _PAGE;
	}

	@Override
	protected void setAttributes(HttpServletRequest request) {
		request.setAttribute("maj-captcha:recaptcha:action", _action);
	}

	private static final String _PAGE = "/recaptcha/page.jsp";

	private String _action;

}

Crear los .jsp de nuestro taglib

Aquí indicamos el código html que debe mostrar este nuevo taglib. Como ves, este es el mismo texto prácticamente que en el post anterior añadíamos “manualmente”

<c:if test="<%= captchaEnabled %>">

   <input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response" value="" />
   <input type="hidden" name="g-recaptcha-response-action" id="g-recaptcha-response-action" value="<%=action%>" />

   <script data-senna-track="temporary">
       grecaptcha.ready(function() {
           grecaptcha.execute('<%= captchaConfiguration.reCaptchaPublicKey() %>', {action: '<%=action%>'}).then(function(token) {
               $('#g-recaptcha-response').val(token);
           });
       });
   </script>

</c:if>

Crear el fichero .tld que define el taglib

<?xml version="1.0"?>

<taglib
  version="2.1"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
>
  <tlib-version>1.0</tlib-version>
  <short-name>maj-captcha</short-name>
  <uri>http://miguelangeljulvez.com/tld/recaptcha</uri>
  <tag>
     <description>Crea los elementos necesarios para usar reCAPTCHAv3</description>
     <name>recaptcha</name>
     <tag-class>com.miguelangeljulvez.recaptcha.taglib.servlet.taglib.CaptchaTag</tag-class>
     <body-content>JSP</body-content>
     <attribute>
        <description>La action a supervisar por el captcha</description>
        <name>action</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
     </attribute>
  </tag>
</taglib>

Aquí lo importantes es que el tag-class coincida con la ruta a la clase de configuración del Tag

Configurar el bnd.bnd para exportar este taglib

Bundle-Name: reCAPTCHA Taglib
Bundle-SymbolicName: com.miguelangeljulvez.recaptcha.taglib
Bundle-Version: 1.0.0
Export-Package: com.miguelangeljulvez.recaptcha.taglib
Provide-Capability:\
  osgi.extender;\
     osgi.extender="jsp.taglib";\
     uri="http://miguelangeljulvez.com/tld/recaptcha";\
     version:Version="${Bundle-Version}"
Web-ContextPath: /recaptcha-taglib

Aquí lo importante es que el valor del campo uri coincida con el valor definido en el .tld

En este proyecto GitHub tienes el código disponible que he desarrollado en este post para que simplemente lo compiles y despliegues en tu instalación.

De esta manera, ahora para poder usar reCAPTCHAv3 en nuestros desarrollos lo único que tenemos que hacer es lo siguiente

Añadir la nueva dependencia al portlet del formulario

compileOnly project(":modules:com.miguelangeljulvez.recaptcha:recaptcha-taglib")

Añadir el nuevo taglib a nuestro formulario

<%@ taglib prefix="maj" uri="http://miguelangeljulvez.com/tld/recaptcha" %>

<aui:form action="${addContactoURL}" method="post" name="fm">

    <liferay-ui:error exception="<%= CaptchaTextException.class %>" message="text-verification-failed" />
    <liferay-ui:error exception="<%= CaptchaConfigurationException.class %>" message="a-captcha-error-occurred-please-contact-an-administrator" />
    <liferay-ui:error exception="<%= CaptchaException.class %>" message="a-captcha-error-occurred-please-contact-an-administrator" />

    <aui:input type="textarea" name="message" label="message" />

    <maj:recaptcha action="contacto" />

    <aui:button name="submit" type="submit" value="Enviar"/>
</aui:form>

Verificar el captcha recibido

public void addContacto(ActionRequest actionRequest, ActionResponse actionResponse) {

      //Recoger parámetros y validar

   try {
       CaptchaUtil.check(actionRequest);

       //Llamar al backend para guardar el contacto
   } catch (CaptchaConfigurationException e) {
       SessionErrors.add(actionRequest, CaptchaConfigurationException.class.getName());
   } catch (CaptchaTextException e) {
       SessionErrors.add(actionRequest, CaptchaTextException.class.getName());
   } catch (CaptchaException e) {
       SessionErrors.add(actionRequest, CaptchaException.class.getName());
   }
}