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.cache.CmsVfsMemoryObjectCache;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.types.CmsResourceTypeFunctionConfig;
034import org.opencms.flex.CmsFlexController;
035import org.opencms.jsp.CmsJspTagInclude;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.CmsRuntimeException;
039import org.opencms.main.OpenCms;
040import org.opencms.util.CmsRequestUtil;
041import org.opencms.util.CmsUUID;
042import org.opencms.xml.containerpage.CmsContainerElementBean;
043import org.opencms.xml.containerpage.CmsDynamicFunctionBean;
044import org.opencms.xml.containerpage.CmsDynamicFunctionParser;
045import org.opencms.xml.containerpage.CmsFunctionFormatterBean;
046import org.opencms.xml.containerpage.I_CmsFormatterBean;
047import org.opencms.xml.content.CmsXmlContent;
048import org.opencms.xml.content.CmsXmlContentFactory;
049
050import java.io.IOException;
051import java.util.HashMap;
052import java.util.Locale;
053import java.util.Map;
054import java.util.Map.Entry;
055
056import javax.servlet.http.HttpServletRequest;
057import javax.servlet.http.HttpServletResponse;
058import javax.servlet.jsp.JspException;
059import javax.servlet.jsp.PageContext;
060
061import org.apache.commons.logging.Log;
062
063/**
064 * Class used for rendering dynamic functions (v2).<p>
065 */
066public class CmsFunctionRenderer {
067
068    /** The logger instance for this class. */
069    private static final Log LOG = CmsLog.getLog(CmsFunctionRenderer.class);
070
071    /** The current cms context. */
072    private CmsObject m_cms;
073
074    /** The page context. */
075    private PageContext m_context;
076
077    /** The JSP context bean. */
078    private CmsJspStandardContextBean m_contextBean;
079
080    /** The element to render. */
081    private CmsContainerElementBean m_element;
082
083    /** The request. */
084    private HttpServletRequest m_request;
085
086    /** The response. */
087    private HttpServletResponse m_response;
088
089    /**
090     * Constructor.<p>
091     *
092     * @param context the page context
093     * @param req the request
094     * @param res the response
095     */
096    public CmsFunctionRenderer(PageContext context, HttpServletRequest req, HttpServletResponse res) {
097
098        m_context = context;
099        m_request = req;
100        m_response = res;
101        CmsFlexController controller = CmsFlexController.getController(req);
102        if (controller == null) {
103            handleMissingFlexController();
104            return;
105        }
106        m_cms = controller.getCmsObject();
107        m_contextBean = CmsJspStandardContextBean.getInstance(m_request);
108        m_element = m_contextBean.getElement();
109    }
110
111    /**
112     * Returns the default output for functions without configured JSPs.
113     *
114     * @param request the current request
115     * @return the default HTML output
116     */
117    public static String defaultHtml(HttpServletRequest request) {
118
119        CmsObject cms = CmsFlexController.getController(request).getCmsObject();
120
121        // We only want the big red warning in Offline mode
122        if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
123            return "<div><!--Dynamic function not configured--></div>";
124        } else {
125            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
126            String message = Messages.get().getBundle(locale).key(Messages.GUI_FUNCTION_DEFAULT_HTML_0);
127            return "<div style=\"border: 2px solid red; padding: 10px;\">" + message + "</div>";
128        }
129    }
130
131    /**
132     * Cached method for accessing the default function formatter.<p>
133     *
134     * @param cms the current CMS context
135     * @return the default function formatter resource
136     */
137    public static CmsResource getDefaultFunctionInstance(CmsObject cms) {
138
139        String path = "/system/modules/org.opencms.base/formatters/function-default.xml";
140        return getDefaultResource(cms, path);
141    }
142
143    /**
144     * Cached method for accessing the default function formatter JSP.<p>
145     *
146     * @param cms the current CMS context
147     * @return the default function formatter JSP
148     */
149    public static CmsResource getDefaultFunctionJsp(CmsObject cms) {
150
151        return getDefaultResource(cms, "/system/modules/org.opencms.base/formatters/function-default.jsp");
152    }
153
154    /**
155     * Helper method for cached reading of resources under specific, fixed paths.<p>
156     *
157     * @param cms the current CMS context
158     * @param path the path to read
159     *
160     * @return the resource which has been read
161     */
162    private static CmsResource getDefaultResource(CmsObject cms, String path) {
163
164        CmsResource resource = (CmsResource)CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().getCachedObject(
165            cms,
166            path);
167        if (resource == null) {
168            try {
169                resource = cms.readResource(path);
170                CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().putCachedObject(cms, path, resource);
171            } catch (CmsException e) {
172                LOG.warn(e.getLocalizedMessage(), e);
173            }
174        }
175        return resource;
176    }
177
178    /**
179     * Renders the requested element content with the flex formatter string template.<p>
180     *
181     * @throws IOException in case writing to to page context out fails
182     * @throws JspException in case something goes wrong during the JSP include
183     */
184    public void render() throws IOException, JspException {
185
186        boolean isNewFunctionType = OpenCms.getResourceManager().matchResourceType(
187            CmsResourceTypeFunctionConfig.TYPE_NAME,
188            m_element.getResource().getTypeId());
189        if (isNewFunctionType) {
190            CmsFunctionFormatterBean function = getFormatterBean(m_cms);
191            if (function != null) {
192                CmsUUID jspId = function.getRealJspId();
193                if (jspId != null) {
194                    CmsJspTagInclude.includeTagAction(
195                        m_context,
196                        m_cms.getRequestContext().removeSiteRoot(function.getRealJspRootPath()),
197                        null,
198                        m_cms.getRequestContext().getLocale(),
199                        false,
200                        m_cms.getRequestContext().getCurrentProject().isOnlineProject(),
201                        function.getParameters(),
202                        CmsRequestUtil.getAttributeMap(m_request),
203                        m_request,
204                        m_response);
205                } else {
206                    m_context.getOut().print(defaultHtml(m_request));
207                }
208            } else {
209                m_context.getOut().print(defaultHtml(m_request));
210            }
211        } else {
212            CmsDynamicFunctionBean.Format format = getFunctionFormat();
213            if ((format != null) && m_cms.existsResource(format.getJspStructureId())) {
214                try {
215                    CmsResource jspResource = m_cms.readResource(format.getJspStructureId());
216                    Map<String, String[]> params = new HashMap<>();
217                    for (Entry<String, String> paramEntry : format.getParameters().entrySet()) {
218                        params.put(paramEntry.getKey(), new String[] {paramEntry.getValue()});
219                    }
220                    CmsJspTagInclude.includeTagAction(
221                        m_context,
222
223                        m_cms.getSitePath(jspResource),
224                        null,
225                        m_cms.getRequestContext().getLocale(),
226                        false,
227                        m_cms.getRequestContext().getCurrentProject().isOnlineProject(),
228                        params,
229                        CmsRequestUtil.getAttributeMap(m_request),
230                        m_request,
231                        m_response);
232                } catch (CmsException e) {
233                    LOG.error(e.getLocalizedMessage(), e);
234                }
235            } else {
236                m_context.getOut().print(defaultHtml(m_request));
237            }
238        }
239    }
240
241    /**
242     * Gets the formatter bean for the current element.<p>
243     *
244     * @param cms the current CMS contxt
245     * @return the formatter bean for the current element
246     */
247    private CmsFunctionFormatterBean getFormatterBean(CmsObject cms) {
248
249        Map<CmsUUID, I_CmsFormatterBean> formatters = OpenCms.getADEManager().getCachedFormatters(
250            m_cms.getRequestContext().getCurrentProject().isOnlineProject()).getFormatters();
251
252        // Using getId(), *not* getFormatterId() here , since the former is the id of the function formatter configuration,
253        // and the latter is just the id of the internal JSP used to render it
254        I_CmsFormatterBean formatterConfig = formatters.get(m_element.getId());
255        CmsFunctionFormatterBean function = (CmsFunctionFormatterBean)formatterConfig;
256        return function;
257
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}