Invalid tagdir attribute while web-fragment is used - jsp-tags

I am using web-fragment feature to maintain my JSPs and tags in a jar, and using this approach, my JSP pages are unable to find the tagdir, and cause "The value of the tagdir attribute for this tag library is invalid."
Here is the structure in my jar.
META-INF
-- resources
-- WEB-INF
-- tags
-- mytag.tag
-- mypage.jsp
-- web-fragment.xml
In mypage.jsp, I specify the taglib as following:
And I got these errors:
mypage.jsp:7:4: The taglib directive must specify either the "uri" or "tagdir" attribute.
mypage.jsp:7:33: The value of the tagdir attribute for this tag library is invalid.
It seems like under this approach, it cannot find the tagdir. I tried the same structure in my war with web.xml, and it was able to find the tags. So I wonder if there is any way I can use the similar solution with web-fragment approach.

You don't need WEB-INF in the web-fragment project. Use this structure instead:
web-fragment project:
META-INF/
-- resources/
-- tags/
-- mytag.tag
-- mypage.jsp
-- web-fragment.xml
Everything inside the resources directory will now be available to referencing projects as if they were deployed inside WEB-INF.
web project:
WEB-INF/
-- tags/
-- othertag.tag
-- (mytag.tag)
-- otherpage.jsp
-- (mypage.jsp)
So to reference mytag.tag from the web project:
<%# taglib prefix="my" tagdir="/WEB-INF/tags" %>
...
<my:mytag ... />
As for the errors you are seeing, I don't know what you tried, but here's how you'd do it:
for a .tag you must specify tagdir and set it to the tag's folder:
<%# taglib prefix="my" tagdir="/WEB-INF/tags" %>
for a .tld you must specify uri and set it to the tld's URI:
<%# taglib prefix="mytld" uri="http://example.com/tld/my" %>

Related

Liferay: localize layout template names

