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.containerpage.CmsDetailOnlyContainerUtil;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.main.CmsException;
035import org.opencms.main.CmsLog;
036import org.opencms.main.OpenCms;
037import org.opencms.util.CmsCollectionsGenericWrapper;
038import org.opencms.util.CmsFileUtil;
039import org.opencms.xml.containerpage.CmsContainerBean;
040import org.opencms.xml.containerpage.CmsContainerElementBean;
041import org.opencms.xml.containerpage.CmsContainerPageBean;
042import org.opencms.xml.containerpage.CmsXmlContainerPage;
043import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
044
045import java.util.ArrayList;
046import java.util.Collections;
047import java.util.List;
048import java.util.Locale;
049import java.util.Map;
050import java.util.Set;
051
052import org.apache.commons.collections.Transformer;
053import org.apache.commons.logging.Log;
054
055import com.google.common.base.Optional;
056import com.google.common.collect.Lists;
057import com.google.common.collect.Sets;
058
059/**
060 * Allows accessing 'attachments' of an XML content via the EL in JSP code, which in OpenCms are defined as the contents of its detail-only containers.
061 */
062public class CmsJspContentAttachmentsBean {
063
064    /** The logger instance for this class. */
065    private static final Log LOG = CmsLog.getLog(CmsJspContentAttachmentsBean.class);
066
067    /** The container page bean. */
068    protected CmsContainerPageBean m_page;
069
070    /** Lazy map from container names to lists of elements. */
071    private Map<String, List<CmsContainerElementBean>> m_byContainer;
072
073    /** Lazy map from type names to lists of elements. */
074    private Map<String, List<CmsContainerElementBean>> m_byType;
075
076    /** Flag which indicates whether this is an empty attachments bean. */
077    private boolean m_undefined;
078
079    /** The CmsObject to read the detail page and the resources on that page. */
080    private CmsObject m_cms;
081
082    /**
083     * Creates an 'undefined' attachments bean.<p>
084     */
085    public CmsJspContentAttachmentsBean() {
086
087        m_page = new CmsContainerPageBean(new ArrayList<CmsContainerBean>());
088        m_undefined = true;
089
090    }
091
092    /**
093     * Initializes this bean with the contents of a detail-only page.<p>
094     *
095     * @param cms the CMS context
096     * @param pageResource the detail-only container page
097     *
098     * @throws CmsException if something goes wrong
099     */
100    public CmsJspContentAttachmentsBean(CmsObject cms, CmsResource pageResource)
101    throws CmsException {
102
103        CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(
104            cms,
105            cms.readFile(pageResource),
106            true,
107            /*nocache=*/true); // using cache causes problems for the EL container rendering feature
108        m_page = xmlContainerPage.getContainerPage(cms);
109        m_cms = cms;
110
111    }
112
113    /**
114     * Gets the list of locales for which attachments / detail-only containers are available.<p>
115     *
116     * @param cms the current CMS context
117     * @param content the content resource
118     *
119     * @return the list of locales for which there are attachments
120     */
121    public static List<String> getAttachmentLocales(CmsObject cms, CmsResource content) {
122
123        List<CmsResource> detailOnlyResources = CmsDetailOnlyContainerUtil.getDetailOnlyResources(cms, content);
124        Set<String> validLocaleNames = Sets.newHashSet();
125        List<String> result = Lists.newArrayList();
126        for (Locale locale : OpenCms.getLocaleManager().getAvailableLocales()) {
127            validLocaleNames.add(locale.toString());
128        }
129        validLocaleNames.add(CmsDetailOnlyContainerUtil.LOCALE_ALL);
130        for (CmsResource resource : detailOnlyResources) {
131            String parent = CmsResource.getParentFolder(resource.getRootPath());
132            String parentName = CmsResource.getName(parent);
133            parentName = CmsFileUtil.removeTrailingSeparator(parentName);
134            if (validLocaleNames.contains(parentName)) {
135                result.add(parentName);
136            }
137        }
138        return result;
139
140    }
141
142    /**
143     * Gets the attachments / detail-only contents for the current page (i.e. cms.getRequestContext().getUri()).<p>
144     *
145     * @param cms the CMS context
146     * @param content the content for which to get the attachments
147     * @return a bean providing access to the attachments for the resource
148     *
149     * @throws CmsException if something goes wrong
150     */
151    public static CmsJspContentAttachmentsBean getAttachmentsForCurrentPage(CmsObject cms, CmsResource content)
152    throws CmsException {
153
154        CmsResource page = cms.readResource(cms.getRequestContext().getUri(), CmsResourceFilter.IGNORE_EXPIRATION);
155        String locale = CmsDetailOnlyContainerUtil.getDetailContainerLocale(
156            cms,
157            cms.getRequestContext().getLocale().toString(),
158            page);
159        Optional<CmsResource> detailOnly = CmsDetailOnlyContainerUtil.getDetailOnlyPage(cms, content, locale);
160        if (detailOnly.isPresent()) {
161            try {
162                return new CmsJspContentAttachmentsBean(cms, detailOnly.get());
163            } catch (Exception e) {
164                LOG.error(e.getLocalizedMessage(), e);
165                return new CmsJspContentAttachmentsBean();
166            }
167        } else {
168            return new CmsJspContentAttachmentsBean();
169        }
170    }
171
172    /**
173     * Loads the attachments for a given content.<p>
174     *
175     * @param cms the CMS context
176     * @param content the content
177     * @param locale the locale
178     *
179     * @return the attachment bean for the given content and locale
180     */
181    public static CmsJspContentAttachmentsBean getAttachmentsForLocale(
182        CmsObject cms,
183        CmsResource content,
184        String locale) {
185
186        Optional<CmsResource> detailOnly = CmsDetailOnlyContainerUtil.getDetailOnlyPage(cms, content, locale);
187        if (!detailOnly.isPresent()) {
188            return new CmsJspContentAttachmentsBean();
189        } else {
190            try {
191                return new CmsJspContentAttachmentsBean(cms, detailOnly.get());
192            } catch (Exception e) {
193                LOG.error(e.getLocalizedMessage(), e);
194                return new CmsJspContentAttachmentsBean();
195            }
196        }
197    }
198
199    /**
200     * Gets lazy map that returns lists of element beans for the container whose name is given as a key.<p>
201     *
202     * @return a lazy map to fetch contents of a container
203     */
204    public Map<String, List<CmsContainerElementBean>> getByContainer() {
205
206        if (m_byContainer == null) {
207            m_byContainer = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
208
209                public Object transform(Object arg0) {
210
211                    String key = (String)arg0;
212                    CmsContainerBean container = m_page.getContainers().get(key);
213                    if (container == null) {
214                        return Collections.emptyList();
215                    } else {
216                        return container.getElements();
217                    }
218                }
219            });
220        }
221        return m_byContainer;
222    }
223
224    /**
225     * Gets lazy map that maps type names to lists of container elements of that type.<p>
226     *
227     * @return a map from type names to lists of container elements
228     */
229    public Map<String, List<CmsContainerElementBean>> getByType() {
230
231        if (m_byType == null) {
232            m_byType = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
233
234                @SuppressWarnings("synthetic-access")
235                public Object transform(Object arg0) {
236
237                    String key = (String)arg0;
238                    List<CmsContainerElementBean> result = Lists.newArrayList();
239                    for (Map.Entry<String, CmsContainerBean> entry : getPage().getContainers().entrySet()) {
240                        CmsContainerBean value = entry.getValue();
241                        for (CmsContainerElementBean element : value.getElements()) {
242                            try {
243                                element.initResource(m_cms);
244                                if (key.equals(element.getTypeName())) {
245                                    result.add(element);
246                                }
247                            } catch (CmsException e) {
248                                LOG.error(
249                                    "Could not initialize resource with site path \""
250                                        + element.getSitePath()
251                                        + "\" to determine the container elements type.",
252                                    e);
253                            }
254                        }
255                    }
256                    return result;
257                }
258            });
259        }
260        return m_byType;
261    }
262
263    /**
264     * Converts this to a container page wrapper.
265     *
266     * @return the container page wrapper
267     */
268    public CmsJspContainerPageWrapper getContainerPage() {
269
270        return new CmsJspContainerPageWrapper(m_page);
271    }
272
273    /**
274     * Gets the set of container names.<p>
275     *
276     * @return the set of container names
277     */
278    public Set<String> getContainers() {
279
280        return Sets.newHashSet(m_page.getContainers().keySet());
281    }
282
283    /**
284     * Returns true if the attachments are undefined.<p>
285     *
286     * @return true if the attachments are undefined
287     */
288    public boolean isUndefined() {
289
290        return m_undefined;
291    }
292
293    /**
294     * Gets the container page bean for the detail-only page.<p>
295     *
296     * @return the container page bean
297     */
298    protected CmsContainerPageBean getPage() {
299
300        return m_page;
301    }
302
303}