001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.taglib.util;
016    
017    import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
018    import com.liferay.portal.kernel.freemarker.FreeMarkerEngineUtil;
019    import com.liferay.portal.kernel.freemarker.FreeMarkerVariablesUtil;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.servlet.PipingServletResponse;
024    import com.liferay.portal.kernel.servlet.ServletContextPool;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.util.ThemeHelper;
028    import com.liferay.portal.kernel.util.UnsyncPrintWriterPool;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.kernel.util.WebKeys;
031    import com.liferay.portal.kernel.velocity.VelocityContext;
032    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
033    import com.liferay.portal.kernel.velocity.VelocityVariablesUtil;
034    import com.liferay.portal.model.PortletConstants;
035    import com.liferay.portal.model.Theme;
036    import com.liferay.portal.theme.PortletDisplay;
037    import com.liferay.portal.theme.ThemeDisplay;
038    
039    import freemarker.ext.jsp.TaglibFactory;
040    import freemarker.ext.servlet.HttpRequestHashModel;
041    import freemarker.ext.servlet.ServletContextHashModel;
042    
043    import freemarker.template.ObjectWrapper;
044    
045    import java.io.IOException;
046    import java.io.Writer;
047    
048    import javax.servlet.GenericServlet;
049    import javax.servlet.RequestDispatcher;
050    import javax.servlet.Servlet;
051    import javax.servlet.ServletContext;
052    import javax.servlet.ServletException;
053    import javax.servlet.ServletRequest;
054    import javax.servlet.ServletResponse;
055    import javax.servlet.http.HttpServletRequest;
056    import javax.servlet.http.HttpServletResponse;
057    import javax.servlet.jsp.PageContext;
058    
059    import org.apache.struts.taglib.tiles.ComponentConstants;
060    import org.apache.struts.tiles.ComponentContext;
061    
062    /**
063     * @author Brian Wing Shun Chan
064     * @author Brian Myunghun Kim
065     * @author Raymond Augé
066     * @author Mika Koivisto
067     * @author Shuyang Zhou
068     */
069    public class ThemeUtil {
070    
071            public static String getPortletId(HttpServletRequest request) {
072                    String portletId = null;
073    
074                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
075                            WebKeys.THEME_DISPLAY);
076    
077                    if (themeDisplay != null) {
078                            PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
079    
080                            portletId = portletDisplay.getId();
081                    }
082    
083                    return portletId;
084            }
085    
086            public static void include(
087                            ServletContext servletContext, HttpServletRequest request,
088                            HttpServletResponse response, PageContext pageContext, String page,
089                            Theme theme)
090                    throws Exception {
091    
092                    String extension = theme.getTemplateExtension();
093    
094                    if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_FTL)) {
095                            includeFTL(servletContext, request, pageContext, page, theme, true);
096                    }
097                    else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
098                            includeVM(servletContext, request, pageContext, page, theme, true);
099                    }
100                    else {
101                            String path = theme.getTemplatesPath() + StringPool.SLASH + page;
102    
103                            includeJSP(servletContext, request, response, path, theme);
104                    }
105            }
106    
107            public static String includeFTL(
108                            ServletContext servletContext, HttpServletRequest request,
109                            PageContext pageContext, String path, Theme theme, boolean write)
110                    throws Exception {
111    
112                    // The servlet context name will be null when the theme is deployed to
113                    // the root directory in Tomcat. See
114                    // com.liferay.portal.servlet.MainServlet and
115                    // com.liferay.portlet.PortletContextImpl for other cases where a null
116                    // servlet context name is also converted to an empty string.
117    
118                    String servletContextName = GetterUtil.getString(
119                            theme.getServletContextName());
120    
121                    if (ServletContextPool.get(servletContextName) == null) {
122    
123                            // This should only happen if the FreeMarker template is the first
124                            // page to be accessed in the system
125    
126                            ServletContextPool.put(servletContextName, servletContext);
127                    }
128    
129                    String portletId = getPortletId(request);
130    
131                    String resourcePath = theme.getResourcePath(
132                            servletContext, portletId, path);
133    
134                    if (Validator.isNotNull(portletId) &&
135                            !FreeMarkerEngineUtil.resourceExists(resourcePath) &&
136                            portletId.contains(PortletConstants.INSTANCE_SEPARATOR)) {
137    
138                            String rootPortletId = PortletConstants.getRootPortletId(
139                                    portletId);
140    
141                            resourcePath = theme.getResourcePath(
142                                    servletContext, rootPortletId, path);
143                    }
144    
145                    if (Validator.isNotNull(portletId) &&
146                            !FreeMarkerEngineUtil.resourceExists(resourcePath)) {
147    
148                            resourcePath = theme.getResourcePath(servletContext, null, path);
149                    }
150    
151                    if (!FreeMarkerEngineUtil.resourceExists(resourcePath)) {
152                            _log.error(resourcePath + " does not exist");
153    
154                            return null;
155                    }
156    
157                    FreeMarkerContext freeMarkerContext =
158                            FreeMarkerEngineUtil.getWrappedStandardToolsContext();
159    
160                    // FreeMarker variables
161    
162                    FreeMarkerVariablesUtil.insertVariables(freeMarkerContext, request);
163    
164                    // Theme servlet context
165    
166                    ServletContext themeServletContext = ServletContextPool.get(
167                            servletContextName);
168    
169                    freeMarkerContext.put("themeServletContext", themeServletContext);
170    
171                    // Tag libraries
172    
173                    HttpServletResponse response =
174                            (HttpServletResponse)pageContext.getResponse();
175    
176                    Writer writer = null;
177    
178                    if (write) {
179    
180                            // Wrapping is needed because of a bug in FreeMarker
181    
182                            writer = UnsyncPrintWriterPool.borrow(pageContext.getOut());
183                    }
184                    else {
185                            writer = new UnsyncStringWriter();
186                    }
187    
188                    VelocityTaglib velocityTaglib = new VelocityTaglib(
189                            servletContext, request,
190                            new PipingServletResponse(response, writer), pageContext);
191    
192                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
193    
194                    freeMarkerContext.put("taglibLiferay", velocityTaglib);
195                    freeMarkerContext.put("theme", velocityTaglib);
196    
197                    // Portal JSP tag library factory
198    
199                    TaglibFactory portalTaglib = new TaglibFactory(servletContext);
200    
201                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
202    
203                    // Theme JSP tag library factory
204    
205                    TaglibFactory themeTaglib = new TaglibFactory(themeServletContext);
206    
207                    freeMarkerContext.put("ThemeJspTaglibs", themeTaglib);
208    
209                    // FreeMarker JSP tag library support
210    
211                    final Servlet servlet = (Servlet)pageContext.getPage();
212    
213                    GenericServlet genericServlet = null;
214    
215                    if (servlet instanceof GenericServlet) {
216                            genericServlet = (GenericServlet) servlet;
217                    }
218                    else {
219                            genericServlet = new GenericServlet() {
220    
221                                    @Override
222                                    public void service(
223                                                    ServletRequest servletRequest,
224                                                    ServletResponse servletResponse)
225                                            throws ServletException, IOException {
226    
227                                            servlet.service(servletRequest, servletResponse);
228                                    }
229    
230                            };
231    
232                            genericServlet.init(pageContext.getServletConfig());
233                    }
234    
235                    ServletContextHashModel servletContextHashModel =
236                            new ServletContextHashModel(
237                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
238    
239                    freeMarkerContext.put("Application", servletContextHashModel);
240    
241                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
242                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
243    
244                    freeMarkerContext.put("Request", httpRequestHashModel);
245    
246                    // Merge templates
247    
248                    FreeMarkerEngineUtil.mergeTemplate(
249                            resourcePath, freeMarkerContext, writer);
250    
251                    if (write) {
252                            return null;
253                    }
254                    else {
255                            return writer.toString();
256                    }
257            }
258    
259            public static void includeJSP(
260                            ServletContext servletContext, HttpServletRequest request,
261                            HttpServletResponse response, String path, Theme theme)
262                    throws Exception {
263    
264                    insertTilesVariables(request);
265    
266                    if (theme.isWARFile()) {
267                            ServletContext themeServletContext = servletContext.getContext(
268                                    theme.getContextPath());
269    
270                            if (themeServletContext == null) {
271                                    _log.error(
272                                            "Theme " + theme.getThemeId() + " cannot find its " +
273                                                    "servlet context at " + theme.getServletContextName());
274                            }
275                            else {
276                                    RequestDispatcher requestDispatcher =
277                                            themeServletContext.getRequestDispatcher(path);
278    
279                                    if (requestDispatcher == null) {
280                                            _log.error(
281                                                    "Theme " + theme.getThemeId() + " does not have " +
282                                                            path);
283                                    }
284                                    else {
285                                            requestDispatcher.include(request, response);
286                                    }
287                            }
288                    }
289                    else {
290                            RequestDispatcher requestDispatcher =
291                                    servletContext.getRequestDispatcher(path);
292    
293                            if (requestDispatcher == null) {
294                                    _log.error(
295                                            "Theme " + theme.getThemeId() + " does not have " + path);
296                            }
297                            else {
298                                    requestDispatcher.include(request, response);
299                            }
300                    }
301            }
302    
303            public static String includeVM(
304                            ServletContext servletContext, HttpServletRequest request,
305                            PageContext pageContext, String page, Theme theme, boolean write)
306                    throws Exception {
307    
308                    // The servlet context name will be null when the theme is deployed to
309                    // the root directory in Tomcat. See
310                    // com.liferay.portal.servlet.MainServlet and
311                    // com.liferay.portlet.PortletContextImpl for other cases where a null
312                    // servlet context name is also converted to an empty string.
313    
314                    String servletContextName = GetterUtil.getString(
315                            theme.getServletContextName());
316    
317                    if (ServletContextPool.get(servletContextName) == null) {
318    
319                            // This should only happen if the Velocity template is the first
320                            // page to be accessed in the system
321    
322                            ServletContextPool.put(servletContextName, servletContext);
323                    }
324    
325                    String portletId = getPortletId(request);
326    
327                    String resourcePath = theme.getResourcePath(
328                            servletContext, portletId, page);
329    
330                    boolean checkResourceExists = true;
331    
332                    if (Validator.isNotNull(portletId)) {
333                            if (portletId.contains(PortletConstants.INSTANCE_SEPARATOR) &&
334                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
335                                            resourcePath))) {
336    
337                                    String rootPortletId = PortletConstants.getRootPortletId(
338                                            portletId);
339    
340                                    resourcePath = theme.getResourcePath(
341                                            servletContext, rootPortletId, page);
342                            }
343    
344                            if (checkResourceExists &&
345                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
346                                            resourcePath))) {
347    
348                                    resourcePath = theme.getResourcePath(
349                                            servletContext, null, page);
350                            }
351                    }
352    
353                    if (checkResourceExists &&
354                            !VelocityEngineUtil.resourceExists(resourcePath)) {
355    
356                            _log.error(resourcePath + " does not exist");
357    
358                            return null;
359                    }
360    
361                    VelocityContext velocityContext =
362                            VelocityEngineUtil.getWrappedStandardToolsContext();
363    
364                    // Velocity variables
365    
366                    VelocityVariablesUtil.insertVariables(velocityContext, request);
367    
368                    // Page context
369    
370                    velocityContext.put("pageContext", pageContext);
371    
372                    // Theme servlet context
373    
374                    ServletContext themeServletContext = ServletContextPool.get(
375                            servletContextName);
376    
377                    velocityContext.put("themeServletContext", themeServletContext);
378    
379                    // Tag libraries
380    
381                    HttpServletResponse response =
382                            (HttpServletResponse)pageContext.getResponse();
383    
384                    Writer writer = null;
385    
386                    if (write) {
387                            writer = pageContext.getOut();
388                    }
389                    else {
390                            writer = new UnsyncStringWriter();
391                    }
392    
393                    VelocityTaglib velocityTaglib = new VelocityTaglib(
394                            servletContext, request,
395                            new PipingServletResponse(response, writer), pageContext);
396    
397                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
398    
399                    velocityContext.put("taglibLiferay", velocityTaglib);
400                    velocityContext.put("theme", velocityTaglib);
401                    velocityContext.put("writer", writer);
402    
403                    // Merge templates
404    
405                    VelocityEngineUtil.mergeTemplate(resourcePath, velocityContext, writer);
406    
407                    if (write) {
408                            return null;
409                    }
410                    else {
411                            return ((UnsyncStringWriter)writer).toString();
412                    }
413            }
414    
415            public static void insertTilesVariables(HttpServletRequest request) {
416                    ComponentContext componentContext =
417                            (ComponentContext)request.getAttribute(
418                                    ComponentConstants.COMPONENT_CONTEXT);
419    
420                    if (componentContext == null) {
421                            return;
422                    }
423    
424                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
425                            WebKeys.THEME_DISPLAY);
426    
427                    String tilesTitle = (String)componentContext.getAttribute("title");
428                    String tilesContent = (String)componentContext.getAttribute("content");
429                    boolean tilesSelectable = GetterUtil.getBoolean(
430                            (String)componentContext.getAttribute("selectable"));
431    
432                    themeDisplay.setTilesTitle(tilesTitle);
433                    themeDisplay.setTilesContent(tilesContent);
434                    themeDisplay.setTilesSelectable(tilesSelectable);
435            }
436    
437            private static Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
438    
439    }