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                    String language = ddmTemplate.getLanguage();
343    
344                    TemplateHandler templateHandler =
345                            TemplateHandlerRegistryUtil.getTemplateHandler(
346                                    ddmTemplate.getClassNameId());
347    
348                    if (templateHandler instanceof BasePortletDisplayTemplateHandler) {
349                            BasePortletDisplayTemplateHandler portletDisplayTemplateHandler =
350                                    (BasePortletDisplayTemplateHandler)templateHandler;
351    
352                            Map<String, Object> customContextObjects =
353                                    portletDisplayTemplateHandler.getCustomContextObjects();
354    
355                            for (String variableName : customContextObjects.keySet()) {
356                                    if (contextObjects.containsKey(variableName)) {
357                                            continue;
358                                    }
359    
360                                    Object object = customContextObjects.get(variableName);
361    
362                                    if (object instanceof Class) {
363                                            if (language.equals(TemplateConstants.LANG_TYPE_FTL)) {
364                                                    _addStaticClassSupportFTL(
365                                                            contextObjects, variableName, (Class<?>)object);
366                                            }
367                                            else if (language.equals(TemplateConstants.LANG_TYPE_VM)) {
368                                                    _addStaticClassSupportVM(
369                                                            contextObjects, variableName, (Class<?>)object);
370                                            }
371                                    }
372                                    else {
373                                            contextObjects.put(variableName, object);
374                                    }
375                            }
376                    }
377    
378                    // Taglibs
379    
380                    if (language.equals(TemplateConstants.LANG_TYPE_FTL)) {
381                            _addTaglibSupportFTL(contextObjects, request, response);
382                    }
383                    else if (language.equals(TemplateConstants.LANG_TYPE_VM)) {
384                            _addTaglibSupportVM(contextObjects, request, response);
385                    }
386    
387                    contextObjects.putAll(_getPortletPreferences(renderRequest));
388    
389                    return _transformer.transform(
390                            themeDisplay, contextObjects, ddmTemplate.getScript(), language);
391            }
392    
393            private void _addStaticClassSupportFTL(
394                    Map<String, Object> contextObjects, String variableName,
395                    Class<?> variableClass) {
396    
397                    try {
398                            BeansWrapper beansWrapper = BeansWrapper.getDefaultInstance();
399    
400                            TemplateHashModel templateHashModel =
401                                    beansWrapper.getStaticModels();
402    
403                            TemplateModel templateModel = templateHashModel.get(
404                                    variableClass.getCanonicalName());
405    
406                            contextObjects.put(variableName, templateModel);
407                    }
408                    catch (TemplateModelException e) {
409                            if (_log.isWarnEnabled()) {
410                                    _log.warn("Variable " + variableName + " registration fail", e);
411                            }
412                    }
413            }
414    
415            private void _addStaticClassSupportVM(
416                    Map<String, Object> contextObjects, String variableName,
417                    Class<?> variableClass) {
418    
419                    contextObjects.put(variableName, variableClass);
420            }
421    
422            private void _addTaglibSupportFTL(
423                            Map<String, Object> contextObjects, HttpServletRequest request,
424                            HttpServletResponse response)
425                    throws Exception {
426    
427                    // FreeMarker servlet application
428    
429                    GenericServlet genericServlet = new JSPSupportServlet(
430                            request.getServletContext());
431    
432                    ServletContextHashModel servletContextHashModel =
433                            new ServletContextHashModel(
434                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
435    
436                    contextObjects.put(
437                            PortletDisplayTemplateConstants.FREEMARKER_SERVLET_APPLICATION,
438                            servletContextHashModel);
439    
440                    // FreeMarker servlet request
441    
442                    HttpRequestHashModel requestHashModel = new HttpRequestHashModel(
443                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
444    
445                    contextObjects.put(
446                            PortletDisplayTemplateConstants.FREEMARKER_SERVLET_REQUEST,
447                            requestHashModel);
448    
449                    // Taglib Liferay hash
450    
451                    TemplateHashModel taglibLiferayHash =
452                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
453                                    request.getServletContext());
454    
455                    contextObjects.put(
456                            PortletDisplayTemplateConstants.TAGLIB_LIFERAY_HASH,
457                            taglibLiferayHash);
458            }
459    
460            private void _addTaglibSupportVM(
461                    Map<String, Object> contextObjects, HttpServletRequest request,
462                    HttpServletResponse response) {
463    
464                    contextObjects.put(
465                            PortletDisplayTemplateConstants.TAGLIB_LIFERAY,
466                            _getVelocityTaglib(request, response));
467            }
468    
469            private Map<String, Object> _getPortletPreferences(
470                    RenderRequest renderRequest) {
471    
472                    Map<String, Object> contextObjects = new HashMap<String, Object>();
473    
474                    PortletPreferences portletPreferences = renderRequest.getPreferences();
475    
476                    Map<String, String[]> map = portletPreferences.getMap();
477    
478                    contextObjects.put(
479                            PortletDisplayTemplateConstants.PORTLET_PREFERENCES, map);
480    
481                    for (Map.Entry<String, String[]> entry : map.entrySet()) {
482                            String[] values = entry.getValue();
483    
484                            if (ArrayUtil.isEmpty(values)) {
485                                    continue;
486                            }
487    
488                            String value = values[0];
489    
490                            if (value == null) {
491                                    continue;
492                            }
493    
494                            contextObjects.put(entry.getKey(), value);
495                    }
496    
497                    return contextObjects;
498            }
499    
500            private VelocityTaglib _getVelocityTaglib(
501                    HttpServletRequest request, HttpServletResponse response) {
502    
503                    HttpSession session = request.getSession();
504    
505                    ServletContext servletContext = session.getServletContext();
506    
507                    try {
508                            VelocityTaglib velocityTaglib = new VelocityTaglibImpl(
509                                    servletContext, request,
510                                    new PipingServletResponse(response, response.getWriter()),
511                                    null);
512    
513                            return velocityTaglib;
514                    }
515                    catch (IOException ioe) {
516                            throw new IllegalStateException(ioe);
517                    }
518            }
519    
520            private static final Log _log = LogFactoryUtil.getLog(
521                    PortletDisplayTemplateImpl.class);
522    
523            private final Transformer _transformer = new Transformer(
524                    PropsKeys.DYNAMIC_DATA_LISTS_ERROR_TEMPLATE, true);
525    
526    }