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.portlet.layoutconfiguration.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.security.pacl.DoPrivileged;
021    import com.liferay.portal.kernel.servlet.PipingPageContext;
022    import com.liferay.portal.kernel.servlet.PipingServletResponse;
023    import com.liferay.portal.kernel.servlet.PluginContextListener;
024    import com.liferay.portal.kernel.servlet.ServletContextPool;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.JavaConstants;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Validator;
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.LayoutTemplate;
035    import com.liferay.portal.model.LayoutTemplateConstants;
036    import com.liferay.portal.model.Portlet;
037    import com.liferay.portal.model.PortletConstants;
038    import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
039    import com.liferay.portal.service.PortletLocalServiceUtil;
040    import com.liferay.portal.theme.PortletDisplay;
041    import com.liferay.portal.theme.PortletDisplayFactory;
042    import com.liferay.portal.theme.ThemeDisplay;
043    import com.liferay.portal.util.ClassLoaderUtil;
044    import com.liferay.portal.util.PortalUtil;
045    import com.liferay.portal.util.WebKeys;
046    import com.liferay.portlet.layoutconfiguration.util.velocity.CustomizationSettingsProcessor;
047    import com.liferay.portlet.layoutconfiguration.util.velocity.TemplateProcessor;
048    import com.liferay.portlet.layoutconfiguration.util.xml.ActionURLLogic;
049    import com.liferay.portlet.layoutconfiguration.util.xml.PortletLogic;
050    import com.liferay.portlet.layoutconfiguration.util.xml.RenderURLLogic;
051    import com.liferay.portlet.layoutconfiguration.util.xml.RuntimeLogic;
052    import com.liferay.taglib.util.DummyVelocityTaglib;
053    import com.liferay.taglib.util.VelocityTaglibImpl;
054    
055    import java.util.HashMap;
056    import java.util.Map;
057    
058    import javax.portlet.PortletConfig;
059    import javax.portlet.PortletRequest;
060    import javax.portlet.RenderRequest;
061    import javax.portlet.RenderResponse;
062    
063    import javax.servlet.ServletContext;
064    import javax.servlet.http.HttpServletRequest;
065    import javax.servlet.http.HttpServletResponse;
066    import javax.servlet.jsp.JspWriter;
067    import javax.servlet.jsp.PageContext;
068    
069    /**
070     * @author Brian Wing Shun Chan
071     * @author Raymond Aug??
072     * @author Shuyang Zhou
073     */
074    @DoPrivileged
075    public class RuntimePortletImpl implements RuntimePortlet {
076    
077            @Override
078            public StringBundler getProcessedTemplate(
079                            ServletContext servletContext, HttpServletRequest request,
080                            HttpServletResponse response, PageContext pageContext,
081                            JspWriter jspWriter, String portletId, String velocityTemplateId,
082                            String velocityTemplateContent)
083                    throws Exception {
084    
085                    return doDispatch(
086                            servletContext, request, response, pageContext, jspWriter,
087                            portletId, velocityTemplateId, velocityTemplateContent, true);
088            }
089    
090            @Override
091            public String processCustomizationSettings(
092                            ServletContext servletContext, HttpServletRequest request,
093                            HttpServletResponse response, PageContext pageContext,
094                            String velocityTemplateId, String velocityTemplateContent)
095                    throws Exception {
096    
097                    StringBundler sb = doDispatch(
098                            servletContext, request, response, pageContext, null, null,
099                            velocityTemplateId, velocityTemplateContent, false);
100    
101                    return sb.toString();
102            }
103    
104            @Override
105            public String processPortlet(
106                            ServletContext servletContext, HttpServletRequest request,
107                            HttpServletResponse response, Portlet portlet, String queryString,
108                            String columnId, Integer columnPos, Integer columnCount,
109                            String path, boolean writeOutput)
110                    throws Exception {
111    
112                    return processPortlet(
113                            servletContext, request, response, null, null, portlet,
114                            portlet.getPortletId(), queryString, columnId, columnPos,
115                            columnCount, path, writeOutput);
116            }
117    
118            @Override
119            public String processPortlet(
120                            ServletContext servletContext, HttpServletRequest request,
121                            HttpServletResponse response, RenderRequest renderRequest,
122                            RenderResponse renderResponse, Portlet portlet, String portletId,
123                            String queryString, String columnId, Integer columnPos,
124                            Integer columnCount, String path, boolean writeOutput)
125                    throws Exception {
126    
127                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
128                            WebKeys.THEME_DISPLAY);
129    
130                    if (portlet == null) {
131                            portlet = PortletLocalServiceUtil.getPortletById(
132                                    themeDisplay.getCompanyId(), portletId);
133                    }
134    
135                    if ((portlet != null) && portlet.isInstanceable() &&
136                            !portlet.isAddDefaultResource()) {
137    
138                            String instanceId = portlet.getInstanceId();
139    
140                            if (Validator.isNotNull(instanceId) &&
141                                    Validator.isPassword(instanceId) &&
142                                    (instanceId.length() >= 4)) {
143    
144                                    /*portletId += PortletConstants.INSTANCE_SEPARATOR + instanceId;
145    
146                                    portlet = PortletLocalServiceUtil.getPortletById(
147                                            themeDisplay.getCompanyId(), portletId);*/
148                            }
149                            else {
150                                    if (_log.isDebugEnabled()) {
151                                            _log.debug(
152                                                    "Portlet " + portlet.getPortletId() +
153                                                            " is instanceable but does not have a " +
154                                                                    "valid instance id");
155                                    }
156    
157                                    portlet = null;
158                            }
159                    }
160    
161                    if (portlet == null) {
162                            return StringPool.BLANK;
163                    }
164    
165                    // Capture the current portlet's settings to reset them once the child
166                    // portlet is rendered
167    
168                    PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
169    
170                    PortletDisplay portletDisplayClone = PortletDisplayFactory.create();
171    
172                    portletDisplay.copyTo(portletDisplayClone);
173    
174                    PortletConfig portletConfig = (PortletConfig)request.getAttribute(
175                            JavaConstants.JAVAX_PORTLET_CONFIG);
176    
177                    String lifecycle = (String)request.getAttribute(
178                            PortletRequest.LIFECYCLE_PHASE);
179    
180                    try {
181                            return PortalUtil.renderPortlet(
182                                    servletContext, request, response, portlet, queryString,
183                                    columnId, columnPos, columnCount, path, writeOutput);
184                    }
185                    finally {
186                            portletDisplay.copyFrom(portletDisplayClone);
187    
188                            portletDisplayClone.recycle();
189    
190                            _defineObjects(
191                                    request, portletConfig, renderRequest, renderResponse);
192    
193                            if (lifecycle != null) {
194                                    request.setAttribute(PortletRequest.LIFECYCLE_PHASE, lifecycle);
195                            }
196                    }
197            }
198    
199            @Override
200            public String processPortlet(
201                            ServletContext servletContext, HttpServletRequest request,
202                            HttpServletResponse response, RenderRequest renderRequest,
203                            RenderResponse renderResponse, String portletId, String queryString,
204                            boolean writeOutput)
205                    throws Exception {
206    
207                    return processPortlet(
208                            servletContext, request, response, renderRequest, renderResponse,
209                            portletId, queryString, null, null, null, writeOutput);
210            }
211    
212            @Override
213            public String processPortlet(
214                            ServletContext servletContext, HttpServletRequest request,
215                            HttpServletResponse response, RenderRequest renderRequest,
216                            RenderResponse renderResponse, String portletId, String queryString,
217                            String columnId, Integer columnPos, Integer columnCount,
218                            boolean writeOutput)
219                    throws Exception {
220    
221                    return processPortlet(
222                            servletContext, request, response, renderRequest, renderResponse,
223                            null, portletId, queryString, columnId, columnPos, columnCount,
224                            null, writeOutput);
225            }
226    
227            @Override
228            public void processTemplate(
229                            ServletContext servletContext, HttpServletRequest request,
230                            HttpServletResponse response, PageContext pageContext,
231                            JspWriter jspWriter, String velocityTemplateId,
232                            String velocityTemplateContent)
233                    throws Exception {
234    
235                    processTemplate(
236                            servletContext, request, response, pageContext, jspWriter, null,
237                            velocityTemplateId, velocityTemplateContent);
238            }
239    
240            @Override
241            public void processTemplate(
242                            ServletContext servletContext, HttpServletRequest request,
243                            HttpServletResponse response, PageContext pageContext,
244                            JspWriter jspWriter, String portletId, String velocityTemplateId,
245                            String velocityTemplateContent)
246                    throws Exception {
247    
248                    StringBundler sb = doDispatch(
249                            servletContext, request, response, pageContext, jspWriter,
250                            portletId, velocityTemplateId, velocityTemplateContent, true);
251    
252                    sb.writeTo(pageContext.getOut());
253            }
254    
255            @Override
256            public String processXML(
257                            HttpServletRequest request, String content,
258                            RuntimeLogic runtimeLogic)
259                    throws Exception {
260    
261                    if (Validator.isNull(content)) {
262                            return StringPool.BLANK;
263                    }
264    
265                    int index = content.indexOf(runtimeLogic.getOpenTag());
266    
267                    if (index == -1) {
268                            return content;
269                    }
270    
271                    Portlet renderPortlet = (Portlet)request.getAttribute(
272                            WebKeys.RENDER_PORTLET);
273    
274                    Boolean renderPortletResource = (Boolean)request.getAttribute(
275                            WebKeys.RENDER_PORTLET_RESOURCE);
276    
277                    String outerPortletId = (String)request.getAttribute(
278                            WebKeys.OUTER_PORTLET_ID);
279    
280                    if (outerPortletId == null) {
281                            request.setAttribute(
282                                    WebKeys.OUTER_PORTLET_ID, renderPortlet.getPortletId());
283                    }
284    
285                    try {
286                            request.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
287    
288                            StringBundler sb = new StringBundler();
289    
290                            int x = 0;
291                            int y = index;
292    
293                            while (y != -1) {
294                                    sb.append(content.substring(x, y));
295    
296                                    int close1 = content.indexOf(runtimeLogic.getClose1Tag(), y);
297                                    int close2 = content.indexOf(runtimeLogic.getClose2Tag(), y);
298    
299                                    if ((close2 == -1) || ((close1 != -1) && (close1 < close2))) {
300                                            x = close1 + runtimeLogic.getClose1Tag().length();
301                                    }
302                                    else {
303                                            x = close2 + runtimeLogic.getClose2Tag().length();
304                                    }
305    
306                                    String runtimePortletTag = content.substring(y, x);
307    
308                                    if ((renderPortlet != null) &&
309                                            runtimePortletTag.contains(renderPortlet.getPortletId())) {
310    
311                                            return StringPool.BLANK;
312                                    }
313    
314                                    sb.append(runtimeLogic.processXML(runtimePortletTag));
315    
316                                    y = content.indexOf(runtimeLogic.getOpenTag(), x);
317                            }
318    
319                            if (y == -1) {
320                                    sb.append(content.substring(x));
321                            }
322    
323                            return sb.toString();
324                    }
325                    finally {
326                            if (outerPortletId == null) {
327                                    request.removeAttribute(WebKeys.OUTER_PORTLET_ID);
328                            }
329    
330                            request.setAttribute(WebKeys.RENDER_PORTLET, renderPortlet);
331    
332                            if (renderPortletResource == null) {
333                                    request.removeAttribute(WebKeys.RENDER_PORTLET_RESOURCE);
334                            }
335                            else {
336                                    request.setAttribute(
337                                            WebKeys.RENDER_PORTLET_RESOURCE, renderPortletResource);
338                            }
339                    }
340            }
341    
342            @Override
343            public String processXML(
344                            ServletContext servletContext, HttpServletRequest request,
345                            HttpServletResponse response, RenderRequest renderRequest,
346                            RenderResponse renderResponse, String content)
347                    throws Exception {
348    
349                    RuntimeLogic portletLogic = new PortletLogic
350                            (servletContext, request, response, renderRequest, renderResponse);
351                    RuntimeLogic actionURLLogic = new ActionURLLogic(renderResponse);
352                    RuntimeLogic renderURLLogic = new RenderURLLogic(renderResponse);
353    
354                    content = processXML(request, content, portletLogic);
355                    content = processXML(request, content, actionURLLogic);
356                    content = processXML(request, content, renderURLLogic);
357    
358                    return content;
359            }
360    
361            protected StringBundler doDispatch(
362                            ServletContext servletContext, HttpServletRequest request,
363                            HttpServletResponse response, PageContext pageContext,
364                            JspWriter jspWriter, String portletId, String velocityTemplateId,
365                            String velocityTemplateContent, boolean processTemplate)
366                    throws Exception {
367    
368                    if (Validator.isNull(velocityTemplateContent)) {
369                            return null;
370                    }
371    
372                    ClassLoader pluginClassLoader = null;
373    
374                    LayoutTemplate layoutTemplate = getLayoutTemplate(velocityTemplateId);
375    
376                    if (layoutTemplate != null) {
377                            String pluginServletContextName = GetterUtil.getString(
378                                    layoutTemplate.getServletContextName());
379    
380                            ServletContext pluginServletContext = ServletContextPool.get(
381                                    pluginServletContextName);
382    
383                            if (pluginServletContext != null) {
384                                    pluginClassLoader =
385                                            (ClassLoader)pluginServletContext.getAttribute(
386                                                    PluginContextListener.PLUGIN_CLASS_LOADER);
387                            }
388                    }
389    
390                    ClassLoader contextClassLoader =
391                            ClassLoaderUtil.getContextClassLoader();
392    
393                    try {
394                            if ((pluginClassLoader != null) &&
395                                    (pluginClassLoader != contextClassLoader)) {
396    
397                                    ClassLoaderUtil.setContextClassLoader(pluginClassLoader);
398                            }
399    
400                            if (processTemplate) {
401                                    return doProcessTemplate(
402                                            servletContext, request, response, pageContext, jspWriter,
403                                            portletId, velocityTemplateId, velocityTemplateContent);
404                            }
405                            else {
406                                    return new StringBundler(
407                                            doProcessCustomizationSettings(
408                                                    servletContext, request, response, pageContext,
409                                                    velocityTemplateId, velocityTemplateContent));
410                            }
411                    }
412                    finally {
413                            if ((pluginClassLoader != null) &&
414                                    (pluginClassLoader != contextClassLoader)) {
415    
416                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
417                            }
418                    }
419            }
420    
421            protected String doProcessCustomizationSettings(
422                            ServletContext servletContext, HttpServletRequest request,
423                            HttpServletResponse response, PageContext pageContext,
424                            String velocityTemplateId, String velocityTemplateContent)
425                    throws Exception {
426    
427                    if (Validator.isNull(velocityTemplateContent)) {
428                            return StringPool.BLANK;
429                    }
430    
431                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
432    
433                    CustomizationSettingsProcessor processor =
434                            new CustomizationSettingsProcessor(
435                                    request, new PipingPageContext(pageContext, unsyncStringWriter),
436                                    unsyncStringWriter);
437    
438                    VelocityContext velocityContext =
439                            VelocityEngineUtil.getWrappedClassLoaderToolsContext();
440    
441                    velocityContext.put("processor", processor);
442    
443                    // Velocity variables
444    
445                    VelocityVariablesUtil.insertVariables(velocityContext, request);
446    
447                    // liferay:include tag library
448    
449                    Object velocityTaglib = new DummyVelocityTaglib();
450    
451                    velocityContext.put("taglibLiferay", velocityTaglib);
452                    velocityContext.put("theme", velocityTaglib);
453    
454                    try {
455                            VelocityEngineUtil.mergeTemplate(
456                                    velocityTemplateId, velocityTemplateContent, velocityContext,
457                                    unsyncStringWriter);
458                    }
459                    catch (Exception e) {
460                            _log.error(e, e);
461    
462                            throw e;
463                    }
464    
465                    return unsyncStringWriter.toString();
466            }
467    
468            protected StringBundler doProcessTemplate(
469                            ServletContext servletContext, HttpServletRequest request,
470                            HttpServletResponse response, PageContext pageContext,
471                            JspWriter jspWriter, String portletId, String velocityTemplateId,
472                            String velocityTemplateContent)
473                    throws Exception {
474    
475                    if (Validator.isNull(velocityTemplateContent)) {
476                            return null;
477                    }
478    
479                    TemplateProcessor processor = new TemplateProcessor(
480                            servletContext, request, response, portletId);
481    
482                    VelocityContext velocityContext =
483                            VelocityEngineUtil.getWrappedClassLoaderToolsContext();
484    
485                    velocityContext.put("processor", processor);
486    
487                    // Velocity variables
488    
489                    VelocityVariablesUtil.insertVariables(velocityContext, request);
490    
491                    // liferay:include tag library
492    
493                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
494    
495                    Object velocityTaglib = new VelocityTaglibImpl(
496                            pageContext.getServletContext(), request,
497                            new PipingServletResponse(response, unsyncStringWriter),
498                            pageContext);
499    
500                    velocityContext.put("taglibLiferay", velocityTaglib);
501                    velocityContext.put("theme", velocityTaglib);
502    
503                    try {
504                            VelocityEngineUtil.mergeTemplate(
505                                    velocityTemplateId, velocityTemplateContent, velocityContext,
506                                    unsyncStringWriter);
507                    }
508                    catch (Exception e) {
509                            _log.error(e, e);
510    
511                            throw e;
512                    }
513    
514                    String output = unsyncStringWriter.toString();
515    
516                    Map<Portlet, Object[]> portletsMap = processor.getPortletsMap();
517    
518                    Map<String, StringBundler> contentsMap =
519                            new HashMap<String, StringBundler>(portletsMap.size());
520    
521                    for (Map.Entry<Portlet, Object[]> entry : portletsMap.entrySet()) {
522                            Portlet portlet = entry.getKey();
523                            Object[] value = entry.getValue();
524    
525                            String queryString = (String)value[0];
526                            String columnId = (String)value[1];
527                            Integer columnPos = (Integer)value[2];
528                            Integer columnCount = (Integer)value[3];
529    
530                            UnsyncStringWriter portletUnsyncStringWriter =
531                                    new UnsyncStringWriter();
532    
533                            PipingServletResponse pipingServletResponse =
534                                    new PipingServletResponse(response, portletUnsyncStringWriter);
535    
536                            processPortlet(
537                                    servletContext, request, pipingServletResponse, portlet,
538                                    queryString, columnId, columnPos, columnCount, null, true);
539    
540                            contentsMap.put(
541                                    portlet.getPortletId(),
542                                    portletUnsyncStringWriter.getStringBundler());
543                    }
544    
545                    StringBundler sb = StringUtil.replaceWithStringBundler(
546                            output, "[$TEMPLATE_PORTLET_", "$]", contentsMap);
547    
548                    return sb;
549            }
550    
551            protected LayoutTemplate getLayoutTemplate(String velocityTemplateId) {
552                    String separator = LayoutTemplateConstants.CUSTOM_SEPARATOR;
553                    boolean standard = false;
554    
555                    if (velocityTemplateId.contains(
556                                    LayoutTemplateConstants.STANDARD_SEPARATOR)) {
557    
558                            separator = LayoutTemplateConstants.STANDARD_SEPARATOR;
559                            standard = true;
560                    }
561    
562                    String layoutTemplateId = null;
563    
564                    String themeId = null;
565    
566                    int pos = velocityTemplateId.indexOf(separator);
567    
568                    if (pos != -1) {
569                            layoutTemplateId = velocityTemplateId.substring(
570                                    pos + separator.length());
571    
572                            themeId = velocityTemplateId.substring(0, pos);
573                    }
574    
575                    pos = layoutTemplateId.indexOf(PortletConstants.INSTANCE_SEPARATOR);
576    
577                    if (pos != -1) {
578                            layoutTemplateId = layoutTemplateId.substring(
579                                    pos + PortletConstants.INSTANCE_SEPARATOR.length() + 1);
580    
581                            pos = layoutTemplateId.indexOf(StringPool.UNDERLINE);
582    
583                            layoutTemplateId = layoutTemplateId.substring(pos + 1);
584                    }
585    
586                    return LayoutTemplateLocalServiceUtil.getLayoutTemplate(
587                            layoutTemplateId, standard, themeId);
588            }
589    
590            private static void _defineObjects(
591                    HttpServletRequest request, PortletConfig portletConfig,
592                    RenderRequest renderRequest, RenderResponse renderResponse) {
593    
594                    if (portletConfig != null) {
595                            request.setAttribute(
596                                    JavaConstants.JAVAX_PORTLET_CONFIG, portletConfig);
597                    }
598    
599                    if (renderRequest != null) {
600                            request.setAttribute(
601                                    JavaConstants.JAVAX_PORTLET_REQUEST, renderRequest);
602                    }
603    
604                    if (renderResponse != null) {
605                            request.setAttribute(
606                                    JavaConstants.JAVAX_PORTLET_RESPONSE, renderResponse);
607                    }
608            }
609    
610            private static Log _log = LogFactoryUtil.getLog(RuntimePortletImpl.class);
611    
612    }