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.xml.containerpage;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsElementView;
032import org.opencms.ade.containerpage.shared.CmsContainer;
033import org.opencms.ade.galleries.shared.CmsGallerySearchBean;
034import org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode;
035import org.opencms.configuration.preferences.CmsElementViewPreference;
036import org.opencms.file.CmsObject;
037import org.opencms.jsp.util.CmsJspStandardContextBean.TemplateBean;
038import org.opencms.main.CmsLog;
039import org.opencms.util.CmsUUID;
040import org.opencms.workplace.CmsWorkplace;
041import org.opencms.xml.content.CmsXmlContent;
042
043import java.util.ArrayList;
044import java.util.Arrays;
045import java.util.HashMap;
046import java.util.HashSet;
047import java.util.List;
048import java.util.Map;
049import java.util.Set;
050import java.util.concurrent.ConcurrentHashMap;
051
052import javax.servlet.http.HttpServletRequest;
053
054import org.apache.commons.logging.Log;
055
056/**
057 * ADE's session cache.<p>
058 *
059 * @since 8.0.0
060 */
061public final class CmsADESessionCache {
062
063    /**
064     * Stores information about the container page which was last edited, so we can jump back to it later.<p>
065     */
066    public static class LastPageBean {
067
068        /** The detail id (may be null). */
069        private CmsUUID m_detailId;
070
071        /** The page structure id. */
072        private CmsUUID m_pageId;
073
074        /** The site root. */
075        private String m_siteRoot;
076
077        /**
078         * Creates a new instance.<p>
079         *
080         * @param siteRoot the site root
081         * @param pageId the page id
082         * @param detailId the detail content id (may be null)
083         */
084        public LastPageBean(String siteRoot, CmsUUID pageId, CmsUUID detailId) {
085
086            super();
087            m_siteRoot = siteRoot;
088            m_pageId = pageId;
089            m_detailId = detailId;
090        }
091
092        /**
093         * Returns the detailId.<p>
094         *
095         * @return the detailId
096         */
097        public CmsUUID getDetailId() {
098
099            return m_detailId;
100        }
101
102        /**
103         * Returns the pageId.<p>
104         *
105         * @return the pageId
106         */
107        public CmsUUID getPageId() {
108
109            return m_pageId;
110        }
111
112        /**
113         * Returns the siteRoot.<p>
114         *
115         * @return the siteRoot
116         */
117        public String getSiteRoot() {
118
119            return m_siteRoot;
120        }
121
122    }
123
124    /** Session attribute name constant. */
125    public static final String SESSION_ATTR_ADE_CACHE = "__OCMS_ADE_CACHE__";
126
127    /** The log instance for this class. */
128    private static final Log LOG = CmsLog.getLog(CmsADESessionCache.class);
129
130    /** The list size for recently used formatters. */
131    private static final int RECENT_FORMATTERS_SIZE = 10;
132
133    /** The container elements. */
134    private Map<String, CmsContainerElementBean> m_containerElements;
135
136    /** The current values of dynamically loaded attributes in the Acacia editor. */
137    private Map<String, String> m_dynamicValues;
138
139    /** The current element view id. */
140    private CmsUUID m_elementView;
141
142    /** Flag which controls whether small elements should be shown. */
143    private boolean m_isEditSmallElements;
144
145    /** Bean containing last page info. */
146    private LastPageBean m_lastPage;
147
148    /** The last stored gallery search for the page editor. */
149    private CmsGallerySearchBean m_lastPageEditorGallerySearch;
150
151    /** The recently used formatters by resource type. */
152    private Map<String, List<CmsUUID>> m_recentFormatters = new ConcurrentHashMap<String, List<CmsUUID>>();
153
154    /** The sitemap editor mode. */
155    private EditorMode m_sitemapEditorMode;
156
157    /** Template bean cache. */
158    private Map<String, TemplateBean> m_templateBeanCache = new HashMap<String, TemplateBean>();
159
160    /** The tool-bar visibility flag. */
161    private boolean m_toolbarVisible;
162
163    /** The cached XML content documents by structure id. */
164    private Map<CmsUUID, CmsXmlContent> m_xmlContents;
165
166    /**
167     * Initializes the session cache.<p>
168     *
169     * @param cms the cms context
170     * @param request the current request
171     */
172    protected CmsADESessionCache(CmsObject cms, HttpServletRequest request) {
173
174        // container element cache
175        m_containerElements = new ConcurrentHashMap<String, CmsContainerElementBean>();
176
177        // XML content cache, used during XML content edit
178        m_xmlContents = new ConcurrentHashMap<CmsUUID, CmsXmlContent>();
179
180        String elementView = null;
181        // within the test cases the request will be null
182        if (request != null) {
183            elementView = CmsWorkplace.getWorkplaceSettings(cms, request).getUserSettings().getAdditionalPreference(
184                CmsElementViewPreference.PREFERENCE_NAME,
185                false);
186        }
187        if (elementView == null) {
188            // use the default element view
189            m_elementView = CmsElementView.DEFAULT_ELEMENT_VIEW.getId();
190        } else {
191            try {
192                m_elementView = new CmsUUID(elementView);
193            } catch (NumberFormatException e) {
194                // use the default element view
195                m_elementView = CmsElementView.DEFAULT_ELEMENT_VIEW.getId();
196                LOG.warn("Malformed element view id '" + elementView + "'.", e);
197            }
198        }
199        // toolbar should be visible initially
200        m_toolbarVisible = true;
201    }
202
203    /**
204     * Gets the session cache for the current session.<p>
205     * In case the request is not editable, <code>null</code> will be returned.<p>
206     *
207     * @param request the current request
208     * @param cms the current CMS context
209     *
210     * @return the ADE session cache for the current session
211     */
212    public static CmsADESessionCache getCache(HttpServletRequest request, CmsObject cms) {
213
214        CmsADESessionCache cache = (CmsADESessionCache)request.getSession().getAttribute(
215            CmsADESessionCache.SESSION_ATTR_ADE_CACHE);
216        if (cache == null) {
217            cache = new CmsADESessionCache(cms, request);
218            request.getSession().setAttribute(CmsADESessionCache.SESSION_ATTR_ADE_CACHE, cache);
219        }
220        return cache;
221    }
222
223    /**
224     * Adds the formatter id to the recently used list for the given type.<p>
225     *
226     * @param resType the resource type
227     * @param formatterId the formatter id
228     */
229    public void addRecentFormatter(String resType, CmsUUID formatterId) {
230
231        List<CmsUUID> formatterIds = m_recentFormatters.get(resType);
232        if (formatterIds == null) {
233            formatterIds = new ArrayList<CmsUUID>();
234            m_recentFormatters.put(resType, formatterIds);
235        }
236        formatterIds.remove(formatterId);
237        if (formatterIds.size() >= (RECENT_FORMATTERS_SIZE)) {
238            formatterIds.remove(RECENT_FORMATTERS_SIZE - 1);
239        }
240        formatterIds.add(0, formatterId);
241    }
242
243    /**
244     * Clear the cache values that are dynamically loaded in the Acacia content editor.
245     */
246    public void clearDynamicValues() {
247
248        m_dynamicValues = null;
249    }
250
251    /**
252     * Removes the information about the last edited container page.<p>
253     */
254    public void clearLastPage() {
255
256        m_lastPage = null;
257    }
258
259    /**
260     * Returns the cached container element under the given key.<p>
261     *
262     * @param key the cache key
263     *
264     * @return  the cached container element or <code>null</code> if not found
265     */
266    public CmsContainerElementBean getCacheContainerElement(String key) {
267
268        return m_containerElements.get(key);
269    }
270
271    /**
272     * Returns the cached XML content document.<p>
273     *
274     * @param structureId the structure id
275     *
276     * @return the XML document
277     */
278    public CmsXmlContent getCacheXmlContent(CmsUUID structureId) {
279
280        return m_xmlContents.get(structureId);
281    }
282
283    /**
284     * Get cached value that is dynamically loaded by the Acacia content editor.
285     *
286     * @param attribute the attribute to load the value to
287     * @return the cached value
288     */
289    public String getDynamicValue(String attribute) {
290
291        return null == m_dynamicValues ? null : m_dynamicValues.get(attribute);
292    }
293
294    /**
295     * Returns the current element view id.<p>
296     *
297     * @return the current element view id
298     */
299    public CmsUUID getElementView() {
300
301        return m_elementView;
302    }
303
304    /**
305     * Returns the lastPage.<p>
306     *
307     * @return the lastPage
308     */
309    public LastPageBean getLastPage() {
310
311        return m_lastPage;
312    }
313
314    /**
315     * Returns the lastPageEditorGallerySearch.<p>
316     *
317     * @return the lastPageEditorGallerySearch
318     */
319    public CmsGallerySearchBean getLastPageEditorGallerySearch() {
320
321        return m_lastPageEditorGallerySearch;
322    }
323
324    /**
325     * Returns the least recently used matching formatter for the given resource type.<p>
326     *
327     * @param resType the resource type
328     * @param container the container to match
329     * @param config the config data
330     *
331     * @return the formatter if any
332     */
333    public I_CmsFormatterBean getRecentFormatter(String resType, CmsContainer container, CmsADEConfigData config) {
334
335        I_CmsFormatterBean result = null;
336        List<CmsUUID> formatterIds = m_recentFormatters.get(resType);
337        if (formatterIds != null) {
338            Map<CmsUUID, I_CmsFormatterBean> availableFormatters = config.getActiveFormatters();
339            Set<String> types = new HashSet<String>(Arrays.asList(container.getType().trim().split(" *, *")));
340            for (CmsUUID id : formatterIds) {
341                I_CmsFormatterBean formatter = availableFormatters.get(id);
342                if ((formatter != null)
343                    && CmsFormatterConfiguration.matchFormatter(formatter, types, container.getWidth())) {
344                    result = formatter;
345                    break;
346                }
347            }
348        }
349
350        return result;
351    }
352
353    /**
354     * Returns the sitemap editor mode.<p>
355     *
356     * @return the sitemap editor mode
357     */
358    public EditorMode getSitemapEditorMode() {
359
360        return m_sitemapEditorMode;
361    }
362
363    /**
364     * Gets the cached template bean for a given container page uri.<p>
365     *
366     * @param uri the container page uri
367     * @param safe if true, return a valid template bean even if it hasn't been cached before
368     *
369     * @return the template bean
370     */
371    public TemplateBean getTemplateBean(String uri, boolean safe) {
372
373        TemplateBean templateBean = m_templateBeanCache.get(uri);
374        if ((templateBean != null) || !safe) {
375            return templateBean;
376        }
377        return new TemplateBean("", "");
378    }
379
380    /**
381     * Returns true if, in this session, a newly opened container page editor window should display edit points for
382     * small elements initially.<p>
383     *
384     * @return true if small elements should be editable initially
385     */
386    public boolean isEditSmallElements() {
387
388        return m_isEditSmallElements;
389    }
390
391    /**
392     * Returns the tool-bar visibility.<p>
393     *
394     * @return the tool-bar visibility
395     */
396    public boolean isToolbarVisible() {
397
398        return m_toolbarVisible;
399    }
400
401    /**
402     * Caches the given container element under the given key.<p>
403     *
404     * @param key the cache key
405     * @param containerElement the object to cache
406     */
407    public void setCacheContainerElement(String key, CmsContainerElementBean containerElement) {
408
409        m_containerElements.put(key, containerElement);
410    }
411
412    /**
413     * Caches the given XML content document.<p>
414     *
415     * @param structureId the structure id
416     * @param xmlContent the XML document
417     */
418    public void setCacheXmlContent(CmsUUID structureId, CmsXmlContent xmlContent) {
419
420        m_xmlContents.put(structureId, xmlContent);
421    }
422
423    /**
424     * Set cached value for the attribute. Used for dynamically loaded values in the Acacia content editor.
425     *
426     * @param attribute the attribute for which the value should be cached
427     * @param value the value to cache
428     */
429    public void setDynamicValue(String attribute, String value) {
430
431        if (null == m_dynamicValues) {
432            m_dynamicValues = new ConcurrentHashMap<String, String>();
433        }
434        m_dynamicValues.put(attribute, value);
435    }
436
437    /**
438     * Sets the default initial setting for small element editability in this session.<p>
439     *
440     * @param editSmallElements true if small elements should be initially editable
441     */
442    public void setEditSmallElements(boolean editSmallElements) {
443
444        m_isEditSmallElements = editSmallElements;
445    }
446
447    /**
448     * Sets the current element view id.<p>
449     *
450     * @param elementView the current element view id
451     */
452    public void setElementView(CmsUUID elementView) {
453
454        m_elementView = elementView;
455    }
456
457    /**
458     * Stores information about the last edited container page.<p>
459     *
460     * @param cms the CMS context
461     * @param pageId the page id
462     * @param detailId the detail content id
463     */
464    public void setLastPage(CmsObject cms, CmsUUID pageId, CmsUUID detailId) {
465
466        m_lastPage = new LastPageBean(cms.getRequestContext().getSiteRoot(), pageId, detailId);
467
468    }
469
470    /**
471     * Sets the last stored gallery search from the page editor.<p>
472     *
473     * @param searchObj the search to store
474     */
475    public void setLastPageEditorGallerySearch(CmsGallerySearchBean searchObj) {
476
477        m_lastPageEditorGallerySearch = searchObj;
478    }
479
480    /**
481     * Sets the sitemap editor mode.<p>
482     *
483     * @param sitemapEditorMode the sitemap editor mode
484     */
485    public void setSitemapEditorMode(EditorMode sitemapEditorMode) {
486
487        m_sitemapEditorMode = sitemapEditorMode;
488    }
489
490    /**
491     * Caches a template bean for a given container page URI.<p>
492     *
493     * @param uri the container page uri
494     * @param templateBean the template bean to cache
495     */
496    public void setTemplateBean(String uri, TemplateBean templateBean) {
497
498        m_templateBeanCache.put(uri, templateBean);
499    }
500
501    /**
502     * Sets the tool-bar visibility flag.<p>
503     *
504     * @param toolbarVisible the tool-bar visibility to set
505     */
506    public void setToolbarVisible(boolean toolbarVisible) {
507
508        m_toolbarVisible = toolbarVisible;
509    }
510
511    /**
512     * Purges the XML content document by the given id from the cache.<p>
513     *
514     * @param structureId the structure id
515     */
516    public void uncacheXmlContent(CmsUUID structureId) {
517
518        m_xmlContents.remove(structureId);
519        m_dynamicValues = null;
520    }
521}