001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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    import com.liferay.util.freemarker.FreeMarkerTaglibFactoryUtil;
039    
040    import freemarker.ext.servlet.HttpRequestHashModel;
041    import freemarker.ext.servlet.ServletContextHashModel;
042    
043    import freemarker.template.ObjectWrapper;
044    import freemarker.template.TemplateHashModel;
045    
046    import java.io.IOException;
047    import java.io.Writer;
048    
049    import javax.servlet.GenericServlet;
050    import javax.servlet.RequestDispatcher;
051    import javax.servlet.Servlet;
052    import javax.servlet.ServletContext;
053    import javax.servlet.ServletException;
054    import javax.servlet.ServletRequest;
055    import javax.servlet.ServletResponse;
056    import javax.servlet.http.HttpServletRequest;
057    import javax.servlet.http.HttpServletResponse;
058    import javax.servlet.jsp.PageContext;
059    
060    import org.apache.struts.taglib.tiles.ComponentConstants;
061    import org.apache.struts.tiles.ComponentContext;
062    
063    /**
064     * @author Brian Wing Shun Chan
065     * @author Brian Myunghun Kim
066     * @author Raymond Augé
067     * @author Mika Koivisto
068     * @author Shuyang Zhou
069     */
070    public class ThemeUtil {
071    
072            public static String getPortletId(HttpServletRequest request) {
073                    String portletId = null;
074    
075                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
076                            WebKeys.THEME_DISPLAY);
077    
078                    if (themeDisplay != null) {
079                            PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
080    
081                            portletId = portletDisplay.getId();
082                    }
083    
084                    return portletId;
085            }
086    
087            public static void include(
088                            ServletContext servletContext, HttpServletRequest request,
089                            HttpServletResponse response, PageContext pageContext, String page,
090                            Theme theme)
091                    throws Exception {
092    
093                    String extension = theme.getTemplateExtension();
094    
095                    if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_FTL)) {
096                            includeFTL(servletContext, request, pageContext, page, theme, true);
097                    }
098                    else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
099                            includeVM(servletContext, request, pageContext, page, theme, true);
100                    }
101                    else {
102                            String path = theme.getTemplatesPath() + StringPool.SLASH + page;
103    
104                            includeJSP(servletContext, request, response, path, theme);
105                    }
106            }
107    
108            public static String includeFTL(
109                            ServletContext servletContext, HttpServletRequest request,
110                            PageContext pageContext, String path, Theme theme, boolean write)
111                    throws Exception {
112    
113                    // The servlet context name will be null when the theme is deployed to
114                    // the root directory in Tomcat. See
115                    // com.liferay.portal.servlet.MainServlet and
116                    // com.liferay.portlet.PortletContextImpl for other cases where a null
117                    // servlet context name is also converted to an empty string.
118    
119                    String servletContextName = GetterUtil.getString(
120                            theme.getServletContextName());
121    
122                    if (ServletContextPool.get(servletContextName) == null) {
123    
124                            // This should only happen if the FreeMarker template is the first
125                            // page to be accessed in the system
126    
127                            ServletContextPool.put(servletContextName, servletContext);
128                    }
129    
130                    String portletId = getPortletId(request);
131    
132                    String resourcePath = theme.getResourcePath(
133                            servletContext, portletId, path);
134    
135                    if (Validator.isNotNull(portletId) &&
136                            !FreeMarkerEngineUtil.resourceExists(resourcePath) &&
137                            portletId.contains(PortletConstants.INSTANCE_SEPARATOR)) {
138    
139                            String rootPortletId = PortletConstants.getRootPortletId(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                    TemplateHashModel portalTaglib =
200                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(servletContext);
201    
202                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
203    
204                    // Theme JSP tag library factory
205    
206                    TemplateHashModel themeTaglib =
207                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
208                                    themeServletContext);
209    
210                    freeMarkerContext.put("ThemeJspTaglibs", themeTaglib);
211    
212                    // FreeMarker JSP tag library support
213    
214                    final Servlet servlet = (Servlet)pageContext.getPage();
215    
216                    GenericServlet genericServlet = null;
217    
218                    if (servlet instanceof GenericServlet) {
219                            genericServlet = (GenericServlet) servlet;
220                    }
221                    else {
222                            genericServlet = new GenericServlet() {
223    
224                                    @Override
225                                    public void service(
226                                                    ServletRequest servletRequest,
227                                                    ServletResponse servletResponse)
228                                            throws ServletException, IOException {
229    
230                                            servlet.service(servletRequest, servletResponse);
231                                    }
232    
233                            };
234    
235                            genericServlet.init(pageContext.getServletConfig());
236                    }
237    
238                    ServletContextHashModel servletContextHashModel =
239                            new ServletContextHashModel(
240                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
241    
242                    freeMarkerContext.put("Application", servletContextHashModel);
243    
244                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
245                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
246    
247                    freeMarkerContext.put("Request", httpRequestHashModel);
248    
249                    // Merge templates
250    
251                    FreeMarkerEngineUtil.mergeTemplate(
252                            resourcePath, freeMarkerContext, writer);
253    
254                    if (write) {
255                            return null;
256                    }
257                    else {
258                            return writer.toString();
259                    }
260            }
261    
262            public static void includeJSP(
263                            ServletContext servletContext, HttpServletRequest request,
264                            HttpServletResponse response, String path, Theme theme)
265                    throws Exception {
266    
267                    insertTilesVariables(request);
268    
269                    if (theme.isWARFile()) {
270                            ServletContext themeServletContext = servletContext.getContext(
271                                    theme.getContextPath());
272    
273                            if (themeServletContext == null) {
274                                    _log.error(
275                                            "Theme " + theme.getThemeId() + " cannot find its " +
276                                                    "servlet context at " + theme.getServletContextName());
277                            }
278                            else {
279                                    RequestDispatcher requestDispatcher =
280                                            themeServletContext.getRequestDispatcher(path);
281    
282                                    if (requestDispatcher == null) {
283                                            _log.error(
284                                                    "Theme " + theme.getThemeId() + " does not have " +
285                                                            path);
286                                    }
287                                    else {
288                                            requestDispatcher.include(request, response);
289                                    }
290                            }
291                    }
292                    else {
293                            RequestDispatcher requestDispatcher =
294                                    servletContext.getRequestDispatcher(path);
295    
296                            if (requestDispatcher == null) {
297                                    _log.error(
298                                            "Theme " + theme.getThemeId() + " does not have " + path);
299                            }
300                            else {
301                                    requestDispatcher.include(request, response);
302                            }
303                    }
304            }
305    
306            public static String includeVM(
307                            ServletContext servletContext, HttpServletRequest request,
308                            PageContext pageContext, String page, Theme theme, boolean write)
309                    throws Exception {
310    
311                    // The servlet context name will be null when the theme is deployed to
312                    // the root directory in Tomcat. See
313                    // com.liferay.portal.servlet.MainServlet and
314                    // com.liferay.portlet.PortletContextImpl for other cases where a null
315                    // servlet context name is also converted to an empty string.
316    
317                    String servletContextName = GetterUtil.getString(
318                            theme.getServletContextName());
319    
320                    if (ServletContextPool.get(servletContextName) == null) {
321    
322                            // This should only happen if the Velocity template is the first
323                            // page to be accessed in the system
324    
325                            ServletContextPool.put(servletContextName, servletContext);
326                    }
327    
328                    String portletId = getPortletId(request);
329    
330                    String resourcePath = theme.getResourcePath(
331                            servletContext, portletId, page);
332    
333                    boolean checkResourceExists = true;
334    
335                    if (Validator.isNotNull(portletId)) {
336                            if (portletId.contains(PortletConstants.INSTANCE_SEPARATOR) &&
337                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
338                                            resourcePath))) {
339    
340                                    String rootPortletId = PortletConstants.getRootPortletId(
341                                            portletId);
342    
343                                    resourcePath = theme.getResourcePath(
344                                            servletContext, rootPortletId, page);
345                            }
346    
347                            if (checkResourceExists &&
348                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
349                                            resourcePath))) {
350    
351                                    resourcePath = theme.getResourcePath(
352                                            servletContext, null, page);
353                            }
354                    }
355    
356                    if (checkResourceExists &&
357                            !VelocityEngineUtil.resourceExists(resourcePath)) {
358    
359                            _log.error(resourcePath + " does not exist");
360    
361                            return null;
362                    }
363    
364                    VelocityContext velocityContext =
365                            VelocityEngineUtil.getWrappedStandardToolsContext();
366    
367                    // Velocity variables
368    
369                    VelocityVariablesUtil.insertVariables(velocityContext, request);
370    
371                    // Page context
372    
373                    velocityContext.put("pageContext", pageContext);
374    
375                    // Theme servlet context
376    
377                    ServletContext themeServletContext = ServletContextPool.get(
378                            servletContextName);
379    
380                    velocityContext.put("themeServletContext", themeServletContext);
381    
382                    // Tag libraries
383    
384                    HttpServletResponse response =
385                            (HttpServletResponse)pageContext.getResponse();
386    
387                    Writer writer = null;
388    
389                    if (write) {
390                            writer = pageContext.getOut();
391                    }
392                    else {
393                            writer = new UnsyncStringWriter();
394                    }
395    
396                    VelocityTaglib velocityTaglib = new VelocityTaglib(
397                            servletContext, request,
398                            new PipingServletResponse(response, writer), pageContext);
399    
400                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
401    
402                    velocityContext.put("taglibLiferay", velocityTaglib);
403                    velocityContext.put("theme", velocityTaglib);
404                    velocityContext.put("writer", writer);
405    
406                    // Merge templates
407    
408                    VelocityEngineUtil.mergeTemplate(resourcePath, velocityContext, writer);
409    
410                    if (write) {
411                            return null;
412                    }
413                    else {
414                            return ((UnsyncStringWriter)writer).toString();
415                    }
416            }
417    
418            public static void insertTilesVariables(HttpServletRequest request) {
419                    ComponentContext componentContext =
420                            (ComponentContext)request.getAttribute(
421                                    ComponentConstants.COMPONENT_CONTEXT);
422    
423                    if (componentContext == null) {
424                            return;
425                    }
426    
427                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
428                            WebKeys.THEME_DISPLAY);
429    
430                    String tilesTitle = (String)componentContext.getAttribute("title");
431                    String tilesContent = (String)componentContext.getAttribute("content");
432                    boolean tilesSelectable = GetterUtil.getBoolean(
433                            (String)componentContext.getAttribute("selectable"));
434    
435                    themeDisplay.setTilesTitle(tilesTitle);
436                    themeDisplay.setTilesContent(tilesContent);
437                    themeDisplay.setTilesSelectable(tilesSelectable);
438            }
439    
440            private static Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
441    
442    }