001    /**
002     * Copyright (c) 2000-2010 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.freemarker.FreeMarkerVariables;
018    import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
019    import com.liferay.portal.kernel.freemarker.FreeMarkerEngineUtil;
020    import com.liferay.portal.kernel.io.unsync.UnsyncPrintWriter;
021    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.servlet.PipingServletResponse;
025    import com.liferay.portal.kernel.servlet.ServletContextPool;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.WebKeys;
030    import com.liferay.portal.kernel.velocity.VelocityContext;
031    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
032    import com.liferay.portal.model.Theme;
033    import com.liferay.portal.theme.ThemeDisplay;
034    import com.liferay.portal.velocity.VelocityVariables;
035    
036    import freemarker.ext.jsp.TaglibFactory;
037    import freemarker.ext.servlet.HttpRequestHashModel;
038    import freemarker.ext.servlet.ServletContextHashModel;
039    
040    import freemarker.template.ObjectWrapper;
041    
042    import java.io.Writer;
043    
044    import javax.servlet.GenericServlet;
045    import javax.servlet.RequestDispatcher;
046    import javax.servlet.ServletContext;
047    import javax.servlet.http.HttpServletRequest;
048    import javax.servlet.http.HttpServletResponse;
049    import javax.servlet.jsp.PageContext;
050    
051    import org.apache.struts.taglib.tiles.ComponentConstants;
052    import org.apache.struts.tiles.ComponentContext;
053    
054    /**
055     * @author Brian Wing Shun Chan
056     * @author Brian Myunghun Kim
057     * @author Raymond Augé
058     * @author Mika Koivisto
059     * @author Shuyang Zhou
060     */
061    public class ThemeUtil {
062    
063            public static void include(
064                            ServletContext servletContext, HttpServletRequest request,
065                            HttpServletResponse response, PageContext pageContext, String page,
066                            Theme theme)
067                    throws Exception {
068    
069                    String extension = theme.getTemplateExtension();
070    
071                    if (extension.equals(_TEMPLATE_EXTENSION_FTL)) {
072                            includeFTL(servletContext, request, pageContext, page, theme, true);
073                    }
074                    else if (extension.equals(_TEMPLATE_EXTENSION_VM)) {
075                            includeVM(servletContext, request, pageContext, page, theme, true);
076                    }
077                    else {
078                            String path =
079                                    theme.getTemplatesPath() + StringPool.SLASH + page;
080    
081                            includeJSP(servletContext, request, response, path, theme);
082                    }
083            }
084    
085            public static String includeFTL(
086                            ServletContext servletContext, HttpServletRequest request,
087                            PageContext pageContext, String page, Theme theme, boolean write)
088                    throws Exception {
089    
090                    // The servlet context name will be null when the theme is deployed to
091                    // the root directory in Tomcat. See
092                    // com.liferay.portal.servlet.MainServlet and
093                    // com.liferay.portlet.PortletContextImpl for other cases where a null
094                    // servlet context name is also converted to an empty string.
095    
096                    String servletContextName = GetterUtil.getString(
097                            theme.getServletContextName());
098    
099                    if (ServletContextPool.get(servletContextName) == null) {
100    
101                            // This should only happen if the FreeMarker template is the first
102                            // page to be accessed in the system
103    
104                            ServletContextPool.put(servletContextName, servletContext);
105                    }
106    
107                    int pos = page.lastIndexOf(StringPool.PERIOD);
108    
109                    StringBundler sb = new StringBundler(7);
110    
111                    sb.append(servletContextName);
112                    sb.append(theme.getFreeMarkerTemplateLoader());
113                    sb.append(theme.getTemplatesPath());
114                    sb.append(StringPool.SLASH);
115                    sb.append(page.substring(0, pos));
116                    sb.append(StringPool.PERIOD);
117                    sb.append(_TEMPLATE_EXTENSION_FTL);
118    
119                    String source = sb.toString();
120    
121                    if (!FreeMarkerEngineUtil.resourceExists(source)) {
122                            _log.error(source + " does not exist");
123    
124                            return null;
125                    }
126    
127                    FreeMarkerContext freeMarkerContext =
128                            FreeMarkerEngineUtil.getWrappedStandardToolsContext();
129    
130                    // FreeMarker variables
131    
132                    FreeMarkerVariables.insertVariables(freeMarkerContext, request);
133    
134                    // Theme servlet context
135    
136                    ServletContext themeServletContext = ServletContextPool.get(
137                            servletContextName);
138    
139                    freeMarkerContext.put("themeServletContext", themeServletContext);
140    
141                    // Tag libraries
142    
143                    HttpServletResponse response =
144                            (HttpServletResponse)pageContext.getResponse();
145    
146                    Writer writer = null;
147    
148                    if (write) {
149    
150                            // Wrapping is needed because of a bug in FreeMarker
151    
152                            writer = new UnsyncPrintWriter(pageContext.getOut());
153                    }
154                    else {
155                            writer = new UnsyncStringWriter();
156                    }
157    
158                    VelocityTaglib velocityTaglib = new VelocityTaglib(
159                            servletContext, request,
160                            new PipingServletResponse(response, writer), pageContext);
161    
162                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
163    
164                    freeMarkerContext.put("taglibLiferay", velocityTaglib);
165                    freeMarkerContext.put("theme", velocityTaglib);
166    
167                    // Portal JSP tag library factory
168    
169                    TaglibFactory portalTaglib = new TaglibFactory(servletContext);
170    
171                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
172    
173                    // Theme JSP tag library factory
174    
175                    TaglibFactory themeTaglib = new TaglibFactory(themeServletContext);
176    
177                    freeMarkerContext.put("ThemeJspTaglibs", themeTaglib);
178    
179                    // FreeMarker JSP tag library support
180    
181                    ServletContextHashModel servletContextHashModel =
182                            new ServletContextHashModel(
183                                    (GenericServlet)pageContext.getPage(),
184                                    ObjectWrapper.DEFAULT_WRAPPER);
185    
186                    freeMarkerContext.put("Application", servletContextHashModel);
187    
188                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
189                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
190    
191                    freeMarkerContext.put("Request", httpRequestHashModel);
192    
193                    // Merge templates
194    
195                    FreeMarkerEngineUtil.mergeTemplate(source, freeMarkerContext, writer);
196    
197                    if (write) {
198                            return null;
199                    }
200                    else {
201                            return ((UnsyncStringWriter)writer).toString();
202                    }
203            }
204    
205            public static void includeJSP(
206                            ServletContext servletContext, HttpServletRequest request,
207                            HttpServletResponse response, String path, Theme theme)
208                    throws Exception {
209    
210                    if (theme.isWARFile()) {
211                            ServletContext themeServletContext = servletContext.getContext(
212                                    theme.getContextPath());
213    
214                            if (themeServletContext == null) {
215                                    _log.error(
216                                            "Theme " + theme.getThemeId() + " cannot find its " +
217                                                    "servlet context at " + theme.getServletContextName());
218                            }
219                            else {
220                                    RequestDispatcher requestDispatcher =
221                                            themeServletContext.getRequestDispatcher(path);
222    
223                                    if (requestDispatcher == null) {
224                                            _log.error(
225                                                    "Theme " + theme.getThemeId() + " does not have " +
226                                                            path);
227                                    }
228                                    else {
229                                            requestDispatcher.include(request, response);
230                                    }
231                            }
232                    }
233                    else {
234                            RequestDispatcher requestDispatcher =
235                                    servletContext.getRequestDispatcher(path);
236    
237                            if (requestDispatcher == null) {
238                                    _log.error(
239                                            "Theme " + theme.getThemeId() + " does not have " + path);
240                            }
241                            else {
242                                    requestDispatcher.include(request, response);
243                            }
244                    }
245            }
246    
247            public static String includeVM(
248                            ServletContext servletContext, HttpServletRequest request,
249                            PageContext pageContext, String page, Theme theme, boolean write)
250                    throws Exception {
251    
252                    // The servlet context name will be null when the theme is deployed to
253                    // the root directory in Tomcat. See
254                    // com.liferay.portal.servlet.MainServlet and
255                    // com.liferay.portlet.PortletContextImpl for other cases where a null
256                    // servlet context name is also converted to an empty string.
257    
258                    String servletContextName = GetterUtil.getString(
259                            theme.getServletContextName());
260    
261                    if (ServletContextPool.get(servletContextName) == null) {
262    
263                            // This should only happen if the Velocity template is the first
264                            // page to be accessed in the system
265    
266                            ServletContextPool.put(servletContextName, servletContext);
267                    }
268    
269                    int pos = page.lastIndexOf(StringPool.PERIOD);
270    
271                    StringBundler sb = new StringBundler(7);
272    
273                    sb.append(servletContextName);
274                    sb.append(theme.getVelocityResourceListener());
275                    sb.append(theme.getTemplatesPath());
276                    sb.append(StringPool.SLASH);
277                    sb.append(page.substring(0, pos));
278                    sb.append(StringPool.PERIOD);
279                    sb.append(_TEMPLATE_EXTENSION_VM);
280    
281                    String source = sb.toString();
282    
283                    if (!VelocityEngineUtil.resourceExists(source)) {
284                            _log.error(source + " does not exist");
285    
286                            return null;
287                    }
288    
289                    VelocityContext velocityContext =
290                            VelocityEngineUtil.getWrappedStandardToolsContext();
291    
292                    // Velocity variables
293    
294                    VelocityVariables.insertVariables(velocityContext, request);
295    
296                    // Theme servlet context
297    
298                    ServletContext themeServletContext = ServletContextPool.get(
299                            servletContextName);
300    
301                    velocityContext.put("themeServletContext", themeServletContext);
302    
303                    // Tag libraries
304    
305                    HttpServletResponse response =
306                            (HttpServletResponse)pageContext.getResponse();
307    
308                    Writer writer = null;
309    
310                    if (write) {
311                            writer = pageContext.getOut();
312                    }
313                    else {
314                            writer = new UnsyncStringWriter();
315                    }
316    
317                    VelocityTaglib velocityTaglib = new VelocityTaglib(
318                            servletContext, request,
319                            new PipingServletResponse(response, writer), pageContext);
320    
321                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
322    
323                    velocityContext.put("taglibLiferay", velocityTaglib);
324                    velocityContext.put("theme", velocityTaglib);
325                    velocityContext.put("writer", writer);
326    
327                    // Merge templates
328    
329                    VelocityEngineUtil.mergeTemplate(source, velocityContext, writer);
330    
331                    if (write) {
332    
333                            return null;
334                    }
335                    else {
336                            return ((UnsyncStringWriter)writer).toString();
337                    }
338            }
339    
340            public static void insertTilesVariables(HttpServletRequest request) {
341                    ComponentContext componentContext =
342                            (ComponentContext)request.getAttribute(
343                                    ComponentConstants.COMPONENT_CONTEXT);
344    
345                    if (componentContext == null) {
346                            return;
347                    }
348    
349                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
350                            WebKeys.THEME_DISPLAY);
351    
352                    String tilesTitle = (String)componentContext.getAttribute("title");
353                    String tilesContent = (String)componentContext.getAttribute("content");
354                    boolean tilesSelectable = GetterUtil.getBoolean(
355                            (String)componentContext.getAttribute("selectable"));
356    
357                    themeDisplay.setTilesTitle(tilesTitle);
358                    themeDisplay.setTilesContent(tilesContent);
359                    themeDisplay.setTilesSelectable(tilesSelectable);
360            }
361    
362            private static final String _TEMPLATE_EXTENSION_FTL = "ftl";
363    
364            private static final String _TEMPLATE_EXTENSION_VM = "vm";
365    
366            private static Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
367    
368    }