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.ade.containerpage.shared.CmsFormatterConfig;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsResource;
034import org.opencms.file.CmsResourceFilter;
035import org.opencms.flex.CmsFlexController;
036import org.opencms.jsp.CmsJspTagContainer;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.util.CmsUUID;
041import org.opencms.xml.containerpage.CmsContainerBean;
042import org.opencms.xml.containerpage.CmsContainerElementBean;
043import org.opencms.xml.containerpage.CmsContainerPageBean;
044import org.opencms.xml.containerpage.CmsFormatterConfiguration;
045import org.opencms.xml.containerpage.I_CmsFormatterBean;
046import org.opencms.xml.content.CmsXmlContentProperty;
047
048import java.util.HashMap;
049import java.util.HashSet;
050import java.util.List;
051import java.util.Locale;
052import java.util.Map;
053import java.util.Objects;
054import java.util.Set;
055
056import org.apache.commons.logging.Log;
057
058/**
059 * Wrapper for using container pages in JSPs.
060 */
061public class CmsJspContainerPageWrapper {
062
063    /** Logger instance for this class. */
064    private static final Log LOG = CmsLog.getLog(CmsJspContainerPageWrapper.class);
065
066    /** The wrapped container page bean. */
067    private CmsContainerPageBean m_page;
068
069    /** The CMS context. */
070    private CmsObject m_cms;
071
072    /**
073     * Creates a new instance.
074     *
075     * @param page the container page to wrap
076     */
077    public CmsJspContainerPageWrapper(CmsObject cms, CmsContainerPageBean page) {
078
079        m_cms = cms;
080        m_page = page;
081
082    }
083
084    /**
085     * Checks if the page contains an element with a given id.
086     *
087     * @param elementId the element id to check
088     * @return true if the page contains the element
089     */
090    public boolean containsElement(CmsUUID elementId) {
091
092        return m_page.containsElement(elementId);
093    }
094
095    /**
096     * Gets the containers.
097     *
098     * @return the containers
099     */
100    public Map<String, CmsContainerBean> getContainers() {
101
102        return m_page.getContainers();
103    }
104
105    /**
106     * Returns a map which tracks which element ids are part of the page.
107     *
108     * @return a map from element ids to their page membership
109     */
110    public Map<CmsUUID, Boolean> getContainsElement() {
111
112        return m_page.getContainsElement();
113    }
114
115    /**
116     * Gets the element ids.
117     *
118     * @return the container element ids
119     */
120    public List<CmsUUID> getElementIds() {
121
122        return m_page.getElementIds();
123    }
124
125    /**
126     * Gets the container elements
127     * @return the container elements
128     */
129    public List<CmsContainerElementBean> getElements() {
130
131        return m_page.getElements();
132    }
133
134    /**
135     * Gets the container names.
136     *
137     * @return the container names
138     */
139    public List<String> getNames() {
140
141        return m_page.getNames();
142    }
143
144    /**
145     * Gets the element settings for the element with the specific instance id.
146     *
147     * <p>The returned map contains the setting names as keys and the corresponding setting values as wrapper objects.
148     *
149     * @param elementInstanceId the element instance id
150     * @return the map of setting wrappers
151     */
152    public Map<?, ?> getSettingsForElement(String elementInstanceId) {
153
154        for (CmsContainerBean container : m_page.getContainers().values()) {
155            for (CmsContainerElementBean element : container.getElements()) {
156                if (Objects.equals(element.getInstanceId(), elementInstanceId)) {
157                    String containerName = container.getName();
158                    Map<String, String> settings = element.getSettings();
159                    I_CmsFormatterBean formatter = null;
160                    CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache(
161                        m_cms,
162                        m_cms.getRequestContext().addSiteRoot(m_cms.getRequestContext().getUri()));
163
164                    if (settings != null) {
165                        String formatterConfigId = settings.get(
166                            CmsFormatterConfig.getSettingsKeyForContainer(containerName));
167                        I_CmsFormatterBean dynamicFmt = config.findFormatter(formatterConfigId);
168                        if (dynamicFmt != null) {
169                            formatter = dynamicFmt;
170                        }
171                    }
172                    if (formatter == null) {
173                        try {
174                            CmsResource resource = m_cms.readResource(
175                                element.getId(),
176                                CmsResourceFilter.ignoreExpirationOffline(m_cms));
177                            CmsFormatterConfiguration formatters = config.getFormatters(m_cms, resource);
178                            int width = -2;
179                            try {
180                                width = Integer.parseInt(container.getWidth());
181                            } catch (Exception e) {
182                                LOG.debug(e.getLocalizedMessage(), e);
183                            }
184                            formatter = formatters.getDefaultSchemaFormatter(container.getType(), width);
185                        } catch (CmsException e1) {
186                            if (LOG.isWarnEnabled()) {
187                                LOG.warn(e1.getLocalizedMessage(), e1);
188                            }
189                        } catch (Exception e) {
190                            LOG.error(e.getLocalizedMessage(), e);
191                        }
192                    }
193
194                    final I_CmsFormatterBean finalFormatter = formatter;
195
196                    Map<String, CmsXmlContentProperty> formatterSettingsConfig = OpenCms.getADEManager().getFormatterSettings(
197                        m_cms,
198                        config,
199                        formatter,
200                        element.getResource(),
201                        m_cms.getRequestContext().getLocale(),
202                        null);
203                    Set<String> keys = new HashSet<>(element.getSettings().keySet());
204                    Map<String, CmsJspElementSettingValueWrapper> result = new HashMap<>();
205                    for (String key : keys) {
206                        String value = element.getSettings().get(key);
207                        boolean exists;
208                        if (finalFormatter != null) {
209                            exists = (formatterSettingsConfig.get(key) != null)
210                                || finalFormatter.getSettings(config).containsKey(key);
211                        } else {
212                            exists = value != null;
213                        }
214                        result.put(
215                            key,
216                            new CmsJspElementSettingValueWrapper(m_cms, element.getSettings().get(key), exists));
217                    }
218                    return result;
219                }
220            }
221        }
222        return CmsJspContentAccessBean.CONSTANT_NULL_VALUE_WRAPPER_MAP;
223    }
224
225    public Set<String> getTypes() {
226
227        return m_page.getTypes();
228    }
229
230    /**
231     * Renders the element in the container with the given name or name prefix.
232     * @param context the context bean
233     * @param name the container name or name prefix
234     * @return the rendered HTML
235     */
236    public String renderContainer(CmsJspStandardContextBean context, String name) {
237
238        CmsContainerBean container = findContainer(name);
239        if (container == null) {
240            return null;
241        }
242        return render(context, container);
243    }
244
245    /**
246     * Helper method for locating a container with the given name or name prefix.
247     * @param name the name or name prefix
248     * @return the container, or null if none were found
249     */
250    private CmsContainerBean findContainer(String name) {
251
252        CmsContainerBean result = m_page.getContainers().get(name);
253        if (result == null) {
254            for (Map.Entry<String, CmsContainerBean> entry : m_page.getContainers().entrySet()) {
255                if (entry.getKey().endsWith("-" + name)) {
256                    result = entry.getValue();
257                    break;
258                }
259            }
260        }
261        return result;
262    }
263
264    /**
265     * Renders the elements from the given container as HTML and returns it.
266     *
267     * @param context the context bean
268     * @param container the container whose elements should be rendered
269     * @return the HTML of the container elements, without a surrounding element
270     */
271    private String render(CmsJspStandardContextBean context, CmsContainerBean container) {
272
273        CmsFlexController controller = CmsFlexController.getController(context.getRequest());
274        CmsObject m_cms = context.getCmsObject();
275        CmsContainerBean oldContainer = context.getContainer();
276        CmsContainerElementBean oldElement = context.getRawElement();
277        CmsContainerPageBean oldPage = context.getPage();
278        boolean oldForceDisableEdit = context.isForceDisableEditMode();
279        Locale locale = m_cms.getRequestContext().getLocale();
280        context.getRequest();
281        try {
282            context.setContainer(container);
283            context.setPage(m_page);
284            // The forceDisableEditMode flag may be incorrectly cached in the standard
285            // context bean copies stored in flex cache entries, but it doesn't matter since edit mode is never
286            // active in the Online project anyway
287            context.setForceDisableEditMode(true);
288
289            int containerWidth = -1;
290            try {
291                containerWidth = Integer.parseInt(container.getWidth());
292            } catch (Exception e) {
293                LOG.debug(e.getLocalizedMessage(), e);
294            }
295            CmsADEConfigData adeConfig = context.getSitemapConfigInternal();
296            StringBuilder buffer = new StringBuilder();
297            for (CmsContainerElementBean element : container.getElements()) {
298
299                try {
300                    element.initResource(m_cms);
301                    I_CmsFormatterBean formatterBean = CmsJspTagContainer.ensureValidFormatterSettings(
302                        m_cms,
303                        element,
304                        adeConfig,
305                        container.getName(),
306                        container.getType(),
307                        containerWidth);
308                    element.initSettings(m_cms, adeConfig, formatterBean, locale, controller.getCurrentRequest(), null);
309                    context.setElement(element);
310                    CmsResource formatterRes = m_cms.readResource(
311                        formatterBean.getJspStructureId(),
312                        CmsResourceFilter.IGNORE_EXPIRATION);
313                    byte[] formatterOutput = OpenCms.getResourceManager().getLoader(formatterRes).dump(
314                        m_cms,
315                        formatterRes,
316                        null,
317                        locale,
318                        controller.getCurrentRequest(),
319                        controller.getCurrentResponse());
320                    String encoding = controller.getCurrentResponse().getEncoding();
321                    String formatterOutputStr = new String(formatterOutput, encoding);
322                    buffer.append(formatterOutputStr);
323                } catch (Exception e) {
324                    LOG.error(e.getLocalizedMessage(), e);
325                }
326            }
327            String resultHtml = buffer.toString();
328            return resultHtml;
329        } finally {
330            context.setPage(oldPage);
331            context.setContainer(oldContainer);
332            context.setElement(oldElement);
333            context.setForceDisableEditMode(oldForceDisableEdit);
334        }
335
336    }
337
338}