001    /**
002     * Copyright (c) 2000-2013 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.io.unsync.UnsyncStringWriter;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.servlet.PipingServletResponse;
021    import com.liferay.portal.kernel.servlet.PluginContextListener;
022    import com.liferay.portal.kernel.servlet.ServletContextPool;
023    import com.liferay.portal.kernel.template.Template;
024    import com.liferay.portal.kernel.template.TemplateConstants;
025    import com.liferay.portal.kernel.template.TemplateManagerUtil;
026    import com.liferay.portal.kernel.template.TemplateResource;
027    import com.liferay.portal.kernel.template.TemplateResourceLoaderUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.ThemeHelper;
031    import com.liferay.portal.kernel.util.UnsyncPrintWriterPool;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.kernel.util.WebKeys;
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 path,
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, path, theme, true);
097                    }
098                    else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
099                            includeVM(servletContext, request, pageContext, path, theme, true);
100                    }
101                    else {
102                            path = theme.getTemplatesPath() + StringPool.SLASH + path;
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                    return doDispatch(
114                            servletContext, request, null, pageContext, path, theme, write,
115                            ThemeHelper.TEMPLATE_EXTENSION_FTL);
116            }
117    
118            public static void includeJSP(
119                            ServletContext servletContext, HttpServletRequest request,
120                            HttpServletResponse response, String path, Theme theme)
121                    throws Exception {
122    
123                    doDispatch(
124                            servletContext, request, response, null, path, theme, true,
125                            ThemeHelper.TEMPLATE_EXTENSION_JSP);
126            }
127    
128            public static String includeVM(
129                            ServletContext servletContext, HttpServletRequest request,
130                            PageContext pageContext, String path, Theme theme, boolean write)
131                    throws Exception {
132    
133                    return doDispatch(
134                            servletContext, request, null, pageContext, path, theme, write,
135                            ThemeHelper.TEMPLATE_EXTENSION_VM);
136            }
137    
138            protected static String doDispatch(
139                            ServletContext servletContext, HttpServletRequest request,
140                            HttpServletResponse response, PageContext pageContext, String path,
141                            Theme theme, boolean write, String extension)
142                    throws Exception {
143    
144                    String pluginServletContextName = GetterUtil.getString(
145                            theme.getServletContextName());
146    
147                    ServletContext pluginServletContext = ServletContextPool.get(
148                            pluginServletContextName);
149    
150                    ClassLoader pluginClassLoader = null;
151    
152                    if (pluginServletContext != null) {
153                            pluginClassLoader = (ClassLoader)pluginServletContext.getAttribute(
154                                    PluginContextListener.PLUGIN_CLASS_LOADER);
155                    }
156    
157                    Thread currentThread = Thread.currentThread();
158    
159                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
160    
161                    if ((pluginClassLoader != null) &&
162                            (pluginClassLoader != contextClassLoader)) {
163    
164                            currentThread.setContextClassLoader(pluginClassLoader);
165                    }
166    
167                    try {
168                            if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_FTL)) {
169                                    return doIncludeFTL(
170                                            servletContext, request, pageContext, path, theme, false,
171                                            write);
172                            }
173                            else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_JSP)) {
174                                    doIncludeJSP(servletContext, request, response, path, theme);
175                            }
176                            else if (extension.equals(ThemeHelper.TEMPLATE_EXTENSION_VM)) {
177                                    return doIncludeVM(
178                                            servletContext, request, pageContext, path, theme, false,
179                                            write);
180                            }
181    
182                            return null;
183                    }
184                    finally {
185                            if ((pluginClassLoader != null) &&
186                                    (pluginClassLoader != contextClassLoader)) {
187    
188                                    currentThread.setContextClassLoader(contextClassLoader);
189                            }
190                    }
191            }
192    
193            protected static String doIncludeFTL(
194                            ServletContext servletContext, HttpServletRequest request,
195                            PageContext pageContext, String path, Theme theme,
196                            boolean restricted, boolean write)
197                    throws Exception {
198    
199                    // The servlet context name will be null when the theme is deployed to
200                    // the root directory in Tomcat. See
201                    // com.liferay.portal.servlet.MainServlet and
202                    // com.liferay.portlet.PortletContextImpl for other cases where a null
203                    // servlet context name is also converted to an empty string.
204    
205                    String servletContextName = GetterUtil.getString(
206                            theme.getServletContextName());
207    
208                    if (ServletContextPool.get(servletContextName) == null) {
209    
210                            // This should only happen if the FreeMarker template is the first
211                            // page to be accessed in the system
212    
213                            ServletContextPool.put(servletContextName, servletContext);
214                    }
215    
216                    String portletId = getPortletId(request);
217    
218                    String resourcePath = theme.getResourcePath(
219                            servletContext, portletId, path);
220    
221                    if (Validator.isNotNull(portletId) &&
222                            PortletConstants.hasInstanceId(portletId) &&
223                            !TemplateResourceLoaderUtil.hasTemplateResource(
224                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
225    
226                            String rootPortletId = PortletConstants.getRootPortletId(portletId);
227    
228                            resourcePath = theme.getResourcePath(
229                                    servletContext, rootPortletId, path);
230                    }
231    
232                    if (Validator.isNotNull(portletId) &&
233                            !TemplateResourceLoaderUtil.hasTemplateResource(
234                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
235    
236                            resourcePath = theme.getResourcePath(servletContext, null, path);
237                    }
238    
239                    if (!TemplateResourceLoaderUtil.hasTemplateResource(
240                                    TemplateConstants.LANG_TYPE_FTL, resourcePath)) {
241    
242                            _log.error(resourcePath + " does not exist");
243    
244                            return null;
245                    }
246    
247                    TemplateResource templateResource =
248                            TemplateResourceLoaderUtil.getTemplateResource(
249                                    TemplateConstants.LANG_TYPE_FTL, resourcePath);
250    
251                    Template template = TemplateManagerUtil.getTemplate(
252                            TemplateConstants.LANG_TYPE_FTL, templateResource, restricted);
253    
254                    // FreeMarker variables
255    
256                    template.prepare(request);
257    
258                    // Theme servlet context
259    
260                    ServletContext themeServletContext = ServletContextPool.get(
261                            servletContextName);
262    
263                    template.put("themeServletContext", themeServletContext);
264    
265                    // Tag libraries
266    
267                    HttpServletResponse response =
268                            (HttpServletResponse)pageContext.getResponse();
269    
270                    Writer writer = null;
271    
272                    if (write) {
273    
274                            // Wrapping is needed because of a bug in FreeMarker
275    
276                            writer = UnsyncPrintWriterPool.borrow(pageContext.getOut());
277                    }
278                    else {
279                            writer = new UnsyncStringWriter();
280                    }
281    
282                    VelocityTaglib velocityTaglib = new VelocityTaglibImpl(
283                            servletContext, request,
284                            new PipingServletResponse(response, writer), pageContext, template);
285    
286                    template.put(TemplateConstants.WRITER, writer);
287                    template.put("taglibLiferay", velocityTaglib);
288                    template.put("theme", velocityTaglib);
289    
290                    // Portal JSP tag library factory
291    
292                    TemplateHashModel portalTaglib =
293                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(servletContext);
294    
295                    template.put("PortalJspTagLibs", portalTaglib);
296    
297                    // Theme JSP tag library factory
298    
299                    TemplateHashModel themeTaglib =
300                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
301                                    themeServletContext);
302    
303                    template.put("ThemeJspTaglibs", themeTaglib);
304    
305                    // FreeMarker JSP tag library support
306    
307                    final Servlet servlet = (Servlet)pageContext.getPage();
308    
309                    GenericServlet genericServlet = null;
310    
311                    if (servlet instanceof GenericServlet) {
312                            genericServlet = (GenericServlet)servlet;
313                    }
314                    else {
315                            genericServlet = new GenericServlet() {
316    
317                                    @Override
318                                    public void service(
319                                                    ServletRequest servletRequest,
320                                                    ServletResponse servletResponse)
321                                            throws IOException, ServletException {
322    
323                                            servlet.service(servletRequest, servletResponse);
324                                    }
325    
326                            };
327    
328                            genericServlet.init(pageContext.getServletConfig());
329                    }
330    
331                    ServletContextHashModel servletContextHashModel =
332                            new ServletContextHashModel(
333                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
334    
335                    template.put("Application", servletContextHashModel);
336    
337                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
338                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
339    
340                    template.put("Request", httpRequestHashModel);
341    
342                    // Merge templates
343    
344                    template.processTemplate(writer);
345    
346                    if (write) {
347                            return null;
348                    }
349                    else {
350                            return writer.toString();
351                    }
352            }
353    
354            protected static void doIncludeJSP(
355                            ServletContext servletContext, HttpServletRequest request,
356                            HttpServletResponse response, String path, Theme theme)
357                    throws Exception {
358    
359                    insertTilesVariables(request);
360    
361                    if (theme.isWARFile()) {
362                            ServletContext themeServletContext = servletContext.getContext(
363                                    theme.getContextPath());
364    
365                            if (themeServletContext == null) {
366                                    _log.error(
367                                            "Theme " + theme.getThemeId() + " cannot find its " +
368                                                    "servlet context at " + theme.getServletContextName());
369                            }
370                            else {
371                                    RequestDispatcher requestDispatcher =
372                                            themeServletContext.getRequestDispatcher(path);
373    
374                                    if (requestDispatcher == null) {
375                                            _log.error(
376                                                    "Theme " + theme.getThemeId() + " does not have " +
377                                                            path);
378                                    }
379                                    else {
380                                            requestDispatcher.include(request, response);
381                                    }
382                            }
383                    }
384                    else {
385                            RequestDispatcher requestDispatcher =
386                                    servletContext.getRequestDispatcher(path);
387    
388                            if (requestDispatcher == null) {
389                                    _log.error(
390                                            "Theme " + theme.getThemeId() + " does not have " + path);
391                            }
392                            else {
393                                    requestDispatcher.include(request, response);
394                            }
395                    }
396            }
397    
398            protected static String doIncludeVM(
399                            ServletContext servletContext, HttpServletRequest request,
400                            PageContext pageContext, String page, Theme theme,
401                            boolean restricted, boolean write)
402                    throws Exception {
403    
404                    // The servlet context name will be null when the theme is deployed to
405                    // the root directory in Tomcat. See
406                    // com.liferay.portal.servlet.MainServlet and
407                    // com.liferay.portlet.PortletContextImpl for other cases where a null
408                    // servlet context name is also converted to an empty string.
409    
410                    String servletContextName = GetterUtil.getString(
411                            theme.getServletContextName());
412    
413                    if (ServletContextPool.get(servletContextName) == null) {
414    
415                            // This should only happen if the Velocity template is the first
416                            // page to be accessed in the system
417    
418                            ServletContextPool.put(servletContextName, servletContext);
419                    }
420    
421                    String portletId = getPortletId(request);
422    
423                    String resourcePath = theme.getResourcePath(
424                            servletContext, portletId, page);
425    
426                    boolean checkResourceExists = true;
427    
428                    if (Validator.isNotNull(portletId)) {
429                            if (PortletConstants.hasInstanceId(portletId) &&
430                                    (checkResourceExists = !
431                                    TemplateResourceLoaderUtil.hasTemplateResource(
432                                            TemplateConstants.LANG_TYPE_VM, resourcePath))) {
433    
434                                    String rootPortletId = PortletConstants.getRootPortletId(
435                                            portletId);
436    
437                                    resourcePath = theme.getResourcePath(
438                                            servletContext, rootPortletId, page);
439                            }
440    
441                            if (checkResourceExists &&
442                                    (checkResourceExists = !
443                                    TemplateResourceLoaderUtil.hasTemplateResource(
444                                            TemplateConstants.LANG_TYPE_VM, resourcePath))) {
445    
446                                    resourcePath = theme.getResourcePath(
447                                            servletContext, null, page);
448                            }
449                    }
450    
451                    if (checkResourceExists &&
452                            !TemplateResourceLoaderUtil.hasTemplateResource(
453                                    TemplateConstants.LANG_TYPE_VM, resourcePath)) {
454    
455                            _log.error(resourcePath + " does not exist");
456    
457                            return null;
458                    }
459    
460                    TemplateResource templateResource =
461                            TemplateResourceLoaderUtil.getTemplateResource(
462                                    TemplateConstants.LANG_TYPE_VM, resourcePath);
463    
464                    if (templateResource == null) {
465                            throw new Exception(
466                                    "Unable to load template resource " + resourcePath);
467                    }
468    
469                    Template template = TemplateManagerUtil.getTemplate(
470                            TemplateConstants.LANG_TYPE_VM, templateResource, restricted);
471    
472                    // Velocity variables
473    
474                    template.prepare(request);
475    
476                    // Page context
477    
478                    template.put("pageContext", pageContext);
479    
480                    // Theme servlet context
481    
482                    ServletContext themeServletContext = ServletContextPool.get(
483                            servletContextName);
484    
485                    template.put("themeServletContext", themeServletContext);
486    
487                    // Tag libraries
488    
489                    HttpServletResponse response =
490                            (HttpServletResponse)pageContext.getResponse();
491    
492                    Writer writer = null;
493    
494                    if (write) {
495                            writer = pageContext.getOut();
496                    }
497                    else {
498                            writer = new UnsyncStringWriter();
499                    }
500    
501                    VelocityTaglib velocityTaglib = new VelocityTaglibImpl(
502                            servletContext, request,
503                            new PipingServletResponse(response, writer), pageContext, template);
504    
505                    template.put(TemplateConstants.WRITER, writer);
506                    template.put("taglibLiferay", velocityTaglib);
507                    template.put("theme", velocityTaglib);
508    
509                    // Merge templates
510    
511                    template.processTemplate(writer);
512    
513                    if (write) {
514                            return null;
515                    }
516                    else {
517                            return ((UnsyncStringWriter)writer).toString();
518                    }
519            }
520    
521            protected static void insertTilesVariables(HttpServletRequest request) {
522                    ComponentContext componentContext =
523                            (ComponentContext)request.getAttribute(
524                                    ComponentConstants.COMPONENT_CONTEXT);
525    
526                    if (componentContext == null) {
527                            return;
528                    }
529    
530                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
531                            WebKeys.THEME_DISPLAY);
532    
533                    String tilesTitle = (String)componentContext.getAttribute("title");
534                    String tilesContent = (String)componentContext.getAttribute("content");
535                    boolean tilesSelectable = GetterUtil.getBoolean(
536                            (String)componentContext.getAttribute("selectable"));
537    
538                    themeDisplay.setTilesTitle(tilesTitle);
539                    themeDisplay.setTilesContent(tilesContent);
540                    themeDisplay.setTilesSelectable(tilesSelectable);
541            }
542    
543            private static Log _log = LogFactoryUtil.getLog(ThemeUtil.class);
544    
545    }