001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.util;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.cache.CmsVfsMemoryObjectCache;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsResource;
034import org.opencms.file.types.CmsResourceTypeFunctionConfig;
035import org.opencms.flex.CmsFlexController;
036import org.opencms.jsp.CmsJspTagInclude;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.CmsRuntimeException;
040import org.opencms.main.OpenCms;
041import org.opencms.util.CmsRequestUtil;
042import org.opencms.util.CmsUUID;
043import org.opencms.xml.containerpage.CmsContainerElementBean;
044import org.opencms.xml.containerpage.CmsDynamicFunctionBean;
045import org.opencms.xml.containerpage.CmsDynamicFunctionParser;
046import org.opencms.xml.containerpage.CmsFunctionFormatterBean;
047import org.opencms.xml.containerpage.I_CmsFormatterBean;
048import org.opencms.xml.content.CmsXmlContent;
049import org.opencms.xml.content.CmsXmlContentFactory;
050
051import java.io.IOException;
052import java.util.HashMap;
053import java.util.Locale;
054import java.util.Map;
055import java.util.Map.Entry;
056
057import javax.servlet.http.HttpServletRequest;
058import javax.servlet.http.HttpServletResponse;
059import javax.servlet.jsp.JspException;
060import javax.servlet.jsp.PageContext;
061
062import org.apache.commons.logging.Log;
063
064/**
065 * Class used for rendering dynamic functions (v2).<p>
066 */
067public class CmsFunctionRenderer {
068
069    /** The logger instance for this class. */
070    private static final Log LOG = CmsLog.getLog(CmsFunctionRenderer.class);
071
072    /** The current cms context. */
073    private CmsObject m_cms;
074
075    /** The page context. */
076    private PageContext m_context;
077
078    /** The JSP context bean. */
079    private CmsJspStandardContextBean m_contextBean;
080
081    /** The element to render. */
082    private CmsContainerElementBean m_element;
083
084    /** The request. */
085    private HttpServletRequest m_request;
086
087    /** The response. */
088    private HttpServletResponse m_response;
089
090    /**
091     * Constructor.<p>
092     *
093     * @param context the page context
094     * @param req the request
095     * @param res the response
096     */
097    public CmsFunctionRenderer(PageContext context, HttpServletRequest req, HttpServletResponse res) {
098
099        m_context = context;
100        m_request = req;
101        m_response = res;
102        CmsFlexController controller = CmsFlexController.getController(req);
103        if (controller == null) {
104            handleMissingFlexController();
105            return;
106        }
107        m_cms = controller.getCmsObject();
108        m_contextBean = CmsJspStandardContextBean.getInstance(m_request);
109        m_element = m_contextBean.getElement();
110    }
111
112    /**
113     * Returns the default output for functions without configured JSPs.
114     *
115     * @param request the current request
116     * @return the default HTML output
117     */
118    public static String defaultHtml(HttpServletRequest request) {
119
120        CmsObject cms = CmsFlexController.getController(request).getCmsObject();
121
122        // We only want the big red warning in Offline mode
123        if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
124            return "<div><!--Dynamic function not configured--></div>";
125        } else {
126            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
127            String message = Messages.get().getBundle(locale).key(Messages.GUI_FUNCTION_DEFAULT_HTML_0);
128            return "<div style=\"border: 2px solid red; padding: 10px;\">" + message + "</div>";
129        }
130    }
131
132    /**
133     * Cached method for accessing the default function formatter.<p>
134     *
135     * @param cms the current CMS context
136     * @return the default function formatter resource
137     */
138    public static CmsResource getDefaultFunctionInstance(CmsObject cms) {
139
140        String path = "/system/modules/org.opencms.base/formatters/function-default.xml";
141        return getDefaultResource(cms, path);
142    }
143
144    /**
145     * Cached method for accessing the default function formatter JSP.<p>
146     *
147     * @param cms the current CMS context
148     * @return the default function formatter JSP
149     */
150    public static CmsResource getDefaultFunctionJsp(CmsObject cms) {
151
152        return getDefaultResource(cms, "/system/modules/org.opencms.base/formatters/function-default.jsp");
153    }
154
155    /**
156     * Helper method for cached reading of resources under specific, fixed paths.<p>
157     *
158     * @param cms the current CMS context
159     * @param path the path to read
160     *
161     * @return the resource which has been read
162     */
163    private static CmsResource getDefaultResource(CmsObject cms, String path) {
164
165        CmsResource resource = (CmsResource)CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().getCachedObject(
166            cms,
167            path);
168        if (resource == null) {
169            try {
170                resource = cms.readResource(path);
171                CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().putCachedObject(cms, path, resource);
172            } catch (CmsException e) {
173                LOG.warn(e.getLocalizedMessage(), e);
174            }
175        }
176        return resource;
177    }
178
179    /**
180     * Renders the requested element content with the flex formatter string template.<p>
181     *
182     * @throws IOException in case writing to to page context out fails
183     * @throws JspException in case something goes wrong during the JSP include
184     */
185    public void render() throws IOException, JspException {
186
187        boolean isNewFunctionType = OpenCms.getResourceManager().matchResourceType(
188            CmsResourceTypeFunctionConfig.TYPE_NAME,
189            m_element.getResource().getTypeId());
190        if (isNewFunctionType) {
191            CmsFunctionFormatterBean function = getFormatterBean(m_cms);
192            if (function != null) {
193                CmsUUID jspId = function.getRealJspId();
194                if (jspId != null) {
195                    CmsJspTagInclude.includeTagAction(
196                        m_context,
197                        m_cms.getRequestContext().removeSiteRoot(function.getRealJspRootPath()),
198                        null,
199                        m_cms.getRequestContext().getLocale(),
200                        false,
201                        m_cms.getRequestContext().getCurrentProject().isOnlineProject(),
202                        function.getParameters(),
203                        CmsRequestUtil.getAttributeMap(m_request),
204                        m_request,
205                        m_response);
206                } else {
207                    m_context.getOut().print(defaultHtml(m_request));
208                }
209            } else {
210                m_context.getOut().print(defaultHtml(m_request));
211            }
212        } else {
213            CmsDynamicFunctionBean.Format format = getFunctionFormat();
214            if ((format != null) && m_cms.existsResource(format.getJspStructureId())) {
215                try {
216                    CmsResource jspResource = m_cms.readResource(format.getJspStructureId());
217                    Map<String, String[]> params = new HashMap<>();
218                    for (Entry<String, String> paramEntry : format.getParameters().entrySet()) {
219                        params.put(paramEntry.getKey(), new String[] {paramEntry.getValue()});
220                    }
221                    CmsJspTagInclude.includeTagAction(
222                        m_context,
223
224                        m_cms.getSitePath(jspResource),
225                        null,
226                        m_cms.getRequestContext().getLocale(),
227                        false,
228                        m_cms.getRequestContext().getCurrentProject().isOnlineProject(),
229                        params,
230                        CmsRequestUtil.getAttributeMap(m_request),
231                        m_request,
232                        m_response);
233                } catch (CmsException e) {
234                    LOG.error(e.getLocalizedMessage(), e);
235                }
236            } else {
237                m_context.getOut().print(defaultHtml(m_request));
238            }
239        }
240    }
241
242    /**
243     * Gets the formatter bean for the current element.<p>
244     *
245     * @param cms the current CMS contxt
246     * @return the formatter bean for the current element
247     */
248    private CmsFunctionFormatterBean getFormatterBean(CmsObject cms) {
249
250        CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
251            cms,
252            cms.getRequestContext().getRootUri());
253        // Using getId(), *not* getFormatterId() here , since the former is the id of the function formatter configuration,
254        // and the latter is just the id of the internal JSP used to render it
255        I_CmsFormatterBean formatterConfig = config.findFormatter(m_element.getId());
256        CmsFunctionFormatterBean function = (CmsFunctionFormatterBean)formatterConfig;
257        return function;
258    }
259
260    /**
261     * Returns the function format for the current element.<p>
262     *
263     * @return the function format
264     */
265    private CmsDynamicFunctionBean.Format getFunctionFormat() {
266
267        CmsDynamicFunctionBean functionBean = null;
268        try {
269            CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, m_cms.readFile(m_element.getResource()));
270            CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser();
271            functionBean = parser.parseFunctionBean(m_cms, content);
272        } catch (CmsException e) {
273            LOG.debug(e.getLocalizedMessage(), e);
274            return null;
275        }
276        CmsJspStandardContextBean contextBean = CmsJspStandardContextBean.getInstance(m_request);
277        String type = contextBean.getContainer().getType();
278        String width = contextBean.getContainer().getWidth();
279        int widthNum = -1;
280        try {
281            widthNum = Integer.parseInt(width);
282        } catch (NumberFormatException e) {
283            LOG.debug(e.getLocalizedMessage(), e);
284        }
285        return functionBean.getFormatForContainer(m_cms, type, widthNum);
286    }
287
288    /**
289     * This method is called when the flex controller can not be found during initialization.<p>
290     *
291     * Override this if you are reusing old workplace classes in a context where no flex controller is available.
292     */
293    private void handleMissingFlexController() {
294
295        // controller not found - this request was not initialized properly
296        throw new CmsRuntimeException(
297            org.opencms.jsp.Messages.get().container(
298                org.opencms.jsp.Messages.ERR_MISSING_CMS_CONTROLLER_1,
299                CmsMacroFormatterResolver.class.getName()));
300    }
301
302}