I have written a maven module, which contains my custom layout for liferay. The layout works fine but I also want it's name to be translated in a few languages.
Here's my liferay-layout-templates.xml file:
<?xml version="1.0"?>
<!DOCTYPE layout-templates PUBLIC "-//Liferay//DTD Layout Templates 6.2.0//EN" "http://www.liferay.com/dtd/liferay-layout-templates_6_2_0.dtd">
<layout-templates>
<custom>
<layout-template id="2_columns_layout-75_25" name="myLayout">
<template-path>/2_columns_layout-75_25/2_columns_75_25.tpl</template-path>
<wap-template-path>/2_columns_layout-75_25/2_columns_75_25.wap.tpl</wap-template-path>
<thumbnail-path>/2_columns_layout-75_25/2_columns_75_25.png</thumbnail-path>
</layout-template>
</custom>
</layout-templates>
I tried to delete name attribute and add language keys(which were the same to my template id) and values to my hook module, but it didn't work.
At first I was inclined to state that there's no built-in way to localize layout templates. However, I've looked up the JSP in question (in Liferay's html/portlet/layouts_admin/layout/layout_templates_list.jsp) and found the following line (shortened and edited):
<%= HtmlUtil.escape(LanguageUtil.get(locale,
"layout-template-" + layoutTemplateName, layoutTemplateName)) %>
So it looks like the localization keys that you'll need to use for translating your layout-template's name needs to be starting with "layout-template-" followed by the layout template's own name, e.g. given the xml from your question: layout-template-myLayout=My first localized layout template

Liferay 6.1.20 : Minimize and bundle theme Javascript

Is there a way to apply Liferay's built-in javascript minimizing and bundling capability to the javascript I've included in my theme? I have javascript.fast.load=true in portal-ext.properties and Liferay's javascript is getting bundled & minified in everything.jsp as expected. Also, all portlet javascript that is included via a portlet's liferay-portal.xml file is getting minified as expected. However, I've got many javascript files that are included in my theme because they are utilized on every page and I would like them to get minimized and bundled into everything.jsp along with all the Liferay portal javascript. I've tried the approach suggested by this question, but I think this will only work with a hook because the MinifyFilter will look for files to minify & bundle within the context of the portal web app, i.e. <TOMCAT>/webapps/ROOT. Is there a way I can specify a path to files in a different web app (the theme in this case) as the javascript.bundle.dir parameter? In other words, something like javascript.bundle.dir[javascript.jquery.files]=/<theme-path>/js. I've tried many variations and combinations of javascript.bundle.ids, javascript.bundle.dependencies, etc. to no avail. I know I can get around the problem by putting the javascript in a hook or putting it in portlet and embedding it in the theme but I'd really like to just keep the javascript in the theme. Is there a reasonable way to accomplish this?
There doesn't seem to be a good way to include javascript files from the theme with the minified and bundled Liferay javascript. While you can define a javascript bundle in portal-ext.properties that includes your files, you can't order the dependencies the way you need to in order to get everything to work using only configuration. You can configure the "everything" bundle to depend on your custom bundle but that's not very useful. It would be far more useful if you could configure Liferay to use your custom bundle as the new "everything" bundle and tell Liferay that your bundle depends on Liferay's "everything" bundle. However, the actual bundle ids that are included are hard-coded in Liferay's top_js.jspf file. So the only way to get everything to work would be to override Liferay's definition of javascript.everything.files to include both Liferay's files and your custom javascript. This doesn't seem like a very good solution since it tampers with Liferay's list of included javascript which would certainly be a pain when you need to upgrade Liferay. As Olaf suggested, you can minify and bundle the js yourself and just include it in the portal_normal template. That is a very reasonable solution and what I would normally recommend. Unfortunately I was in a situation where my customer was requesting that all the files be bundled in one file and we are not allowed to modify the build process.
Hook Workaround
There is a workaround using a hook that I don't necessarily recommend but it does accomplish the goal of getting all javascript minimized and bundled along with Liferay's javascript. The basic process is to move the javascript from the theme into a hook, configure a new bundle in portal-ext.properties that includes all of your files, then create a jsp hook for top_js.jspf that includes your new bundle instead of the hard-coded javascript.everything.files or javascript.barebones.files bundles. The steps are:
Move your javascript files into a hook project and place them under html/js. This will cause
the files to be copied to the javascript directory under the portal
web app, i.e. <TOMCAT_HOME>/ROOT/html/js. This is where the Liferay
MinifyFilter looks for javascript files to minify & bundle.
Define a javascript bundle in portal-ext.properties that references
all of your javascript files that need to be included in the bundle
created by the MinifyFilter. Your portal-ext.properties file should
look something like this:
minifier.enabled=true
javascript.fast.load=true
javascript.my.js.files =\
jquery.1.11.1,\
my-js-lib.js,\
my-other-js-lib.js
javascript.bundle.ids=\
javascript.barebone.files,\
javascript.everything.files,\
javascript.my.js.files
javascript.bundle.dir[javascript.my.js.files]=/html/js
# our bundle depends on all the files in the "everything" bundle
javascript.bundle.dependencies[javascript.my.js.files]=javascript.everything.files
Create a JSP hook for top_js.jspf. This file is under
<TOMCAT_HOME>/ROOT/html/common/themes. It is the file that includes
either the barebones.jsp or everything.jsp based on whether the user
is authenticated (if the user is authenticated they get
everything.jsp otherwise barebones.jsp is included). Replace the
references to the javascript.everything.files and/or
javascript.barebones.files bundles with a reference to your new
bundle based on your requirements. For example, if you only want to
include your javascript when the user is authenticated you just have
to replace references to javascript.everything.files.
Specifically, you make the following changes:
This line:
<script src="<%= HtmlUtil.escape(PortalUtil.getStaticResourceURL(request, themeDisplay.getCDNDynamicResourcesHost() + themeDisplay.getPathJavaScript() + "/everything.jsp", "minifierBundleId=javascript.everything.files", javaScriptLastModified)) %>" type="text/javascript"></script>
is changed to this:
<script src="<%= HtmlUtil.escape(PortalUtil.getStaticResourceURL(request, themeDisplay.getCDNDynamicResourcesHost() + themeDisplay.getPathJavaScript() + "/everything.jsp", "minifierBundleId=javascript.my.js.files", javaScriptLastModified)) %>" type="text/javascript"></script>
and this line:
javaScriptFiles = JavaScriptBundleUtil.getFileNames(PropsKeys.JAVASCRIPT_EVERYTHING_FILES);
is changed to this:
javaScriptFiles = JavaScriptBundleUtil.getFileNames("javascript.my.js.files");
* Non-Global Hook Caveat *
If you are putting the javascript and top_js.jspf hooks in a project with other hooks and the project is configured to use non-global jsp hooks, i.e. <custom-jsp-global>false</custom-jsp-global> the solution becomes more complicated. This is because setting <custom-jsp-global>true</custom-jsp-global> makes Liferay rename your hook jsp files rather than renaming the portal's jsp files. For example, if custom-jsp-global is set to true, which is the default setting, then when I make a hook for a page called top_js.jspf, the portal will rename the original top_js.jspf file to top_js.portal.jsp and my hook file will be used instead of the original. However, when custom-jsp-global is set to false then the original file stays intact and the jsp hook file is renamed to something that includes the name of the hook like top_js.my-hook.jspf. This is a problem when you're creating a hook for included files such as top_js.jspf because the file that includes top_js.jspf will still reference the old file, not the hook which is named top_js.my-hook.jspf. This means you have to also create a hook for the file that includes your hook. Likewise, if that file is included by another file you have to make hook for that file and so on until you reach the top level page. So, in the example of trying to create a hook for top_js.jspf we have to also do the following:
Create a hook for top_head.jspf and replace the reference to top_js.jspf with a reference to our hook, top_js.my-hook.jspf.
So this line
<%# include file="/html/common/themes/top_js.jspf" %>
becomes this
<%# include file="/html/common/themes/top_js.my-hook.jspf" %>
The top_head.jspf file is actually included by the theme in
portal_normal.vm using a Velocity variable that is initialized in
init.vm on the following line:
You need to assign $top_head_include to the top_head.my-hook.jspf hook in the theme's init_custom.vm, like this:
#set ($top_head_include = "$dir_include/common/themes/top_head.my-hook.jsp")
Your Theme has access to all of the HTML the portal generates. While you might need one extra file to be loaded (css gets minified for the whole theme anyway), you can easily add all of the (already) minified js files to your theme and include them in your templates/portal-normal.ftl implementation.
It would be as easy as having this section in portal-normal.ftl:
<head>
<title>${the_title} - ${company_name}</title>
<meta content="initial-scale=1.0, width=device-width" name="viewport" />
${theme.include(top_head_include)}
<script src="${javascript_folder}/my-minified-javascript.js"/>
</head>
Note: All but the <script> line is already in the default ftl file. This way you'll end up with two js files being loaded (the barebones or everything, plus your own), but that's not too bad. You can also add the minification to your theme's build process, so that you don't have to maintain the minified code manually.
Another alternative, which I haven't tried, is examining the use of Liferay's javascript minifier (e.g. in webapps/ROOT/html/common/themes/tom_js.jsp) to see how to utilize it to dynamically minify your files.
For completeness reason (maybe it helps someone else) I'm leaving my first answer here, which you couldn't use as you say in the first comment:
There's a section in portal.properties, to be overloaded in portal-ext.properties with this heading:
##
## JavaScript
##
#
# Set a list of JavaScript files that will be loaded automatically in
# /html/common/themes/top_js.jsp.
#
# There are two lists of files specified in the properties
# "javascript.barebone.files" and "javascript.everything.files".
#
# As the name suggests, the barebone list is the minimum list of JavaScript
# files required for most cases. The everything list includes everything
# else not listed in the barebone list.
#
# The two lists of files exist for performance reasons because
# unauthenticated users usually do not utilize all the JavaScript that is
# available. See the property "javascript.barebone.enabled" for more
# information on the logic of when the barebone list is used and when the
# everything list is used and how to customize that logic.
#
# The list of files are also merged and packed for further performance
# improvements. See the property "javascript.fast.load" for more details.
#
e.g. configure javascript.everything.files (the default is below that comment, for brevity I'm not copying that here)

Unable to find or serve resource, dataList.xhtml, from library, org.apache.myfaces.custom

I am trying to implement pagination in JSF and Hibernate.
I have these statements on my html page.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"
xmlns:t="http://myfaces.apache.org/tomahawk">
<t:dataList value="#{med.pages}" var="page">
I have included tomahawk20-1.1.14-bin - the jar files in /build/web/WEB-INF/lib and tomahawk-examples-1.1.14-bin - all the war files in /build/web/WEB-INF/src/META-INF
But, I get this error : Unable to find or serve resource, dataList.xhtml, from library, org.apache.myfaces.custom.
What should I do ?
I think it is caused by Mojarra (it gets confused reading the .taglib.xml, even if is valid syntax to use that file for composite and normal components, it was clarified in the new 2.2 spec) Use MyFaces JSF implementation instead to get it fixed.
I believe this is a tomahawk issue.
The JSF 2.2 spec mentions this:
As specified in facelet taglibrary schema, the runtime must also
support the composite-library-name element. The runtime must
interpret the contents of this element as the name of a resource
library as described in Section 2.6.1.4 “Libraries of Localized and
Versioned Resources”. If a facelet tag library descriptor file is
encountered that contains this element, the runtime must examine the
element in that same tag library descriptor and make it
available for use in an XML namespace declaration in facelet pages.
And there's also this in the spec:
If you want to employ a cc with a namespace other than
http://java.sun.com/jsf/composite/libraryName you need to have a
taglib file that declares composite-library-name. Currently you must
not declare any tag elements in such a taglib file. All the tags in
such a library must come from the same resource library.
In the case of tomahawk, composite-library-name does not point to a resource (a directory name under META-INF/resources), hence the errors.
The simple solution here may be to remove the composite-library-name element from the tomahawk.taglib.xml file (if it's not needed for any other purpose of course). I haven't tested it however.

Where to put properties files in a JSF application and how to load it with f:loadBundle tag?

I tried to add properties files to my xhtml page in JSF by using f:loadBundle tag.
I tried do it many times in different ways but nothing works
(prop_files/logowanie, prop_files.logowanie).
I kept property files in WebContent/prop_files folder and xhtml files are in WebContent.
Please help me how to add properties to page.
Maybe I keep this files in wrong folder?
The basename of a f:loadBundle tag should be like a class name as the properties files is loaded by the class loader like this:
<f:loadBundle basename="com.examples.messages" var="msgs"/>
The file extension should be .properties.
So place the file in your classpath for example in your src/java/com/examples/messages.properties

Including Openlayer.js in JSF 2.1 Project

I am trying to include OpenLayers.js in JSF project.
<h:outputScript library="js" name="OpenLayers.js" target="head"/>
I didn't have any resources folder so I created one and added openlayers.js and other folder as this thread tells.
I also tried to put it into lib folder
But I always get:
script type="text/javascript" src="RES_NOT_FOUND
Do I need to do additional configuration to run this js library?
We have the same problem. OpenLayers.js calculate the script location URI and based on this location include the other components like styles etc.
So it we use option 1 the JS is well included but there are broken link to CSS and IMAGES.
As a temporary solution we are using now the second option and it works well.
1: <h:outputScript library="js/openlayers" name="OpenLayers.js" />
2: <script src="resources/js/openlayers/OpenLayers.js"></script>
The cause is that JSF overrides the link so OpenLayers.js.xhtml does not match the regexp anymore:
http://HOST/CONTEXT/javax.faces.resource/OpenLayers.js.xhtml?ln=js/openlayers
It's not a problem to use the option2 as long as you don't something like MapConponent in a jar.

Resources