001    /**
002     * Copyright (c) 2000-2012 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                    freeMarkerContext.put("writer", writer);
197    
198                    // Portal JSP tag library factory
199    
200                    TemplateHashModel portalTaglib =
201                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(servletContext);
202    
203                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
204    
205                    // Theme JSP tag library factory
206    
207                    TemplateHashModel themeTaglib =
208                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
209                                    themeServletContext);
210    
211                    freeMarkerContext.put("ThemeJspTaglibs", themeTaglib);
212    
213                    // FreeMarker JSP tag library support
214    
215                    final Servlet servlet = (Servlet)pageContext.getPage();
216    
217                    GenericServlet genericServlet = null;
218    
219                    if (servlet instanceof GenericServlet) {
220                            genericServlet = (GenericServlet) servlet;
221                    }
222                    else {
223                            genericServlet = new GenericServlet() {
224    
225                                    @Override
226                                    public void service(
227                                                    ServletRequest servletRequest,
228                                                    ServletResponse servletResponse)
229                                            throws ServletException, IOException {
230    
231                                            servlet.service(servletRequest, servletResponse);
232                                    }
233    
234                            };
235    
236                            genericServlet.init(pageContext.getServletConfig());
237                    }
238    
239                    ServletContextHashModel servletContextHashModel =
240                            new ServletContextHashModel(
241                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
242    
243                    freeMarkerContext.put("Application", servletContextHashModel);
244    
245                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
246                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
247    
248                    freeMarkerContext.put("Request", httpRequestHashModel);
249    
250                    // Merge templates
251    
252                    FreeMarkerEngineUtil.mergeTemplate(
253                            resourcePath, freeMarkerContext, writer);
254    
255                    if (write) {
256                            return null;
257                    }
258                    else {
259                            return writer.toString();
260                    }
261            }
262    
263            public static void includeJSP(
264                            ServletContext servletContext, HttpServletRequest request,
265                            HttpServletResponse response, String path, Theme theme)
266                    throws Exception {
267    
268                    insertTilesVariables(request);
269    
270                    if (theme.isWARFile()) {
271                            ServletContext themeServletContext = servletContext.getContext(
272                                    theme.getContextPath());
273    
274                            if (themeServletContext == null) {
275                                    _log.error(
276                                            "Theme " + theme.getThemeId() + " cannot find its " +
277                                                    "servlet context at " + theme.getServletContextName());
278                            }
279                            else {
280                                    RequestDispatcher requestDispatcher =
281                                            themeServletContext.getRequestDispatcher(path);
282    
283                                    if (requestDispatcher == null) {
284                                            _log.error(
285                                                    "Theme " + theme.getThemeId() + " does not have " +
286                                                            path);
287                                    }
288                                    else {
289                                            requestDispatcher.include(request, response);
290                                    }
291                            }
292                    }
293                    else {
294                            RequestDispatcher requestDispatcher =
295                                    servletContext.getRequestDispatcher(path);
296    
297                            if (requestDispatcher == null) {
298                                    _log.error(
299                                            "Theme " + theme.getThemeId() + " does not have " + path);
300                            }
301                            else {
302                                    requestDispatcher.include(request, response);
303                            }
304                    }
305            }
306    
307            public static String includeVM(
308                            ServletContext servletContext, HttpServletRequest request,
309                            PageContext pageContext, String page, Theme theme, boolean write)
310                    throws Exception {
311    
312                    // The servlet context name will be null when the theme is deployed to
313                    // the root directory in Tomcat. See
314                    // com.liferay.portal.servlet.MainServlet and
315                    // com.liferay.portlet.PortletContextImpl for other cases where a null
316                    // servlet context name is also converted to an empty string.
317    
318                    String servletContextName = GetterUtil.getString(
319                            theme.getServletContextName());
320    
321                    if (ServletContextPool.get(servletContextName) == null) {
322    
323                            // This should only happen if the Velocity template is the first
324                            // page to be accessed in the system
325    
326                            ServletContextPool.put(servletContextName, servletContext);
327                    }
328    
329                    String portletId = getPortletId(request);
330    
331                    String resourcePath = theme.getResourcePath(
332                            servletContext, portletId, page);
333    
334                    boolean checkResourceExists = true;
335    
336                    if (Validator.isNotNull(portletId)) {
337                            if (portletId.contains(PortletConstants.INSTANCE_SEPARATOR) &&
338                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
339                                            resourcePath))) {
340    
341                                    String rootPortletId = PortletConstants.getRootPortletId(
342                                            portletId);
343    
344                                    resourcePath = theme.getResourcePath(
345                                            servletContext, rootPortletId, page);
346                            }
347    
348                            if (checkResourceExists &&
349                                    (checkResourceExists = !VelocityEngineUtil.resourceExists(
350                                            resourcePath))) {
351    
352                                    resourcePath = theme.getResourcePath(
353                                            servletContext, null, page);
354                            }
355                    }
356    
357                    if (checkResourceExists &&
358                            !VelocityEngineUtil.resourceExists(resourcePath)) {
359    
360                            _log.error(resourcePath + " does not exist");
361    
362                            return null;
363                    }
364    
365                    VelocityContext velocityContext =
366                            VelocityEngineUtil.getWrappedStandardToolsContext();
367    
368                    // Velocity variables
369    
370                    VelocityVariablesUtil.insertVariables(velocityContext, request);
371    
372                    // Page context
373    
374                    velocityContext.put("pageContext", pageContext);
375    
376                    // Theme servlet context
377    
378                    ServletContext themeServletContext = ServletContextPool.get(
379                            servletContextName);
380    
381                    velocityContext.put("themeServletContext", themeServletContext);
382    
383                    // Tag libraries
384    
385                    HttpServletResponse response =
386                            (HttpServletResponse)pageContext.getResponse();
387    
388                    Writer writer = null;
389    
390                    if (write) {
391                            writer = pageContext.getOut();
392                    }
393                    else {
394                            writer = new UnsyncStringWriter();
395                    }
396    
397                    VelocityTaglib velocityTaglib = new VelocityTaglib(
398                            servletContext, request,
399                            new PipingServletResponse(response, writer), pageContext);
400    
401                    request.setAttribute(WebKeys.VELOCITY_TAGLIB, velocityTaglib);
402    
403                    velocityContext.put("taglibLiferay", velocityTaglib);
404                    velocityContext.put("theme", velocityTaglib);
405                    velocityContext.put("writer", writer);
406    
407                    // Merge templates
408    
409                    VelocityEngineUtil.mergeTemplate(resourcePath, velocityContext, writer);
410    
411                    if (write) {
412                            return null;
413                    }
414                    else {
415                            return ((UnsyncStringWriter)writer).toString();
416                    }
417            }
418    
419            public static void insertTilesVariables(HttpServletRequest request) {
420                    ComponentContext componentContext =
421                            (ComponentContext)request.getAttribute(
422                                    ComponentConstants.COMPONENT_CONTEXT);
423    
424                    if (componentContext == null) {
425                            return;
426                    }
427    
428                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
429                            WebKeys.THEME_DISPLAY);
430    
431                    String tilesTitle = (String)componentContext.getAttribute("title");
432                    String tilesContent = (String)componentContext.getAttribute("content");
433                    boolean tilesSelectable = GetterUtil.getBoolean(
434                            (String)componentContext.getAttribute("selectable"));
435    
436                    themeDisplay.setTilesTitle(tilesTitle);
437                    themeDisplay.setTilesContent(tilesContent);
438                    themeDisplay.setTilesSelectable(tilesSelectable);
439            }
440    
441            private static Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
442    
443    }