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 GmbH & Co. KG, 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.workplace.editors;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.jsp.CmsJspActionElement;
034import org.opencms.jsp.CmsJspNavBuilder;
035import org.opencms.jsp.CmsJspNavElement;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.util.CmsCollectionsGenericWrapper;
040
041import java.io.ByteArrayInputStream;
042import java.io.IOException;
043import java.io.InputStream;
044import java.util.List;
045import java.util.Map;
046import java.util.Properties;
047
048import org.apache.commons.logging.Log;
049
050/**
051 * Provides methods to determine the display options of a workplace editor for the current user.<p>
052 *
053 * On the editor JSP, do the following:
054 * <ul>
055 * <li>get the object instance with <code>OpenCms.getWorkplaceManager().getEditorDisplayOptions()</code>.</li>
056 * <li>get the Properties for the current user with <code>getDisplayOptions(CmsJspActionElement)</code>.</li>
057 * <li>use <code>showElement(key, Properties)</code> to determine if an element is shown.</li>
058 * </ul>
059 *
060 * Define your editor display options in property files located in the VFS folder
061 * <code>/system/config/wysiwyg/</code>.<p>
062 *
063 * Set navigation position property values on the configuration files
064 * and use the permission system to determine which groups and users
065 * should use which configuration file.<p>
066 *
067 * The configuration with the most enabled options should be the first in navigation,
068 * followed by configurations with less enabled options, because
069 * the first file readable for the current user will be used for configuration.<p>
070 *
071 * If no configuration file can be found for the current user,
072 * all display options will be disabled by default.<p>
073 *
074 * @since 6.0.0
075 */
076public class CmsEditorDisplayOptions {
077
078    /** The name of the configuration folder.<p> */
079    public static final String FOLDER_EDITORCONFIGURATION = "wysiwyg/";
080
081    /** Mapping entry name that is used if no mapping is available for the user.<p> */
082    public static final String NO_MAPPING_FOR_USER = "na";
083
084    /** Maximum size of the stored editor configurations.<p> */
085    public static final int SIZE_CONFIGURATIONFILES = 12;
086
087    /** Maximum size of the user editor configuration mappings.<p> */
088    public static final int SIZE_USERENTRIES = 100;
089
090    /** The log object for this class. */
091    private static final Log LOG = CmsLog.getLog(CmsEditorDisplayOptions.class);
092
093    /** Stores all loaded editor configuration options.<p> */
094    private Map<Object, Object> m_loadedConfigurations;
095
096    /** Stores the mappings of users to their configuration options to use.<p> */
097    private Map<Object, Object> m_userMappings;
098
099    /**
100     * Constructor that initializes the editor display options for the workplace.<p>
101     */
102    public CmsEditorDisplayOptions() {
103
104        // initialize members
105        m_userMappings = CmsCollectionsGenericWrapper.createLRUMap(SIZE_USERENTRIES);
106        m_loadedConfigurations = CmsCollectionsGenericWrapper.createLRUMap(SIZE_CONFIGURATIONFILES);
107    }
108
109    /**
110     * Clears the cached user configuration data, casing a reload off all configurations.<p>
111     */
112    public synchronized void clearCache() {
113
114        m_userMappings.clear();
115        m_loadedConfigurations.clear();
116    }
117
118    /**
119     * Reads the editor configuration file valid for the current user and caches the result in a Map.<p>
120     *
121     * The configuration settings of the found file are stored in a Map holding the loaded configuration
122     * with the configuration file name as key.<p>
123     *
124     * The configuration file name to use for the current user is stored in another Map with the user name
125     * as key.<p>
126     *
127     * @param jsp the JSP action element to access the VFS and current user information
128     * @return the display options to use for the current user or null if no display options were found
129     */
130    public Properties getDisplayOptions(CmsJspActionElement jsp) {
131
132        return getDisplayOptions(jsp.getCmsObject());
133    }
134
135    /**
136     * Reads the editor configuration file valid for the current user and caches the result in a Map.<p>
137     *
138     * The configuration settings of the found file are stored in a Map holding the loaded configuration
139     * with the configuration file name as key.<p>
140     *
141     * The configuration file name to use for the current user is stored in another Map with the user name
142     * as key.<p>
143     *
144     * @param cms the CmsObject to access the VFS and current user information
145     * @return the display options to use for the current user or null if no display options were found
146     */
147    public Properties getDisplayOptions(CmsObject cms) {
148
149        // get the configuration file name for the current user
150        String mappedConfigFile = (String)m_userMappings.get(cms.getRequestContext().getCurrentUser().getName());
151        Properties displayOptions;
152        if (mappedConfigFile == null) {
153            // no configuration file name stored for user, get the navigation items of the configuration folder
154            String configFolder = OpenCms.getSystemInfo().getConfigFilePath(cms, FOLDER_EDITORCONFIGURATION);
155            List<CmsJspNavElement> items = new CmsJspNavBuilder(cms).getNavigationForFolder(configFolder);
156            if (items.size() > 0) {
157                // get first found configuration file
158                CmsJspNavElement nav = items.get(0);
159                mappedConfigFile = nav.getFileName();
160                synchronized (m_loadedConfigurations) {
161                    // must sync read/write access to shared map
162                    displayOptions = (Properties)m_loadedConfigurations.get(nav.getFileName());
163                    if (displayOptions == null) {
164                        // configuration file has not yet been loaded, load it
165                        try {
166                            // read configuration file
167                            CmsFile optionFile = cms.readFile(
168                                nav.getResourceName(),
169                                CmsResourceFilter.IGNORE_EXPIRATION);
170                            InputStream in = new ByteArrayInputStream(optionFile.getContents());
171                            displayOptions = new Properties();
172                            displayOptions.load(in);
173                            // store loaded options
174                            m_loadedConfigurations.put(nav.getFileName(), displayOptions);
175                        } catch (CmsException e) {
176                            // set configuration to not available
177                            if (LOG.isInfoEnabled()) {
178                                LOG.info(e.getLocalizedMessage(), e);
179                            }
180                            mappedConfigFile = NO_MAPPING_FOR_USER;
181                        } catch (IOException e) {
182                            // set configuration to not available
183                            if (LOG.isInfoEnabled()) {
184                                LOG.info(e.getLocalizedMessage(), e);
185                            }
186                            mappedConfigFile = NO_MAPPING_FOR_USER;
187                            displayOptions = null;
188                        }
189                    }
190                }
191            } else {
192                // no configuration available for current user, store this in mapping
193                mappedConfigFile = NO_MAPPING_FOR_USER;
194                displayOptions = null;
195            }
196            if (LOG.isDebugEnabled()) {
197                // check which mapping has been stored
198                LOG.debug(
199                    Messages.get().getBundle().key(
200                        Messages.LOG_MAP_CONFIG_FILE_TO_USER_2,
201                        mappedConfigFile,
202                        cms.getRequestContext().getCurrentUser().getName()));
203            }
204            // store the file name of the configuration file for the current user
205            m_userMappings.put(cms.getRequestContext().getCurrentUser().getName(), mappedConfigFile);
206        } else {
207            // configuration file for current user is known, get options from loaded configurations
208            displayOptions = (Properties)m_loadedConfigurations.get(mappedConfigFile);
209        }
210        // return the editor display options for this user
211        return displayOptions;
212    }
213
214    /**
215     * Returns the value for the given key from the display options.<p>
216     *
217     * @param key he element key name which should be read
218     * @param defaultValue the default value to use in case the property is not found
219     * @param displayOptions the display options for the current user
220     *
221     * @return the value for the given key from the display options
222     */
223    public String getOptionValue(String key, String defaultValue, Properties displayOptions) {
224
225        if (displayOptions == null) {
226            return defaultValue;
227        }
228        return displayOptions.getProperty(key, defaultValue);
229    }
230
231    /**
232     * Determines if the given element should be shown in the editor.<p>
233     *
234     * @param key the element key name which should be displayed
235     * @param displayOptions the display options for the current user
236     *
237     * @return true if the element should be shown, otherwise false
238     */
239    public boolean showElement(String key, Properties displayOptions) {
240
241        return showElement(key, null, displayOptions);
242    }
243
244    /**
245     * Determines if the given element should be shown in the editor.<p>
246     *
247     * @param key the element key name which should be displayed
248     * @param defaultValue the default value to use in case the property is not found, should be a boolean value as String
249     * @param displayOptions the display options for the current user
250     *
251     * @return true if the element should be shown, otherwise false
252     */
253    public boolean showElement(String key, String defaultValue, Properties displayOptions) {
254
255        if (defaultValue == null) {
256            return ((displayOptions != null) && Boolean.valueOf(displayOptions.getProperty(key)).booleanValue());
257        }
258        if (displayOptions == null) {
259            return Boolean.valueOf(defaultValue).booleanValue();
260        }
261        return Boolean.valueOf(displayOptions.getProperty(key, defaultValue)).booleanValue();
262    }
263}