Theme JSP Overrides

Ok, so in the last post I talked about JSP Include Buffer.

That post talked about how you'd override JSPs from Liferay's core in a maintainable way. Now I'd like to introduce you to a Liferay Theme feature, new in 6.1, that will grant you much more power when it comes to manipulating portal views from your theme using that plus this additional feature.

From your theme you can now include templates (following the same path maning as they are found in the core) in your themes which override those of the core, provided they have a template extension matching that of your themes. They will override the default view of the portatl but only where and when your theme is applied.

Now think about this! When you apply a theme onto any page of your portal, you can alter the view of any portlet on just those pages!

Let me illustrate with an example. I'll use the exact same example that was intriduced in the previous post just so it easier to follow along.

Assuming for the moment that our theme is a Freemarker theme, we add the following template to our theme:

<SDK_ROOT>/themes/my-theme/docroot/_diffs/templates/html/taglib/ui/page_iterator/start.ftl

In this template we place the following code (using the buffer pattern):

<#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] />

<@liferay_util["buffer"] var="html">
    <@liferay_util["include"] 
        page="/html/taglib/ui/page_iterator/start.jsp" 
        strict=true 
        useCustomPage=false 
    /> 
</@> 
 
<div style="border: 4px solid red; padding: 4px;">
    ${html}
</div>

So, you should note two things.

First, unlike in the previous post I don't have to use the .portal encoded name for the jsp because it's still where it started, the portal didn't relocate it since my override is in the theme (it only does that when using hook plugins).

