Liferay Dynamic Includes

Liferay Dynamic Includes

How to customize JSPs with Dynamic Includes

Overview


There are several ways for JSP customization in Liferay. For Core JSPs you can use a Custom JSP Bag. For module JSPs you can use an OSGi Fragment Bundle. These approaches are powerful: you can make any changes you need to the JSPs being customized. But they have a common drawback: after portal upgrade to the next version you might need to come back to these modules, and merge changes from Liferay with your customizations. Dynamic Includes can solve this problem, and make portal upgrades easier and safer. 


What is a Dynamic Include?


Dynamic Include is a Liferay extension point, which provides developers with a possibility to modify core and module JSPs without a complete override. 

It consists of two parts:

  • A liferay-util:dynamic-include tag - used by Liferay to provide the extension points in certain places for JSP files;
  • A DynamicInclude interface - to be implemented by developers for inserting additional content on JSP files, where liferay-util:dynamic-include tag is present.

In this example of JSP file (to render a web content article) there are two occurrences of the liferay-util:dynamic-include tag:

  • At the top of the JSP file:

    <liferay-util:dynamic-include key="com.liferay.journal.taglib#/journal_article/page.jsp#pre" />

  • At the bottom of the JSP file:
    <liferay-util:dynamic-include key="com.liferay.journal.taglib#/journal_article/page.jsp#pre" />

As we see, tag uses a key attribute, which should be unique. Developers can register their Dynamic Include components for specific keys to insert content into a specific position on the JSP being customized. DynamicInclude interface provides a register method to register component for the given key, and also an include method to actually render the custom content:


public interface DynamicInclude {

  public void include(

        HttpServletRequest httpServletRequest,

        HttpServletResponse httpServletResponse, String key)

     throws IOException;

  public void register(DynamicIncludeRegistry dynamicIncludeRegistry);

  public interface DynamicIncludeRegistry {

     public void register(String key);

  }

}


The DynamicIncludeUtil class takes care of Dynamic Include components registration and rendering content from them. It’s invoked in the tag’s handler for liferay-util:dynamic-include tag - DynamicIncludeTag. This way, content provided by developers becomes rendered on portal pages.

DynamicInclude interface has several implementations: BaseDynamicInclude - a basic one, and BaseJSPDynamicInclude - a base implementation for JSP-based Dynamic Includes.

An example of Dynamic Include in Liferay sources is a JQueryTopHeadDynamicInclude class, which renders import of jQuery-based files if it’s enabled in portal settings. 



How to Implement a Custom Dynamic Include? 


To customize a JSP with a Dynamic Include we need to create a module in the Liferay Workspace:



build.gradle is quite simple:


dependencies {

  compileOnly group: "com.liferay.portal", name: "release.portal.api"

}


As well as bnd.bnd:


Bundle-Name: LifeDev Dynamic Include

Bundle-SymbolicName: com.lifedev.dynamic.include

Bundle-Version: 1.0.0


Now we need to create a component for DynamicInclude, register it for a specific key and implement logic for rendering content.

In this sample we’ll extend the JSP for rendering a web content article and output custom messages at the start and end of the JSP file.

Web content articles are rendered with a liferay-journal:journal-article tag, and inside the tag handler’s JSP (page.jsp) we have two dynamic include tags (see previous chapter). 

Now let’s implement two Dynamic Include components for #pre and #post dynamic include tags.

 For #pre we’ll render a custom title on top of web content:


@Component(service = DynamicInclude.class)

public class JournalArticlePreDynamicInclude extends BaseDynamicInclude {


   @Override

   public void include(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String key) throws IOException {

       PrintWriter printWriter = httpServletResponse.getWriter();

       printWriter.write("<h2>LifeDev Article</h2>");

   }


   @Override

   public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {

       dynamicIncludeRegistry.register("com.liferay.journal.taglib#/journal_article/page.jsp#pre");

   }


}


For #post we’ll render a copyright message below:


@Component(service = DynamicInclude.class)

public class JournalArticlePostDynamicInclude extends BaseDynamicInclude {


   @Override

   public void include(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String key) throws IOException {

       PrintWriter printWriter = httpServletResponse.getWriter();

       printWriter.write("<p>&copy; LifeDev.</p>");

   }


   @Override

   public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {

       dynamicIncludeRegistry.register("com.liferay.journal.taglib#/journal_article/page.jsp#post");

   }


}


It works in the following way:


Custom content is rendered in the places, where appropriate liferay-util:dynamic-include tags are defined.

Once we deploy such module - we should see custom message above and below the displayed article:




Enjoy 😏

 

Comments

  1. Thanks for sharing! I will visit this blog every day!

    ReplyDelete
  2. Is it possible to place '' tags wherever within the .jsp target file, or, on the contrary, are you bound to work with the '' tags which come defined within the target .jsp file?

    ReplyDelete
  3. Is it possible to place 'liferay dynamic-include' tags wherever within the .jsp target file, or, on the contrary, are you bound to work with the 'liferay dynamic-include' tags which come defined within the target .jsp file?

    ReplyDelete

Post a Comment

Popular posts from this blog

Liferay Search Container Example

Liferay DXP - max upload file size

Liferay Keycloak integration