001    /**
002     * Copyright (c) 2000-present 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.portletdisplaytemplate.util;
016    
017    import com.liferay.portal.kernel.bean.ClassLoaderBeanHandler;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.portletdisplaytemplate.BasePortletDisplayTemplateHandler;
021    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
022    import com.liferay.portal.kernel.servlet.JSPSupportServlet;
023    import com.liferay.portal.kernel.template.TemplateConstants;
024    import com.liferay.portal.kernel.template.TemplateHandler;
025    import com.liferay.portal.kernel.template.TemplateHandlerRegistryUtil;
026    import com.liferay.portal.kernel.template.TemplateVariableGroup;
027    import com.liferay.portal.kernel.util.ArrayUtil;
028    import com.liferay.portal.kernel.util.JavaConstants;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.ProxyUtil;
031    import com.liferay.portal.kernel.util.Validator;
032    import com.liferay.portal.kernel.util.WebKeys;
033    import com.liferay.portal.model.Group;
034    import com.liferay.portal.service.GroupLocalServiceUtil;
035    import com.liferay.portal.templateparser.Transformer;
036    import com.liferay.portal.theme.ThemeDisplay;
037    import com.liferay.portal.util.PortletKeys;
038    import com.liferay.portlet.PortletURLUtil;
039    import com.liferay.portlet.dynamicdatamapping.NoSuchTemplateException;
040    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
041    import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
042    import com.liferay.taglib.servlet.PipingServletResponse;
043    import com.liferay.taglib.util.VelocityTaglib;
044    import com.liferay.taglib.util.VelocityTaglibImpl;
045    import com.liferay.util.freemarker.FreeMarkerTaglibFactoryUtil;
046    
047    import freemarker.ext.beans.BeansWrapper;
048    import freemarker.ext.servlet.HttpRequestHashModel;
049    import freemarker.ext.servlet.ServletContextHashModel;
050    
051    import freemarker.template.ObjectWrapper;
052    import freemarker.template.TemplateHashModel;
053    import freemarker.template.TemplateModel;
054    import freemarker.template.TemplateModelException;
055    
056    import java.io.IOException;
057    
058    import java.lang.reflect.InvocationHandler;
059    
060    import java.util.ArrayList;
061    import java.util.HashMap;
062    import java.util.LinkedHashMap;
063    import java.util.List;
064    import java.util.Locale;
065    import java.util.Map;
066    
067    import javax.portlet.PortletPreferences;
068    import javax.portlet.PortletURL;
069    import javax.portlet.RenderRequest;
070    import javax.portlet.RenderResponse;
071    
072    import javax.servlet.GenericServlet;
073    import javax.servlet.ServletContext;
074    import javax.servlet.http.HttpServletRequest;
075    import javax.servlet.http.HttpServletResponse;
076    import javax.servlet.http.HttpSession;
077    
078    /**
079     * @author Eduardo Garcia
080     * @author Juan Fern??ndez
081     * @author Brian Wing Shun Chan
082     */
083    @DoPrivileged
084    public class PortletDisplayTemplateImpl implements PortletDisplayTemplate {
085    
086            @Override
087            public DDMTemplate fetchDDMTemplate(long groupId, String displayStyle) {
088                    try {
089                            Group group = GroupLocalServiceUtil.getGroup(groupId);
090    
091                            Group companyGroup = GroupLocalServiceUtil.getCompanyGroup(
092                                    group.getCompanyId());
093    
094                            String uuid = getDDMTemplateUuid(displayStyle);
095    
096                            if (Validator.isNull(uuid)) {
097                                    return null;
098                            }
099    
100                            try {
101                                    return
102                                            DDMTemplateLocalServiceUtil.getDDMTemplateByUuidAndGroupId(
103                                                    uuid, groupId);
104                            }
105                            catch (NoSuchTemplateException nste) {
106                            }
107    
108                            try {
109                                    return
110                                            DDMTemplateLocalServiceUtil.getDDMTemplateByUuidAndGroupId(
111                                                    uuid, companyGroup.getGroupId());
112                            }
113                            catch (NoSuchTemplateException nste) {
114                            }
115                    }
116                    catch (Exception e) {
117                            if (_log.isWarnEnabled()) {
118                                    _log.warn(e, e);
119                            }
120                    }
121    
122                    return null;
123            }
124    
125            @Override
126            public long getDDMTemplateGroupId(long groupId) {
127                    try {
128                            Group group = GroupLocalServiceUtil.getGroup(groupId);
129    
130                            if (group.isLayout()) {
131                                    group = group.getParentGroup();
132                            }
133    
134                            if (group.isStagingGroup()) {
135                                    Group liveGroup = group.getLiveGroup();
136    
137                                    if (!liveGroup.isStagedPortlet(
138                                                    PortletKeys.PORTLET_DISPLAY_TEMPLATES)) {
139    
140                                            return liveGroup.getGroupId();
141                                    }
142                            }
143    
144                            return group.getGroupId();
145                    }
146                    catch (Exception e) {
147                            if (_log.isWarnEnabled()) {
148                                    _log.warn(e, e);
149                            }
150                    }
151    
152                    return groupId;
153            }
154    
155            @Override
156            public String getDDMTemplateUuid(String displayStyle) {
157                    if (!displayStyle.startsWith(DISPLAY_STYLE_PREFIX)) {
158                            return null;
159                    }
160    
161                    return displayStyle.substring(DISPLAY_STYLE_PREFIX.length());
162            }
163    
164            @Override
165            public long getPortletDisplayTemplateDDMTemplateId(
166                    long groupId, String displayStyle) {
167    
168                    long portletDisplayDDMTemplateId = 0;
169    
170                    long portletDisplayDDMTemplateGroupId = getDDMTemplateGroupId(groupId);
171    
172                    if (displayStyle.startsWith(DISPLAY_STYLE_PREFIX)) {
173                            DDMTemplate portletDisplayDDMTemplate = fetchDDMTemplate(
174                                    portletDisplayDDMTemplateGroupId, displayStyle);
175    
176                            if (portletDisplayDDMTemplate != null) {
177                                    portletDisplayDDMTemplateId =
178                                            portletDisplayDDMTemplate.getTemplateId();
179                            }
180                    }
181    
182                    return portletDisplayDDMTemplateId;
183            }
184    
185            @Override
186            public List<TemplateHandler> getPortletDisplayTemplateHandlers() {
187                    List<TemplateHandler> templateHandlers =
188                            TemplateHandlerRegistryUtil.getTemplateHandlers();
189    
190                    List<TemplateHandler> portletDisplayTemplateHandlers =
191                            new ArrayList<TemplateHandler>();
192    
193                    for (TemplateHandler templateHandler : templateHandlers) {
194                            if (templateHandler instanceof BasePortletDisplayTemplateHandler) {
195                                    portletDisplayTemplateHandlers.add(templateHandler);
196                            }
197                            else if (ProxyUtil.isProxyClass(templateHandler.getClass())) {
198                                    InvocationHandler invocationHandler =
199                                            ProxyUtil.getInvocationHandler(templateHandler);
200    
201                                    if (invocationHandler instanceof ClassLoaderBeanHandler) {
202                                            ClassLoaderBeanHandler classLoaderBeanHandler =
203                                                    (ClassLoaderBeanHandler)invocationHandler;
204    
205                                            Object bean = classLoaderBeanHandler.getBean();
206    
207                                            if (bean instanceof BasePortletDisplayTemplateHandler) {
208                                                    portletDisplayTemplateHandlers.add(templateHandler);
209                                            }
210                                    }
211                            }
212                    }
213    
214                    return portletDisplayTemplateHandlers;
215            }
216    
217            @Override
218            public Map<String, TemplateVariableGroup> getTemplateVariableGroups(
219                    String language) {
220    
221                    Map<String, TemplateVariableGroup> templateVariableGroups =
222                            new LinkedHashMap<String, TemplateVariableGroup>();
223    
224                    TemplateVariableGroup fieldsTemplateVariableGroup =
225                            new TemplateVariableGroup("fields");
226    
227                    fieldsTemplateVariableGroup.addCollectionVariable(
228                            "entries", List.class, PortletDisplayTemplateConstants.ENTRIES,
229                            "entries-item", null, "curEntry", null);
230                    fieldsTemplateVariableGroup.addVariable(
231                            "entry", null, PortletDisplayTemplateConstants.ENTRY);
232    
233                    templateVariableGroups.put("fields", fieldsTemplateVariableGroup);
234    
235                    TemplateVariableGroup generalVariablesTemplateVariableGroup =
236                            new TemplateVariableGroup("general-variables");
237    
238                    generalVariablesTemplateVariableGroup.addVariable(
239                            "current-url", String.class,
240                            PortletDisplayTemplateConstants.CURRENT_URL);
241                    generalVariablesTemplateVariableGroup.addVariable(
242                            "locale", Locale.class, PortletDisplayTemplateConstants.LOCALE);
243                    generalVariablesTemplateVariableGroup.addVariable(
244                            "portlet-preferences", Map.class,
245                            PortletDisplayTemplateConstants.PORTLET_PREFERENCES);
246                    generalVariablesTemplateVariableGroup.addVariable(
247                            "template-id", null, PortletDisplayTemplateConstants.TEMPLATE_ID);
248                    generalVariablesTemplateVariableGroup.addVariable(
249                            "theme-display", ThemeDisplay.class,
250                            PortletDisplayTemplateConstants.THEME_DISPLAY);
251    
252                    templateVariableGroups.put(
253                            "general-variables", generalVariablesTemplateVariableGroup);
254    
255                    TemplateVariableGroup utilTemplateVariableGroup =
256                            new TemplateVariableGroup("util");
257    
258                    utilTemplateVariableGroup.addVariable(
259                            "http-request", HttpServletRequest.class,
260                            PortletDisplayTemplateConstants.REQUEST);
261    
262                    if (language.equals(TemplateConstants.LANG_TYPE_VM)) {
263                            utilTemplateVariableGroup.addVariable(
264                                    "liferay-taglib", VelocityTaglib.class,
265                                    PortletDisplayTemplateConstants.TAGLIB_LIFERAY);
266                    }
267    
268                    utilTemplateVariableGroup.addVariable(
269                            "render-request", RenderRequest.class,
270                            PortletDisplayTemplateConstants.RENDER_REQUEST);
271                    utilTemplateVariableGroup.addVariable(
272                            "render-response", RenderResponse.class,
273                            PortletDisplayTemplateConstants.RENDER_RESPONSE);
274    
275                    templateVariableGroups.put("util", utilTemplateVariableGroup);
276    
277                    return templateVariableGroups;
278            }
279    
280            @Override
281            public String renderDDMTemplate(
282                            HttpServletRequest request, HttpServletResponse response,
283                            long ddmTemplateId, List<?> entries)
284                    throws Exception {
285    
286                    Map<String, Object> contextObjects = new HashMap<String, Object>();
287    
288                    return renderDDMTemplate(
289                            request, response, ddmTemplateId, entries, contextObjects);
290            }
291    
292            @Override
293            public String renderDDMTemplate(
294                            HttpServletRequest request, HttpServletResponse response,
295                            long ddmTemplateId, List<?> entries,
296                            Map<String, Object> contextObjects)
297                    throws Exception {
298    
299                    contextObjects.put(
300                            PortletDisplayTemplateConstants.TEMPLATE_ID, ddmTemplateId);
301                    contextObjects.put(PortletDisplayTemplateConstants.ENTRIES, entries);
302    
303                    if (!entries.isEmpty()) {
304                            contextObjects.put(
305                                    PortletDisplayTemplateConstants.ENTRY, entries.get(0));
306                    }
307    
308                    contextObjects.put(
309                            PortletDisplayTemplateConstants.LOCALE, request.getLocale());
310    
311                    contextObjects.put(PortletDisplayTemplateConstants.REQUEST, request);
312    
313                    RenderRequest renderRequest = (RenderRequest)request.getAttribute(
314                            JavaConstants.JAVAX_PORTLET_REQUEST);
315    
316                    contextObjects.put(
317                            PortletDisplayTemplateConstants.RENDER_REQUEST, renderRequest);
318    
319                    RenderResponse renderResponse = (RenderResponse)request.getAttribute(
320                            JavaConstants.JAVAX_PORTLET_RESPONSE);
321    
322                    contextObjects.put(
323                            PortletDisplayTemplateConstants.RENDER_RESPONSE, renderResponse);
324    
325                    PortletURL currentURL = PortletURLUtil.getCurrent(
326                            renderRequest, renderResponse);
327    
328                    contextObjects.put(
329                            PortletDisplayTemplateConstants.CURRENT_URL, currentURL.toString());
330    
331                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
332                            WebKeys.THEME_DISPLAY);
333    
334                    contextObjects.put(
335                            PortletDisplayTemplateConstants.THEME_DISPLAY, themeDisplay);
336    
337                    // Custom context objects
338    
339                    DDMTemplate ddmTemplate = DDMTemplateLocalServiceUtil.getTemplate(
340                            ddmTemplateId);
341    
342                    contextObjects.put(
343                            TemplateConstants.CLASS_NAME_ID, ddmTemplate.getClassNameId());
344    
345                    String language = ddmTemplate.getLanguage();
346    
347                    TemplateHandler templateHandler =
348                            TemplateHandlerRegistryUtil.getTemplateHandler(
349                                    ddmTemplate.getClassNameId());
350    
351                    if (templateHandler instanceof BasePortletDisplayTemplateHandler) {
352                            BasePortletDisplayTemplateHandler portletDisplayTemplateHandler =
353                                    (BasePortletDisplayTemplateHandler)templateHandler;
354    
355                            Map<String, Object> customContextObjects =
356                                    portletDisplayTemplateHandler.getCustomContextObjects();
357    
358                            for (String variableName : customContextObjects.keySet()) {
359                                    if (contextObjects.containsKey(variableName)) {
360                                            continue;
361                                    }
362    
363                                    Object object = customContextObjects.get(variableName);
364    
365                                    if (object instanceof Class) {
366                                            if (language.equals(TemplateConstants.LANG_TYPE_FTL)) {
367                                                    _addStaticClassSupportFTL(
368                                                            contextObjects, variableName, (Class<?>)object);
369                                            }
370                                            else if (language.equals(TemplateConstants.LANG_TYPE_VM)) {
371                                                    _addStaticClassSupportVM(
372                                                            contextObjects, variableName, (Class<?>)object);
373                                            }
374                                    }
375                                    else {
376                                            contextObjects.put(variableName, object);
377                                    }
378                            }
379                    }
380    
381                    // Taglibs
382    
383                    if (language.equals(TemplateConstants.LANG_TYPE_FTL)) {
384                            _addTaglibSupportFTL(contextObjects, request, response);
385                    }
386                    else if (language.equals(TemplateConstants.LANG_TYPE_VM)) {
387                            _addTaglibSupportVM(contextObjects, request, response);
388                    }
389    
390                    contextObjects.putAll(_getPortletPreferences(renderRequest));
391    
392                    return _transformer.transform(
393                            themeDisplay, contextObjects, ddmTemplate.getScript(), language);
394            }
395    
396            private void _addStaticClassSupportFTL(
397                    Map<String, Object> contextObjects, String variableName,
398                    Class<?> variableClass) {
399    
400                    try {
401                            BeansWrapper beansWrapper = BeansWrapper.getDefaultInstance();
402    
403                            TemplateHashModel templateHashModel =
404                                    beansWrapper.getStaticModels();
405    
406                            TemplateModel templateModel = templateHashModel.get(
407                                    variableClass.getCanonicalName());
408    
409                            contextObjects.put(variableName, templateModel);
410                    }
411                    catch (TemplateModelException e) {
412                            if (_log.isWarnEnabled()) {
413                                    _log.warn("Variable " + variableName + " registration fail", e);
414                            }
415                    }
416            }
417    
418            private void _addStaticClassSupportVM(
419                    Map<String, Object> contextObjects, String variableName,
420                    Class<?> variableClass) {
421    
422                    contextObjects.put(variableName, variableClass);
423            }
424    
425            private void _addTaglibSupportFTL(
426                            Map<String, Object> contextObjects, HttpServletRequest request,
427                            HttpServletResponse response)
428                    throws Exception {
429    
430                    // FreeMarker servlet application
431    
432                    GenericServlet genericServlet = new JSPSupportServlet(
433                            request.getServletContext());
434    
435                    ServletContextHashModel servletContextHashModel =
436                            new ServletContextHashModel(
437                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
438    
439                    contextObjects.put(
440                            PortletDisplayTemplateConstants.FREEMARKER_SERVLET_APPLICATION,
441                            servletContextHashModel);
442    
443                    // FreeMarker servlet request
444    
445                    HttpRequestHashModel requestHashModel = new HttpRequestHashModel(
446                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
447    
448                    contextObjects.put(
449                            PortletDisplayTemplateConstants.FREEMARKER_SERVLET_REQUEST,
450                            requestHashModel);
451    
452                    // Taglib Liferay hash
453    
454                    TemplateHashModel taglibLiferayHash =
455                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
456                                    request.getServletContext());
457    
458                    contextObjects.put(
459                            PortletDisplayTemplateConstants.TAGLIB_LIFERAY_HASH,
460                            taglibLiferayHash);
461            }
462    
463            private void _addTaglibSupportVM(
464                    Map<String, Object> contextObjects, HttpServletRequest request,
465                    HttpServletResponse response) {
466    
467                    contextObjects.put(
468                            PortletDisplayTemplateConstants.TAGLIB_LIFERAY,
469                            _getVelocityTaglib(request, response));
470            }
471    
472            private Map<String, Object> _getPortletPreferences(
473                    RenderRequest renderRequest) {
474    
475                    Map<String, Object> contextObjects = new HashMap<String, Object>();
476    
477                    PortletPreferences portletPreferences = renderRequest.getPreferences();
478    
479                    Map<String, String[]> map = portletPreferences.getMap();
480    
481                    contextObjects.put(
482                            PortletDisplayTemplateConstants.PORTLET_PREFERENCES, map);
483    
484                    for (Map.Entry<String, String[]> entry : map.entrySet()) {
485                            String[] values = entry.getValue();
486    
487                            if (ArrayUtil.isEmpty(values)) {
488                                    continue;
489                            }
490    
491                            String value = values[0];
492    
493                            if (value == null) {
494                                    continue;
495                            }
496    
497                            contextObjects.put(entry.getKey(), value);
498                    }
499    
500                    return contextObjects;
501            }
502    
503            private VelocityTaglib _getVelocityTaglib(
504                    HttpServletRequest request, HttpServletResponse response) {
505    
506                    HttpSession session = request.getSession();
507    
508                    ServletContext servletContext = session.getServletContext();
509    
510                    try {
511                            VelocityTaglib velocityTaglib = new VelocityTaglibImpl(
512                                    servletContext, request,
513                                    new PipingServletResponse(response, response.getWriter()),
514                                    null);
515    
516                            return velocityTaglib;
517                    }
518                    catch (IOException ioe) {
519                            throw new IllegalStateException(ioe);
520                    }
521            }
522    
523            private static final Log _log = LogFactoryUtil.getLog(
524                    PortletDisplayTemplateImpl.class);
525    
526            private final Transformer _transformer = new Transformer(
527                    PropsKeys.DYNAMIC_DATA_LISTS_ERROR_TEMPLATE, true);
528    
529    }