Second, the attribute strict=true. As I mentioned before, but will again, this is to prevent the portal from doing a lookup for overrides when making the call to the underlying JSP (this is to avoid infinite recursion since we're in an override right now).

Ok, I can noew enhance or alter this view no problem without having to reproduce the whole of the original logic and I'm doing it from my theme no less!

What more could you ask for?

Well it turns out there IS more that could be asked and since it was asked for, we made it reality (that's what we do at Liferay, we make wishes come true).

The question was "Could you override a JSP, but only for a particular portlet?"

Since overriding a blog JSP only in blogs doesn't really make sense, nor does overriding a blog JSP only for message boards, the main case here is with taglib JSPs. Liferay has a vast number of tags and their view logic are largely implemented with JSPs.

So, the answer to that is, "Yes, now you can override a JSP in your theme for only specific portlets!"

How do you do it?

Simple! All you need to do is add the portlet Id of the target portlet, between the file name of the original JSP and the extension of the template like so:

<SDK_ROOT>/themes/my-theme/docroot/_diffs/templates/html/taglib/ui/page_iterator/start.19.ftl

Using a similar template containing (note the green border instead):

<#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] />

<@liferay_util["buffer"] var="html">
    <@liferay_util["include"]
        page="/html/taglib/ui/page_iterator/start.jsp"
        strict=true 
        useCustomPage=false 
    />
</@>

<div style="border: 4px solid green; padding: 4px;"> 
    ${html} 
</div>  

Now, you'll note that we have two new tempalate files in our theme:

_diffs/templates/html/taglib/ui/page_iterator/start.ftl
_diffs/templates/html/taglib/ui/page_iterator/start.19.ftl  

These two files do not conflict, and the precendence will be:

1. portlet specific template
2. non portlet specific template
3. (no file) default

If your template is trying to override the JSP for a portlet that is in a plugin, make sure to use the fully qualified portletId (which is encoded using the pattern portletid_WAR_plugincontextname).

Now if you place two portlets on the page while this theme is in effect on it, and each of those portlets use the liferay-util:page-iterator tag, and one of those portlets is the Message Boards Portlet (19) then you should see both red and green boxes around those parts of the page.

 

Oh, and remember that you CAN do the same from a Velocity theme:

_diffs/templates/html/taglib/ui/page_iterator/start.vm
_diffs/templates/html/taglib/ui/page_iterator/start.19.vm

The template (using the buffer pattern) would look more like this:

#set ($bufferTagClass = $portal.class.forName("com.liferay.taglib.util.BufferTag"))
#set ($includeTagClass = $portal.class.forName("com.liferay.taglib.util.IncludeTag"))
 
#set ($bufferTag = $bufferTagClass.newInstance())
#set ($V = $bufferTag.setPageContext($pageContext))
#set ($V = $bufferTag.setParent(null))
#set ($V = $bufferTag.setVar('html'))
 
#if ($bufferTag.doStartTag() == 2)
    #set ($V = $bufferTag.setBodyContent($pageContext.pushBody()))
    #set ($V = $bufferTag.doInitBody())
    #set ($includeTag = $includeTagClass.newInstance())
    #set ($V = $includeTag.setPageContext($pageContext))

    #set ($V = $includeTag.setPage('/html/taglib/ui/page_iterator/start.jsp'))

    #set ($V = $includeTag.setStrict(true))
    #set ($V = $includeTag.setUseCustomPage(false))
    #set ($V = $includeTag.runTag())
    #set ($V = $bufferTag.doAfterBody())
    #set ($V = $pageContext.popBody())
    #set ($V = $bufferTag.doEndTag())
#end
 
<div style="border: 4px solid red; padding: 4px;">
    ${pageContext.findAttribute('html')}
</div>

Now, I hope you find more uses for these features that colourful boxes. Good luck!

ブログ
Awesome, although I does raise the question where to put an extension if it needs to both change the style (theme specific) and some logic (hook specific). Any thoughts on that? I am also curious as to whether it is possible to use an instance portlet ID, if for example I wanted to change only the look and feel of the asset publisher on my front page?
Well, it's not hard to work out where a change should go.

- If it should affect the whole portal, put it in a hook
- if it should not affect the whole portal, put it in a theme

You can use instance portletIds. To make sure I tested (and fixed) the scenario of applying first to all instanceable portlets on a page (include no instance in the naming) as well as overriding a single portlet with a more specific change (using the instanceId).

e.g.
_diffs/templates/html/taglib/ui/page_iterator/start.101_INSTANCE_0dmaduACd9V6.ftl
_diffs/templates/html/taglib/ui/page_iterator/start.101.ftl

The change should appear in trunk today once https://github.com/brianchandotcom/liferay-portal/pull/1046 is accepted.
[...] Basically you can't: http://www.liferay.com/community/forums/-/message_boards/message/14269385 BUT, if you take a look here: http://www.liferay.com/web/raymond.auge/blog/-/blogs/jsp-include-buffer... [...] Read More
hello,
I have followed your tutorial and got my red box ^ ^

For cons, I wondered if it was possible, to follow this to overload an entire jsp?
I think about : ROOT\html\taglib\ui\search\start.jsp

thanks
hello,
I have followed your tutorial and got my red box ^ ^

For cons, I wondered if it was possible, to follow this to overload an entire jsp?
I think about : ROOT\html\taglib\ui\search\start.jsp

thanks
Yes it's possible! Simply do not delegate to the original jsp emoticon

i.e. don't use the buffer logic. Just output whatever you need.
agree
but I can't put code "jsp" directly into the template.
For example, if I put the first line <% @ include file = "/ html / taglib / ui / search / init.jsp '%>
it is not analyzed, but just displayed.
I should not use the right tag for that ... ?
I'm trying to apply this approach in Liferay 6.2 ga3 but nothing happens. What can I do to override a jsp from a theme?
Thanks
Did you enable the portal property?

https://github.com/liferay/liferay-portal/blob/6.2.x/portal-impl/src/portal.properties#L656-L660
The link on the previos comment didn't work, but the one on the e-mail did
Thanks,
Sadly the comments don't support real links.. you have to copy paste!

Let's call it a "security feature"!
I know, but if you try to copy and paste that one, doesn't work, at least on my browser emoticon
Hi Ray.
With the property theme.jsp.override.enabled=true I can use this approach. But now the question is that I'm unable to make it works because I only get ${pageContext.findAttribute('html')} printed, like on this forum post
https://www.liferay.com/es/web/raymond.auge/blog/-/blogs/jsp-include-buffer
Thanks in advance